<commit_before>var wfjs = (function () {
var wfjs1 = {};

var SVGNS = "http://www.w3.org/2000/svg";

var TEXT_DEFAULT_OPTIONS = {
	fill : "black",
	"font-size" : "1em",
};

var CIRCLE_DEFAULT_ATTRIBUTES = {
	r: 30,
	stroke: "blue",
	"stroke-width": "2",
	fill: "yellow",
	filter: "url(#dropshadow1)",
	style: "cursor: move;",
};

var DIAMOND_DEFAULT_ATTRIBUTES = {
	stroke: "yellow",
	"stroke-width": "2",
	fill: "DarkSeaGreen",
	width : 60,
	height : 60,
	filter: "url(#dropshadow1)",
	style: "cursor: move;",
};

wfjs1.Canvas = (function () {
	var draggingNode = null;
	var draggingNodeOffsetX = 0;
	var draggingNodeOffsetY = 0;

	var _canvasInstance;
    function Canvas(elementId, width, height) {
		_canvasInstance = this;

		var _onMouseUp = function(e){
			if(draggingNode != null){
				draggingNode = null;
			}
		};

		var _onMouseMove = function(e){
			if(draggingNode != null ){
				var x, y;
//				if(e.layerX==undefined){ // Firefox
//					x = e.pageX-$(_canvasInstance.svgElement).offset().left;
//					y = e.pageY-$(_canvasInstance.svgElement).offset().top;
//				}else{ // Chrome
//					x = e.layerX;
//					y = e.layerY;
					x = e.pageX - _canvasInstance.offsetBase().x;
					y = e.pageY - _canvasInstance.offsetBase().y;
//				}

				draggingNode.move(x - draggingNodeOffsetX, y - draggingNodeOffsetY);
				e.stopPropagation();
				e.preventDefault();
//				console.debug("tag:" + e.target.tagName + " screenY: " + e.screenY + " pageY: " + e.pageY + " clientY: " + e.clientY + " offsetY: " + e.offsetY + " e.y:" + e.y + " layerY:" + e.layerY);
			}
		};

        this.elementId = elementId;
		this.targetElement = document.getElementById(elementId);

		this.containerDivElm = document.createElement("div");
		this.containerDivElm.setAttribute("id", "wfjs_container_div");
		this.containerDivElm.setAttribute("style", "margin: 0px; padding 0px; width: " + width + "px; height: " + height + ";");
		this.targetElement.appendChild(this.containerDivElm);
		// $(this.containerDivElm).offset().left --> baseX
		// $(this.containerDivElm).offset().top --> baseY

		var svgElement = document.createElementNS(SVGNS, "svg");
		svgElement.setAttribute("id", "wfjs_svg");
		svgElement.setAttribute("style", "width: " + width + "px; height: " + height + ";");
		svgElement.setAttribute("version", "1.1");
		//svgElement.addEventListener("mouseup", _onMouseUp, false);
//		svgElement.addEventListener("mousemove", _onMouseMove, true);
		document.addEventListener("mouseup", _onMouseUp, false);
		document.addEventListener("mousemove", _onMouseMove, false);

		this.containerDivElm.appendChild(svgElement);
		this.svgElement = svgElement;

		var defsElement = document.createElementNS(SVGNS, "defs");
		svgElement.appendChild(defsElement);

		var filterElement = document.createElementNS(SVGNS, "filter");
		filterElement.setAttribute("id", "dropshadow1");
		defsElement.appendChild(filterElement);

		var feGaussianBlurElement = document.createElementNS(SVGNS, "feGaussianBlur");
		feGaussianBlurElement.setAttribute("in", "SourceAlpha");
		feGaussianBlurElement.setAttribute("stdDeviation", "3");
		filterElement.appendChild(feGaussianBlurElement);

		var feOffsetElement = document.createElementNS(SVGNS, "feOffset");
		feOffsetElement.setAttribute("dx", "2");
		feOffsetElement.setAttribute("dy", "2");
		feOffsetElement.setAttribute("result", "offsetblur");
		filterElement.appendChild(feOffsetElement);

		var feMergeElement = document.createElementNS(SVGNS, "feMerge");
		filterElement.appendChild(feMergeElement);

		var feMergeNodeElement1 = document.createElementNS(SVGNS, "feMergeNode");
		feMergeElement.appendChild(feMergeNodeElement1);

		var feMergeNodeElement2 = document.createElementNS(SVGNS, "feMergeNode");
		feMergeNodeElement2.setAttribute("in", "SourceGraphic");
		feMergeElement.appendChild(feMergeNodeElement2);

		this.nodes = [];
		this.nodes.getById = function(id){
			for(var i = 0; i < this.length; i++){
				if(this[i].id == id){
					return this[i];
				}
			}
			return null;
		}
    } // End of Canvas()

	Canvas.prototype.offsetBase = function(){
		return {
			x: $(this.containerDivElm).offset().left,
			y: $(this.containerDivElm).offset().top,
		};
	};

	Canvas.prototype._onMouseDown = function(e){
		var targetId = e.target.getAttribute("id");
		draggingNode = _canvasInstance.nodes.getById(targetId);
//		if(e.offsetX==undefined){ // Firefox
//			draggingNodeOffsetX = e.pageX-$(_canvasInstance.svgElement).offset().left - draggingNode.y;
//			draggingNodeOffsetY = e.pageY-$(_canvasInstance.svgElement).offset().top - draggingNode.y;
////			draggingNodeOffsetX = e.clientX - draggingNode.x;
////			draggingNodeOffsetY = e.clientY - draggingNode.y;
//		}else{ // Chrome
			draggingNodeOffsetX = e.pageX - _canvasInstance.offsetBase().x - draggingNode.x;
			draggingNodeOffsetY = e.pageY - _canvasInstance.offsetBase().y - draggingNode.y;
//		}
		e.preventDefault();
	};

    return Canvas;
})();

wfjs1.CircleNode = (function () {
	var index = 0;

    function CircleNode(canvas, x, y, label, circle_attrs, text_options) {
		this.id = "wfjs_circle_node_" + index;
		index++;
        this.canvas = canvas;
        this.x = x;
        this.y = y;
        this.label = label;

		this.circle_attrs = {};
		for(var name in CIRCLE_DEFAULT_ATTRIBUTES){
			this.circle_attrs[name] = CIRCLE_DEFAULT_ATTRIBUTES[name];
		}
		for(var name in circle_attrs){
			this.circle_attrs[name] = circle_attrs[name];
		}

        this.text_options = text_options;
		this.flowlines = [];
    };

    CircleNode.prototype.show = function () {

		this.circleElement = document.createElementNS(SVGNS, "circle");
		this.circleElement.setAttribute("id", this.id);

		for(var name in this.circle_attrs){
			this.circleElement.setAttribute(name, this.circle_attrs[name]);
		}

		this.circleElement.addEventListener("mousedown", this.canvas._onMouseDown, false);
		this.canvas.svgElement.appendChild(this.circleElement);

		var textElement = document.createElementNS(SVGNS, "text");

		textElement.textContent = this.label;

		for(var name in TEXT_DEFAULT_OPTIONS){
			textElement.setAttribute(name, TEXT_DEFAULT_OPTIONS[name]);
		}

		if(this.text_options !=null && this.text_options !== undefined){
			for(var name in this.text_options){
				textElement.setAttribute(name, this.text_options[name]);
			}
		}

		this.canvas.svgElement.appendChild(textElement);
		this.labelTextElement = textElement;
		this.canvas.nodes.push(this);

		this.move(this.x, this.y);

    }; // End of show()

    CircleNode.prototype.connectTo = function(node) {
		var flowLine = new wfjs1.FlowLine(this.canvas, this, node);
		this.flowlines.push(flowLine);
		node.flowlines.push(flowLine);

    }; // End of connectTo()

    CircleNode.prototype.move = function(x, y) {
		this.x = x;
		this.y = y;
		this.circleElement.setAttribute("cx", x);
		this.circleElement.setAttribute("cy", y);
		// label position
		var rect = this.labelTextElement.getBBox();
		this.labelTextElement.setAttribute("x", x - (rect.width/2));
		this.labelTextElement.setAttribute("y", y + 5); // TODO what is this magic number?
		for(var i = 0; i < this.flowlines.length; i++){
			this.flowlines[i].relocate();
		}

    }; // End of move()

    return CircleNode;
})(); // End of wfjs1.Node

wfjs1.DiamondNode = (function () {
	var index = 0;

    function DiamondNode(canvas, x, y, label){
		this.id = "wfjs_diamond_node_" + index;
		index++;
		this.canvas = canvas;
        this.x = x;
        this.y = y;
        this.label = label;

		this.rectElement = document.createElementNS(SVGNS, "rect");
		this.rectElement.addEventListener("mousedown", this.canvas._onMouseDown, false);
		this.rectElement.setAttribute("id", this.id);

		for(var name in DIAMOND_DEFAULT_ATTRIBUTES){
			this.rectElement.setAttribute(name, DIAMOND_DEFAULT_ATTRIBUTES[name]);
		}
		this.canvas.svgElement.appendChild(this.rectElement);

		// label
		this.labelTextElement = document.createElementNS(SVGNS, "text");
		this.labelTextElement.textContent = this.label;

		this.canvas.svgElement.appendChild(this.labelTextElement);
		this.canvas.nodes.push(this);

		this.move(x, y);
	};

    DiamondNode.prototype.move = function(x, y) {

		this.rectElement.setAttribute("transform", "rotate(45 " + x + " " + y + ")");
		this.rectElement.setAttribute("x", x);
		this.rectElement.setAttribute("y", y);

		var rect = this.labelTextElement.getBBox();
		this.labelTextElement.setAttribute("x", x - (rect.width/2));
		this.labelTextElement.setAttribute("y", y + 46); // TODO magic number

    }; // End of move()

    return DiamondNode;
})(); // End of wfjs1.DiamondNode

wfjs1.FlowLine = (function () {
	var HEAD_SHAPE_PATH = "l-5 10 l10 0 Z";

    function FlowLine(canvas, startNode, endNode){
		this.canvas = canvas;
		this.startNode = startNode;
		this.endNode = endNode;

		this.lineElement = document.createElementNS(SVGNS, "line");
		this.lineElement.setAttribute("style", "stroke: black;stroke-width:1;");
		this.canvas.svgElement.appendChild(this.lineElement);

		this.arrowPathElement = document.createElementNS(SVGNS, "path");
		this.arrowPathElement.setAttribute("fill", "black");
		this.arrowPathElement.setAttribute("stroke", "black");
		this.canvas.svgElement.appendChild(this.arrowPathElement);

		this.relocate();

	};

    FlowLine.prototype.relocate = function(){
		// The line end should not be the center of the target object, since it needs an arrow mark.
		var startRadian = Math.atan2(this.endNode.y - this.startNode.y, this.endNode.x - this.startNode.x);
		var startX = this.startNode.circle_attrs.r * Math.cos(startRadian) + this.startNode.x;
		var startY = this.startNode.circle_attrs.r * Math.sin(startRadian) + this.startNode.y;
		this.lineElement.setAttribute("x1", startX);
		this.lineElement.setAttribute("y1", startY);

		var endRadian = Math.atan2(this.startNode.y - this.endNode.y, this.startNode.x - this.endNode.x);
		var endX = this.endNode.circle_attrs.r * Math.cos(endRadian) + this.endNode.x;
		var endY = this.endNode.circle_attrs.r * Math.sin(endRadian) + this.endNode.y;
		this.lineElement.setAttribute("x2", endX);
		this.lineElement.setAttribute("y2", endY);

		var endDigree = (endRadian * 180 / Math.PI) - 90;
		this.arrowPathElement.setAttribute("d", "M" + endX + " " + endY + " " + HEAD_SHAPE_PATH);
		this.arrowPathElement.setAttribute("transform", "rotate(" + endDigree + " " + endX + " " + endY + ")");

    }; // End of relocate()

    return FlowLine;
})(); // End of wfjs1.FlowLine

return wfjs1;
})();

<commit_msg>removed comments<commit_after>var wfjs = (function () {
var wfjs1 = {};

var SVGNS = "http://www.w3.org/2000/svg";

var TEXT_DEFAULT_OPTIONS = {
	fill : "black",
	"font-size" : "1em",
};

var CIRCLE_DEFAULT_ATTRIBUTES = {
	r: 30,
	stroke: "blue",
	"stroke-width": "2",
	fill: "yellow",
	filter: "url(#dropshadow1)",
	style: "cursor: move;",
};

var DIAMOND_DEFAULT_ATTRIBUTES = {
	stroke: "yellow",
	"stroke-width": "2",
	fill: "DarkSeaGreen",
	width : 60,
	height : 60,
	filter: "url(#dropshadow1)",
	style: "cursor: move;",
};

wfjs1.Canvas = (function () {
	var draggingNode = null;
	var draggingNodeOffset = {};

	var _canvasInstance;
    function Canvas(elementId, width, height) {
		_canvasInstance = this;

		var _onMouseUp = function(e){
			if(draggingNode != null){
				draggingNode = null;
			}
		};

		var _onMouseMove = function(e){
			if(draggingNode != null ){
				var x = e.pageX - _canvasInstance.offsetBase().x;
				var y = e.pageY - _canvasInstance.offsetBase().y;

				draggingNode.move(x - draggingNodeOffset.x, y - draggingNodeOffset.y);
				e.stopPropagation();
				e.preventDefault();
//				console.debug("tag:" + e.target.tagName + " screenY: " + e.screenY + " pageY: " + e.pageY + " clientY: " + e.clientY + " offsetY: " + e.offsetY + " e.y:" + e.y + " layerY:" + e.layerY);
			}
		};

        this.elementId = elementId;
		this.targetElement = document.getElementById(elementId);

		this.containerDivElm = document.createElement("div");
		this.containerDivElm.setAttribute("id", "wfjs_container_div");
		this.containerDivElm.setAttribute("style", "margin: 0px; padding 0px; width: " + width + "px; height: " + height + ";");
		this.targetElement.appendChild(this.containerDivElm);
		// $(this.containerDivElm).offset().left --> baseX
		// $(this.containerDivElm).offset().top --> baseY

		var svgElement = document.createElementNS(SVGNS, "svg");
		svgElement.setAttribute("id", "wfjs_svg");
		svgElement.setAttribute("style", "width: " + width + "px; height: " + height + ";");
		svgElement.setAttribute("version", "1.1");
		//svgElement.addEventListener("mouseup", _onMouseUp, false);
//		svgElement.addEventListener("mousemove", _onMouseMove, true);
		document.addEventListener("mouseup", _onMouseUp, false);
		document.addEventListener("mousemove", _onMouseMove, false);

		this.containerDivElm.appendChild(svgElement);
		this.svgElement = svgElement;

		var defsElement = document.createElementNS(SVGNS, "defs");
		svgElement.appendChild(defsElement);

		var filterElement = document.createElementNS(SVGNS, "filter");
		filterElement.setAttribute("id", "dropshadow1");
		defsElement.appendChild(filterElement);

		var feGaussianBlurElement = document.createElementNS(SVGNS, "feGaussianBlur");
		feGaussianBlurElement.setAttribute("in", "SourceAlpha");
		feGaussianBlurElement.setAttribute("stdDeviation", "3");
		filterElement.appendChild(feGaussianBlurElement);

		var feOffsetElement = document.createElementNS(SVGNS, "feOffset");
		feOffsetElement.setAttribute("dx", "2");
		feOffsetElement.setAttribute("dy", "2");
		feOffsetElement.setAttribute("result", "offsetblur");
		filterElement.appendChild(feOffsetElement);

		var feMergeElement = document.createElementNS(SVGNS, "feMerge");
		filterElement.appendChild(feMergeElement);

		var feMergeNodeElement1 = document.createElementNS(SVGNS, "feMergeNode");
		feMergeElement.appendChild(feMergeNodeElement1);

		var feMergeNodeElement2 = document.createElementNS(SVGNS, "feMergeNode");
		feMergeNodeElement2.setAttribute("in", "SourceGraphic");
		feMergeElement.appendChild(feMergeNodeElement2);

		this.nodes = [];
		this.nodes.getById = function(id){
			for(var i = 0; i < this.length; i++){
				if(this[i].id == id){
					return this[i];
				}
			}
			return null;
		}
    } // End of Canvas()

	Canvas.prototype.offsetBase = function(){
		return {
			x: $(this.containerDivElm).offset().left,
			y: $(this.containerDivElm).offset().top,
		};
	};

	Canvas.prototype._onMouseDown = function(e){
		var targetId = e.target.getAttribute("id");
		draggingNode = _canvasInstance.nodes.getById(targetId);
		draggingNodeOffset.x = e.pageX - _canvasInstance.offsetBase().x - draggingNode.x;
		draggingNodeOffset.y = e.pageY - _canvasInstance.offsetBase().y - draggingNode.y;
		e.preventDefault();
	};

    return Canvas;
})();

wfjs1.CircleNode = (function () {
	var index = 0;

    function CircleNode(canvas, x, y, label, circle_attrs, text_options) {
		this.id = "wfjs_circle_node_" + index;
		index++;
        this.canvas = canvas;
        this.x = x;
        this.y = y;
        this.label = label;

		this.circle_attrs = {};
		for(var name in CIRCLE_DEFAULT_ATTRIBUTES){
			this.circle_attrs[name] = CIRCLE_DEFAULT_ATTRIBUTES[name];
		}
		for(var name in circle_attrs){
			this.circle_attrs[name] = circle_attrs[name];
		}

        this.text_options = text_options;
		this.flowlines = [];
    };

    CircleNode.prototype.show = function () {

		this.circleElement = document.createElementNS(SVGNS, "circle");
		this.circleElement.setAttribute("id", this.id);

		for(var name in this.circle_attrs){
			this.circleElement.setAttribute(name, this.circle_attrs[name]);
		}

		this.circleElement.addEventListener("mousedown", this.canvas._onMouseDown, false);
		this.canvas.svgElement.appendChild(this.circleElement);

		var textElement = document.createElementNS(SVGNS, "text");

		textElement.textContent = this.label;

		for(var name in TEXT_DEFAULT_OPTIONS){
			textElement.setAttribute(name, TEXT_DEFAULT_OPTIONS[name]);
		}

		if(this.text_options !=null && this.text_options !== undefined){
			for(var name in this.text_options){
				textElement.setAttribute(name, this.text_options[name]);
			}
		}

		this.canvas.svgElement.appendChild(textElement);
		this.labelTextElement = textElement;
		this.canvas.nodes.push(this);

		this.move(this.x, this.y);

    }; // End of show()

    CircleNode.prototype.connectTo = function(node) {
		var flowLine = new wfjs1.FlowLine(this.canvas, this, node);
		this.flowlines.push(flowLine);
		node.flowlines.push(flowLine);

    }; // End of connectTo()

    CircleNode.prototype.move = function(x, y) {
		this.x = x;
		this.y = y;
		this.circleElement.setAttribute("cx", x);
		this.circleElement.setAttribute("cy", y);
		// label position
		var rect = this.labelTextElement.getBBox();
		this.labelTextElement.setAttribute("x", x - (rect.width/2));
		this.labelTextElement.setAttribute("y", y + 5); // TODO what is this magic number?
		for(var i = 0; i < this.flowlines.length; i++){
			this.flowlines[i].relocate();
		}

    }; // End of move()

    return CircleNode;
})(); // End of wfjs1.Node

wfjs1.DiamondNode = (function () {
	var index = 0;

    function DiamondNode(canvas, x, y, label){
		this.id = "wfjs_diamond_node_" + index;
		index++;
		this.canvas = canvas;
        this.x = x;
        this.y = y;
        this.label = label;

		this.rectElement = document.createElementNS(SVGNS, "rect");
		this.rectElement.addEventListener("mousedown", this.canvas._onMouseDown, false);
		this.rectElement.setAttribute("id", this.id);

		for(var name in DIAMOND_DEFAULT_ATTRIBUTES){
			this.rectElement.setAttribute(name, DIAMOND_DEFAULT_ATTRIBUTES[name]);
		}
		this.canvas.svgElement.appendChild(this.rectElement);

		// label
		this.labelTextElement = document.createElementNS(SVGNS, "text");
		this.labelTextElement.textContent = this.label;

		this.canvas.svgElement.appendChild(this.labelTextElement);
		this.canvas.nodes.push(this);

		this.move(x, y);
	};

    DiamondNode.prototype.move = function(x, y) {

		this.rectElement.setAttribute("transform", "rotate(45 " + x + " " + y + ")");
		this.rectElement.setAttribute("x", x);
		this.rectElement.setAttribute("y", y);

		var rect = this.labelTextElement.getBBox();
		this.labelTextElement.setAttribute("x", x - (rect.width/2));
		this.labelTextElement.setAttribute("y", y + 46); // TODO magic number

    }; // End of move()

    return DiamondNode;
})(); // End of wfjs1.DiamondNode

wfjs1.FlowLine = (function () {
	var HEAD_SHAPE_PATH = "l-5 10 l10 0 Z";

    function FlowLine(canvas, startNode, endNode){
		this.canvas = canvas;
		this.startNode = startNode;
		this.endNode = endNode;

		this.lineElement = document.createElementNS(SVGNS, "line");
		this.lineElement.setAttribute("style", "stroke: black;stroke-width:1;");
		this.canvas.svgElement.appendChild(this.lineElement);

		this.arrowPathElement = document.createElementNS(SVGNS, "path");
		this.arrowPathElement.setAttribute("fill", "black");
		this.arrowPathElement.setAttribute("stroke", "black");
		this.canvas.svgElement.appendChild(this.arrowPathElement);

		this.relocate();

	};

    FlowLine.prototype.relocate = function(){
		// The line end should not be the center of the target object, since it needs an arrow mark.
		var startRadian = Math.atan2(this.endNode.y - this.startNode.y, this.endNode.x - this.startNode.x);
		var startX = this.startNode.circle_attrs.r * Math.cos(startRadian) + this.startNode.x;
		var startY = this.startNode.circle_attrs.r * Math.sin(startRadian) + this.startNode.y;
		this.lineElement.setAttribute("x1", startX);
		this.lineElement.setAttribute("y1", startY);

		var endRadian = Math.atan2(this.startNode.y - this.endNode.y, this.startNode.x - this.endNode.x);
		var endX = this.endNode.circle_attrs.r * Math.cos(endRadian) + this.endNode.x;
		var endY = this.endNode.circle_attrs.r * Math.sin(endRadian) + this.endNode.y;
		this.lineElement.setAttribute("x2", endX);
		this.lineElement.setAttribute("y2", endY);

		var endDigree = (endRadian * 180 / Math.PI) - 90;
		this.arrowPathElement.setAttribute("d", "M" + endX + " " + endY + " " + HEAD_SHAPE_PATH);
		this.arrowPathElement.setAttribute("transform", "rotate(" + endDigree + " " + endX + " " + endY + ")");

    }; // End of relocate()

    return FlowLine;
})(); // End of wfjs1.FlowLine

return wfjs1;
})();

<|endoftext|>


<commit_before>var ozController = {

  map: null,
  base_layer: null,
  oz_layer: null,

  api_url: '/api/adoptions/?format=json',
  adoptions: {},

  oz_url_lq: 'https://services1.arcgis.com/DnZ5orhsUGGdUZ3h/arcgis/rest/services/OZLowRes2/FeatureServer/0',
  oz_url_hq: 'https://services1.arcgis.com/DnZ5orhsUGGdUZ3h/ArcGIS/rest/services/OZ2014/FeatureServer/0',
  oz_url: this.oz_url_lq,

  lq_oz_start_at_level: 1,
  hq_oz_start_at_level: 1,

  init: function(){

    this.fetchAdoptions();

    this.map = L.map('map', {
      minZoom: 2
    }).setView([52.3667, 4.9000], 7),
    // }).setView([0, 0], 2), //center

    // load basemap
    // this.base_layer = L.esri.basemapLayer('DarkGray').addTo(this.map) ;

    this.map.on('zoomend', $.proxy(this.zoomend, this)) ;

    // this.addOzLayer() ;
    this.setAppropiateOzQuality() ;

    return this ;

  },

  fetchAdoptions: function(){

    // fetching from the api
    // proxy to retain 'this' context
    $.getJSON(this.api_url, $.proxy(function(response){

      /*
        reformatting data like below for quick access when rendering the map
        {
          'NLD-NOH': 2013,
          'NLD-FRI': 2020
        }
      */
      for (var i = response.results.length - 1; i >= 0; i--) {

        //shortcut for clarity
        var adoption = response.results[i] ;
        this.adoptions[adoption['worldid']] = adoption['targetyear'];

      };

    }, this));

  },

  addOzLayer: function(){

    this.oz_layer = L.esri.featureLayer(this.oz_url, {

      // only load specific fields to reduce redundant data being sent back and forth
      // 'WorldID', 'OBJECTID' are for HQ OZ2014 layer
      // ''OBJECTID_1', 'World', 'FID' are for LQ OZ2008 layer
      fields: ['WorldID', 'OBJECTID', 'OBJECTID_1', 'World', 'FID'],

      // esri's library smartly 'uglifies' polygons for us for performance
      // reduce for prettier maps, increase for faster ugly
      simplifyFactor: 1,

      // initially style adopted oz's + give world id class name for later lookup
      style: $.proxy(function(feature){

        // give it a class name we can look up later
        var style = {
          // eg; oz-NLD-NOH
          className: 'oz-' + feature.properties.WorldID
        } ;

        // if oz is adopted already, give class
        if(this.adoptions.hasOwnProperty(feature.properties.WorldID)){
          style['className'] = style['className'] + ' adopted-1'
        }

        return style ;

      }, this)

    });

    this.oz_layer.addTo(this.map);

  },

  removeOzLayer: function(){
    if(this.oz_layer !== null){
      this.map.removeLayer(this.oz_layer) ;
    }
  },

  reloadOzLayer: function(){
    this.removeOzLayer() ;
    this.addOzLayer() ;
  },

  zoomend: function(ev){
    this.setAppropiateOzQuality() ;
  },

  setAppropiateOzQuality: function(){

    var z = this.map.getZoom() ;

    if(z >= this.lq_oz_start_at_level
    && z < this.hq_oz_start_at_level) {

      //only if something changes, or else pointless/ugly reload
      if(this.oz_url !== this.oz_url_lq) {
        this.oz_url = this.oz_url_lq ;
        this.reloadOzLayer() ;
      }

    }

    if(z >= this.hq_oz_start_at_level) {

      //only if this is new
      if(this.oz_url !== this.oz_url_hq) {
        this.oz_url = this.oz_url_hq ;
        this.reloadOzLayer() ;
      }

    }

    //#yolo!
    if(z < this.lq_oz_start_at_level) {
      this.oz_url = null ;
      this.removeOzLayer() ;
    }

  }

} ;<commit_msg>moves focus to turkey + enables base map again!<commit_after>var ozController = {

  map: null,
  base_layer: null,
  oz_layer: null,

  api_url: '/api/adoptions/?format=json',
  adoptions: {},

  oz_url_lq: 'https://services1.arcgis.com/DnZ5orhsUGGdUZ3h/arcgis/rest/services/OZLowRes2/FeatureServer/0',
  oz_url_hq: 'https://services1.arcgis.com/DnZ5orhsUGGdUZ3h/ArcGIS/rest/services/OZ2014/FeatureServer/0',
  oz_url: this.oz_url_lq,

  lq_oz_start_at_level: 1,
  hq_oz_start_at_level: 1,

  init: function(){

    this.fetchAdoptions();

    this.map = L.map('map', {
      minZoom: 2
    // }).setView([52.3667, 4.9000], 7), // amsterdam
    }).setView([39.9167, 32.8333], 5), // turkey
    // }).setView([0, 0], 2), //center

    // load basemap
    this.base_layer = L.esri.basemapLayer('DarkGray').addTo(this.map) ;

    this.map.on('zoomend', $.proxy(this.zoomend, this)) ;

    // this.addOzLayer() ;
    this.setAppropiateOzQuality() ;

    return this ;

  },

  fetchAdoptions: function(){

    // fetching from the api
    // proxy to retain 'this' context
    $.getJSON(this.api_url, $.proxy(function(response){

      /*
        reformatting data like below for quick access when rendering the map
        {
          'NLD-NOH': 2013,
          'NLD-FRI': 2020
        }
      */
      for (var i = response.results.length - 1; i >= 0; i--) {

        //shortcut for clarity
        var adoption = response.results[i] ;
        this.adoptions[adoption['worldid']] = adoption['targetyear'];

      };

    }, this));

  },

  addOzLayer: function(){

    this.oz_layer = L.esri.featureLayer(this.oz_url, {

      // only load specific fields to reduce redundant data being sent back and forth
      // 'WorldID', 'OBJECTID' are for HQ OZ2014 layer
      // ''OBJECTID_1', 'World', 'FID' are for LQ OZ2008 layer
      fields: ['WorldID', 'OBJECTID', 'OBJECTID_1', 'World', 'FID'],

      // esri's library smartly 'uglifies' polygons for us for performance
      // reduce for prettier maps, increase for faster ugly
      simplifyFactor: 1,

      // initially style adopted oz's + give world id class name for later lookup
      style: $.proxy(function(feature){

        // give it a class name we can look up later
        var style = {
          // eg; oz-NLD-NOH
          className: 'oz-' + feature.properties.WorldID
        } ;

        // if oz is adopted already, give class
        if(this.adoptions.hasOwnProperty(feature.properties.WorldID)){
          style['className'] = style['className'] + ' adopted-1'
        }

        return style ;

      }, this)

    });

    this.oz_layer.addTo(this.map);

  },

  removeOzLayer: function(){
    if(this.oz_layer !== null){
      this.map.removeLayer(this.oz_layer) ;
    }
  },

  reloadOzLayer: function(){
    this.removeOzLayer() ;
    this.addOzLayer() ;
  },

  zoomend: function(ev){
    this.setAppropiateOzQuality() ;
  },

  setAppropiateOzQuality: function(){

    var z = this.map.getZoom() ;

    if(z >= this.lq_oz_start_at_level
    && z < this.hq_oz_start_at_level) {

      //only if something changes, or else pointless/ugly reload
      if(this.oz_url !== this.oz_url_lq) {
        this.oz_url = this.oz_url_lq ;
        this.reloadOzLayer() ;
      }

    }

    if(z >= this.hq_oz_start_at_level) {

      //only if this is new
      if(this.oz_url !== this.oz_url_hq) {
        this.oz_url = this.oz_url_hq ;
        this.reloadOzLayer() ;
      }

    }

    //#yolo!
    if(z < this.lq_oz_start_at_level) {
      this.oz_url = null ;
      this.removeOzLayer() ;
    }

  }

} ;<|endoftext|>


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

    angular.module('myApp.view1')
    .controller('View1Controller', [
        'TodosService',
        View1Controller]);

    function View1Controller (
        TodosService
    ) {
        var vm = this;
        vm.listTitle = 'All the things';

        //Public Functions
        vm.getItems = getItems;
        vm.getAllItems = getAllItems;
        vm.getItem = getItem;

        function init() {
            getAllItems();
        }

        /**
         * Get all available To-Do Items
         */
        function getAllItems() {
            TodosService.getTodoItems().then(function(todos) {
                vm.todos = todos;
                vm.listTitle = 'All ' + todos.length + ' things';
            });
        }

        /**
         * Get items completed or not
         * @param done - If true get completed items
         */
        function getItems(done) {
            TodosService.getTodoItems().then(function(todos) {
                vm.todos = _filterTodoItems(todos, done);
                vm.listTitle = vm.todos.length + ' things ' + (done?'Completed':'to do');
            });
        }

        /**
         * Get a specific item by id
         * @param id
         */
        function getItem(id) {
            TodosService.getTodoItem(id).then(
                function(item) {
                    vm.currentItem = item;
                },
                function(err) {
                    vm.error = err;
                }
            );
        }

        /**
         * Filter out To-Do items that are completed or pending
         * @param todoList Full list of items
         * @param completed Y or N
         * @returns {Array} Filtered list
         * @private
         */
        function _filterTodoItems(todoList, completed) {
            var todoListOut = [];
            for (var todo in todoList) {
                if (todoList[todo].completed === completed) {
                    todoListOut.push(todoList[todo]);
                }
            }
            return todoListOut;
        }

        init();

    }

}());<commit_msg>unit-test-workshop-04-4<commit_after>(function() {
    'use strict';

    angular.module('myApp.view1')
    .controller('View1Controller', [
        'TodosService',
        View1Controller]);

    function View1Controller (
        TodosService
    ) {
        var vm = this;
        vm.listTitle = 'All the things';

        //Public Functions
        vm.getItems = getItems;
        vm.getAllItems = getAllItems;
        vm.getItem = getItem;
        vm._init = init;

        function init() {
            getAllItems();
        }

        /**
         * Get all available To-Do Items
         */
        function getAllItems() {
            TodosService.getTodoItems().then(function(todos) {
                vm.todos = todos;
                vm.listTitle = 'All ' + todos.length + ' things';
            });
        }

        /**
         * Get items completed or not
         * @param done - If true get completed items
         */
        function getItems(done) {
            TodosService.getTodoItems().then(function(todos) {
                vm.todos = _filterTodoItems(todos, done);
                vm.listTitle = vm.todos.length + ' things ' + (done?'Completed':'to do');
            });
        }

        /**
         * Get a specific item by id
         * @param id
         */
        function getItem(id) {
            TodosService.getTodoItem(id).then(
                function(item) {
                    vm.currentItem = item;
                },
                function(err) {
                    vm.error = err;
                }
            );
        }

        /**
         * Filter out To-Do items that are completed or pending
         * @param todoList Full list of items
         * @param completed Y or N
         * @returns {Array} Filtered list
         * @private
         */
        function _filterTodoItems(todoList, completed) {
            var todoListOut = [];
            for (var todo in todoList) {
                if (todoList[todo].completed === completed) {
                    todoListOut.push(todoList[todo]);
                }
            }
            return todoListOut;
        }

        init();

    }

}());<|endoftext|>


<commit_before>var WebSocketServer = require('ws').Server;
var Redis = require('redis');

var port = 8078;

var wss = new WebSocketServer({port: port});

wss.on('connection', function(ws) {
  // console.log("New websockets connection");
  ws.on('message', function(message) {
    var redis = Redis.createClient(6379, 'localhost');
    var channel = 'webmention.io::' + message;
    redis.subscribe(channel);
    // console.log('Listening for comments on channel ' + channel);
    redis.on('message', function (channel, message) {
      console.log('Sent comment to channel ' + channel);
      ws.send(message);
    });
    ws.on('close', function(){
      // console.log('Killing listener for channel ' + channel);
      redis.unsubscribe();
      redis.end();
    });
    ws.on('error', function(){
      // console.log('Killing listener for channel ' + channel);
      redis.unsubscribe();
      redis.end();
    });
  });
});

console.log("WebSocket Server Listening on port "+port);
<commit_msg>update streaming port<commit_after>var WebSocketServer = require('ws').Server;
var Redis = require('redis');

var port = 8080;

var wss = new WebSocketServer({port: port});

wss.on('connection', function(ws) {
  // console.log("New websockets connection");
  ws.on('message', function(message) {
    var redis = Redis.createClient(6379, 'localhost');
    var channel = 'webmention.io::' + message;
    redis.subscribe(channel);
    // console.log('Listening for comments on channel ' + channel);
    redis.on('message', function (channel, message) {
      console.log('Sent comment to channel ' + channel);
      ws.send(message);
    });
    ws.on('close', function(){
      // console.log('Killing listener for channel ' + channel);
      redis.unsubscribe();
      redis.end();
    });
    ws.on('error', function(){
      // console.log('Killing listener for channel ' + channel);
      redis.unsubscribe();
      redis.end();
    });
  });
});

console.log("WebSocket Server Listening on port "+port);
<|endoftext|>


<commit_before>"use strict";
const NodeMw = require('nodemw');
const striptags = require('striptags');
const regExpEscape = require('escape-regexp');

const MIX_CONTEXT_LENGTH = 20;

/**
 * Create a regular expression that matches uses of a set of templates.
 */
const createTemplateRe = templateNames =>
    new RegExp(`\{\{(${templateNames.map(regExpEscape).join('|') })[^]*?\}\}`, 'gi');

/**
 * Try to remove links from wikimarkup.
 */
const removeLinks = markup =>
    markup
        .replace(/\[\[(.+?)\]\]/g, (match, content) => content)
        .replace(/\[\[(.+?)\|(.+?)\]/g, (match, content) => content)
    // ref tags
        .replace(/\n?<ref.*?>[^]+?(<\/ref>|$)/gi, '')
        .replace(/(^|\n?<ref.*?>)[^]+?<\/ref>/gi, '')
    // comment tags
        .replace(/<!--[^]*?-->/gi, '')

    // Markup
        .replace(/''/g, '')
        .replace(/""/g, '');

const char_re = /[\w \.\(\),;:'";]/;

/**
 * Try to extract a string matching context around a dubious tag.
 *
 * Wikimedia doesn't actually mark what the dubious part of a statement is, and these
 * tags get stripped out from the plaintext. The context tries to find some text around
 * the dubious statement using the markup, which can be later matched against the
 * plaintext.
 */
const reverseContext = (text, index) => {
    const out = [];
    const sampleSize = 500;
    const sample = removeLinks(text.substr(index - sampleSize, sampleSize));
    for (let i = sample.length - 1; i >= 0; --i) {
        const c = sample[i];
        if (!c.match(char_re))
            break;
        out.unshift(c);
    }
    return out.join('');
};

var forwardContext = (text, index) => {
    const out = [];
    const sampleSize = 500;
    const sample = removeLinks(text.substr(index, sampleSize));
    for (let i = 0; i < sample.length; ++i) {
        const c = sample[i];
        if (!c.match(char_re))
            break;
        out.push(c);
    }
    return out.join('');
};

const context = (text, match) => {
    let pre = reverseContext(text, match.index);
    let post = forwardContext(text, match.index + match[0].length);

    const matchLength = 50;
    pre = pre.substr(Math.min(matchLength, pre.length));
    post = post.substr(0, Math.min(matchLength, post.length));
    return {
        pre: pre,
        post: post,
        length: pre.length + post.length
    };
};

/**
 *
 */
const extractDubiousness = (text, index) => {
    const out = [];
    let last;
    let inSentance = false;
    // Extract the leading part of the sentance.
    for (let i = index; i >= 0; --i) {
        const c = text[i];
        if (c === '\n')
            break;
        if (c === '.' && last === ' ') {
            if (inSentance)
                break;
            inSentance = true;
        }
        inSentance = inSentance || c.match(/\w/);
        out.unshift(c);
        last = c;
    }
    if (out.length === 0)
        return '';

    out.push('*');
    // If in the middle of a sentence, try extracting the rest.
    if (text[index] !== '.' && text[index - 1] !== '.') {
        for (let i = index + 1; i < text.length; ++i) {
            const c = text[i];
            if (c === '\n')
                break;
            if (c === '.') {
                out.push(c);
                break;
            }
            out.push(c);
        }
    }
    return out.join('');
};

/**
 * Find titles of articles that use a given template.
 */
exports.searchForTemplate = (client, templateName, start, count) =>
    new Promise((resolve, reject) =>
        client.api.call({
            action: 'query',
            format: 'json',
            list: 'search',
            srsearch: `hastemplate:"${templateName}"`,
            sroffset: start,
            srlimit: count
        }, (err, data) => {
            if (err)
                return reject(err);
            if (!data || !data.search)
                return reject('No data returned');
            return resolve(data.search.map(x => x.title));
        }));

/**
 * Get the wiki markup content of an article
 */
const getArticle = (client, title) =>
    new Promise((resolve, reject) =>
        client.getArticle(title, (err, data) => {
            if (err)
                return reject(err);
            return resolve(data);
        }));

/**
 * Try to extract the text contexts of a set of template usages.
 */
const getContexts = (title, templateNames, content) => {
    const out = [];
    const re = createTemplateRe(templateNames);
    let m;
    while (m = re.exec(content)) {
        const target = context(content, m);
        if (target.length > MIX_CONTEXT_LENGTH)
            out.push(target);
    }
    return out;
};

/**
 * Get a text representation of a rendered article.
 */
const getArticleText = (client, title) =>
    new Promise((resolve, reject) =>
        client.api.call({
            action: 'query',
            prop: 'extracts',
            rvprop: 'content',
            titles: title
        }, (err, data) => {
            if (err)
                return reject(err);

            const pages = data && data.pages;
            if (!pages)
                return reject('no page');

            const page = Object.keys(pages)[0];
            if (!page)
                return reject('no page');

            return resolve(pages[page].extract);
        }));

/**
 * Clean up text for output.
 */
const normalizeOutput = text =>
    striptags(text)
        .trim()
        .replace(/^[^\w\*]+/g, '')
        .replace(/[^\w|)|\.\*]+$/g, '');

/**
 * Get all sentances for a given template's usage.
 */
exports.getTemplateUsages = (client, title, templateNames) =>
    getArticle(client, title).then(data => {
        const contexts = getContexts(title, templateNames, data);
        if (!contexts.length)
            return { title: title, usages: [] };

        return getArticleText(client, title).then(data => {
            data = normalizeOutput(data);
            const out = []
            for (let target of contexts) {
                const index = data.indexOf(target.pre + target.post);
                if (index === -1)
                    continue;
                const text = extractDubiousness(data, index - 1 + target.pre.length);
                out.push(normalizeOutput(text));
            }
            return { title: title, usages: out };
        })
    });
<commit_msg>Split out wiki logic and fixing a few bugs<commit_after>"use strict";
const NodeMw = require('nodemw');
const striptags = require('striptags');
const regExpEscape = require('escape-regexp');

const MIX_CONTEXT_LENGTH = 20;

/**
 * Create a regular expression that matches uses of a set of templates.
 */
const createTemplateRe = templateNames =>
    new RegExp(`\{\{(${templateNames.map(regExpEscape).join('|') })[^]*?\}\}`, 'gi');

/**
 * Try to remove links from wikimarkup.
 */
const removeLinks = markup =>
    markup
        .replace(/\[\[(.+?)\]\]/g, (match, content) => content)
        .replace(/\[\[(.+?)\|(.+?)\]/g, (match, content) => content)
    // ref tags
        .replace(/\n?<ref.*?>[^]+?(<\/ref>|$)/gi, '')
        .replace(/(^|\n?<ref.*?>)[^]+?<\/ref>/gi, '')
    // comment tags
        .replace(/<!--[^]*?-->/gi, '')

    // Markup
        .replace(/''/g, '')
        .replace(/""/g, '');

const char_re = /[\w \.\(\),;:'";]/;

/**
 * Try to extract a string matching context around a dubious tag.
 *
 * Wikimedia doesn't actually mark what the dubious part of a statement is, and these
 * tags get stripped out from the plaintext. The context tries to find some text around
 * the dubious statement using the markup, which can be later matched against the
 * plaintext.
 */
const reverseContext = (text, index) => {
    const out = [];
    const sampleSize = 500;
    const sample = removeLinks(text.substr(index - sampleSize, sampleSize));
    for (let i = sample.length - 1; i >= 0; --i) {
        const c = sample[i];
        if (!c.match(char_re))
            break;
        out.unshift(c);
    }
    return out.join('');
};

var forwardContext = (text, index) => {
    const out = [];
    const sampleSize = 500;
    const sample = removeLinks(text.substr(index, sampleSize));
    for (let i = 0; i < sample.length; ++i) {
        const c = sample[i];
        if (!c.match(char_re))
            break;
        out.push(c);
    }
    return out.join('');
};

const context = (text, match) => {
    let pre = reverseContext(text, match.index);
    let post = forwardContext(text, match.index + match[0].length);

    const matchLength = 50;
    pre = pre.substr(Math.min(matchLength, pre.length));
    post = post.substr(0, Math.min(matchLength, post.length));
    return {
        pre: pre,
        post: post,
        length: pre.length + post.length
    };
};

/**
 *
 */
const extractDubiousness = (text, index) => {
    const out = [];
    let last;
    let inSentance = false;
    // Extract the leading part of the sentance.
    for (let i = index; i >= 0; --i) {
        const c = text[i];
        if (c === '\n')
            break;
        if (c === '.' && last === ' ') {
            if (inSentance)
                break;
            inSentance = true;
        }
        inSentance = inSentance || c.match(/\w/);
        out.unshift(c);
        last = c;
    }
    if (out.length === 0)
        return '';

    out.push('*');
    // If in the middle of a sentence, try extracting the rest.
    if (text[index] !== '.' && text[index - 1] !== '.') {
        for (let i = index + 1; i < text.length; ++i) {
            const c = text[i];
            if (c === '\n')
                break;
            if (c === '.') {
                out.push(c);
                break;
            }
            out.push(c);
        }
    }
    return out.join('');
};

/**
 * Find titles of articles that use a given template.
 */
exports.searchForTemplate = (client, templateName, start, count) =>
    new Promise((resolve, reject) =>
        client.api.call({
            action: 'query',
            format: 'json',
            list: 'search',
            srsearch: `hastemplate:"${templateName}"`,
            sroffset: start,
            srlimit: count
        }, (err, data) => {
            if (err)
                return reject(err);
            if (!data || !data.search)
                return reject('No data returned');
            return resolve(data.search.map(x => x.title));
        }));

/**
 * Get the wiki markup content of an article
 */
const getArticle = (client, title) =>
    new Promise((resolve, reject) =>
        client.getArticle(title, (err, data) => {
            if (err)
                return reject(err);
            return resolve(data);
        }));

/**
 * Try to extract the text contexts of a set of template usages.
 */
const getContexts = (title, templateNames, content) => {
    const out = [];
    const re = createTemplateRe(templateNames);
    let m;
    while (m = re.exec(content)) {
        const target = context(content, m);
        if (target.length > MIX_CONTEXT_LENGTH)
            out.push(target);
    }
    return out;
};

/**
 * Get a text representation of a rendered article.
 */
const getArticleText = (client, title) =>
    new Promise((resolve, reject) =>
        client.api.call({
            action: 'query',
            prop: 'extracts',
            rvprop: 'content',
            titles: title
        }, (err, data) => {
            if (err)
                return reject(err);

            const pages = data && data.pages;
            if (!pages)
                return reject('no page');

            const page = Object.keys(pages)[0];
            if (!page)
                return reject('no page');

            return resolve(pages[page].extract);
        }));

/**
 * Clean up text for output.
 */
const normalizeOutput = text =>
    striptags(text)
        .trim()
        .replace(/^[^\w\*]+/g, '')
        .replace(/[^\w|)|\.\*]+$/g, '');

/**
 * Get all sentances for a given template's usage.
 */
exports.getTemplateUsages = (client, title, templateNames) =>
    getArticle(client, title).then(data => {
        const contexts = getContexts(title, templateNames, data);
        if (!contexts.length)
            return { title: title, usages: [] };

        return getArticleText(client, title).then(data => {
            data = normalizeOutput(data);console.log(data);
            const out = []
            for (let target of contexts) {
                const index = data.indexOf(target.pre + target.post);
                if (index === -1)
                    continue;
                const text = extractDubiousness(data, index - 1 + target.pre.length);
                out.push(normalizeOutput(text));
            }
            return { title: title, usages: out };
        })
    });
<|endoftext|>


<commit_before>import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { tracked } from '@glimmer/tracking';

class Field {
  @tracked name;
  @tracked class;
}

export default class ClassFieldDescription extends Component {
  @service
  legacyModuleMappings;

  field = new Field();

  get hasImportExample() {
    return this.legacyModuleMappings.hasFunctionMapping(
      this.field.name,
      this.field.class
    );
  }

  /**
   * Callback for updating the anchor with the field name that was clicked by a user.
   *
   * @method updateAnchor
   * @method fieldName String The name representing the field that was clicked.
   */
  updateAnchor() {}
}
<commit_msg>Remove @tracked as it is not used<commit_after>import { inject as service } from '@ember/service';
import Component from '@ember/component';

export default class ClassFieldDescription extends Component {
  @service
  legacyModuleMappings;

  get hasImportExample() {
    return this.legacyModuleMappings.hasFunctionMapping(
      this.args.field.name,
      this.args.field.class
    );
  }

  /**
   * Callback for updating the anchor with the field name that was clicked by a user.
   *
   * @method updateAnchor
   * @method fieldName String The name representing the field that was clicked.
   */
  updateAnchor() {}
}
<|endoftext|>


<commit_before>/*
 * HomePage
 * This is the first thing users see of our App
 */

import { asyncChangeProjectName, asyncChangeOwnerName } from './HomePage.actions';
import React from 'react';
import { connect } from 'react-redux';

import Button from '../../components/Button/Button.react';

import styles from './HomePage.css';

class HomePage extends React.Component {

  constructor() {
    this.onChangeProjectName = this.onChangeProjectName.bind(this);
    this.onChangeOwnerName = this.onChangeOwnerName.bind(this);
  }

  onChangeProjectName() {
    this.props.dispatch(asyncChangeProjectName(this.props.projectName));
  }

  onChangeOwnerName() {
    this.props.dispatch(asyncChangeOwnerName(this.props.ownerName));
  }

  render() {
    const { projectName, ownerName } = this.props.data;
    return (

Hello World!

This is the demo for the { projectName } by
          <a href={`https://twitter.com/${ownerName}`}className={styles.link} >@{ ownerName }

        Change to your project name:
          <input className={styles.input} type="text"
            onChange={this.onChangeProjectName()}
            defaultValue="React.js Boilerplate" value={projectName}
          />

        Change to your name:
          <input className={styles.input} type="text"
            onChange={this.onChangeOwnerName()}
            defaultValue="mxstbr" value={ownerName}
          />

        <Button route="/readme">Setup</Button>

    );
  }
}

// REDUX STUFF

// Which props do we want to inject, given the global state?
function select(state) {
  return {
    data: state
  };
}

// Wrap the component to inject dispatch and state into it
export default connect(select)(HomePage);
<commit_msg>Fix HomePage<commit_after>/*
 * HomePage
 * This is the first thing users see of our App
 */

import { asyncChangeProjectName, asyncChangeOwnerName } from './HomePage.actions';
import React from 'react';
import { connect } from 'react-redux';

import Button from '../../components/Button/Button.react';

import styles from './HomePage.css';

class HomePage extends React.Component {
  constructor() {
    super();
    this.onChangeProjectName = this.onChangeProjectName.bind(this);
    this.onChangeOwnerName = this.onChangeOwnerName.bind(this);
  }

  onChangeProjectName(evt) {
    this.props.dispatch(asyncChangeProjectName(evt.target.value));
  }

  onChangeOwnerName(evt) {
    this.props.dispatch(asyncChangeOwnerName(evt.target.value));
  }

  render() {
    const { projectName, ownerName } = this.props.data;

    return (

Hello World!

This is the demo for the { projectName } by
          <a href={`https://twitter.com/${ownerName}`} className={styles.link} > @{ ownerName }

        Change to your project name:
          <input className={styles.input} type="text"
            onChange={this.onChangeProjectName}
            defaultValue="React.js Boilerplate" value={projectName}
          />

        Change to your name:
          <input className={styles.input} type="text"
            onChange={this.onChangeOwnerName}
            defaultValue="mxstbr" value={ownerName}
          />

        <Button route="/readme">Setup</Button>

    );
  }
}

// REDUX STUFF

// Which props do we want to inject, given the global state?
function select(state) {
  return {
    data: state
  };
}

// Wrap the component to inject dispatch and state into it
export default connect(select)(HomePage);
<|endoftext|>


<commit_before>/* Copyright 2009-2015 Luc Saffre */
document.domain = '{{settings.STATIC_URL.strip("http://").strip("https://").strip('/')}}';
Lino.edit_tinymce_text = function(panel, options) {
  // edit the text in own window.
  // `panel` is the RichTextPanel
  // console.log(20150520, panel);
  //~ var rec = panel.get_current_record();
  var rec = panel.containing_panel.get_current_record();
  var value = rec ? rec.data[panel.editor.name] : '';
  var saving = false;
  var todo_after_save = false;
  var discard_changes = false;

  function save() {
    if (saving) {alert('tried to save again'); return; }
    var url = panel.containing_panel.get_record_url(rec.id);
    var params = Ext.apply({}, panel.containing_panel.get_base_params());
    params[panel.editor.name] = editor.getValue();
    //~ params.{{constants.URL_PARAM_SUBST_USER}} = Lino.subst_user;
    //~ Lino.insert_subst_user(params);

    // 20150325 http://trac.lino-framework.org/ticket/131
    var action_name = panel.containing_panel.save_action_name;
    if (!action_name)
        action_name = panel.containing_panel.action_name;
    params.{{constants.URL_PARAM_ACTION_NAME}} = action_name;

    var a = {
      params: params,
      method: 'PUT',
      url: url,
      failure: function() {
          //~ if (editor.ed.getContainer())
          editor.ed.setProgressState(0);
          todo_after_save = false;
          saving = false;
          console.log('tinymce.save() failed. sorry.',arguments);
        },
      success: function() {
        saving = false;
        //~ if (editor.ed.getContainer())
        editor.ed.setProgressState(0);
        rec.data[panel.editor.name] = editor.getValue();
        if(todo_after_save) {
            var fn = todo_after_save;
            todo_after_save = false;
            fn();
        }
        //~ panel.containing_window.set_current_record(rec);
        panel.refresh();
      }
    };
    //~ if (editor.ed.getContainer())
    editor.ed.setProgressState(1); // Show progress
    saving = true;
    //~ console.log(a);
    Ext.Ajax.request(a);
  };
  function save_callback() {
      save();
      //~ save(function(){editor.ed.setDirty(false);})
      /* return true have the save button disabled.
      That's not perfect because the PUT is asynchronous
      and the response is not yet known.
      */
      return true;
  }
  //~ var actions = [
    //~ {text:"Save",handler:save}
  //~ ];
  //~ console.log(20110610,panel.editor.disabled);
  var settings = {};
  Ext.apply(settings,{
        readonly: panel.editor.disabled,
        //~ language: "de",
        plugins : "save,emotions,spellchecker,advhr,insertdatetime,preview,table,searchreplace,template",
        // Theme options - button# indicated the row# only
        theme_advanced_buttons1 : "{{site.plugins.tinymce.window_buttons1}}",
        theme_advanced_buttons2 : "{{site.plugins.tinymce.window_buttons2}}",
        theme_advanced_buttons3 : "{{site.plugins.tinymce.window_buttons3}}",
        theme_advanced_resizing : false,
        convert_urls : false,
        save_onsavecallback : save_callback,
        save_enablewhendirty : true
        //~ save_oncancelcallback: on_cancel
  });
  Ext.apply(settings,options);
  var editor = new Ext.ux.TinyMCE({
      value : value,
      tinymceSettings: settings
    });
  var win = new Ext.Window({
    title: rec.title,
    //~ bbar: actions,
    layout: 'fit',
    items: editor,
    width: {{site.plugins.tinymce.window_width}},
    height: {{site.plugins.tinymce.window_height}},
    minWidth: 100,
    minHeight: 100,
    modal: true,
    resizable: true,
    maximizable: true,
    //~ maximized: true,
    //~ closeAction: "close"
    closeAction: "hide"
    //~ hideMode: "offsets",
    //~ constrainHeader: true,
    //~ bodyStyle: 'padding: 10px'
  });

  //~ win.on('beforeclose',function() {
  win.on('beforehide',function() {
    if (todo_after_save) return false;
    if (discard_changes) return true;
    if (editor.isDirty()) {
        //~ var ok = false;
        //~ var allowClose = true;
        var config = {title:"{{_('Confirmation')}}"};
        config.buttons = Ext.MessageBox.YESNOCANCEL;
        config.msg = "{{_('Save changes to text ?')}}";
        config.modal = true;
        config.fn = function(buttonId,text,opt) {
          //~ console.log('do_when_clean',buttonId)
          if (buttonId == "yes") {
              /* we cancel this close, but save()'s onSuccess will call again.*/
              //~ allowClose = false;
              todo_after_save = function(){win.hide();}
              editor.ed.execCommand('mceSave');
              //~ editor.ed.save(function(){win.close();});
          } else if (buttonId == "no") {
              discard_changes = true;
              win.hide()
          //~ } else if (buttonId == "cancel") {
            //~ ok = true;
              //~ allowClose = false;
          //~ } else {
            //~ console.log('unknwon buttonId:',buttonId);
          }
        }
        Ext.MessageBox.show(config);
        return false;
        //~ return allowClose;
    }
  });
  win.show();
}

Lino.RichTextPanel = Ext.extend(Ext.Panel,Lino.PanelMixin);
Lino.RichTextPanel = Ext.extend(Lino.RichTextPanel,Lino.FieldBoxMixin);
Lino.RichTextPanel = Ext.extend(Lino.RichTextPanel,{

  //~ initComponent : function(){
    //~ Lino.RichTextPanel.superclass.initComponent.call(this);
  //~ },
  constructor : function(config, params) {
      // console.log('20150520a Lino.RichTextPanel.initComponent', this);
    //~ var url = TEMPLATES_URL + config.ls_url + "/" + String(rec.id) + "/" + config.name;
    //~ var url = TEMPLATES_URL + config.ls_url + "/" + config.name;
    var t = this;
    var tinymce_options = {
        theme : "advanced",
        content_css: '{{site.build_static_url("extjs/lino.css")}},{{site.build_static_url("tinymce_content.css")}}',

        theme_advanced_font_sizes : "12px,13px,14px,16px,18px,20px,24px",
        font_size_style_values : "12px,13px,14px,16px,18px,20px,24px",
        language: '{{language[:2]}}',
        //~ template_external_list_url : url,
        theme_advanced_toolbar_location : "top",
        theme_advanced_toolbar_align : "left",
        theme_advanced_statusbar_location : "bottom",
        template_popup_width : {{site.plugins.tinymce.window_width}},
        template_popup_height : {{site.plugins.tinymce.window_height}},
        template_replace_values : {
            data_field : function(element){
                //~ console.log(20110722,fieldName,t.containing_window.get_current_record());
                var fieldName = element.innerHTML;
                element.innerHTML = t.containing_panel.get_current_record().data[fieldName];
            }
        }
      };

    var editorConfig = {
      tinymceSettings: {
        plugins : "noneditable,template",
        // Theme options - button# indicated the row# only
        theme_advanced_buttons1 : "{{site.plugins.tinymce.field_buttons}}",
        theme_advanced_buttons2 : "",
        theme_advanced_buttons3 : "", // ,|,sub,sup,|,charmap",
        theme_advanced_resizing : false
        //~ save_onsavecallback : save_callback,
        //~ save_enablewhendirty : true
        //~ save_oncancelcallback: on_cancel

    }};
    Ext.apply(editorConfig.tinymceSettings,tinymce_options);
    //~ editorConfig.name = config.action_name;
    editorConfig.name = config.name;
    delete config.name;
    //~ config.title = config.label;
    //~ delete config.label;
    this.before_init(config,params);

    // console.log('20150520b Lino.RichTextPanel.initComponent', this);

    this.editor = new Ext.ux.TinyMCE(editorConfig);
    var t = this;
    config.tools = [{
                      qtip: "{{_('Edit text in own window')}}",
                      id: "up",
                      handler: function(){
                        if(t.editor.isDirty()) {
                            var record = t.containing_panel.get_current_record();
                            record.data[t.editor.name] = t.editor.getValue();
                        }
                        Lino.edit_tinymce_text(t, tinymce_options)
                      }
                    }];

    config.items = this.editor;
    config.layout = "fit";
    // console.log('20150520c Lino.RichTextPanel.initComponent', this);
    Lino.RichTextPanel.superclass.constructor.call(this, config);
    // console.log('20150520d Lino.RichTextPanel.initComponent', this);
  },
  refresh : function(unused) {
      this.refresh_with_after();
  },
  /* RichTextPanel */
  refresh_with_after : function(after) {
    var record = this.containing_panel.get_current_record();
    // console.log('20140504 RichTextPanel.refresh()',
    //             this.title,record.title, record);
    var todo = function() {
      if (record) {
        var url = '{{site.plugins.tinymce.build_plain_url("templates")}}'
              + this.containing_panel.ls_url + "/"
              + String(record.id) + "/" + this.editor.name;
        // console.log('20150415 RichTextPanel.refresh()',url);
        if (this.editor.ed)
            this.editor.ed.settings.template_external_list_url = url;
            // else {console.log("20150415 no editor")}
        this.set_base_params(this.containing_panel.get_master_params());
        //~ var v = record ? this.format_data(record.data[this.editor.name]) : ''
        var v = this.format_data(record.data[this.editor.name])
        this.editor.setValue(v);
      } else {
        this.editor.setValue('(no data)');
      }
    };
    Lino.do_when_visible(this,todo.createDelegate(this));
  }
});
//~ Ext.override(Lino.RichTextPanel,Lino.FieldBoxMixin);

<commit_msg>http://luc.lino-framework.org/blog/2015/0824.html<commit_after>/* Copyright 2009-2015 Luc Saffre */
document.domain = '{{settings.STATIC_URL.replace("http://","").replace("https://","").rstrip('/')}}';
Lino.edit_tinymce_text = function(panel, options) {
  // edit the text in own window.
  // `panel` is the RichTextPanel
  // console.log(20150520, panel);
  //~ var rec = panel.get_current_record();
  var rec = panel.containing_panel.get_current_record();
  var value = rec ? rec.data[panel.editor.name] : '';
  var saving = false;
  var todo_after_save = false;
  var discard_changes = false;

  function save() {
    if (saving) {alert('tried to save again'); return; }
    var url = panel.containing_panel.get_record_url(rec.id);
    var params = Ext.apply({}, panel.containing_panel.get_base_params());
    params[panel.editor.name] = editor.getValue();
    //~ params.{{constants.URL_PARAM_SUBST_USER}} = Lino.subst_user;
    //~ Lino.insert_subst_user(params);

    // 20150325 http://trac.lino-framework.org/ticket/131
    var action_name = panel.containing_panel.save_action_name;
    if (!action_name)
        action_name = panel.containing_panel.action_name;
    params.{{constants.URL_PARAM_ACTION_NAME}} = action_name;

    var a = {
      params: params,
      method: 'PUT',
      url: url,
      failure: function() {
          //~ if (editor.ed.getContainer())
          editor.ed.setProgressState(0);
          todo_after_save = false;
          saving = false;
          console.log('tinymce.save() failed. sorry.',arguments);
        },
      success: function() {
        saving = false;
        //~ if (editor.ed.getContainer())
        editor.ed.setProgressState(0);
        rec.data[panel.editor.name] = editor.getValue();
        if(todo_after_save) {
            var fn = todo_after_save;
            todo_after_save = false;
            fn();
        }
        //~ panel.containing_window.set_current_record(rec);
        panel.refresh();
      }
    };
    //~ if (editor.ed.getContainer())
    editor.ed.setProgressState(1); // Show progress
    saving = true;
    //~ console.log(a);
    Ext.Ajax.request(a);
  };
  function save_callback() {
      save();
      //~ save(function(){editor.ed.setDirty(false);})
      /* return true have the save button disabled.
      That's not perfect because the PUT is asynchronous
      and the response is not yet known.
      */
      return true;
  }
  //~ var actions = [
    //~ {text:"Save",handler:save}
  //~ ];
  //~ console.log(20110610,panel.editor.disabled);
  var settings = {};
  Ext.apply(settings,{
        readonly: panel.editor.disabled,
        //~ language: "de",
        plugins : "save,emotions,spellchecker,advhr,insertdatetime,preview,table,searchreplace,template",
        // Theme options - button# indicated the row# only
        theme_advanced_buttons1 : "{{site.plugins.tinymce.window_buttons1}}",
        theme_advanced_buttons2 : "{{site.plugins.tinymce.window_buttons2}}",
        theme_advanced_buttons3 : "{{site.plugins.tinymce.window_buttons3}}",
        theme_advanced_resizing : false,
        convert_urls : false,
        save_onsavecallback : save_callback,
        save_enablewhendirty : true
        //~ save_oncancelcallback: on_cancel
  });
  Ext.apply(settings,options);
  var editor = new Ext.ux.TinyMCE({
      value : value,
      tinymceSettings: settings
    });
  var win = new Ext.Window({
    title: rec.title,
    //~ bbar: actions,
    layout: 'fit',
    items: editor,
    width: {{site.plugins.tinymce.window_width}},
    height: {{site.plugins.tinymce.window_height}},
    minWidth: 100,
    minHeight: 100,
    modal: true,
    resizable: true,
    maximizable: true,
    //~ maximized: true,
    //~ closeAction: "close"
    closeAction: "hide"
    //~ hideMode: "offsets",
    //~ constrainHeader: true,
    //~ bodyStyle: 'padding: 10px'
  });

  //~ win.on('beforeclose',function() {
  win.on('beforehide',function() {
    if (todo_after_save) return false;
    if (discard_changes) return true;
    if (editor.isDirty()) {
        //~ var ok = false;
        //~ var allowClose = true;
        var config = {title:"{{_('Confirmation')}}"};
        config.buttons = Ext.MessageBox.YESNOCANCEL;
        config.msg = "{{_('Save changes to text ?')}}";
        config.modal = true;
        config.fn = function(buttonId,text,opt) {
          //~ console.log('do_when_clean',buttonId)
          if (buttonId == "yes") {
              /* we cancel this close, but save()'s onSuccess will call again.*/
              //~ allowClose = false;
              todo_after_save = function(){win.hide();}
              editor.ed.execCommand('mceSave');
              //~ editor.ed.save(function(){win.close();});
          } else if (buttonId == "no") {
              discard_changes = true;
              win.hide()
          //~ } else if (buttonId == "cancel") {
            //~ ok = true;
              //~ allowClose = false;
          //~ } else {
            //~ console.log('unknwon buttonId:',buttonId);
          }
        }
        Ext.MessageBox.show(config);
        return false;
        //~ return allowClose;
    }
  });
  win.show();
}

Lino.RichTextPanel = Ext.extend(Ext.Panel,Lino.PanelMixin);
Lino.RichTextPanel = Ext.extend(Lino.RichTextPanel,Lino.FieldBoxMixin);
Lino.RichTextPanel = Ext.extend(Lino.RichTextPanel,{

  //~ initComponent : function(){
    //~ Lino.RichTextPanel.superclass.initComponent.call(this);
  //~ },
  constructor : function(config, params) {
      // console.log('20150520a Lino.RichTextPanel.initComponent', this);
    //~ var url = TEMPLATES_URL + config.ls_url + "/" + String(rec.id) + "/" + config.name;
    //~ var url = TEMPLATES_URL + config.ls_url + "/" + config.name;
    var t = this;
    var tinymce_options = {
        theme : "advanced",
        content_css: '{{site.build_static_url("extjs/lino.css")}},{{site.build_static_url("tinymce_content.css")}}',

        theme_advanced_font_sizes : "12px,13px,14px,16px,18px,20px,24px",
        font_size_style_values : "12px,13px,14px,16px,18px,20px,24px",
        language: '{{language[:2]}}',
        //~ template_external_list_url : url,
        theme_advanced_toolbar_location : "top",
        theme_advanced_toolbar_align : "left",
        theme_advanced_statusbar_location : "bottom",
        template_popup_width : {{site.plugins.tinymce.window_width}},
        template_popup_height : {{site.plugins.tinymce.window_height}},
        template_replace_values : {
            data_field : function(element){
                //~ console.log(20110722,fieldName,t.containing_window.get_current_record());
                var fieldName = element.innerHTML;
                element.innerHTML = t.containing_panel.get_current_record().data[fieldName];
            }
        }
      };

    var editorConfig = {
      tinymceSettings: {
        plugins : "noneditable,template",
        // Theme options - button# indicated the row# only
        theme_advanced_buttons1 : "{{site.plugins.tinymce.field_buttons}}",
        theme_advanced_buttons2 : "",
        theme_advanced_buttons3 : "", // ,|,sub,sup,|,charmap",
        theme_advanced_resizing : false
        //~ save_onsavecallback : save_callback,
        //~ save_enablewhendirty : true
        //~ save_oncancelcallback: on_cancel

    }};
    Ext.apply(editorConfig.tinymceSettings,tinymce_options);
    //~ editorConfig.name = config.action_name;
    editorConfig.name = config.name;
    delete config.name;
    //~ config.title = config.label;
    //~ delete config.label;
    this.before_init(config,params);

    // console.log('20150520b Lino.RichTextPanel.initComponent', this);

    this.editor = new Ext.ux.TinyMCE(editorConfig);
    var t = this;
    config.tools = [{
                      qtip: "{{_('Edit text in own window')}}",
                      id: "up",
                      handler: function(){
                        if(t.editor.isDirty()) {
                            var record = t.containing_panel.get_current_record();
                            record.data[t.editor.name] = t.editor.getValue();
                        }
                        Lino.edit_tinymce_text(t, tinymce_options)
                      }
                    }];

    config.items = this.editor;
    config.layout = "fit";
    // console.log('20150520c Lino.RichTextPanel.initComponent', this);
    Lino.RichTextPanel.superclass.constructor.call(this, config);
    // console.log('20150520d Lino.RichTextPanel.initComponent', this);
  },
  refresh : function(unused) {
      this.refresh_with_after();
  },
  /* RichTextPanel */
  refresh_with_after : function(after) {
    var record = this.containing_panel.get_current_record();
    // console.log('20140504 RichTextPanel.refresh()',
    //             this.title,record.title, record);
    var todo = function() {
      if (record) {
        var url = '{{site.plugins.tinymce.build_plain_url("templates")}}'
              + this.containing_panel.ls_url + "/"
              + String(record.id) + "/" + this.editor.name;
        // console.log('20150415 RichTextPanel.refresh()',url);
        if (this.editor.ed)
            this.editor.ed.settings.template_external_list_url = url;
            // else {console.log("20150415 no editor")}
        this.set_base_params(this.containing_panel.get_master_params());
        //~ var v = record ? this.format_data(record.data[this.editor.name]) : ''
        var v = this.format_data(record.data[this.editor.name])
        this.editor.setValue(v);
      } else {
        this.editor.setValue('(no data)');
      }
    };
    Lino.do_when_visible(this,todo.createDelegate(this));
  }
});
//~ Ext.override(Lino.RichTextPanel,Lino.FieldBoxMixin);

<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview
 * Class representing the host-list portion of the home screen UI.
 */

'use strict';

/** @suppress {duplicate} */
var remoting = remoting || {};

/**
 * Create a host list consisting of the specified HTML elements, which should
 * have a common parent that contains only host-list UI as it will be hidden
 * if the host-list is empty.
 *
 * @constructor
 * @param {Element} table The HTML <table> to contain host-list.
 * @param {Element} errorDiv The HTML <div> to display error messages.
 */
remoting.HostList = function(table, errorDiv) {
  /**
   * @type {Element}
   * @private
   */
  this.table_ = table;
  /**
   * @type {Element}
   * @private
   */
  this.errorDiv_ = errorDiv;
  /**
   * @type {Array.<remoting.HostTableEntry>}
   * @private
   */
  this.hostTableEntries_ = [];
  /**
   * @type {Array.<remoting.Host>}
   * @private
   */
  this.hosts_ = [];
  /**
   * @type {string}
   * @private
   */
  this.lastError_ = '';

  // Load the cache of the last host-list, if present.
  var cached = /** @type {string} */
      (window.localStorage.getItem(remoting.HostList.HOSTS_KEY));
  if (cached) {
    try {
      this.hosts_ = /** @type {Array} */ JSON.parse(cached);
    } catch (err) {
      console.error('Invalid host list cache:', /** @type {*} */(err));
    }
  }
};

/**
 * Search the host list for a host with the specified id.
 *
 * @param {string} hostId The unique id of the host.
 * @return {remoting.Host?} The host, if any.
 */
remoting.HostList.prototype.getHostForId = function(hostId) {
  for (var i = 0; i < this.hosts_.length; ++i) {
    if (this.hosts_[i].hostId == hostId) {
      return this.hosts_[i];
    }
  }
  return null;
};

/**
 * Query the Remoting Directory for the user's list of hosts.
 *
 * @param {function(boolean):void} onDone Callback invoked with true on success
 *     or false on failure.
 * @return {void} Nothing.
 */
remoting.HostList.prototype.refresh = function(onDone) {
  /** @type {remoting.HostList} */
  var that = this;
  /** @param {XMLHttpRequest} xhr The response from the server. */
  var parseHostListResponse = function(xhr) {
    that.parseHostListResponse_(xhr, onDone);
  }
  /** @param {string} token The OAuth2 token. */
  var getHosts = function(token) {
    // TODO(simonmorris): Pass the access token in a header, not a URL
    // parameter, when crbug.com/116574 has a better fix.
    var params = { 'access_token': token };
    remoting.xhr.get(
        'https://www.googleapis.com/chromoting/v1/@me/hosts',
        parseHostListResponse, params);
  };
  remoting.oauth2.callWithToken(getHosts);
};

/**
 * Handle the results of the host list request.  A success response will
 * include a JSON-encoded list of host descriptions, which we display if we're
 * able to successfully parse it.
 *
 * @param {XMLHttpRequest} xhr The XHR object for the host list request.
 * @param {function(boolean):void} onDone The callback passed to |refresh|.
 * @return {void} Nothing.
 * @private
 */
remoting.HostList.prototype.parseHostListResponse_ = function(xhr, onDone) {
  this.hosts_ = [];
  this.lastError_ = '';
  try {
    if (xhr.status == 200) {
      var parsed_response =
          /** @type {{data: {items: Array}}} */ JSON.parse(xhr.responseText);
      if (parsed_response.data && parsed_response.data.items) {
        this.hosts_ = parsed_response.data.items;
        /**
         * @param {remoting.Host} a
         * @param {remoting.Host} b
         */
        var cmp = function(a, b) {
          if (a.status < b.status) {
            return 1;
          } else if (b.status < a.status) {
            return -1;
          }
          return 0;
        };
        this.hosts_ = /** @type {Array} */ this.hosts_.sort(cmp);
      }
    } else {
      // Some other error.
      console.error('Bad status on host list query: ', xhr);
      if (xhr.status == 403) {
        // The user's account is not enabled for Me2Me, so fail silently.
      } else if (xhr.status >= 400 && xhr.status < 500) {
        // For other errors, tell the user to re-authorize us.
        this.lastError_ = remoting.Error.GENERIC;
      } else if (xhr.status == 503) {
        this.lastError_ = remoting.Error.SERVICE_UNAVAILABLE;
      } else {
        this.lastError_ = remoting.Error.UNEXPECTED;
      }
    }
  } catch (er) {
    var typed_er = /** @type {Object} */ (er);
    console.error('Error processing response: ', xhr, typed_er);
    this.lastError_ = remoting.Error.UNEXPECTED;
  }
  window.localStorage.setItem(remoting.HostList.HOSTS_KEY,
                              JSON.stringify(this.hosts_));
  onDone(this.lastError_ == '');
};

/**
 * Display the list of hosts or error condition.
 *
 * @return {void} Nothing.
 */
remoting.HostList.prototype.display = function() {
  this.table_.innerHTML = '';
  this.errorDiv_.innerText = '';
  this.hostTableEntries_ = [];

  /**
   * @type {remoting.HostList}
   */
  var that = this;
  /**
   * @param {remoting.HostTableEntry} hostTableEntry The entry being renamed.
   */
  var onRename = function(hostTableEntry) { that.renameHost_(hostTableEntry); }
  /**
   * @param {remoting.HostTableEntry} hostTableEntry The entry beign deleted.
   */
  var onDelete = function(hostTableEntry) { that.deleteHost_(hostTableEntry); }

  this.table_.hidden = (this.hosts_.length == 0);

  for (var i = 0; i < this.hosts_.length; ++i) {
    /** @type {remoting.Host} */
    var host = this.hosts_[i];
    // Validate the entry to make sure it has all the fields we expect.
    if (host.hostName && host.hostId && host.status && host.jabberId &&
        host.publicKey) {
      var hostTableEntry = new remoting.HostTableEntry();
      hostTableEntry.init(host, onRename, onDelete);
      this.hostTableEntries_[i] = hostTableEntry;
      this.table_.appendChild(hostTableEntry.tableRow);
    }
  }

  if (this.lastError_ != '') {
    l10n.localizeElementFromTag(this.errorDiv_, this.lastError_);
  }
  this.errorDiv_.hidden = (this.lastError_ == '');

  this.showOrHide_(this.hosts_.length != 0 || this.lastError_ != '');
};

/**
 * Show or hide the host-list UI.
 *
 * @param {boolean} show True to show the UI, or false to hide it.
 * @return {void} Nothing.
 * @private
 */
remoting.HostList.prototype.showOrHide_ = function(show) {
  var parent = /** @type {Element} */ (this.table_.parentNode);
  parent.hidden = !show;
};

/**
 * Remove a host from the list, and deregister it.
 * @param {remoting.HostTableEntry} hostTableEntry The host to be removed.
 * @return {void} Nothing.
 * @private
 */
remoting.HostList.prototype.deleteHost_ = function(hostTableEntry) {
  this.table_.removeChild(hostTableEntry.tableRow);
  var index = this.hostTableEntries_.indexOf(hostTableEntry);
  if (index != -1) {
    this.hostTableEntries_.splice(index, 1);
  }

  /** @param {string} token The OAuth2 token. */
  var deleteHost = function(token) {
    var headers = { 'Authorization': 'OAuth ' + token };
    remoting.xhr.remove(
        'https://www.googleapis.com/chromoting/v1/@me/hosts/' +
        hostTableEntry.host.hostId,
        function() {}, '', headers);
  }
  remoting.oauth2.callWithToken(deleteHost);

  this.showOrHide_(this.hostTableEntries_.length != 0);
};

/**
 * Prepare a host for renaming by replacing its name with an edit box.
 * @param {remoting.HostTableEntry} hostTableEntry The host to be renamed.
 * @return {void} Nothing.
 * @private
 */
remoting.HostList.prototype.renameHost_ = function(hostTableEntry) {
  for (var i = 0; i < this.hosts_.length; ++i) {
    if (this.hosts_[i].hostId == hostTableEntry.host.hostId) {
      this.hosts_[i].hostName = hostTableEntry.host.hostName;
      break;
    }
  }
  window.localStorage.setItem(remoting.HostList.HOSTS_KEY,
                              JSON.stringify(this.hosts_));

  /** @param {string} token */
  var renameHost = function(token) {
    var headers = {
      'Authorization': 'OAuth ' + token,
      'Content-type' : 'application/json; charset=UTF-8'
    };
    var newHostDetails = { data: {
      hostId: hostTableEntry.host.hostId,
      hostName: hostTableEntry.host.hostName,
      publicKey: hostTableEntry.host.publicKey
    } };
    remoting.xhr.put(
        'https://www.googleapis.com/chromoting/v1/@me/hosts/' +
        hostTableEntry.host.hostId,
        function(xhr) {},
        JSON.stringify(newHostDetails),
        headers);
  }
  remoting.oauth2.callWithToken(renameHost);
};

/**
 * Key name under which Me2Me hosts are cached.
 */
remoting.HostList.HOSTS_KEY = 'me2me-cached-hosts';

/** @type {remoting.HostList} */
remoting.hostList = null;
<commit_msg>[Chromoting] Let the webapp handle a host with no JID correctly.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview
 * Class representing the host-list portion of the home screen UI.
 */

'use strict';

/** @suppress {duplicate} */
var remoting = remoting || {};

/**
 * Create a host list consisting of the specified HTML elements, which should
 * have a common parent that contains only host-list UI as it will be hidden
 * if the host-list is empty.
 *
 * @constructor
 * @param {Element} table The HTML <table> to contain host-list.
 * @param {Element} errorDiv The HTML <div> to display error messages.
 */
remoting.HostList = function(table, errorDiv) {
  /**
   * @type {Element}
   * @private
   */
  this.table_ = table;
  /**
   * @type {Element}
   * @private
   */
  this.errorDiv_ = errorDiv;
  /**
   * @type {Array.<remoting.HostTableEntry>}
   * @private
   */
  this.hostTableEntries_ = [];
  /**
   * @type {Array.<remoting.Host>}
   * @private
   */
  this.hosts_ = [];
  /**
   * @type {string}
   * @private
   */
  this.lastError_ = '';

  // Load the cache of the last host-list, if present.
  var cached = /** @type {string} */
      (window.localStorage.getItem(remoting.HostList.HOSTS_KEY));
  if (cached) {
    try {
      this.hosts_ = /** @type {Array} */ JSON.parse(cached);
    } catch (err) {
      console.error('Invalid host list cache:', /** @type {*} */(err));
    }
  }
};

/**
 * Search the host list for a host with the specified id.
 *
 * @param {string} hostId The unique id of the host.
 * @return {remoting.Host?} The host, if any.
 */
remoting.HostList.prototype.getHostForId = function(hostId) {
  for (var i = 0; i < this.hosts_.length; ++i) {
    if (this.hosts_[i].hostId == hostId) {
      return this.hosts_[i];
    }
  }
  return null;
};

/**
 * Query the Remoting Directory for the user's list of hosts.
 *
 * @param {function(boolean):void} onDone Callback invoked with true on success
 *     or false on failure.
 * @return {void} Nothing.
 */
remoting.HostList.prototype.refresh = function(onDone) {
  /** @type {remoting.HostList} */
  var that = this;
  /** @param {XMLHttpRequest} xhr The response from the server. */
  var parseHostListResponse = function(xhr) {
    that.parseHostListResponse_(xhr, onDone);
  }
  /** @param {string} token The OAuth2 token. */
  var getHosts = function(token) {
    // TODO(simonmorris): Pass the access token in a header, not a URL
    // parameter, when crbug.com/116574 has a better fix.
    var params = { 'access_token': token };
    remoting.xhr.get(
        'https://www.googleapis.com/chromoting/v1/@me/hosts',
        parseHostListResponse, params);
  };
  remoting.oauth2.callWithToken(getHosts);
};

/**
 * Handle the results of the host list request.  A success response will
 * include a JSON-encoded list of host descriptions, which we display if we're
 * able to successfully parse it.
 *
 * @param {XMLHttpRequest} xhr The XHR object for the host list request.
 * @param {function(boolean):void} onDone The callback passed to |refresh|.
 * @return {void} Nothing.
 * @private
 */
remoting.HostList.prototype.parseHostListResponse_ = function(xhr, onDone) {
  this.hosts_ = [];
  this.lastError_ = '';
  try {
    if (xhr.status == 200) {
      var parsed_response =
          /** @type {{data: {items: Array}}} */ JSON.parse(xhr.responseText);
      if (parsed_response.data && parsed_response.data.items) {
        this.hosts_ = parsed_response.data.items;
        /**
         * @param {remoting.Host} a
         * @param {remoting.Host} b
         */
        var cmp = function(a, b) {
          if (a.status < b.status) {
            return 1;
          } else if (b.status < a.status) {
            return -1;
          }
          return 0;
        };
        this.hosts_ = /** @type {Array} */ this.hosts_.sort(cmp);
      }
    } else {
      // Some other error.
      console.error('Bad status on host list query: ', xhr);
      if (xhr.status == 403) {
        // The user's account is not enabled for Me2Me, so fail silently.
      } else if (xhr.status >= 400 && xhr.status < 500) {
        // For other errors, tell the user to re-authorize us.
        this.lastError_ = remoting.Error.GENERIC;
      } else if (xhr.status == 503) {
        this.lastError_ = remoting.Error.SERVICE_UNAVAILABLE;
      } else {
        this.lastError_ = remoting.Error.UNEXPECTED;
      }
    }
  } catch (er) {
    var typed_er = /** @type {Object} */ (er);
    console.error('Error processing response: ', xhr, typed_er);
    this.lastError_ = remoting.Error.UNEXPECTED;
  }
  window.localStorage.setItem(remoting.HostList.HOSTS_KEY,
                              JSON.stringify(this.hosts_));
  onDone(this.lastError_ == '');
};

/**
 * Display the list of hosts or error condition.
 *
 * @return {void} Nothing.
 */
remoting.HostList.prototype.display = function() {
  this.table_.innerHTML = '';
  this.errorDiv_.innerText = '';
  this.hostTableEntries_ = [];

  /**
   * @type {remoting.HostList}
   */
  var that = this;
  /**
   * @param {remoting.HostTableEntry} hostTableEntry The entry being renamed.
   */
  var onRename = function(hostTableEntry) { that.renameHost_(hostTableEntry); }
  /**
   * @param {remoting.HostTableEntry} hostTableEntry The entry beign deleted.
   */
  var onDelete = function(hostTableEntry) { that.deleteHost_(hostTableEntry); }

  this.table_.hidden = (this.hosts_.length == 0);

  for (var i = 0; i < this.hosts_.length; ++i) {
    /** @type {remoting.Host} */
    var host = this.hosts_[i];
    // Validate the entry to make sure it has all the fields we expect.
    // If the host has never sent a heartbeat, then there will be no jabberId.
    if (host.hostName && host.hostId && host.status && host.publicKey) {
      var hostTableEntry = new remoting.HostTableEntry();
      hostTableEntry.init(host, onRename, onDelete);
      this.hostTableEntries_[i] = hostTableEntry;
      this.table_.appendChild(hostTableEntry.tableRow);
    }
  }

  if (this.lastError_ != '') {
    l10n.localizeElementFromTag(this.errorDiv_, this.lastError_);
  }
  this.errorDiv_.hidden = (this.lastError_ == '');

  this.showOrHide_(this.hosts_.length != 0 || this.lastError_ != '');
};

/**
 * Show or hide the host-list UI.
 *
 * @param {boolean} show True to show the UI, or false to hide it.
 * @return {void} Nothing.
 * @private
 */
remoting.HostList.prototype.showOrHide_ = function(show) {
  var parent = /** @type {Element} */ (this.table_.parentNode);
  parent.hidden = !show;
};

/**
 * Remove a host from the list, and deregister it.
 * @param {remoting.HostTableEntry} hostTableEntry The host to be removed.
 * @return {void} Nothing.
 * @private
 */
remoting.HostList.prototype.deleteHost_ = function(hostTableEntry) {
  this.table_.removeChild(hostTableEntry.tableRow);
  var index = this.hostTableEntries_.indexOf(hostTableEntry);
  if (index != -1) {
    this.hostTableEntries_.splice(index, 1);
  }

  /** @param {string} token The OAuth2 token. */
  var deleteHost = function(token) {
    var headers = { 'Authorization': 'OAuth ' + token };
    remoting.xhr.remove(
        'https://www.googleapis.com/chromoting/v1/@me/hosts/' +
        hostTableEntry.host.hostId,
        function() {}, '', headers);
  }
  remoting.oauth2.callWithToken(deleteHost);

  this.showOrHide_(this.hostTableEntries_.length != 0);
};

/**
 * Prepare a host for renaming by replacing its name with an edit box.
 * @param {remoting.HostTableEntry} hostTableEntry The host to be renamed.
 * @return {void} Nothing.
 * @private
 */
remoting.HostList.prototype.renameHost_ = function(hostTableEntry) {
  for (var i = 0; i < this.hosts_.length; ++i) {
    if (this.hosts_[i].hostId == hostTableEntry.host.hostId) {
      this.hosts_[i].hostName = hostTableEntry.host.hostName;
      break;
    }
  }
  window.localStorage.setItem(remoting.HostList.HOSTS_KEY,
                              JSON.stringify(this.hosts_));

  /** @param {string} token */
  var renameHost = function(token) {
    var headers = {
      'Authorization': 'OAuth ' + token,
      'Content-type' : 'application/json; charset=UTF-8'
    };
    var newHostDetails = { data: {
      hostId: hostTableEntry.host.hostId,
      hostName: hostTableEntry.host.hostName,
      publicKey: hostTableEntry.host.publicKey
    } };
    remoting.xhr.put(
        'https://www.googleapis.com/chromoting/v1/@me/hosts/' +
        hostTableEntry.host.hostId,
        function(xhr) {},
        JSON.stringify(newHostDetails),
        headers);
  }
  remoting.oauth2.callWithToken(renameHost);
};

/**
 * Key name under which Me2Me hosts are cached.
 */
remoting.HostList.HOSTS_KEY = 'me2me-cached-hosts';

/** @type {remoting.HostList} */
remoting.hostList = null;
<|endoftext|>