<commit_before>'use strict';

/** @jsx React.DOM */
var React = require('react/addons');
var emptyFunction = require('react/lib/emptyFunction');
var CX = React.addons.classSet;

function createUIEvent(draggable) {
	return {
		position: {
			top: draggable.state.clientY,
			left: draggable.state.clientX
		}
	};
}

function canDragY(draggable) {
	return draggable.props.axis === 'both' ||
			draggable.props.axis === 'y';
}

function canDragX(draggable) {
	return draggable.props.axis === 'both' ||
			draggable.props.axis === 'x';
}

function isFunction(func) {
  return typeof func === 'function' || Object.prototype.toString.call(func) === '[object Function]'
}

// @credits https://gist.github.com/rogozhnikoff/a43cfed27c41e4e68cdc
function findInArray(array, callback) {
  for (var i = 0, length = array.length, element = null; i < length, element = array[i]; i++) {
    if (callback.apply(callback, [element, i, array])) return element;
  }
}

function matchesSelector(el, selector) {
  var method = findInArray([
    'matches',
    'webkitMatchesSelector',
    'mozMatchesSelector',
    'msMatchesSelector',
    'oMatchesSelector'
  ], function(method){
    return isFunction(el[method]);
  });

  return el[method].call(el, selector);
}

// @credits: http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript/4819886#4819886
var isTouchDevice = 'ontouchstart' in window // works on most browsers
    || 'onmsgesturechange' in window; // works on ie10 on ms surface

// look ::handleDragStart
//function isMultiTouch(e) {
//  return e.touches && Array.isArray(e.touches) && e.touches.length > 1
//}

/**
 * simple abstraction for dragging events names
 * */
var dragEventFor = (function () {
  var eventsFor = {
    touch: {
      start: 'touchstart',
      move: 'touchmove',
      end: 'touchend'
    },
    mouse: {
      start: 'mousedown',
      move: 'mousemove',
      end: 'mouseup'
    }
  };
  return eventsFor[isTouchDevice ? 'touch' : 'mouse'];
})();

/**
 * get {clientX, clientY} positions of control
 * */
function getControlPosition(e) {
  var position = !isTouchDevice ? e : e.touches[0];
  return {
    clientX: position.clientX,
    clientY: position.clientY
  }
}

function addEvent(el, event, handler) {
	if (!el) { return; }
	if (el.attachEvent) {
		el.attachEvent('on' + event, handler);
	} else if (el.addEventListener) {
		el.addEventListener(event, handler, true);
	} else {
		el['on' + event] = handler;
	}
}

function removeEvent(el, event, handler) {
	if (!el) { return; }
	if (el.detachEvent) {
		el.detachEvent('on' + event, handler);
	} else if (el.removeEventListener) {
		el.removeEventListener(event, handler, true);
	} else {
		el['on' + event] = null;
	}
}

module.exports = React.createClass({
	displayName: 'Draggable',

	propTypes: {
		/**
		 * `axis` determines which axis the draggable can move.
		 *
		 * 'both' allows movement horizontally and vertically.
		 * 'x' limits movement to horizontal axis.
		 * 'y' limits movement to vertical axis.
		 *
		 * Defaults to 'both'.
		 */
		axis: React.PropTypes.oneOf(['both', 'x', 'y']),

		/**
		 * `handle` specifies a selector to be used as the handle that initiates drag.
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	    	return (
		 * 	    	 	<Draggable handle=".handle">
		 * 	    	 	  <div>
		 * 	    	 	      <div className="handle">Click me to drag</div>
		 * 	    	 	      <div>This is some other content</div>
		 * 	    	 	  </div>
		 * 	    		</Draggable>
		 * 	    	);
		 * 	    }
		 * 	});
		 * ```
		 */
		handle: React.PropTypes.string,

		/**
		 * `cancel` specifies a selector to be used to prevent drag initialization.
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	        return(
		 * 	            <Draggable cancel=".cancel">
		 * 	                <div>
		 * 	                	<div className="cancel">You can't drag from here</div>
		 *						<div>Dragging here works fine</div>
		 * 	                </div>
		 * 	            </Draggable>
		 * 	        );
		 * 	    }
		 * 	});
		 * ```
		 */
		cancel: React.PropTypes.string,

		/**
		 * `grid` specifies the x and y that dragging should snap to.
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	        return (
		 * 	            <Draggable grid={[25, 25]}>
		 * 	                <div>I snap to a 25 x 25 grid</div>
		 * 	            </Draggable>
		 * 	        );
		 * 	    }
		 * 	});
		 * ```
		 */
		grid: React.PropTypes.arrayOf(React.PropTypes.number),

		/**
		 * `start` specifies the x and y that the dragged item should start at
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	        return (
		 * 	            <Draggable start={{x: 25, y: 25}}>
		 * 	                <div>I start with left: 25px; top: 25px;</div>
		 * 	            </Draggable>
		 * 	        );
		 * 	    }
		 * 	});
		 * ```
		 */
		start: React.PropTypes.object,

		/**
		 * `zIndex` specifies the zIndex to use while dragging.
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	        return (
		 * 	            <Draggable zIndex={100}>
		 * 	                <div>I have a zIndex</div>
		 * 	            </Draggable>
		 * 	        );
		 * 	    }
		 * 	});
		 * ```
		 */
		zIndex: React.PropTypes.number,

		/**
		 * Called when dragging starts.
		 *
		 * Example:
		 *
		 * ```js
		 *	function (event, ui) {}
		 * ```
		 *
		 * `event` is the Event that was triggered.
		 * `ui` is an object:
		 *
		 * ```js
		 *	{
		 *		position: {top: 0, left: 0}
		 *	}
		 * ```
		 */
		onStart: React.PropTypes.func,

		/**
		 * Called while dragging.
		 *
		 * Example:
		 *
		 * ```js
		 *	function (event, ui) {}
		 * ```
		 *
		 * `event` is the Event that was triggered.
		 * `ui` is an object:
		 *
		 * ```js
		 *	{
		 *		position: {top: 0, left: 0}
		 *	}
		 * ```
		 */
		onDrag: React.PropTypes.func,

		/**
		 * Called when dragging stops.
		 *
		 * Example:
		 *
		 * ```js
		 *	function (event, ui) {}
		 * ```
		 *
		 * `event` is the Event that was triggered.
		 * `ui` is an object:
		 *
		 * ```js
		 *	{
		 *		position: {top: 0, left: 0}
		 *	}
		 * ```
		 */
		onStop: React.PropTypes.func,

		/**
		 * A workaround option which can be passed if onMouseDown needs to be accessed, since it'll always be blocked (due to that there's internal use of onMouseDown)
		 *
		 */
		onMouseDown: React.PropTypes.func
	},

	componentWillUnmount: function() {
		// Remove any leftover event handlers
		removeEvent(window, dragEventFor['move'], this.handleDrag);
		removeEvent(window, dragEventFor['end'], this.handleDragEnd);
	},

	getDefaultProps: function () {
		return {
			axis: 'both',
			handle: null,
			cancel: null,
			grid: null,
			start: {
				x: 0,
				y: 0
			},
			zIndex: NaN,
			onStart: emptyFunction,
			onDrag: emptyFunction,
			onStop: emptyFunction,
			onMouseDown: emptyFunction
		};
	},

	getInitialState: function () {
		return {
			// Whether or not currently dragging
			dragging: false,

			// Start top/left of this.getDOMNode()
			startX: 0, startY: 0,

			// Offset between start top/left and mouse top/left
			offsetX: 0, offsetY: 0,

			// Current top/left of this.getDOMNode()
			clientX: this.props.start.x, clientY: this.props.start.y
		};
	},

	handleDragStart: function (e) {
    // todo: write right implementation to prevent multitouch drag
    // prevent multi-touch events
    // if (isMultiTouch(e)) {
    //     this.handleDragEnd.apply(e, arguments);
    //     return
    // }

		// Make it possible to attach event handlers on top of this one
		this.props.onMouseDown(e);

		var node = this.getDOMNode();

		// Short circuit if handle or cancel prop was provided and selector doesn't match
		if ((this.props.handle && !matchesSelector(e.target, this.props.handle)) ||
			(this.props.cancel && matchesSelector(e.target, this.props.cancel))) {
			return;
		}

    var dragPoint = getControlPosition(e);

		// Initiate dragging
		this.setState({
			dragging: true,
			offsetX: dragPoint.clientX,
			offsetY: dragPoint.clientY,
			startX: parseInt(node.style.left, 10) || 0,
			startY: parseInt(node.style.top, 10) || 0
		});

		// Call event handler
		this.props.onStart(e, createUIEvent(this));

		// Add event handlers
		addEvent(window, dragEventFor['move'], this.handleDrag);
		addEvent(window, dragEventFor['end'], this.handleDragEnd);
	},

	handleDragEnd: function (e) {
		// Short circuit if not currently dragging
		if (!this.state.dragging) {
			return;
		}

		// Turn off dragging
		this.setState({
			dragging: false
		});

		// Call event handler
		this.props.onStop(e, createUIEvent(this));

		// Remove event handlers
    removeEvent(window, dragEventFor['move'], this.handleDrag);
    removeEvent(window, dragEventFor['end'], this.handleDragEnd);
	},

	handleDrag: function (e) {
    var dragPoint = getControlPosition(e);

		// Calculate top and left
    var clientX = (this.state.startX + (dragPoint.clientX - this.state.offsetX));
    var clientY = (this.state.startY + (dragPoint.clientY - this.state.offsetY));

		// Snap to grid if prop has been provided
		if (Array.isArray(this.props.grid)) {
			clientX = Math.abs(clientX - this.state.clientX) >= this.props.grid[0]
					? clientX
					: this.state.clientX;

			clientY = Math.abs(clientY - this.state.clientY) >= this.props.grid[1]
					? clientY
					: this.state.clientY;
		}

		// Update top and left
		this.setState({
			clientX: clientX,
			clientY: clientY
		});

		// Call event handler
		this.props.onDrag(e, createUIEvent(this));
	},

	render: function () {
		var style = {
			// Set top if vertical drag is enabled
			top: canDragY(this)
				? this.state.clientY
				: this.state.startY,

			// Set left if horizontal drag is enabled
			left: canDragX(this)
				? this.state.clientX
				: this.state.startX
		};

		// Set zIndex if currently dragging and prop has been provided
		if (this.state.dragging && !isNaN(this.props.zIndex)) {
			style.zIndex = this.props.zIndex;
		}

		var className = CX({
			'react-draggable': true,
			'react-dragging': this.state.dragging
		});
		// Reuse the child provided
		// This makes it flexible to use whatever element is wanted (div, ul, etc)
		return React.addons.cloneWithProps(React.Children.only(this.props.children), {
			style: style,
			className: className,

			onMouseDown: this.handleDragStart,
			onTouchStart: function(ev){
        ev.preventDefault(); // prevent for scroll
        return this.handleDragStart.apply(this, arguments);
      }.bind(this),

			onMouseUp: this.handleDragEnd,
			onTouchEnd: this.handleDragEnd
		});
	}
});
<commit_msg>update dragging className<commit_after>'use strict';

/** @jsx React.DOM */
var React = require('react/addons');
var emptyFunction = require('react/lib/emptyFunction');
var CX = React.addons.classSet;

function createUIEvent(draggable) {
	return {
		position: {
			top: draggable.state.clientY,
			left: draggable.state.clientX
		}
	};
}

function canDragY(draggable) {
	return draggable.props.axis === 'both' ||
			draggable.props.axis === 'y';
}

function canDragX(draggable) {
	return draggable.props.axis === 'both' ||
			draggable.props.axis === 'x';
}

function isFunction(func) {
  return typeof func === 'function' || Object.prototype.toString.call(func) === '[object Function]'
}

// @credits https://gist.github.com/rogozhnikoff/a43cfed27c41e4e68cdc
function findInArray(array, callback) {
  for (var i = 0, length = array.length, element = null; i < length, element = array[i]; i++) {
    if (callback.apply(callback, [element, i, array])) return element;
  }
}

function matchesSelector(el, selector) {
  var method = findInArray([
    'matches',
    'webkitMatchesSelector',
    'mozMatchesSelector',
    'msMatchesSelector',
    'oMatchesSelector'
  ], function(method){
    return isFunction(el[method]);
  });

  return el[method].call(el, selector);
}

// @credits: http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript/4819886#4819886
var isTouchDevice = 'ontouchstart' in window // works on most browsers
    || 'onmsgesturechange' in window; // works on ie10 on ms surface

// look ::handleDragStart
//function isMultiTouch(e) {
//  return e.touches && Array.isArray(e.touches) && e.touches.length > 1
//}

/**
 * simple abstraction for dragging events names
 * */
var dragEventFor = (function () {
  var eventsFor = {
    touch: {
      start: 'touchstart',
      move: 'touchmove',
      end: 'touchend'
    },
    mouse: {
      start: 'mousedown',
      move: 'mousemove',
      end: 'mouseup'
    }
  };
  return eventsFor[isTouchDevice ? 'touch' : 'mouse'];
})();

/**
 * get {clientX, clientY} positions of control
 * */
function getControlPosition(e) {
  var position = !isTouchDevice ? e : e.touches[0];
  return {
    clientX: position.clientX,
    clientY: position.clientY
  }
}

function addEvent(el, event, handler) {
	if (!el) { return; }
	if (el.attachEvent) {
		el.attachEvent('on' + event, handler);
	} else if (el.addEventListener) {
		el.addEventListener(event, handler, true);
	} else {
		el['on' + event] = handler;
	}
}

function removeEvent(el, event, handler) {
	if (!el) { return; }
	if (el.detachEvent) {
		el.detachEvent('on' + event, handler);
	} else if (el.removeEventListener) {
		el.removeEventListener(event, handler, true);
	} else {
		el['on' + event] = null;
	}
}

module.exports = React.createClass({
	displayName: 'Draggable',

	propTypes: {
		/**
		 * `axis` determines which axis the draggable can move.
		 *
		 * 'both' allows movement horizontally and vertically.
		 * 'x' limits movement to horizontal axis.
		 * 'y' limits movement to vertical axis.
		 *
		 * Defaults to 'both'.
		 */
		axis: React.PropTypes.oneOf(['both', 'x', 'y']),

		/**
		 * `handle` specifies a selector to be used as the handle that initiates drag.
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	    	return (
		 * 	    	 	<Draggable handle=".handle">
		 * 	    	 	  <div>
		 * 	    	 	      <div className="handle">Click me to drag</div>
		 * 	    	 	      <div>This is some other content</div>
		 * 	    	 	  </div>
		 * 	    		</Draggable>
		 * 	    	);
		 * 	    }
		 * 	});
		 * ```
		 */
		handle: React.PropTypes.string,

		/**
		 * `cancel` specifies a selector to be used to prevent drag initialization.
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	        return(
		 * 	            <Draggable cancel=".cancel">
		 * 	                <div>
		 * 	                	<div className="cancel">You can't drag from here</div>
		 *						<div>Dragging here works fine</div>
		 * 	                </div>
		 * 	            </Draggable>
		 * 	        );
		 * 	    }
		 * 	});
		 * ```
		 */
		cancel: React.PropTypes.string,

		/**
		 * `grid` specifies the x and y that dragging should snap to.
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	        return (
		 * 	            <Draggable grid={[25, 25]}>
		 * 	                <div>I snap to a 25 x 25 grid</div>
		 * 	            </Draggable>
		 * 	        );
		 * 	    }
		 * 	});
		 * ```
		 */
		grid: React.PropTypes.arrayOf(React.PropTypes.number),

		/**
		 * `start` specifies the x and y that the dragged item should start at
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	        return (
		 * 	            <Draggable start={{x: 25, y: 25}}>
		 * 	                <div>I start with left: 25px; top: 25px;</div>
		 * 	            </Draggable>
		 * 	        );
		 * 	    }
		 * 	});
		 * ```
		 */
		start: React.PropTypes.object,

		/**
		 * `zIndex` specifies the zIndex to use while dragging.
		 *
		 * Example:
		 *
		 * ```jsx
		 * 	var App = React.createClass({
		 * 	    render: function () {
		 * 	        return (
		 * 	            <Draggable zIndex={100}>
		 * 	                <div>I have a zIndex</div>
		 * 	            </Draggable>
		 * 	        );
		 * 	    }
		 * 	});
		 * ```
		 */
		zIndex: React.PropTypes.number,

		/**
		 * Called when dragging starts.
		 *
		 * Example:
		 *
		 * ```js
		 *	function (event, ui) {}
		 * ```
		 *
		 * `event` is the Event that was triggered.
		 * `ui` is an object:
		 *
		 * ```js
		 *	{
		 *		position: {top: 0, left: 0}
		 *	}
		 * ```
		 */
		onStart: React.PropTypes.func,

		/**
		 * Called while dragging.
		 *
		 * Example:
		 *
		 * ```js
		 *	function (event, ui) {}
		 * ```
		 *
		 * `event` is the Event that was triggered.
		 * `ui` is an object:
		 *
		 * ```js
		 *	{
		 *		position: {top: 0, left: 0}
		 *	}
		 * ```
		 */
		onDrag: React.PropTypes.func,

		/**
		 * Called when dragging stops.
		 *
		 * Example:
		 *
		 * ```js
		 *	function (event, ui) {}
		 * ```
		 *
		 * `event` is the Event that was triggered.
		 * `ui` is an object:
		 *
		 * ```js
		 *	{
		 *		position: {top: 0, left: 0}
		 *	}
		 * ```
		 */
		onStop: React.PropTypes.func,

		/**
		 * A workaround option which can be passed if onMouseDown needs to be accessed, since it'll always be blocked (due to that there's internal use of onMouseDown)
		 *
		 */
		onMouseDown: React.PropTypes.func
	},

	componentWillUnmount: function() {
		// Remove any leftover event handlers
		removeEvent(window, dragEventFor['move'], this.handleDrag);
		removeEvent(window, dragEventFor['end'], this.handleDragEnd);
	},

	getDefaultProps: function () {
		return {
			axis: 'both',
			handle: null,
			cancel: null,
			grid: null,
			start: {
				x: 0,
				y: 0
			},
			zIndex: NaN,
			onStart: emptyFunction,
			onDrag: emptyFunction,
			onStop: emptyFunction,
			onMouseDown: emptyFunction
		};
	},

	getInitialState: function () {
		return {
			// Whether or not currently dragging
			dragging: false,

			// Start top/left of this.getDOMNode()
			startX: 0, startY: 0,

			// Offset between start top/left and mouse top/left
			offsetX: 0, offsetY: 0,

			// Current top/left of this.getDOMNode()
			clientX: this.props.start.x, clientY: this.props.start.y
		};
	},

	handleDragStart: function (e) {
    // todo: write right implementation to prevent multitouch drag
    // prevent multi-touch events
    // if (isMultiTouch(e)) {
    //     this.handleDragEnd.apply(e, arguments);
    //     return
    // }

		// Make it possible to attach event handlers on top of this one
		this.props.onMouseDown(e);

		var node = this.getDOMNode();

		// Short circuit if handle or cancel prop was provided and selector doesn't match
		if ((this.props.handle && !matchesSelector(e.target, this.props.handle)) ||
			(this.props.cancel && matchesSelector(e.target, this.props.cancel))) {
			return;
		}

    var dragPoint = getControlPosition(e);

		// Initiate dragging
		this.setState({
			dragging: true,
			offsetX: dragPoint.clientX,
			offsetY: dragPoint.clientY,
			startX: parseInt(node.style.left, 10) || 0,
			startY: parseInt(node.style.top, 10) || 0
		});

		// Call event handler
		this.props.onStart(e, createUIEvent(this));

		// Add event handlers
		addEvent(window, dragEventFor['move'], this.handleDrag);
		addEvent(window, dragEventFor['end'], this.handleDragEnd);
	},

	handleDragEnd: function (e) {
		// Short circuit if not currently dragging
		if (!this.state.dragging) {
			return;
		}

		// Turn off dragging
		this.setState({
			dragging: false
		});

		// Call event handler
		this.props.onStop(e, createUIEvent(this));

		// Remove event handlers
    removeEvent(window, dragEventFor['move'], this.handleDrag);
    removeEvent(window, dragEventFor['end'], this.handleDragEnd);
	},

	handleDrag: function (e) {
    var dragPoint = getControlPosition(e);

		// Calculate top and left
    var clientX = (this.state.startX + (dragPoint.clientX - this.state.offsetX));
    var clientY = (this.state.startY + (dragPoint.clientY - this.state.offsetY));

		// Snap to grid if prop has been provided
		if (Array.isArray(this.props.grid)) {
			clientX = Math.abs(clientX - this.state.clientX) >= this.props.grid[0]
					? clientX
					: this.state.clientX;

			clientY = Math.abs(clientY - this.state.clientY) >= this.props.grid[1]
					? clientY
					: this.state.clientY;
		}

		// Update top and left
		this.setState({
			clientX: clientX,
			clientY: clientY
		});

		// Call event handler
		this.props.onDrag(e, createUIEvent(this));
	},

	render: function () {
		var style = {
			// Set top if vertical drag is enabled
			top: canDragY(this)
				? this.state.clientY
				: this.state.startY,

			// Set left if horizontal drag is enabled
			left: canDragX(this)
				? this.state.clientX
				: this.state.startX
		};

		// Set zIndex if currently dragging and prop has been provided
		if (this.state.dragging && !isNaN(this.props.zIndex)) {
			style.zIndex = this.props.zIndex;
		}

		var className = CX({
			'react-draggable': true,
			'react-draggable-dragging': this.state.dragging
		});
		// Reuse the child provided
		// This makes it flexible to use whatever element is wanted (div, ul, etc)
		return React.addons.cloneWithProps(React.Children.only(this.props.children), {
			style: style,
			className: className,

			onMouseDown: this.handleDragStart,
			onTouchStart: function(ev){
        ev.preventDefault(); // prevent for scroll
        return this.handleDragStart.apply(this, arguments);
      }.bind(this),

			onMouseUp: this.handleDragEnd,
			onTouchEnd: this.handleDragEnd
		});
	}
});
<|endoftext|>


<commit_before>var utile = require('utile');

//can be (type {string}[, subclass {string}])
//or (headers {object/map}[, body {string}])
var Event = exports.Event = function(type, subclass) {
    this.headers = [];

    this.hPtr = null;

    //case where we actually have type/subclass
    if(typeof type === 'string') {
        this.type = type;
        this.subclass = subclass;
        this.body = '';

        this.addHeader('Event-Name', type);
        if(subclass) this.addHeader('Event-Subclass', subclass);
    }
    //case were we have headers/body
    else if(typeof type === 'object') {
        this.type = type['Event-Name'];
        this.subclass = type['Event-Subclass'];
        this.body = subclass || type['_body'] || '';

        var self = this;
        utile.each(type, function(val, key) {
            if(key == '_body') return;

            self.addHeader(key, val);
        });
    }
    //case where we get no params
    else {
        this.type = '';
        this.subclass = null;
        this.body = '';
    }
};

Event.PRIORITY = {
    LOW: 'LOW',
    NORMAL: 'NORMAL',
    HIGH: 'HIGH'
};

//Turns an event into colon-separated 'name: value'
// pairs similar to a sip/email packet
// (the way it looks on '/events plain all').
Event.prototype.serialize = function(format) {
    format = format || 'plain';

    this.addHeader('Content-Length', this.body.length);

    var data;

    switch(format) {
    case 'json':
        /*
        {
            "Event-Name":   "CUSTOM",
            "Core-UUID":    "8b192020-7368-4498-9b11-cbe10f48a784",
            "FreeSWITCH-Hostname":  "smsdev",
            "FreeSWITCH-Switchname":        "smsdev",
            "FreeSWITCH-IPv4":      "10.1.12.115",
            "FreeSWITCH-IPv6":      "::1",
            "Event-Date-Local":     "2012-09-25 14:22:37",
            "Event-Date-GMT":       "Tue, 25 Sep 2012 18:22:37 GMT",
            "Event-Date-Timestamp": "1348597357036551",
            "Event-Calling-File":   "switch_cpp.cpp",
            "Event-Calling-Function":       "Event",
            "Event-Calling-Line-Number":    "262",
            "Event-Sequence":       "11027",
            "Event-Subclass":       "SMS::SEND_MESSAGE",
            "proto":        "sip",
            "dest_proto":   "sip",
            "from": "9515529832",
            "from_full":    "9515529832",
            "to":   "internal/8507585138@sms-proxy-01.bandwidthclec.com",
            "subject":      "PATLive Testing",
            "type": "text/plain",
            "hint": "the hint",
            "replying":     "true",
            "Content-Length":       "23",
            "_body":        "Hello from Chad Engler!"
        }
        */
        data = {};
        this.headers.forEach(function(header, i) {
            data[header.name] = header.value;
        });

        if(this.body) {
            data['_body'] = this.body;
        }

        return JSON.stringify(data, null, 2);

    case 'plain':
        /*
          Event-Name: CUSTOM
          Core-UUID: 8b192020-7368-4498-9b11-cbe10f48a784
          FreeSWITCH-Hostname: smsdev
          FreeSWITCH-Switchname: smsdev
          FreeSWITCH-IPv4: 10.1.12.115
          FreeSWITCH-IPv6: %3A%3A1
          Event-Date-Local: 2012-09-25%2014%3A21%3A56
          Event-Date-GMT: Tue,%2025%20Sep%202012%2018%3A21%3A56%20GMT
          Event-Date-Timestamp: 1348597316736546
          Event-Calling-File: switch_cpp.cpp
          Event-Calling-Function: Event
          Event-Calling-Line-Number: 262
          Event-Sequence: 11021
          Event-Subclass: SMS%3A%3ASEND_MESSAGE
          proto: sip
          dest_proto: sip
          from: 9515529832
          from_full: 9515529832
          to: internal/8507585138%40sms-proxy-01.bandwidthclec.com
          subject: PATLive%20Testing
          type: text/plain
          hint: the%20hint
          replying: true
          Content-Length: 23
         */
        data = '';

        this.headers.forEach(function(header, i) {
            data += header.name + ': ' + header.value + '\n';
        });

        if(this.body) {
            //url encode newlines in the body
            data += '\n' + this.body.replace('\n', '%0A');
        }

        return data.trim();

        /*
          <event>
            <headers>
              <Event-Name>CUSTOM</Event-Name>
              <Core-UUID>8b192020-7368-4498-9b11-cbe10f48a784</Core-UUID>
              <FreeSWITCH-Hostname>smsdev</FreeSWITCH-Hostname>
              <FreeSWITCH-Switchname>smsdev</FreeSWITCH-Switchname>
              <FreeSWITCH-IPv4>10.1.12.115</FreeSWITCH-IPv4>
              <FreeSWITCH-IPv6>%3A%3A1</FreeSWITCH-IPv6>
              <Event-Date-Local>2012-09-25%2014%3A26%3A17</Event-Date-Local>
              <Event-Date-GMT>Tue,%2025%20Sep%202012%2018%3A26%3A17%20GMT</Event-Date-GMT>
              <Event-Date-Timestamp>1348597577616542</Event-Date-Timestamp>
              <Event-Calling-File>switch_cpp.cpp</Event-Calling-File>
              <Event-Calling-Function>Event</Event-Calling-Function>
              <Event-Calling-Line-Number>262</Event-Calling-Line-Number>
              <Event-Sequence>11057</Event-Sequence>
              <Event-Subclass>SMS%3A%3ASEND_MESSAGE</Event-Subclass>
              <proto>sip</proto>
              <dest_proto>sip</dest_proto>
              <from>9515529832</from>
              <from_full>9515529832</from_full>
              <to>internal/8507585138%40sms-proxy-01.bandwidthclec.com</to>
              <subject>PATLive%20Testing</subject>
              <type>text/plain</type>
              <hint>the%20hint</hint>
              <replying>true</replying>
            </headers>
            <Content-Length>23</Content-Length>
            Hello from Chad Engler!
          </event>
         */
    case 'xml':
        data = '<event>\n';

        //add headers
        data += '  <headers>\n';
        this.headers.forEach(function(header, i) {
            data += '    <' + header.name + '>' + header.value + '</' + header.name + '>\n';
        });
        data += '  </headers>\n';

        //add body
        if(this.body) {
            data += '  ' + this.body + '\n';
        }

        data += '</event>';

        return data;
    }
};

//Sets the priority of an event to $number in case it's fired.
//'NORMAL', 'LOW', 'HIGH', 'INVALID'
Event.prototype.setPriority = function(priority) {
    this.addHeader('priority', priority);
};

//Gets the header with the key of $header_name from an event object.
Event.prototype.getHeader = function(name) {
    var h = this._findHeader(name);
    return (h ? h.value : null);
};

//Gets the body of an event object.
Event.prototype.getBody = function() {
    return this.body;
};

//Gets the event type of an event object.
Event.prototype.getType = function() {
    return this.type;
};

//Add $value to the body of an event object.
// This can be called multiple times for the same event object.
Event.prototype.addBody = function(value) {
    return this.body += value;
};

//Add a header with key = $header_name and value = $value
// to an event object. This can be called multiple times
// for the same event object.
Event.prototype.addHeader = function(name, value) {
    var h = this._findHeader(name);

    if(h) h.value = value;
    else this.headers.push({ name: name, value: value });

    return value;
};

//Delete the header with key $header_name from an event object.
Event.prototype.delHeader = function(name) {
    var i = this._findHeaderIndex(name);

    return (i === null ? null : this.headers.splice(i, 1));
};

//Sets the pointer to the first header in an event object,
// and returns it's key name. This must be called before nextHeader is called.
Event.prototype.firstHeader = function() {
    this.hPtr = 0;

    return this.headers[0].name;
};

//Moves the pointer to the next header in an event object,
// and returns it's key name. firstHeader must be called
// before this method to set the pointer. If you're already
// on the last header when this method is called, then it will return NULL.
Event.prototype.nextHeader = function() {
    //if no firstHeader called yet
    if(this.hPtr === null)
        return null;

    //if reached end
    if(this.hPtr === (this.headers.length - 1))
        return this.hPtr = null;

    //increment and return
    return this.headers[++this.hPtr].name;
};

Event.prototype._findHeaderIndex = function(name) {
    for(var i = 0, len = this.headers.length; i < len; ++i) {
        if(this.headers[i].name === name)
            return i;
    }

    return null;
};

Event.prototype._findHeader = function(name) {
    var i = this._findHeaderIndex(name);

    return (i === null ? null : this.headers[i]);
};<commit_msg>Fixed: Wrong Content-Length header, when event body is multibyte string<commit_after>var utile = require('utile');

//can be (type {string}[, subclass {string}])
//or (headers {object/map}[, body {string}])
var Event = exports.Event = function(type, subclass) {
    this.headers = [];

    this.hPtr = null;

    //case where we actually have type/subclass
    if(typeof type === 'string') {
        this.type = type;
        this.subclass = subclass;
        this.body = '';

        this.addHeader('Event-Name', type);
        if(subclass) this.addHeader('Event-Subclass', subclass);
    }
    //case were we have headers/body
    else if(typeof type === 'object') {
        this.type = type['Event-Name'];
        this.subclass = type['Event-Subclass'];
        this.body = subclass || type['_body'] || '';

        var self = this;
        utile.each(type, function(val, key) {
            if(key == '_body') return;

            self.addHeader(key, val);
        });
    }
    //case where we get no params
    else {
        this.type = '';
        this.subclass = null;
        this.body = '';
    }
};

Event.PRIORITY = {
    LOW: 'LOW',
    NORMAL: 'NORMAL',
    HIGH: 'HIGH'
};

//Turns an event into colon-separated 'name: value'
// pairs similar to a sip/email packet
// (the way it looks on '/events plain all').
Event.prototype.serialize = function(format) {
    format = format || 'plain';

    this.addHeader('Content-Length', Buffer.byteLength(this.body, 'utf8'));

    var data;

    switch(format) {
    case 'json':
        /*
        {
            "Event-Name":   "CUSTOM",
            "Core-UUID":    "8b192020-7368-4498-9b11-cbe10f48a784",
            "FreeSWITCH-Hostname":  "smsdev",
            "FreeSWITCH-Switchname":        "smsdev",
            "FreeSWITCH-IPv4":      "10.1.12.115",
            "FreeSWITCH-IPv6":      "::1",
            "Event-Date-Local":     "2012-09-25 14:22:37",
            "Event-Date-GMT":       "Tue, 25 Sep 2012 18:22:37 GMT",
            "Event-Date-Timestamp": "1348597357036551",
            "Event-Calling-File":   "switch_cpp.cpp",
            "Event-Calling-Function":       "Event",
            "Event-Calling-Line-Number":    "262",
            "Event-Sequence":       "11027",
            "Event-Subclass":       "SMS::SEND_MESSAGE",
            "proto":        "sip",
            "dest_proto":   "sip",
            "from": "9515529832",
            "from_full":    "9515529832",
            "to":   "internal/8507585138@sms-proxy-01.bandwidthclec.com",
            "subject":      "PATLive Testing",
            "type": "text/plain",
            "hint": "the hint",
            "replying":     "true",
            "Content-Length":       "23",
            "_body":        "Hello from Chad Engler!"
        }
        */
        data = {};
        this.headers.forEach(function(header, i) {
            data[header.name] = header.value;
        });

        if(this.body) {
            data['_body'] = this.body;
        }

        return JSON.stringify(data, null, 2);

    case 'plain':
        /*
          Event-Name: CUSTOM
          Core-UUID: 8b192020-7368-4498-9b11-cbe10f48a784
          FreeSWITCH-Hostname: smsdev
          FreeSWITCH-Switchname: smsdev
          FreeSWITCH-IPv4: 10.1.12.115
          FreeSWITCH-IPv6: %3A%3A1
          Event-Date-Local: 2012-09-25%2014%3A21%3A56
          Event-Date-GMT: Tue,%2025%20Sep%202012%2018%3A21%3A56%20GMT
          Event-Date-Timestamp: 1348597316736546
          Event-Calling-File: switch_cpp.cpp
          Event-Calling-Function: Event
          Event-Calling-Line-Number: 262
          Event-Sequence: 11021
          Event-Subclass: SMS%3A%3ASEND_MESSAGE
          proto: sip
          dest_proto: sip
          from: 9515529832
          from_full: 9515529832
          to: internal/8507585138%40sms-proxy-01.bandwidthclec.com
          subject: PATLive%20Testing
          type: text/plain
          hint: the%20hint
          replying: true
          Content-Length: 23
         */
        data = '';

        this.headers.forEach(function(header, i) {
            data += header.name + ': ' + header.value + '\n';
        });

        if(this.body) {
            //url encode newlines in the body
            data += '\n' + this.body.replace('\n', '%0A');
        }

        return data.trim();

        /*
          <event>
            <headers>
              <Event-Name>CUSTOM</Event-Name>
              <Core-UUID>8b192020-7368-4498-9b11-cbe10f48a784</Core-UUID>
              <FreeSWITCH-Hostname>smsdev</FreeSWITCH-Hostname>
              <FreeSWITCH-Switchname>smsdev</FreeSWITCH-Switchname>
              <FreeSWITCH-IPv4>10.1.12.115</FreeSWITCH-IPv4>
              <FreeSWITCH-IPv6>%3A%3A1</FreeSWITCH-IPv6>
              <Event-Date-Local>2012-09-25%2014%3A26%3A17</Event-Date-Local>
              <Event-Date-GMT>Tue,%2025%20Sep%202012%2018%3A26%3A17%20GMT</Event-Date-GMT>
              <Event-Date-Timestamp>1348597577616542</Event-Date-Timestamp>
              <Event-Calling-File>switch_cpp.cpp</Event-Calling-File>
              <Event-Calling-Function>Event</Event-Calling-Function>
              <Event-Calling-Line-Number>262</Event-Calling-Line-Number>
              <Event-Sequence>11057</Event-Sequence>
              <Event-Subclass>SMS%3A%3ASEND_MESSAGE</Event-Subclass>
              <proto>sip</proto>
              <dest_proto>sip</dest_proto>
              <from>9515529832</from>
              <from_full>9515529832</from_full>
              <to>internal/8507585138%40sms-proxy-01.bandwidthclec.com</to>
              <subject>PATLive%20Testing</subject>
              <type>text/plain</type>
              <hint>the%20hint</hint>
              <replying>true</replying>
            </headers>
            <Content-Length>23</Content-Length>
            Hello from Chad Engler!
          </event>
         */
    case 'xml':
        data = '<event>\n';

        //add headers
        data += '  <headers>\n';
        this.headers.forEach(function(header, i) {
            data += '    <' + header.name + '>' + header.value + '</' + header.name + '>\n';
        });
        data += '  </headers>\n';

        //add body
        if(this.body) {
            data += '  ' + this.body + '\n';
        }

        data += '</event>';

        return data;
    }
};

//Sets the priority of an event to $number in case it's fired.
//'NORMAL', 'LOW', 'HIGH', 'INVALID'
Event.prototype.setPriority = function(priority) {
    this.addHeader('priority', priority);
};

//Gets the header with the key of $header_name from an event object.
Event.prototype.getHeader = function(name) {
    var h = this._findHeader(name);
    return (h ? h.value : null);
};

//Gets the body of an event object.
Event.prototype.getBody = function() {
    return this.body;
};

//Gets the event type of an event object.
Event.prototype.getType = function() {
    return this.type;
};

//Add $value to the body of an event object.
// This can be called multiple times for the same event object.
Event.prototype.addBody = function(value) {
    return this.body += value;
};

//Add a header with key = $header_name and value = $value
// to an event object. This can be called multiple times
// for the same event object.
Event.prototype.addHeader = function(name, value) {
    var h = this._findHeader(name);

    if(h) h.value = value;
    else this.headers.push({ name: name, value: value });

    return value;
};

//Delete the header with key $header_name from an event object.
Event.prototype.delHeader = function(name) {
    var i = this._findHeaderIndex(name);

    return (i === null ? null : this.headers.splice(i, 1));
};

//Sets the pointer to the first header in an event object,
// and returns it's key name. This must be called before nextHeader is called.
Event.prototype.firstHeader = function() {
    this.hPtr = 0;

    return this.headers[0].name;
};

//Moves the pointer to the next header in an event object,
// and returns it's key name. firstHeader must be called
// before this method to set the pointer. If you're already
// on the last header when this method is called, then it will return NULL.
Event.prototype.nextHeader = function() {
    //if no firstHeader called yet
    if(this.hPtr === null)
        return null;

    //if reached end
    if(this.hPtr === (this.headers.length - 1))
        return this.hPtr = null;

    //increment and return
    return this.headers[++this.hPtr].name;
};

Event.prototype._findHeaderIndex = function(name) {
    for(var i = 0, len = this.headers.length; i < len; ++i) {
        if(this.headers[i].name === name)
            return i;
    }

    return null;
};

Event.prototype._findHeader = function(name) {
    var i = this._findHeaderIndex(name);

    return (i === null ? null : this.headers[i]);
};<|endoftext|>


<commit_before>var contatos = [
    {_id: 1, nome: 'Contato Hebreu 1', email: 'exemplocontato1@gmail.com'},
    {_id: 2, nome: 'Maria exemplo 2', email: 'exemplocontato2@gmail.com'},
    {_id: 3, nome: 'Contato Laranja 3', email: 'exemplocontato3@gmail.com'},
    {_id: 4, nome: 'Ronaldo Camelo 4', email: 'exemplocontato4@gmail.com'},
    {_id: 5, nome: 'Contato Araujo 5', email: 'exemplocontato5@gmail.com'},

]

module.exports = function() {
    var controller =  {};

    controller.listaContatos = function(req, res) {
        res.json(contatos);
    };

    controller.obtemContato = function(req, res) {
        var idContato = req.params.id;
        var contato = contatos.filter(function(contato) {
            return contato._id == idContato;
        })[0];
        contato ? res.json(contato) : res.status(404).send('Contato não encontrado');
    };

    controller.removeContato = function(req, res) {
        var idContato = req.params.id;

        contatos = contatos.filter(function(Contato) {
            return contatos._id != idContato;
        });
        res.status(204).end();
    }

    return controller;
}<commit_msg>modificado nome para entender melhor<commit_after>var lstContatoTemp = [
    {_id: 1, nome: 'Contato Hebreu 1', email: 'exemplocontato1@gmail.com'},
    {_id: 2, nome: 'Maria exemplo 2', email: 'exemplocontato2@gmail.com'},
    {_id: 3, nome: 'Contato Laranja 3', email: 'exemplocontato3@gmail.com'},
    {_id: 4, nome: 'Ronaldo Camelo 4', email: 'exemplocontato4@gmail.com'},
    {_id: 5, nome: 'Contato Araujo 5', email: 'exemplocontato5@gmail.com'},

]

module.exports = function() {
    var controller =  {};

    controller.listaContatos = function(req, res) {
        res.json(lstContatoTemp);
    };

    controller.obtemContato = function(req, res) {
        var idContato = req.params.id;
        var contato = lstContatoTemp.filter(function(contato) {
            return contato._id == idContato;
        })[0];
        contato ? res.json(contato) : res.status(404).send('Contato não encontrado');
    };

    controller.removeContato = function(req, res) {
        var idContato = req.params.id;

        lstContatoTemp = lstContatoTemp.filter(function(Contato) {
            return lstContatoTemp._id != idContato;
        });
        res.status(204).end();
    }

    return controller;
}<|endoftext|>


<commit_before>const TABLE_COLUMN_START = 2;
const TEACHER_INDEX = 18;
let tableBody = document.querySelector('.datadisplaytable tbody');

function getTeacherNames () {
  let teachers = [];
  let teacherName;
  let isPrimaryTeacherIndex;
  let teacherNames = Arrary.prototype.slice(document.querySelectorAll('.datadisplaytable tbody tr:nth-child(n + 3)'));
  isPrimaryTeacherIndex = teacherName.indexOf('(');
  if (isPrimaryTeacherIndex > -1) {
    teacherName = teacherName.slice(0, isPrimaryTeacherIndex - 1);
  }
  teachers.push(teacherName);
}

// This layout table is used to present the sections found

let child;
let parent;
let column;
let ratingText;

column = document.createElement('th');
ratingText = document.createTextNode('COLUMN');
column.appendChild(ratingText);
column.classList.add('ddheader');

parent = document.querySelector('.datadisplaytable tbody tr:nth-child(3)');
child = parent.querySelector('th:nth-child(19)');
parent.insertBefore(column, child);

column = document.createElement('td');
ratingText = document.createTextNode('COLUMN');
column.appendChild(ratingText);
column.classList.add('dddefault');

parent = document.querySelector('.datadisplaytable tbody tr:nth-child(4)');
child = parent.querySelector('td:nth-child(18)');
parent.insertBefore(column, child);

column = document.createElement('td');
ratingText = document.createTextNode('COLUMN');
column.appendChild(ratingText);
column.classList.add('dddefault');

parent = document.querySelector('.datadisplaytable tbody tr:nth-child(5)');
child = parent.querySelector('td:nth-child(18)');
parent.insertBefore(column, child);<commit_msg>clean up banweb scraper, use ES6<commit_after>let tableBody = document.querySelector('.datadisplaytable tbody');

function getTeacherNames () {
  let teacherNameList = Array.from(tableBody.querySelectorAll('tr:nth-child(n+4) td:nth-child(18)'));

  teacherNameList = teacherNameList.map(name => {
    let isPrimaryTeacherIndex;
    let teacherName = name.innerText;
    isPrimaryTeacherIndex = teacherName.indexOf('(');
    if (isPrimaryTeacherIndex > -1) {
      return teacherName.slice(0, isPrimaryTeacherIndex - 1);
    }
    return teacherName;
  });

  return teacherNameList;
}

getTeacherNames();

// This layout table is used to present the sections found

let child;
let parent;
let column;
let ratingText;
//
//
// column = document.createElement('th');
// ratingText = document.createTextNode('COLUMN');
// column.appendChild(ratingText);
// column.classList.add('ddheader');
//
// parent = document.querySelector('.datadisplaytable tbody tr:nth-child(3)');
// child = parent.querySelector('th:nth-child(19)');
// parent.insertBefore(column, child);
//
// column = document.createElement('td');
// ratingText = document.createTextNode('COLUMN');
// column.appendChild(ratingText);
// column.classList.add('dddefault');
//
//
// parent = document.querySelector('.datadisplaytable tbody tr:nth-child(4)');
// child = parent.querySelector('td:nth-child(18)');
// parent.insertBefore(column, child);
//
// column = document.createElement('td');
// ratingText = document.createTextNode('COLUMN');
// column.appendChild(ratingText);
// column.classList.add('dddefault');
//
// parent = document.querySelector('.datadisplaytable tbody tr:nth-child(5)');
// child = parent.querySelector('td:nth-child(18)');
// parent.insertBefore(column, child);<|endoftext|>


<commit_before>var _Libbum$oration$Native_Markdown = function() {

    // VIRTUAL-DOM WIDGETS

    function toHtml(options, factList, rawMarkdown)
    {
	var model = {
	    options: options,
	    markdown: rawMarkdown
	};
	return _elm_lang$virtual_dom$Native_VirtualDom.custom(factList, model, implementation);
    }

    // WIDGET IMPLEMENTATION

    var implementation = {
	render: render,
	diff: diff
    };

    function render(model)
    {
	var html = parse(model.markdown, formatOptions(model.options));
	var div = document.createElement('div');
	div.innerHTML = html;
	return div;
    }

    function diff(a, b)
    {
	if (a.model.markdown === b.model.markdown && a.model.options === b.model.options)
	{
	    return null;
	}

	return {
	    applyPatch: applyPatch,
	    data: parse(b.model.markdown, formatOptions(b.model.options))
	};
    }

    function applyPatch(domNode, data)
    {
	domNode.innerHTML = data;
	return domNode;
    }

    // ACTUAL MARKDOWN PARSER

    var parse = function() {
	// catch the parser object regardless of the outer environment.
	// (ex. a CommonJS module compatible environment.)
	// note that this depends on markdown-it's implementation of environment detection.
	var module = {};
	var exports = module.exports = {};

	// markdown-it 8.4.0 https://github.com//markdown-it/markdown-it @license MIT
	// markdown-it-katex 2.0.3 https://github.com/waylonflinn/markdown-it-katex/ @license MIT
	return function (text, options) {
	    const md = require('markdown-it')(options.preset, options),
		mk = require('markdown-it-katex');

	    md.use(mk);
	    return md.render(text);
	};
    }();

    // FORMAT OPTIONS FOR PARSER IMPLEMENTATION

    function formatOptions(options) {

	function toHighlight(code, lang) {

	    try {
		if (!lang && options.defaultHighlighting.ctor && options.defaultHighlighting.ctor === 'Just') {
		    lang = options.defaultHighlighting._0;
		}

		if (typeof Prism !== 'undefined' && lang && Prism.languages[lang]) {
		    return Prism.highlight(code, Prism.languages[lang]);
		}
	    }
	    catch(e) {
		return code;
	    }
	}

	options.highlight = toHighlight;

	// assign all 'Just' values and delete all 'Nothing' values
	for (var key in options) {
	    var val = options[key];
	    if (!val) {
		continue;
	    }
	    if (val.ctor === 'Just') {
		options[key] = val._0;
	    } else if (val.ctor === 'Nothing') {
		delete options[key];
	    }
	}

	if (options.githubFlavored) {
	    options.preset = 'default';
	} else {
	    options.preset = 'commonmark';
	}

	if (options.quotes) {
	    options.quotes =
		[ options.quotes.doubleLeft
		    , options.quotes.doubleRight
		    , options.quotes.singleLeft
		    , options.quotes.singleRight
		];
	}

	return options;
    }

    // EXPORTS

    return {
	toHtml: F3(toHtml)
    };

}();
<commit_msg>We should also return the text if we fail to mark it up bet everything else is ok.<commit_after>var _Libbum$oration$Native_Markdown = function() {

    // VIRTUAL-DOM WIDGETS

    function toHtml(options, factList, rawMarkdown)
    {
	var model = {
	    options: options,
	    markdown: rawMarkdown
	};
	return _elm_lang$virtual_dom$Native_VirtualDom.custom(factList, model, implementation);
    }

    // WIDGET IMPLEMENTATION

    var implementation = {
	render: render,
	diff: diff
    };

    function render(model)
    {
	var html = parse(model.markdown, formatOptions(model.options));
	var div = document.createElement('div');
	div.innerHTML = html;
	return div;
    }

    function diff(a, b)
    {
	if (a.model.markdown === b.model.markdown && a.model.options === b.model.options)
	{
	    return null;
	}

	return {
	    applyPatch: applyPatch,
	    data: parse(b.model.markdown, formatOptions(b.model.options))
	};
    }

    function applyPatch(domNode, data)
    {
	domNode.innerHTML = data;
	return domNode;
    }

    // ACTUAL MARKDOWN PARSER

    var parse = function() {
	// catch the parser object regardless of the outer environment.
	// (ex. a CommonJS module compatible environment.)
	// note that this depends on markdown-it's implementation of environment detection.
	var module = {};
	var exports = module.exports = {};

	// markdown-it 8.4.0 https://github.com//markdown-it/markdown-it @license MIT
	// markdown-it-katex 2.0.3 https://github.com/waylonflinn/markdown-it-katex/ @license MIT
	return function (text, options) {
	    const md = require('markdown-it')(options.preset, options),
		mk = require('markdown-it-katex');

	    md.use(mk);
	    return md.render(text);
	};
    }();

    // FORMAT OPTIONS FOR PARSER IMPLEMENTATION

    function formatOptions(options) {

	function toHighlight(code, lang) {

	    try {
		if (!lang && options.defaultHighlighting.ctor && options.defaultHighlighting.ctor === 'Just') {
		    lang = options.defaultHighlighting._0;
		}

		if (typeof Prism !== 'undefined' && lang && Prism.languages[lang]) {
		    return Prism.highlight(code, Prism.languages[lang]);
		}

		return code;
	    }
	    catch(e) {
		return code;
	    }
	}

	options.highlight = toHighlight;

	// assign all 'Just' values and delete all 'Nothing' values
	for (var key in options) {
	    var val = options[key];
	    if (!val) {
		continue;
	    }
	    if (val.ctor === 'Just') {
		options[key] = val._0;
	    } else if (val.ctor === 'Nothing') {
		delete options[key];
	    }
	}

	if (options.githubFlavored) {
	    options.preset = 'default';
	} else {
	    options.preset = 'commonmark';
	}

	if (options.quotes) {
	    options.quotes =
		[ options.quotes.doubleLeft
		    , options.quotes.doubleRight
		    , options.quotes.singleLeft
		    , options.quotes.singleRight
		];
	}

	return options;
    }

    // EXPORTS

    return {
	toHtml: F3(toHtml)
    };

}();
<|endoftext|>


<commit_before>import $ from 'blingblingjs'
import hotkeys from 'hotkeys-js'

import { EditText } from './text'
import { canMoveLeft, canMoveRight, canMoveUp } from './move'
import { watchImagesForUpload } from './imageswap'
import { queryPage } from './search'
import { htmlStringToDom, createClassname, isOffBounds, getStyles } from '../utilities/'

export function Selectable() {
  const elements          = $('body')
  let selected            = []
  let selectedCallbacks   = []
  let labels              = []
  let handles             = []

  const listen = () => {
    elements.forEach(el => el.addEventListener('click', on_click, true))
    elements.forEach(el => el.addEventListener('dblclick', on_dblclick, true))
    elements.on('selectstart', on_selection)
    elements.on('mouseover', on_hover)
    elements.on('mouseout', on_hoverout)

    document.addEventListener('copy', on_copy)
    document.addEventListener('cut', on_cut)
    document.addEventListener('paste', on_paste)

    watchCommandKey()

    hotkeys('cmd+alt+c', on_copy_styles)
    hotkeys('cmd+alt+v', e => on_paste_styles())
    hotkeys('esc', on_esc)
    hotkeys('cmd+d', on_duplicate)
    hotkeys('backspace,del,delete', on_delete)
    hotkeys('alt+del,alt+backspace', on_clearstyles)
    hotkeys('cmd+e,cmd+shift+e', on_expand_selection)
    hotkeys('cmd+g,cmd+shift+g', on_group)
    hotkeys('tab,shift+tab,enter,shift+enter', on_keyboard_traversal)
  }

  const unlisten = () => {
    elements.forEach(el => el.removeEventListener('click', on_click, true))
    elements.forEach(el => el.removeEventListener('dblclick', on_dblclick, true))
    elements.off('selectstart', on_selection)
    elements.off('mouseover', on_hover)
    elements.off('mouseout', on_hoverout)

    document.removeEventListener('copy', on_copy)
    document.removeEventListener('cut', on_cut)
    document.removeEventListener('paste', on_paste)

    hotkeys.unbind('esc,cmd+d,backspace,del,delete,alt+del,alt+backspace,cmd+e,cmd+shift+e,cmd+g,cmd+shift+g,tab,shift+tab,enter,shift+enter')
  }

  const on_click = e => {
    if (isOffBounds(e.target) && !selected.filter(el => el == e.target).length)
      return

    e.preventDefault()
    if (!e.altKey) e.stopPropagation()
    if (!e.shiftKey) unselect_all()
    select(e.target)
  }

  const on_dblclick = e => {
    e.preventDefault()
    e.stopPropagation()
    if (isOffBounds(e.target)) return
    $('tool-pallete')[0].toolSelected('text')
  }

  const watchCommandKey = e => {
    let did_hide = false

    document.onkeydown = function(e) {
      if (hotkeys.ctrl && selected.length) {
        $('pb-handles, pb-label').forEach(el =>
          el.style.display = 'none')

        did_hide = true
      }
    }

    document.onkeyup = function(e) {
      if (did_hide) {
        $('pb-handles, pb-label').forEach(el =>
          el.style.display = null)

        did_hide = false
      }
    }
  }

  const on_esc = _ =>
    selected.length && unselect_all()

  const on_duplicate = e => {
    const root_node = selected[0]
    if (!root_node) return

    const deep_clone = root_node.cloneNode(true)
    deep_clone.removeAttribute('data-selected')
    root_node.parentNode.insertBefore(deep_clone, root_node.nextSibling)
    e.preventDefault()
  }

  const on_delete = e =>
    selected.length && delete_all()

  const on_clearstyles = e =>
    selected.forEach(el =>
      el.attr('style', null))

  const on_copy = e => {
    // if user has selected text, dont try to copy an element
    if (window.getSelection().toString().length)
      return

    if (selected[0] && this.node_clipboard !== selected[0]) {
      e.preventDefault()
      let $node = selected[0].cloneNode(true)
      $node.removeAttribute('data-selected')
      this.copy_backup = $node.outerHTML
      e.clipboardData.setData('text/html', this.copy_backup)
    }
  }

  const on_cut = e => {
    if (selected[0] && this.node_clipboard !== selected[0]) {
      let $node = selected[0].cloneNode(true)
      $node.removeAttribute('data-selected')
      this.copy_backup = $node.outerHTML
      e.clipboardData.setData('text/html', this.copy_backup)
      selected[0].remove()
    }
  }

  const on_paste = e => {
    const clipData = e.clipboardData.getData('text/html')
    const potentialHTML = clipData || this.copy_backup
    if (selected[0] && potentialHTML) {
      e.preventDefault()
      selected[0].appendChild(
        htmlStringToDom(potentialHTML))
    }
  }

  const on_copy_styles = e => {
    e.preventDefault()
    this.copied_styles = selected.map(el =>
      getStyles(el))
  }

  const on_paste_styles = (index = 0) =>
    selected.forEach(el => {
      this.copied_styles[index]
        .map(({prop, value}) =>
          el.style[prop] = value)

      index >= this.copied_styles.length - 1
        ? index = 0
        : index++
    })

  const on_expand_selection = (e, {key}) => {
    e.preventDefault()

    expandSelection({
      query:  combineNodeNameAndClass(selected[0]),
      all:    key.includes('shift'),
    })
  }

  const on_group = (e, {key}) => {
    e.preventDefault()

    if (key.split('+').includes('shift')) {
      let $selected = [...selected]
      unselect_all()
      $selected.reverse().forEach(el => {
        let l = el.children.length
        while (el.children.length > 0) {
          var node = el.childNodes[el.children.length - 1]
          if (node.nodeName !== '#text')
            select(node)
          el.parentNode.prepend(node)
        }
        el.parentNode.removeChild(el)
      })
    }
    else {
      let div = document.createElement('div')
      selected[0].parentNode.prepend(
        selected.reverse().reduce((div, el) => {
          div.appendChild(el)
          return div
        }, div)
      )
      unselect_all()
      select(div)
    }
  }

  const on_selection = e =>
    !isOffBounds(e.target)
    && selected.length
    && selected[0].textContent != e.target.textContent
    && e.preventDefault()

  const on_keyboard_traversal = (e, {key}) => {
    if (selected.length !== 1) return

    e.preventDefault()
    e.stopPropagation()

    const current = selected[0]

    if (key.includes('shift')) {
      if (key.includes('tab') && canMoveLeft(current)) {
        unselect_all()
        select(canMoveLeft(current))
      }
      if (key.includes('enter') && canMoveUp(current)) {
        unselect_all()
        select(current.parentNode)
      }
    }
    else {
      if (key.includes('tab') && canMoveRight(current)) {
        unselect_all()
        select(canMoveRight(current))
      }
      if (key.includes('enter') && current.children.length) {
        unselect_all()
        select(current.children[0])
      }
    }
  }

  const on_hover = ({target}) => {
    if (isOffBounds(target)) return
    target.setAttribute('data-hover', true)
  }

  const on_hoverout = ({target}) => {
    target.removeAttribute('data-hover')
  }

  const select = el => {
    el.setAttribute('data-selected', true)
    overlayMetaUI(el)
    selected.unshift(el)
    tellWatchers()
  }

  const unselect_all = () => {
    selected
      .forEach(el =>
        $(el).attr({
          'data-selected':      null,
          'data-selected-hide': null,
          'data-label-id':      null,
          'data-hover':         null,
        }))

    handles.forEach(el =>
      el.remove())

    labels.forEach(el =>
      el.remove())

    labels    = []
    handles   = []
    selected  = []
  }

  const delete_all = () => {
    const selected_after_delete = selected.map(el => {
      if (canMoveRight(el))     return canMoveRight(el)
      else if (canMoveLeft(el)) return canMoveLeft(el)
      else if (el.parentNode)   return el.parentNode
    })

    Array.from([...selected, ...labels, ...handles]).forEach(el =>
      el.remove())

    labels    = []
    handles   = []
    selected  = []

    selected_after_delete.forEach(el =>
      select(el))
  }

  const expandSelection = ({query, all = false}) => {
    if (all) {
      const unselecteds = $(query + ':not([data-selected])')
      unselecteds.forEach(select)
    }
    else {
      const potentials = $(query)
      if (!potentials) return

      const root_node_index = potentials.reduce((index, node, i) =>
        combineNodeNameAndClass(node) == query
          ? index = i
          : index
      , null)

      if (root_node_index !== null) {
        if (!potentials[root_node_index + 1]) {
          const potential = potentials.filter(el => !el.attr('data-selected'))[0]
          if (potential) select(potential)
        }
        else {
          select(potentials[root_node_index + 1])
        }
      }
    }
  }

  const combineNodeNameAndClass = node =>
    `${node.nodeName.toLowerCase()}${createClassname(node)}`

  const overlayMetaUI = el => {
    let handle = createHandle(el)
    let label  = createLabel(el, `
      ${el.nodeName.toLowerCase()}
      ${el.id && '#' + el.id}
      ${createClassname(el).split('.')
        .filter(name => name != '')
        .reduce((links, name) => `
          ${links}
          .${name}
        `, '')
      }
    `)

    let observer        = createObserver(el, {handle,label})
    let parentObserver  = createObserver(el, {handle,label})

    observer.observe(el, { attributes: true })
    parentObserver.observe(el.parentNode, { childList:true, subtree:true })

    $(label).on('DOMNodeRemoved', _ => {
      observer.disconnect()
      parentObserver.disconnect()
    })
  }

  const setLabel = (el, label) =>
    label.update = el.getBoundingClientRect()

  const createLabel = (el, text) => {
    if (!labels[parseInt(el.getAttribute('data-label-id'))]) {
      const label = document.createElement('pb-label')

      label.text = text
      label.position = {
        boundingRect:   el.getBoundingClientRect(),
        node_label_id:  labels.length,
      }
      el.setAttribute('data-label-id', labels.length)

      document.body.appendChild(label)

      $(label).on('query', ({detail}) => {
        if (!detail.text) return
        this.query_text = detail.text

        queryPage('[data-hover]', el =>
          el.setAttribute('data-hover', null))

        queryPage(this.query_text + ':not([data-selected])', el =>
          detail.activator === 'mouseenter'
            ? el.setAttribute('data-hover', true)
            : select(el))
      })

      $(label).on('mouseleave', e => {
        e.preventDefault()
        e.stopPropagation()
        queryPage('[data-hover]', el =>
          el.setAttribute('data-hover', null))
      })

      labels[labels.length] = label
      return label
    }
  }

  const createHandle = el => {
    if (!handles[parseInt(el.getAttribute('data-label-id'))]) {
      const handle = document.createElement('pb-handles')

      handle.position = {
        boundingRect:   el.getBoundingClientRect(),
        node_label_id:  handles.length,
      }

      document.body.appendChild(handle)

      handles[handles.length] = handle
      return handle
    }
  }

  const setHandle = (node, handle) => {
    handle.position = {
      boundingRect:   node.getBoundingClientRect(),
      node_label_id:  node.getAttribute('data-label-id'),
    }
  }

  const createObserver = (node, {label,handle}) =>
    new MutationObserver(list => {
      setLabel(node, label)
      setHandle(node, handle)
    })

  const onSelectedUpdate = (cb, immediateCallback = true) => {
    selectedCallbacks.push(cb)
    if (immediateCallback) cb(selected)
  }

  const removeSelectedCallback = cb =>
    selectedCallbacks = selectedCallbacks.filter(callback => callback != cb)

  const tellWatchers = () =>
    selectedCallbacks.forEach(cb => cb(selected))

  const disconnect = () => {
    unselect_all()
    unlisten()
  }

  watchImagesForUpload()
  listen()

  return {
    select,
    unselect_all,
    onSelectedUpdate,
    removeSelectedCallback,
    disconnect
  }
}<commit_msg>added support for unselecting a selected element #167<commit_after>import $ from 'blingblingjs'
import hotkeys from 'hotkeys-js'

import { EditText } from './text'
import { canMoveLeft, canMoveRight, canMoveUp } from './move'
import { watchImagesForUpload } from './imageswap'
import { queryPage } from './search'
import { htmlStringToDom, createClassname, isOffBounds, getStyles } from '../utilities/'

export function Selectable() {
  const elements          = $('body')
  let selected            = []
  let selectedCallbacks   = []
  let labels              = []
  let handles             = []

  const listen = () => {
    elements.forEach(el => el.addEventListener('click', on_click, true))
    elements.forEach(el => el.addEventListener('dblclick', on_dblclick, true))
    elements.on('selectstart', on_selection)
    elements.on('mouseover', on_hover)
    elements.on('mouseout', on_hoverout)

    document.addEventListener('copy', on_copy)
    document.addEventListener('cut', on_cut)
    document.addEventListener('paste', on_paste)

    watchCommandKey()

    hotkeys('cmd+alt+c', on_copy_styles)
    hotkeys('cmd+alt+v', e => on_paste_styles())
    hotkeys('esc', on_esc)
    hotkeys('cmd+d', on_duplicate)
    hotkeys('backspace,del,delete', on_delete)
    hotkeys('alt+del,alt+backspace', on_clearstyles)
    hotkeys('cmd+e,cmd+shift+e', on_expand_selection)
    hotkeys('cmd+g,cmd+shift+g', on_group)
    hotkeys('tab,shift+tab,enter,shift+enter', on_keyboard_traversal)
  }

  const unlisten = () => {
    elements.forEach(el => el.removeEventListener('click', on_click, true))
    elements.forEach(el => el.removeEventListener('dblclick', on_dblclick, true))
    elements.off('selectstart', on_selection)
    elements.off('mouseover', on_hover)
    elements.off('mouseout', on_hoverout)

    document.removeEventListener('copy', on_copy)
    document.removeEventListener('cut', on_cut)
    document.removeEventListener('paste', on_paste)

    hotkeys.unbind('esc,cmd+d,backspace,del,delete,alt+del,alt+backspace,cmd+e,cmd+shift+e,cmd+g,cmd+shift+g,tab,shift+tab,enter,shift+enter')
  }

  const on_click = e => {
    if (isOffBounds(e.target) && !selected.filter(el => el == e.target).length)
      return

    e.preventDefault()
    if (!e.altKey) e.stopPropagation()
    if (!e.shiftKey) unselect_all()
    if(e.shiftKey) {
      let selectedElem = selected.find( el => $(el).attr('data-label-id') == $(e.target).attr('data-label-id') )

      if(selectedElem) {
        let selectedElemId = $(selectedElem).attr('data-label-id')
        unselect(selectedElemId)
      }
    }
    select(e.target)
  }

  const unselect = id => {

  	labels.find( el => $(el).attr('data-label-id') == id ).remove()
    handles.find( el => $(el).attr('data-label-id') == id ).remove()

    labels = labels.filter( el => $(el).attr('data-label-id') !== id)
    handles = handles.filter( el => $(el).attr('data-label-id') !== id)

    selected.forEach(el => {
      if($(el).attr('data-label-id') == id) {
        $(el).attr({
          'data-selected':      null,
          'data-selected-hide': null,
          'data-label-id':      null,
          'data-hover':         null,
        })
      }
    })

    selected = selected.filter( el => $(el).attr('data-label-id') !== id)
  }

  const on_dblclick = e => {
    e.preventDefault()
    e.stopPropagation()
    if (isOffBounds(e.target)) return
    $('tool-pallete')[0].toolSelected('text')
  }

  const watchCommandKey = e => {
    let did_hide = false

    document.onkeydown = function(e) {
      if (hotkeys.ctrl && selected.length) {
        $('pb-handles, pb-label').forEach(el =>
          el.style.display = 'none')

        did_hide = true
      }
    }

    document.onkeyup = function(e) {
      if (did_hide) {
        $('pb-handles, pb-label').forEach(el =>
          el.style.display = null)

        did_hide = false
      }
    }
  }

  const on_esc = _ =>
    selected.length && unselect_all()

  const on_duplicate = e => {
    const root_node = selected[0]
    if (!root_node) return

    const deep_clone = root_node.cloneNode(true)
    deep_clone.removeAttribute('data-selected')
    root_node.parentNode.insertBefore(deep_clone, root_node.nextSibling)
    e.preventDefault()
  }

  const on_delete = e =>
    selected.length && delete_all()

  const on_clearstyles = e =>
    selected.forEach(el =>
      el.attr('style', null))

  const on_copy = e => {
    // if user has selected text, dont try to copy an element
    if (window.getSelection().toString().length)
      return

    if (selected[0] && this.node_clipboard !== selected[0]) {
      e.preventDefault()
      let $node = selected[0].cloneNode(true)
      $node.removeAttribute('data-selected')
      this.copy_backup = $node.outerHTML
      e.clipboardData.setData('text/html', this.copy_backup)
    }
  }

  const on_cut = e => {
    if (selected[0] && this.node_clipboard !== selected[0]) {
      let $node = selected[0].cloneNode(true)
      $node.removeAttribute('data-selected')
      this.copy_backup = $node.outerHTML
      e.clipboardData.setData('text/html', this.copy_backup)
      selected[0].remove()
    }
  }

  const on_paste = e => {
    const clipData = e.clipboardData.getData('text/html')
    const potentialHTML = clipData || this.copy_backup
    if (selected[0] && potentialHTML) {
      e.preventDefault()
      selected[0].appendChild(
        htmlStringToDom(potentialHTML))
    }
  }

  const on_copy_styles = e => {
    e.preventDefault()
    this.copied_styles = selected.map(el =>
      getStyles(el))
  }

  const on_paste_styles = (index = 0) =>
    selected.forEach(el => {
      this.copied_styles[index]
        .map(({prop, value}) =>
          el.style[prop] = value)

      index >= this.copied_styles.length - 1
        ? index = 0
        : index++
    })

  const on_expand_selection = (e, {key}) => {
    e.preventDefault()

    expandSelection({
      query:  combineNodeNameAndClass(selected[0]),
      all:    key.includes('shift'),
    })
  }

  const on_group = (e, {key}) => {
    e.preventDefault()

    if (key.split('+').includes('shift')) {
      let $selected = [...selected]
      unselect_all()
      $selected.reverse().forEach(el => {
        let l = el.children.length
        while (el.children.length > 0) {
          var node = el.childNodes[el.children.length - 1]
          if (node.nodeName !== '#text')
            select(node)
          el.parentNode.prepend(node)
        }
        el.parentNode.removeChild(el)
      })
    }
    else {
      let div = document.createElement('div')
      selected[0].parentNode.prepend(
        selected.reverse().reduce((div, el) => {
          div.appendChild(el)
          return div
        }, div)
      )
      unselect_all()
      select(div)
    }
  }

  const on_selection = e =>
    !isOffBounds(e.target)
    && selected.length
    && selected[0].textContent != e.target.textContent
    && e.preventDefault()

  const on_keyboard_traversal = (e, {key}) => {
    if (selected.length !== 1) return

    e.preventDefault()
    e.stopPropagation()

    const current = selected[0]

    if (key.includes('shift')) {
      if (key.includes('tab') && canMoveLeft(current)) {
        unselect_all()
        select(canMoveLeft(current))
      }
      if (key.includes('enter') && canMoveUp(current)) {
        unselect_all()
        select(current.parentNode)
      }
    }
    else {
      if (key.includes('tab') && canMoveRight(current)) {
        unselect_all()
        select(canMoveRight(current))
      }
      if (key.includes('enter') && current.children.length) {
        unselect_all()
        select(current.children[0])
      }
    }
  }

  const on_hover = ({target}) => {
    if (isOffBounds(target)) return
    target.setAttribute('data-hover', true)
  }

  const on_hoverout = ({target}) => {
    target.removeAttribute('data-hover')
  }

  const select = el => {
    el.setAttribute('data-selected', true)
    overlayMetaUI(el)
    selected.unshift(el)
    tellWatchers()
  }

  const unselect_all = () => {
    selected
      .forEach(el =>
        $(el).attr({
          'data-selected':      null,
          'data-selected-hide': null,
          'data-label-id':      null,
          'data-hover':         null,
        }))

    handles.forEach(el =>
      el.remove())

    labels.forEach(el =>
      el.remove())

    labels    = []
    handles   = []
    selected  = []
  }

  const delete_all = () => {
    const selected_after_delete = selected.map(el => {
      if (canMoveRight(el))     return canMoveRight(el)
      else if (canMoveLeft(el)) return canMoveLeft(el)
      else if (el.parentNode)   return el.parentNode
    })

    Array.from([...selected, ...labels, ...handles]).forEach(el =>
      el.remove())

    labels    = []
    handles   = []
    selected  = []

    selected_after_delete.forEach(el =>
      select(el))
  }

  const expandSelection = ({query, all = false}) => {
    if (all) {
      const unselecteds = $(query + ':not([data-selected])')
      unselecteds.forEach(select)
    }
    else {
      const potentials = $(query)
      if (!potentials) return

      const root_node_index = potentials.reduce((index, node, i) =>
        combineNodeNameAndClass(node) == query
          ? index = i
          : index
      , null)

      if (root_node_index !== null) {
        if (!potentials[root_node_index + 1]) {
          const potential = potentials.filter(el => !el.attr('data-selected'))[0]
          if (potential) select(potential)
        }
        else {
          select(potentials[root_node_index + 1])
        }
      }
    }
  }

  const combineNodeNameAndClass = node =>
    `${node.nodeName.toLowerCase()}${createClassname(node)}`

  const overlayMetaUI = el => {
    let handle = createHandle(el)
    let label  = createLabel(el, `
      ${el.nodeName.toLowerCase()}
      ${el.id && '#' + el.id}
      ${createClassname(el).split('.')
        .filter(name => name != '')
        .reduce((links, name) => `
          ${links}
          .${name}
        `, '')
      }
    `)

    let observer        = createObserver(el, {handle,label})
    let parentObserver  = createObserver(el, {handle,label})

    observer.observe(el, { attributes: true })
    parentObserver.observe(el.parentNode, { childList:true, subtree:true })

    $(label).on('DOMNodeRemoved', _ => {
      observer.disconnect()
      parentObserver.disconnect()
    })
  }

  const setLabel = (el, label) =>
    label.update = el.getBoundingClientRect()

  const createLabel = (el, text) => {
    if (!labels[parseInt(el.getAttribute('data-label-id'))]) {
      const label = document.createElement('pb-label')

      label.text = text
      label.position = {
        boundingRect:   el.getBoundingClientRect(),
        node_label_id:  labels.length,
      }
      el.setAttribute('data-label-id', labels.length)

      document.body.appendChild(label)

      $(label).on('query', ({detail}) => {
        if (!detail.text) return
        this.query_text = detail.text

        queryPage('[data-hover]', el =>
          el.setAttribute('data-hover', null))

        queryPage(this.query_text + ':not([data-selected])', el =>
          detail.activator === 'mouseenter'
            ? el.setAttribute('data-hover', true)
            : select(el))
      })

      $(label).on('mouseleave', e => {
        e.preventDefault()
        e.stopPropagation()
        queryPage('[data-hover]', el =>
          el.setAttribute('data-hover', null))
      })

      labels[labels.length] = label
      return label
    }
  }

  const createHandle = el => {
    if (!handles[parseInt(el.getAttribute('data-label-id'))]) {
      const handle = document.createElement('pb-handles')

      handle.position = {
        boundingRect:   el.getBoundingClientRect(),
        node_label_id:  handles.length,
      }

      document.body.appendChild(handle)

      handles[handles.length] = handle
      return handle
    }
  }

  const setHandle = (node, handle) => {
    handle.position = {
      boundingRect:   node.getBoundingClientRect(),
      node_label_id:  node.getAttribute('data-label-id'),
    }
  }

  const createObserver = (node, {label,handle}) =>
    new MutationObserver(list => {
      setLabel(node, label)
      setHandle(node, handle)
    })

  const onSelectedUpdate = (cb, immediateCallback = true) => {
    selectedCallbacks.push(cb)
    if (immediateCallback) cb(selected)
  }

  const removeSelectedCallback = cb =>
    selectedCallbacks = selectedCallbacks.filter(callback => callback != cb)

  const tellWatchers = () =>
    selectedCallbacks.forEach(cb => cb(selected))

  const disconnect = () => {
    unselect_all()
    unlisten()
  }

  watchImagesForUpload()
  listen()

  return {
    select,
    unselect_all,
    onSelectedUpdate,
    removeSelectedCallback,
    disconnect
  }
}<|endoftext|>


<commit_before>if (process.env.API_TARGET === 'staging') {
  console.log('Targetting the STAGING API')
}

module.exports = {
  lintOnSave: false,
  runtimeCompiler: true,
  publicPath: process.env.NODE_ENV === 'production' ? '/gwells/' : '/',
  configureWebpack: {
    devServer: {
      watchOptions: {
        ignored: /node_modules/,
        poll: 1000
      }
    }
  },
  devServer: {
    proxy: {
      '^/api/': {
        target: process.env.API_TARGET === 'staging' ? 'https://gwells-staging.pathfinder.gov.bc.ca/' : 'http://backend:8000/',
        pathRewrite: {
          '^/api': '/gwells/api/v2'
        }
      }
    }
  }
}
<commit_msg>Changed API_TARGET to work with any URL instead of just 'staging'<commit_after>if (process.env.API_TARGET) {
  console.log(`Targetting the API ${process.env.API_TARGET}`)
}

module.exports = {
  lintOnSave: false,
  runtimeCompiler: true,
  publicPath: process.env.NODE_ENV === 'production' ? '/gwells/' : '/',
  configureWebpack: {
    devServer: {
      watchOptions: {
        ignored: /node_modules/,
        poll: 1000
      }
    }
  },
  devServer: {
    proxy: {
      '^/api/': {
        target: process.env.API_TARGET || 'http://backend:8000/',
        pathRewrite: {
          '^/api': '/gwells/api/v2'
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>(function(context, localforage) {
	var event = new Event('network-ready'),
		obj = {},
		startedAt = new Date().getTime(),
		storageKey = 'latencyStore';

	obj.ajax = function(url, data, callback) {
		if (typeof data === 'function') {
			callback = data;
			data = null;
		}
		var xhr = new XMLHttpRequest(),
			ajaxCallback = function() {
				recordLatency(data && JSON ? JSON.stringify(data).length : 0, startedAt, function(){
					if (typeof callback === 'function') {
						callback(xhr.response, xhr.status, xhr);
					}
					if (xhr.status !== null) {
						context.dispatchEvent(event);
					}
				});
			};

		xhr.onload = ajaxCallback;
		xhr.onerror = ajaxCallback;
		xhr.open(data !== null ? 'POST' : 'GET', url);

		xhr.send(data || null);

	};

	function recordLatency (size, startedAt, finishedCallback){
		var now = new Date().getTime();
		if (!localforage) {
			return;
		}
		localforage.getItem(storageKey, function(err, value){
			var store = value || [];
			store.push({begin: startedAt, end: now, size: size});
			localforage.setItem(storageKey, store, finishedCallback);
		});
	}

	obj.getLatestAccessTimeStamp = function(callback) {
		if (!localforage) {
			if (typeof callback === 'function') {
				callback(null);
			}
			return;
		}
		localforage.getItem(storageKey, function(err, value) {
			if (value !== null && value.length > 0) {
				var first = value.pop();
				callback(first['end']);
			} else {
				callback(null);
			}
		});
	};

	obj.getHistory = function(callback) {
		if (!localforage) {
			if (typeof callback === 'function') {
				callback(null);
			}
			return;
		}
		localforage.getItem(storageKey, function(err, value) {
			if (typeof callback === 'function') {
				callback(value);
			}
		});
	};

	context.AL = obj;
})(this, typeof localforage !== 'undefined' ? localforage : null);
<commit_msg>I didn't know that sent data needed to be in a string. SO STRINGIFY IT!<commit_after>(function(context, localforage) {
	var event = new Event('network-ready'),
		obj = {},
		startedAt = new Date().getTime(),
		storageKey = 'latencyStore';

	obj.ajax = function(url, data, callback) {
		if (typeof data === 'function') {
			callback = data;
			data = null;
		}
		if (data && typeof data !== 'string' && JSON) {
			data = JSON.stringify(data);
		}
		data = data ? data.toString() : null;
		var xhr = new XMLHttpRequest(),
			ajaxCallback = function() {
				recordLatency(data ? data.length : 0, startedAt, function(){
					if (typeof callback === 'function') {
						callback(xhr.response, xhr.status, xhr);
					}
					if (xhr.status !== null) {
						context.dispatchEvent(event);
					}
				});
			};

		xhr.onload = ajaxCallback;
		xhr.onerror = ajaxCallback;
		xhr.open(data !== null ? 'POST' : 'GET', url);

		xhr.send(data || null);

	};

	function recordLatency (size, startedAt, finishedCallback){
		var now = new Date().getTime();
		if (!localforage) {
			return;
		}
		localforage.getItem(storageKey, function(err, value){
			var store = value || [];
			store.push({begin: startedAt, end: now, size: size});
			localforage.setItem(storageKey, store, finishedCallback);
		});
	}

	obj.getLatestAccessTimeStamp = function(callback) {
		if (!localforage) {
			if (typeof callback === 'function') {
				callback(null);
			}
			return;
		}
		localforage.getItem(storageKey, function(err, value) {
			if (value !== null && value.length > 0) {
				var first = value.pop();
				callback(first['end']);
			} else {
				callback(null);
			}
		});
	};

	obj.getHistory = function(callback) {
		if (!localforage) {
			if (typeof callback === 'function') {
				callback(null);
			}
			return;
		}
		localforage.getItem(storageKey, function(err, value) {
			if (typeof callback === 'function') {
				callback(value);
			}
		});
	};

	context.AL = obj;
})(this, typeof localforage !== 'undefined' ? localforage : null);
<|endoftext|>


<commit_before>"use strict";

var _ = require("lodash"),
    path = require("path"),
    fs = require("fs"),
    keygen = require("./keygen"),
    config = require("../config/config");

if (!fs.existsSync(config.keystorePath)) {
  console.log("Generating keypair at " + config.keystorePath);
  keygen.createKeypair(config.keystorePath, function (params) {
    keygen.createDER(params, config.derFilePath);
  });
}

<commit_msg>Important mission to eradicate double quotes!<commit_after>'use strict';

var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var keygen = require('./keygen');
var config = require('../config/config');

if (!fs.existsSync(config.keystorePath)) {
	console.log('Generating keypair at ' + config.keystorePath);
	keygen.createKeypair(config.keystorePath, function(params) {
		keygen.createDER(params, config.derFilePath);
	});
}<|endoftext|>


<commit_before>//loading overlay vars:
var loading = $("#content").busyBox({spinner: ''} );

_.extend(_, {
  sum: function (arr) {
    return _.reduce(arr, function(sum, v) {return sum + v;}, 0);
  },
  isPresent : function(obj) {
    return !(_.isNull(obj) || _.isUndefined(obj));
  }
});

window.AppView = Backbone.View.extend({
  API_URL : globals.api_url,

  initialize : function() {
    _.bindAll(this, 'api_result', 'handleInputElementsUpdate');

    // this at the moment used for the loading box. to figure out
    // if there are still api calls happening.
    this.api_call_stack = [];

    // Jaap MVC legacy
    this.inputElementsController = window.input_elements;
    this.inputElementsController.bind("change", this.handleInputElementsUpdate);

    this.settings = new Setting(); // At this point settings is empty!!
    // let's get the ruby-fetched api_session_id
    this.settings.set({'api_session_id' : globals.api_session_id});
    this.scenario = new Scenario();
    this.peak_load = null; // initialize later in bootstrap, because we need to know what country it is
  },

  // At this point we have all the settings initialized.
  bootstrap : function() {
    var dashChangeEl = $('#dashboard_change');

    // If a "change dashboard" button is present, set up the DashboardChanger.
    if (dashChangeEl.length > 0) {
      new DashboardChangerView(dashChangeEl);
    }

    // DEBT Add check, so that boostrap is only called once.
    if (this.settings.get('country') == 'nl') {
      this.peak_load = new PeakLoad();
    }
    if (this.scenario.api_session_id() == null) {
      this.scenario.new_session();
      // after copmleting new_session() App.bootstrap is called again and will thus continue the else part
    } else {
      this.load_user_values();
      this.call_api();
    }
  },

  // Load User values for Sliders
  load_user_values : function() {
    this.inputElementsController.load_user_values();
  },

  call_api : function(input_params) {
    var self = this;
    var url = this.scenario.query_url(input_params);
    var keys = window.gqueries.keys();

    var keys_ids = _.select(keys, function(key) { return !key.match(/\(/);});
    var keys_string = _.select(keys, function(key) { return key.match(/\(/);});

    var params = {'r' : keys_ids.join(';'), 'result' : keys_string, 'use_fce' : App.settings.get('use_fce') };

    LockableFunction.setLock('call_api');
    this.showLoading();
    var jsonp = $.jsonp({
      url: url,
      data: params,
      success: this.handle_api_result,
      error: function() {
        console.log("Something went wrong");
        self.hideLoading();
      }
    });
    this.register_api_call(jsonp);
  },

  has_unfinished_api_calls : function() {
    return _.isEmpty(this.api_call_stack);
  },

  register_api_call : function(api_call) {
    this.api_call_stack.push(api_call);
  },

  unregister_api_call : function(api_call) {
    this.api_call_stack = _.without(this.api_call_stack, api_call);
  },

  // The following method could need some refactoring
  // e.g. el.set({ api_result : value_arr })
  // window.charts.first().trigger('change');
  // window.dashboard.trigger('change');
  handle_api_result : function(data) {
    //#############################################
    //
    // ATTENTION: This method is not called in the context of App
    //            but in the context of the jsonp object. So 'this'
    //            refers to the jsonp (that's how we get access to data)
    //            and not the App object.
    //
    //#############################################
    App.unregister_api_call(this);

    LockableFunction.removeLock('call_api');
    loading.fadeIn('fast'); //show loading overlay
    var result   = data.result;   // Results of this request for every "result[]" parameter

    $.each(result, function(gquery_key, value_arr) {
      $.each(window.gqueries.with_key(gquery_key), function(i, gquery) {
        gquery.handle_api_result(value_arr);
      });
    });
    window.charts.each(function(chart) { chart.trigger('change'); });
    window.policy_goals.invoke('update_view');
    window.policy_goals.update_totals();

    if (App.peak_load != null) {
      App.peak_load.trigger('change');
    }

    $("body").trigger("dashboardUpdate");
    App.hideLoading();
  },

  /**
   * Set the update in a cancelable action. When you
   * pull a slider A this method is called. It will call doUpdateRequest
   * after 500ms. When a slider is pulled again, that call will be canceled
   * (so that we don't flood the server). doUpdateRequest will get the dirty
   * sliders, reset all dirtyiness and make an API call.
   */
  handleInputElementsUpdate:function() {
    var func = $.proxy(this.doUpdateRequest, this);
    var lockable_function = function() { LockableFunction.deferExecutionIfLocked('update', func); };
    Util.cancelableAction('update',  lockable_function, {'sleepTime': 100});
  },

  /**
   * Get the value of all changed sliders. Get the chart. Sends those values to the server.
   */
  doUpdateRequest:function() {
    var dirtyInputElements = window.input_elements.dirty();

    if (dirtyInputElements.length == 0) { return; }

    var input_params = window.input_elements.api_update_params();
    window.input_elements.reset_dirty();
    window.App.call_api(input_params);
  },

  /*
   * Shows a busy box. Only if api_call_stack is empty. Make sure
   * that you call the showLoading before adding the jsonp to api_call_stack.
   */
  showLoading : function () {
    if (this.has_unfinished_api_calls()) {
      $("#charts_wrapper").busyBox({
        spinner: ''
      }).fadeIn('fast');
      $("#constraints").busyBox({
        classes: 'busybox ontop',
        spinner: ''
      }).fadeIn('fast');
    }
  },

  /*
   * Closes the loading box. will only close if there's no api_calls
   * running at the moment
   */
  hideLoading : function() {
    if (this.has_unfinished_api_calls()) {
      $("#charts_wrapper").busyBox('close');
      $("#constraints").busyBox('close');
      //loading.busyBox('close');
    }
  }
});

window.App = App = new AppView();

<commit_msg>Position BusyBox correctly when the window is scrolled.<commit_after>//loading overlay vars:
var loading = $("#content").busyBox({spinner: ''} );

_.extend(_, {
  sum: function (arr) {
    return _.reduce(arr, function(sum, v) {return sum + v;}, 0);
  },
  isPresent : function(obj) {
    return !(_.isNull(obj) || _.isUndefined(obj));
  }
});

window.AppView = Backbone.View.extend({
  API_URL : globals.api_url,

  initialize : function() {
    _.bindAll(this, 'api_result', 'handleInputElementsUpdate');

    // this at the moment used for the loading box. to figure out
    // if there are still api calls happening.
    this.api_call_stack = [];

    // Jaap MVC legacy
    this.inputElementsController = window.input_elements;
    this.inputElementsController.bind("change", this.handleInputElementsUpdate);

    this.settings = new Setting(); // At this point settings is empty!!
    // let's get the ruby-fetched api_session_id
    this.settings.set({'api_session_id' : globals.api_session_id});
    this.scenario = new Scenario();
    this.peak_load = null; // initialize later in bootstrap, because we need to know what country it is
  },

  // At this point we have all the settings initialized.
  bootstrap : function() {
    var dashChangeEl = $('#dashboard_change');

    // If a "change dashboard" button is present, set up the DashboardChanger.
    if (dashChangeEl.length > 0) {
      new DashboardChangerView(dashChangeEl);
    }

    // DEBT Add check, so that boostrap is only called once.
    if (this.settings.get('country') == 'nl') {
      this.peak_load = new PeakLoad();
    }
    if (this.scenario.api_session_id() == null) {
      this.scenario.new_session();
      // after copmleting new_session() App.bootstrap is called again and will thus continue the else part
    } else {
      this.load_user_values();
      this.call_api();
    }
  },

  // Load User values for Sliders
  load_user_values : function() {
    this.inputElementsController.load_user_values();
  },

  call_api : function(input_params) {
    var self = this;
    var url = this.scenario.query_url(input_params);
    var keys = window.gqueries.keys();

    var keys_ids = _.select(keys, function(key) { return !key.match(/\(/);});
    var keys_string = _.select(keys, function(key) { return key.match(/\(/);});

    var params = {'r' : keys_ids.join(';'), 'result' : keys_string, 'use_fce' : App.settings.get('use_fce') };

    LockableFunction.setLock('call_api');
    this.showLoading();
    var jsonp = $.jsonp({
      url: url,
      data: params,
      success: this.handle_api_result,
      error: function() {
        console.log("Something went wrong");
        self.hideLoading();
      }
    });
    this.register_api_call(jsonp);
  },

  has_unfinished_api_calls : function() {
    return _.isEmpty(this.api_call_stack);
  },

  register_api_call : function(api_call) {
    this.api_call_stack.push(api_call);
  },

  unregister_api_call : function(api_call) {
    this.api_call_stack = _.without(this.api_call_stack, api_call);
  },

  // The following method could need some refactoring
  // e.g. el.set({ api_result : value_arr })
  // window.charts.first().trigger('change');
  // window.dashboard.trigger('change');
  handle_api_result : function(data) {
    //#############################################
    //
    // ATTENTION: This method is not called in the context of App
    //            but in the context of the jsonp object. So 'this'
    //            refers to the jsonp (that's how we get access to data)
    //            and not the App object.
    //
    //#############################################
    App.unregister_api_call(this);

    LockableFunction.removeLock('call_api');
    loading.fadeIn('fast'); //show loading overlay
    var result   = data.result;   // Results of this request for every "result[]" parameter

    $.each(result, function(gquery_key, value_arr) {
      $.each(window.gqueries.with_key(gquery_key), function(i, gquery) {
        gquery.handle_api_result(value_arr);
      });
    });
    window.charts.each(function(chart) { chart.trigger('change'); });
    window.policy_goals.invoke('update_view');
    window.policy_goals.update_totals();

    if (App.peak_load != null) {
      App.peak_load.trigger('change');
    }

    $("body").trigger("dashboardUpdate");
    App.hideLoading();
  },

  /**
   * Set the update in a cancelable action. When you
   * pull a slider A this method is called. It will call doUpdateRequest
   * after 500ms. When a slider is pulled again, that call will be canceled
   * (so that we don't flood the server). doUpdateRequest will get the dirty
   * sliders, reset all dirtyiness and make an API call.
   */
  handleInputElementsUpdate:function() {
    var func = $.proxy(this.doUpdateRequest, this);
    var lockable_function = function() { LockableFunction.deferExecutionIfLocked('update', func); };
    Util.cancelableAction('update',  lockable_function, {'sleepTime': 100});
  },

  /**
   * Get the value of all changed sliders. Get the chart. Sends those values to the server.
   */
  doUpdateRequest:function() {
    var dirtyInputElements = window.input_elements.dirty();

    if (dirtyInputElements.length == 0) { return; }

    var input_params = window.input_elements.api_update_params();
    window.input_elements.reset_dirty();
    window.App.call_api(input_params);
  },

  /*
   * Shows a busy box. Only if api_call_stack is empty. Make sure
   * that you call the showLoading before adding the jsonp to api_call_stack.
   */
  showLoading : function () {
    if (this.has_unfinished_api_calls()) {
      $("#charts_wrapper").busyBox({
        spinner: ''
      }).fadeIn('fast');

      $("#constraints").busyBox({
        classes: 'busybox ontop',
        spinner: '',
        top:     '22px'
      }).fadeIn('fast');

      // BusyBox doesn't account for elements which have position: fixed when the
      // user has scrolled the window.
      $.fn.busyBox.container.find('.busybox').each(function () {
        var element = $(this), top;

        if (element.css('position') === 'fixed') {
            top = parseInt(element.css('top').match(/^\d+/)[0]);
            element.css('top', top - $(window).scrollTop());
        }
      });
    }
  },

  /*
   * Closes the loading box. will only close if there's no api_calls
   * running at the moment
   */
  hideLoading : function() {
    if (this.has_unfinished_api_calls()) {
      $("#charts_wrapper").busyBox('close');
      $("#constraints").busyBox('close');
      //loading.busyBox('close');
    }
  }
});

window.App = App = new AppView();

<|endoftext|>