<commit_before>'use strict';

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

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(fn) {
	return typeof fn === 'function';
}

function matchesSelector(el, selector) {
	if (isFunction(el.matches)) {
		return el.matches(selector);
	} else if (isFunction(el.webkitMatchesSelector)) {
		return el.webkitMatchesSelector(selector);
	} else if (isFunction(el.mozMatchesSelector)) {
		return el.mozMatchesSelector(selector);
	} else if (isFunction(el.msMatchesSelector)) {
		return el.msMatchesSelector(selector);
	} else if (isFunction(el.oMatchesSelector)) {
		return el.oMatchesSelector(selector);
	} else if (isFunction(el.webkitMatchesSelector)) {
		return el.webkitMatchesSelector(selector);
	}
}

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, 'mousemove', this.handleMouseMove);
		removeEvent(window, 'mouseup', this.handleMouseUp);
	},

	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
		};
	},

	handleMouseDown: function (e) {
		// 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;
		}

		// Initiate dragging
		this.setState({
			dragging: true,
			offsetX: e.clientX,
			offsetY: e.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, 'mousemove', this.handleMouseMove);
		addEvent(window, 'mouseup', this.handleMouseUp);
	},

	handleMouseUp: 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, 'mousemove', this.handleMouseMove);
		removeEvent(window, 'mouseup', this.handleMouseUp);
	},

	handleMouseMove: function (e) {
		// Calculate top and left
		var clientX = (this.state.startX + (e.clientX - this.state.offsetX));
		var clientY = (this.state.startY + (e.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;
		}

		// 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: 'react-draggable',
			onMouseUp: this.handleMouseUp,
			onMouseDown: this.handleMouseDown
		});
	}
});
<commit_msg>remove check-duplicate on matchesSelector<commit_after>'use strict';

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

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(fn) {
	return typeof fn === 'function';
}

function matchesSelector(el, selector) {
	if (isFunction(el.matches)) {
		return el.matches(selector);
	} else if (isFunction(el.webkitMatchesSelector)) {
		return el.webkitMatchesSelector(selector);
	} else if (isFunction(el.mozMatchesSelector)) {
		return el.mozMatchesSelector(selector);
	} else if (isFunction(el.msMatchesSelector)) {
		return el.msMatchesSelector(selector);
	} else if (isFunction(el.oMatchesSelector)) {
		return el.oMatchesSelector(selector);
	}
}

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, 'mousemove', this.handleMouseMove);
		removeEvent(window, 'mouseup', this.handleMouseUp);
	},

	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
		};
	},

	handleMouseDown: function (e) {
		// 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;
		}

		// Initiate dragging
		this.setState({
			dragging: true,
			offsetX: e.clientX,
			offsetY: e.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, 'mousemove', this.handleMouseMove);
		addEvent(window, 'mouseup', this.handleMouseUp);
	},

	handleMouseUp: 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, 'mousemove', this.handleMouseMove);
		removeEvent(window, 'mouseup', this.handleMouseUp);
	},

	handleMouseMove: function (e) {
		// Calculate top and left
		var clientX = (this.state.startX + (e.clientX - this.state.offsetX));
		var clientY = (this.state.startY + (e.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;
		}

		// 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: 'react-draggable',
			onMouseUp: this.handleMouseUp,
			onMouseDown: this.handleMouseDown
		});
	}
});
<|endoftext|>


<commit_before>var fs = require("fs");
var Q = require("q");

function DummyApiError(msg) {
    var self = this;

    self.toString = function () {
        return "<DummyApiError " + self.msg + ">";
    };
}

function DummyApi() {
    var self = this;

    // test hooks

    self.reset = function() {
        // set self.async to true to make everything asynchronous
        self.async = false;
        self.pending_async = 0;
        self.kv_store = {};
        self.config_store = {};
        self.metrics = {};
        self.http_calls = {
            'POST': {},
            'PUT': {},
            'HEAD': {},
            'DELETE': {},
            'GET': {}
        };
        self.outbound_sends = [];
        self.logs = [];
        self.request_calls = [];
        self.waiting_replies = [];
        self.done_calls = 0;
    };
    self.reset();

    self.reset_im = function() {
        self.im.user = null;
        self.im.i18n = null;
        self.im.i18n_lang = null;
        self.im.current_state = null;
    };

    self.add_reply = function(reply) {
        self.waiting_replies.push(reply);
    };

    self.load_http_fixture = function(filename) {
        var data = fs.readFileSync(filename);
        var fixture = JSON.parse(data);
        self.add_http_fixture(fixture);
    };

    self._format_http_request_data = function(content_type, data) {
        // convert a JSON fixture data into the required
        // content type.
        if (content_type == "application/json")
            return JSON.stringify(data);
        return data;
    };

    self._order_json_data = function(data) {
        // return JSON data with consistent key ordering
        // FIXME: This assumes the JSON data is a JS object
        var json = JSON.parse(data);
        var keys = Object.keys(json).sort();
        var frags = keys.map(function (k) {
            return '"' + k + '":' + JSON.stringify(json[k]);
        });
        data = "{" + frags.join(",") + "}";
        return data;
    };

    self._http_request_key = function(method, url, content_type, data) {
        if (content_type == "application/json") {
            data = self._order_json_data(data);
        }
        return url + "|" + content_type + "|" + data;
    };

    self.add_http_fixture = function(fixture) {
        var method_key = 'http_' + fixture.method.toLowerCase();
        if (typeof fixture.data != "undefined") {
            var content_type = fixture.content_type || "application/json";
            var data = self._format_http_request_data(content_type, fixture.data);
            var key = self._http_request_key(fixture.method, fixture.url, content_type, data);
            self.http_calls[fixture.method][key] = JSON.stringify(fixture.body);
        } else {
            self.http_calls[fixture.method][fixture.url] = JSON.stringify(fixture.body);
        }
    };

    self.pending_calls_complete = function() {
        var d = new Q.defer();
        var check = function() {
            if (self.pending_async === 0) {
                d.resolve(null);
            }
            else {
                setImmediate(check);
            }
        };
        setImmediate(check);
        return d.promise;
    };

    self._dispatch_command = function(cmd, reply) {
        var handler_name = ("_handle_" +
                            cmd.cmd.replace('.', '_').replace('-', '_'));
        var handler = self[handler_name];
        if (!handler) {
            throw new DummyApiError("Unknown command " + cmd.cmd);
        }
        var do_reply;
        if (self.async) {
            do_reply = function(cmd) {
                // Using setImmediate to call the reply makes replies
                // effectively async but still deterministicly ordered.
                // Incrementing and then decrementing self.pending_async
                // allows self.pending_calls_complete to detect when all
                // replies have been processed.
                self.pending_async += 1;
                setImmediate(function() {
                    reply(cmd); self.pending_async -= 1;
                });
            };
        }
        else {
            do_reply = reply;
        }
        if (handler) {
            handler.call(self, cmd, do_reply);
        }
    };

    self._populate_reply = function(orig_cmd, reply) {
        reply.cmd = orig_cmd.cmd;
        return reply;
    };

    self._handle_kv_get = function(cmd, reply) {
        var value = self.kv_store[cmd.key];
        value = typeof value === 'undefined' ? null : value;
        reply(self._populate_reply(cmd, {
            success: true,
            value: value
        }));
    };

    self._handle_kv_set = function(cmd, reply) {
        self.kv_store[cmd.key] = cmd.value;
        reply(self._populate_reply(cmd, {
            success: true
        }));
    };

    self._handle_kv_incr = function(cmd, reply) {
        var orig_value = self.kv_store[cmd.key];
        orig_value = (typeof orig_value == 'undefined') ? 0 : orig_value;
        orig_value = Number(orig_value);
        var amount = cmd.amount;
        amount = (typeof amount == 'undefied') ? 1 : amount;
        self.kv_store[cmd.key] = orig_value + amount;
        reply(self._populate_reply(cmd, {
            success: true,
            value: self.kv_store[cmd.key]
        }));
    };

    self._handle_config_get = function(cmd, reply) {
        var value = self.config_store[cmd.key];
        reply(self._populate_reply(cmd, {
            success: true,
            value: value
        }));
    };

    self._record_metric = function (store_name, metric_name, value, agg) {
        var store = self.metrics[store_name];
        if (typeof store == 'undefined') {
            self.metrics[store_name] = store = {};
        }
        var metric = store[metric_name];
        if (typeof metric == 'undefined') {
            store[metric_name] = metric = {'agg': agg, values: []};
        }
        if (metric.agg != agg) return false;
        metric.values.push(value);
        return true;
    };

    self._handle_metrics_fire = function(cmd, reply) {
        var success = self._record_metric(cmd.store, cmd.metric, cmd.value,
                                          cmd.agg);
        reply(self._populate_reply(cmd, {
            success: success
        }));
    };

    self._handle_http_method = function(method, key, cmd, reply) {
        var store = self.http_calls[method] || {};
        var reply_data = {};
        var data = store[key];
        if (typeof data != 'undefined') {
            reply_data.success = true;
            reply_data.body = data;
            reply_data.code = 200;
        }
        else {
            reply_data.success = false;
            reply_data.reason = method + " from unknown key: " + key;
            console.log(reply_data.reason);
        }
        reply(self._populate_reply(cmd, reply_data));
    };

    self._handle_http_get = function(cmd, reply) {
        self._handle_http_method("GET", cmd.url, cmd, reply);
    };

    self._handle_http_post = function(cmd, reply) {
        var headers = cmd.headers || {};
        var content_types = headers['Content-Type'] || [];
        var content_type = content_types[0];
        var key = self._http_request_key('POST', cmd.url, content_type, cmd.data);
        self._handle_http_method("POST", key, cmd, reply);
    };

    self._handle_http_put = function(cmd, reply) {
        var headers = cmd.headers || {};
        var content_types = headers['Content-Type'] || [];
        var content_type = content_types[0];
        var key = self._http_request_key('PUT', cmd.url, content_type, cmd.data);
        self._handle_http_method("PUT", key, cmd, reply);
    };

    self._handle_http_delete = function(cmd, reply) {
        var headers = cmd.headers || {};
        var content_types = headers['Content-Type'] || [];
        var content_type = content_types[0];
        var key = self._http_request_key('DELETE', cmd.url, content_type);
        self._handle_http_method("DELETE", key, cmd, reply);
    };

    self._handle_http_head = function(cmd, reply) {
        var headers = cmd.headers || {};
        var content_types = headers['Content-Type'] || [];
        var content_type = content_types[0];
        var key = self._http_request_key('HEAD', cmd.url, content_type);
        self._handle_http_method("HEAD", key, cmd, reply);
    };

    self._handle_outbound_reply_to = function(cmd, reply) {
         self.request_calls.push(cmd);
         reply(self.waiting_replies.shift());
    };

    self._handle_outbound_send_to_tag = function(cmd, reply) {
        self.outbound_sends.push(cmd);
        reply(self._populate_reply(cmd, {success: true}));
    };

    // original api

    self.populate_command = function(cmd_name, cmd) {
        cmd.cmd = cmd_name;
        return cmd;
    };

    self.request = function(cmd_name, cmd_data, reply) {
        var cmd = self.populate_command(cmd_name, cmd_data);
        self._dispatch_command(cmd, reply);
    };

    self.log_info = function(msg) {
        self.logs.push(msg);
    };

    self.done = function() {
        self.done_calls += 1;
    };
}

// exports
this.DummyApi = DummyApi;
<commit_msg>Add comments about the supported resources.<commit_after>var fs = require("fs");
var Q = require("q");

function DummyApiError(msg) {
    var self = this;

    self.toString = function () {
        return "<DummyApiError " + self.msg + ">";
    };
}

function DummyApi() {
    var self = this;

    // test hooks

    self.reset = function() {
        // set self.async to true to make everything asynchronous
        self.async = false;
        self.pending_async = 0;
        self.kv_store = {};
        self.config_store = {};
        self.metrics = {};
        self.http_calls = {
            'POST': {},
            'PUT': {},
            'HEAD': {},
            'DELETE': {},
            'GET': {}
        };
        self.outbound_sends = [];
        self.logs = [];
        self.request_calls = [];
        self.waiting_replies = [];
        self.done_calls = 0;
    };
    self.reset();

    self.reset_im = function() {
        self.im.user = null;
        self.im.i18n = null;
        self.im.i18n_lang = null;
        self.im.current_state = null;
    };

    self.add_reply = function(reply) {
        self.waiting_replies.push(reply);
    };

    self.load_http_fixture = function(filename) {
        var data = fs.readFileSync(filename);
        var fixture = JSON.parse(data);
        self.add_http_fixture(fixture);
    };

    self._format_http_request_data = function(content_type, data) {
        // convert a JSON fixture data into the required
        // content type.
        if (content_type == "application/json")
            return JSON.stringify(data);
        return data;
    };

    self._order_json_data = function(data) {
        // return JSON data with consistent key ordering
        // FIXME: This assumes the JSON data is a JS object
        var json = JSON.parse(data);
        var keys = Object.keys(json).sort();
        var frags = keys.map(function (k) {
            return '"' + k + '":' + JSON.stringify(json[k]);
        });
        data = "{" + frags.join(",") + "}";
        return data;
    };

    self._http_request_key = function(method, url, content_type, data) {
        if (content_type == "application/json") {
            data = self._order_json_data(data);
        }
        return url + "|" + content_type + "|" + data;
    };

    self.add_http_fixture = function(fixture) {
        var method_key = 'http_' + fixture.method.toLowerCase();
        if (typeof fixture.data != "undefined") {
            var content_type = fixture.content_type || "application/json";
            var data = self._format_http_request_data(content_type, fixture.data);
            var key = self._http_request_key(fixture.method, fixture.url, content_type, data);
            self.http_calls[fixture.method][key] = JSON.stringify(fixture.body);
        } else {
            self.http_calls[fixture.method][fixture.url] = JSON.stringify(fixture.body);
        }
    };

    self.pending_calls_complete = function() {
        var d = new Q.defer();
        var check = function() {
            if (self.pending_async === 0) {
                d.resolve(null);
            }
            else {
                setImmediate(check);
            }
        };
        setImmediate(check);
        return d.promise;
    };

    self._dispatch_command = function(cmd, reply) {
        var handler_name = ("_handle_" +
                            cmd.cmd.replace('.', '_').replace('-', '_'));
        var handler = self[handler_name];
        if (!handler) {
            throw new DummyApiError("Unknown command " + cmd.cmd);
        }
        var do_reply;
        if (self.async) {
            do_reply = function(cmd) {
                // Using setImmediate to call the reply makes replies
                // effectively async but still deterministicly ordered.
                // Incrementing and then decrementing self.pending_async
                // allows self.pending_calls_complete to detect when all
                // replies have been processed.
                self.pending_async += 1;
                setImmediate(function() {
                    reply(cmd); self.pending_async -= 1;
                });
            };
        }
        else {
            do_reply = reply;
        }
        if (handler) {
            handler.call(self, cmd, do_reply);
        }
    };

    self._populate_reply = function(orig_cmd, reply) {
        reply.cmd = orig_cmd.cmd;
        return reply;
    };

    // kv resource

    self._handle_kv_get = function(cmd, reply) {
        var value = self.kv_store[cmd.key];
        value = typeof value === 'undefined' ? null : value;
        reply(self._populate_reply(cmd, {
            success: true,
            value: value
        }));
    };

    self._handle_kv_set = function(cmd, reply) {
        self.kv_store[cmd.key] = cmd.value;
        reply(self._populate_reply(cmd, {
            success: true
        }));
    };

    self._handle_kv_incr = function(cmd, reply) {
        var orig_value = self.kv_store[cmd.key];
        orig_value = (typeof orig_value == 'undefined') ? 0 : orig_value;
        orig_value = Number(orig_value);
        var amount = cmd.amount;
        amount = (typeof amount == 'undefied') ? 1 : amount;
        self.kv_store[cmd.key] = orig_value + amount;
        reply(self._populate_reply(cmd, {
            success: true,
            value: self.kv_store[cmd.key]
        }));
    };

    // config resource

    self._handle_config_get = function(cmd, reply) {
        var value = self.config_store[cmd.key];
        reply(self._populate_reply(cmd, {
            success: true,
            value: value
        }));
    };

    // metrics resource

    self._record_metric = function (store_name, metric_name, value, agg) {
        var store = self.metrics[store_name];
        if (typeof store == 'undefined') {
            self.metrics[store_name] = store = {};
        }
        var metric = store[metric_name];
        if (typeof metric == 'undefined') {
            store[metric_name] = metric = {'agg': agg, values: []};
        }
        if (metric.agg != agg) return false;
        metric.values.push(value);
        return true;
    };

    self._handle_metrics_fire = function(cmd, reply) {
        var success = self._record_metric(cmd.store, cmd.metric, cmd.value,
                                          cmd.agg);
        reply(self._populate_reply(cmd, {
            success: success
        }));
    };

    // http resource

    self._handle_http_method = function(method, key, cmd, reply) {
        var store = self.http_calls[method] || {};
        var reply_data = {};
        var data = store[key];
        if (typeof data != 'undefined') {
            reply_data.success = true;
            reply_data.body = data;
            reply_data.code = 200;
        }
        else {
            reply_data.success = false;
            reply_data.reason = method + " from unknown key: " + key;
            console.log(reply_data.reason);
        }
        reply(self._populate_reply(cmd, reply_data));
    };

    self._handle_http_get = function(cmd, reply) {
        self._handle_http_method("GET", cmd.url, cmd, reply);
    };

    self._handle_http_post = function(cmd, reply) {
        var headers = cmd.headers || {};
        var content_types = headers['Content-Type'] || [];
        var content_type = content_types[0];
        var key = self._http_request_key('POST', cmd.url, content_type, cmd.data);
        self._handle_http_method("POST", key, cmd, reply);
    };

    self._handle_http_put = function(cmd, reply) {
        var headers = cmd.headers || {};
        var content_types = headers['Content-Type'] || [];
        var content_type = content_types[0];
        var key = self._http_request_key('PUT', cmd.url, content_type, cmd.data);
        self._handle_http_method("PUT", key, cmd, reply);
    };

    self._handle_http_delete = function(cmd, reply) {
        var headers = cmd.headers || {};
        var content_types = headers['Content-Type'] || [];
        var content_type = content_types[0];
        var key = self._http_request_key('DELETE', cmd.url, content_type);
        self._handle_http_method("DELETE", key, cmd, reply);
    };

    self._handle_http_head = function(cmd, reply) {
        var headers = cmd.headers || {};
        var content_types = headers['Content-Type'] || [];
        var content_type = content_types[0];
        var key = self._http_request_key('HEAD', cmd.url, content_type);
        self._handle_http_method("HEAD", key, cmd, reply);
    };

    // contact resource

    // TODO: complete

    // outbound resource

    self._handle_outbound_reply_to = function(cmd, reply) {
         self.request_calls.push(cmd);
         reply(self.waiting_replies.shift());
    };

    self._handle_outbound_send_to_tag = function(cmd, reply) {
        self.outbound_sends.push(cmd);
        reply(self._populate_reply(cmd, {success: true}));
    };

    // original api

    self.populate_command = function(cmd_name, cmd) {
        cmd.cmd = cmd_name;
        return cmd;
    };

    self.request = function(cmd_name, cmd_data, reply) {
        var cmd = self.populate_command(cmd_name, cmd_data);
        self._dispatch_command(cmd, reply);
    };

    self.log_info = function(msg) {
        self.logs.push(msg);
    };

    self.done = function() {
        self.done_calls += 1;
    };
}

// exports
this.DummyApi = DummyApi;
<|endoftext|>


<commit_before>/**
 * Framework for making Angular Unit Test's easier to write.
 */
(function() {
  'use strict';

  /**
   * @class EasyTest
   */
  function EasyTest() {}

  /**
   * @function injectify
   * @memberof EasyTest
   * @description
   *
   * Injects a number of services and returns an object that has, as its
   * properties, the injected services.
   *
   * @param {array} injectibles An array of the names of the services to inject.
   *
   * @returns {object} An object that has, as its properties, the services that
   * were injected.
   *
   * @example
   * var services = EasyTest.injectify(['MyServiceOne', 'MyServiceTwo']);
   * expect(services.MyServiceOne).to.be.an.instanceOf(MyServiceOne);
   */
  EasyTest.injectify = function injectify(injectibles) {
    var obj = [];
    injectibles.push(function() {
      for (var i = 0, l = arguments.length; i < l; i++) {
        obj[injectibles[i]] = arguments[i];
      }
    });
    inject(injectibles);
    return obj;
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Mocks a specified module that has already been registered with angular.
   * Optionally registers a number of services with the mocked module.
   *
   * @param {string} moduleName The name of the module to mock.
   * @param {array | object} services An array of objects representing fake
   * services to register with the module. Each object must have a `name` property
   * representing the name of the service, and then one of `provider`, `constant`,
   * `value`, `factory`, or `service`. Alternately, you can supply an object matching
   * names to factories.
   *
   * @example
   * // Mock MyModule and provide the passed fake services to it.
   * EasyTest.mockModule('MyModule', [{
   *   name: 'MyFakeServiceName',
   *   factory: {
   *     get: function() {},
   *     name: 'ServiceName'
   *   }
   * },
   * {
   *   name: 'MyFakeConstant',
   *   constant: 1337
   * }]);
   *
   * @example
   * // Mock MyModule and provide the passed fake services to it.
   * EasyTest.mockModule('MyOtherModule', {
   *   FooFactory: { foo: function() {} },
   *   BarFactory: { bar: function() {} }
   * });
   */
  EasyTest.mockModule = function mockModule(moduleName, services) {
    services = services || [];

    // if services is just a hash, convert to an array of all factories
    services = Array.isArray(services) ? services :
      Object.keys(services).map(function(name) {
        return {
          name: name,
          factory: services[name]
        };
      });

    // iterate over each service, mocking it by type
    var mockTypes = [ 'factory', 'service', 'provider', 'value', 'constant' ];
    angular.mock.module(moduleName, function($provide) {
      services.forEach(function serviceProvide(service) {
        mockTypes.forEach(function mockIfExists(mocktype) {
          if (service[mocktype]) {
            if (mocktype === 'factory' || mocktype === 'service') {
              // if mocktype is factory or service, mock it into $getFn
              $provide[mocktype](service.name, [ function() {
                return service[mocktype];
              }]);
            }
            else {
              // else, mock normally (?)
              $provide[mocktype](service.name, service[mocktype]);
            }
          }
        });
      });
    });
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Mocks a series of modules. The arguments that are passed into this function
   * can be passed in a very flexible manner. String arguments will be split
   * on their spaces and mocked directly, object arguments will be expected to
   * have a `name` property and `values` property which is an array of the
   * values to be provided for the module. See the {@link EasyTest.mockModule}
   * function for more information on how that works.
   *
   * @param {string|object} arguments A series of string or object parameters.
   *
   * @example
   * // Simply mock three modules
   * EasyTest.mockModules('one two three');
   * // or
   * EasyTest.mockModules('one two', 'three');
   * // etc, can mix and match.
   *
   * @example
   * // Mock two simple modules and a third with a fake service.
   * EasyTest.mockModules('one two', { name: 'three', values: [{
   *   name: 'FakeService',
   *   provider: { get: function() {} }
   * }]});
   */
  EasyTest.mockModules = function mockModules() {
    var modules =  Array.prototype.slice.call(arguments, 0);
    modules.forEach(function(arg) {
      if (typeof arg === 'string') {
        return arg.split(' ').forEach(function(moduleName) {
          angular.mock.module(moduleName); // Go directly to angular.mock
        });
      }
      EasyTest.mockModule(arg.name, arg.values);
    });
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Injects a specific service and returns a reference to it.
   *
   * @param {string} serviceName The name of the service to inject.
   *
   * @returns {object} The service that was injected.
   *
   * @example
   * var $q = EasyTest.getService('$q');
   */
  EasyTest.getService = function getService(serviceName) {
    return EasyTest.injectify([ serviceName ])[serviceName];
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Creates a 'test context' for a particular controller. Note that currently
   * you need to mock any modules that this controller may need beforehand.
   * See the {@link EasyTest.mockModule} function.
   *
   * @param {string} controller The name of the controller to load and create a context for.
   *
   * @returns {object} An object with a $scope and controller property representing the
   * controller that has been loaded and its scope.
   *
   * @example
   * var context = EasyTest.createTextContext('MyControllerName');
   * expect(context.$scope).to.have.property('MyExpectedProperty');
   * expect(context.controller.myFunc).to.be.a('function');
   */
  EasyTest.createTestContext = function createTestContext(controller) {
    var testContext = {};
    inject(function($rootScope, $controller) {
      var $scope = $rootScope.$new();
      testContext = {
        $scope: $scope,
        controller: $controller(controller, {
          $scope: $scope
        })
      };
    });
    return testContext;
  };

  /**
  * @memberof EasyTest
  * @description
  *
  * Gets a specific controller and returns a reference to it.
  *
  * @param {string} controllerName The name of the controller to get.
  *
  * @returns {object} The controller that was retreived.
  *
  * @example
  * var myController = EasyTest.getController('myController');
  */
  EasyTest.getController = function getController(controllerName) {
    return EasyTest.createTestContext(controllerName).controller;
  };

  /**
  * @memberof EasyTest
  * @description
  *
  * Gets a specific controller and returns a reference to it. Use this
  * instead of `getController` if the controller belongs to a directive
  * that has `bindToController` set to true.
  *
  * @param {string} controllerName The name of the controller to get.
  *
  * @param {object} scope a object that will be inserted into the controller's
  * scope.
  *
  * @returns {object} The controller that was retreived.
  *
  * @example
  * var scope = { value: 'some value' };
  * var myController = EasyTest.getBoundController('myController', scope);
  */
  EasyTest.getBoundController = function getBoundController(controllerName, scope) {
    var controller = {};

    inject(function($rootScope, $controller) {
      // The third arg for $controller is an undocumented parameter
      // that allows us to delay the instantiation of the controller.
      // This approach may break in a future version of angular, but it is
      // currently the only way to deal with `bindToController` controllers.
      controller = $controller(controllerName, {
        $scope: $rootScope.$new()
      }, true);
    });

    if (scope) {
      angular.extend(controller.instance, scope);
    }

    // Instantiate the controller.
    return controller();
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Compiles a directive and returns the top level element in the compiled
   * directive's HTML. Note that currently you need to mock any modules that
   * this directive may need beforehand. See the {@link EasyTest.mockModule} function.
   *
   * @param {string} directiveHTML The HTML of the directive to be compiled.
   * @param {object} scope a dictionary of variables that will be inserted into the element's scope.
   *
   * @returns {HTMLElement} The top level HTML element of the compiled directive.
   *
   * @example
   * var element = EasyTest.compileDirective('

');
   * expect(element.length).to.equal(expectedLength);
   *
   * @example
   * var scope = { a: 1, b: 'something' };
   * var element = EasyTest.compileDirective('
{{a}} {{b}}
', scope);
   * expect(element.text()).to.equal('1 something');
   */
  EasyTest.compileDirective = function compileDirective(directiveHTML, scope) {
    var $el;
    inject(function($compile, $rootScope) {
      var $scope = $rootScope.$new();
      if (scope) {
        angular.extend($scope, scope);
      }
      $el = angular.element(directiveHTML);
      $compile($el)($scope);
      $scope.$digest();
    });
    return $el;
  };

  /**
  * @memberof EasyTest
  * @description
  *
  * Allows access to the controller scope for directive that has
  * `bindToController` set to true.
  *
  * @param {object} angularElement An angular element gotten from compiled
  * HTML.
  *
  * @returns {object} The scope of the bound controller.
  */
  EasyTest.getBoundScope = function getBoundScope(angularElement) {
    return angularElement.data('$scope').$$childHead.ctrl;
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Tests an HTMLElement or Angular/jQuery element to see if it contains
   * some attribute, optionally with some given value.
   *
   * @param {HTMLElement|angular.element} element the element to be tested.
   * @param {string} attr the name of the attribute being tested.
   * @param {string} val optional: the value of the attribute being tested.
   *
   * @returns {boolean} Whether the element contains the attribute `attr`.
   * If `val` is provided, returns true if and only if the value of `attr` is
   * also `val`.
   *
   * @example
   * var element = EasyTest.compileDirective('

');
   * expect(EasyTest.hasAttr(element[0], 'foo')).to.be.true;
   * expect(EasyTest.hasAttr(element[0], 'foo', 'bar')).to.be.true;
   */
  EasyTest.hasAttr = function hasAttr(element, attr, val) {
    element = angular.element.prototype.isPrototypeOf(element) ?
      element[0] : element;

    var attrs = element.attributes;
    for (var k in attrs) {
      var objAttr = attrs[k];
      if ((objAttr.name === attr) && (typeof val === 'undefined' || objAttr.value === val)) {
        return true;
      }
    }
    return false;
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Tests an object against a JSON specification object.
   *
   * @param {object} object The object to test.
   * @param {object} spec The 'specification' to test the object against. Should
   * have the expected types as properties and the names of the properties with
   * that type as their value, separated by spaces.
   *
   * @returns {object} An error object if anything failed, nothing if it passed.
   *
   * @example
   * // Using Chai you could pass the result directly back to 'done'
   * it('test object', function(done) {
   *   var res = EasyTest.looksLike(object, {
   *     'function': 'funcOne funcTwo',
   *     'number': 'numberOne numberTwo numberThree'
   *   });
   *   done(res);
   * });
   */
  EasyTest.looksLike = function looksLike(object, spec) {
    var types = Object.getOwnPropertyNames(spec);
    for (var i = 0; i < types.length; i++) {
      var type = types[i];
      var properties = spec[type].split(' ');
      for (var y = 0; y < properties.length; y++) {
        var property = properties[y];
        if (!(property in object)) {
          return new Error('Expected object to have the property \'' +
                           property + '\'.');
        }
        if (typeof object[property] !== type) {
          return new Error('Expected property \'' + property + '\' to be of ' +
                           'type ' + type + '.');
        }
      }
    }
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Tests a controller's scope by seeing if it conforms to a JSON specficiation
   * object. See {@link EasyTest.looksLike} for more information.
   *
   * @param {string|object} context The name of the controller to create a test
   * context for or the context itself.
   * @param {array} scopeSpec The scope's spec. An object with the names of the
   * expected types as its keys and a string for its value of all the properties,
   * separated by spaces.
   *
   * @example
   * // Will load the controller and test it using the passed spec.
   * EasyTest.testScope('MyControllerName', {
   *   'function': 'funcOne funcTwo funcThree',
   *   'number': 'numberOne numberTwo',
   *   'boolean': 'hasStuff'
   * });
   */
  EasyTest.testScope = function testScope(context, scopeSpec) {
    if (typeof context === 'string') {
      context = EasyTest.createTestContext(context);
    }
    return EasyTest.looksLike(context.$scope, scopeSpec);
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Tests a controller to see if it conforms to a JSON specification object.
   * See {@link EasyTest.looksLike} for more information.
   *
   * @param {string|object} controller The name of the controller to create or a
   * reference to a controller.
   * @param {array} spec The controller's spec. An object with the names of the
   * expected types as its keys and a string for its value of all the properties,
   * separated by spaces.
   *
   * @example
   * // Will load the controller and test it using the passed spec.
   * EasyTest.testController('MyControllerName', {
   *   'function': 'funcOne funcTwo funcThree',
   *   'number': 'numberOne numberTwo',
   *   'boolean': 'hasStuff'
   * });
   */
  EasyTest.testController = function testScope(controller, spec) {
    if (typeof controller === 'string') {
      controller = EasyTest.getController(controller);
    }
    return EasyTest.looksLike(controller, spec);
  };

  /**
  * @memberof EasyTest
  * @description
  *
  * Tests a service to see if it conforms to a JSON specification object.
  * See {@link EasyTest.looksLike} for more information.
  *
  * @param {string|object} service The name of the service to create or a
  * reference to a service.
  * @param {array} spec  The service's spec. An object with the names of the
  * expected types as its keys and a string for its value of all the properties,
  * separated by spaces.
  *
  * @example
  * // Will load the service and test it using the passed spec.
  * EasyTest.testService('MyServiceName', {
  *   'function': 'funcOne funcTwo funcThree',
  *   'number': 'numberOne numberTwo',
  *   'boolean': 'hasStuff'
  * });
  */
  EasyTest.testService = function testScope(service, spec) {
    if (typeof service === 'string') {
      service = EasyTest.getService(service);
    }
    return EasyTest.looksLike(service, spec);
  };

  window.EasyTest = EasyTest;

}());
<commit_msg>Update erroneous documentation.<commit_after>/**
 * Framework for making Angular Unit Test's easier to write.
 */
(function() {
  'use strict';

  /**
   * @class EasyTest
   */
  function EasyTest() {}

  /**
   * @function injectify
   * @memberof EasyTest
   * @description
   *
   * Injects a number of services and returns an object that has, as its
   * properties, the injected services.
   *
   * @param {array} injectibles An array of the names of the services to inject.
   *
   * @returns {object} An object that has, as its properties, the services that
   * were injected.
   *
   * @example
   * var services = EasyTest.injectify(['MyServiceOne', 'MyServiceTwo']);
   * expect(services.MyServiceOne).to.be.an.instanceOf(MyServiceOne);
   */
  EasyTest.injectify = function injectify(injectibles) {
    var obj = [];
    injectibles.push(function() {
      for (var i = 0, l = arguments.length; i < l; i++) {
        obj[injectibles[i]] = arguments[i];
      }
    });
    inject(injectibles);
    return obj;
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Mocks a specified module that has already been registered with angular.
   * Optionally registers a number of services with the mocked module.
   *
   * @param {string} moduleName The name of the module to mock.
   * @param {array | object} services An array of objects representing fake
   * services to register with the module. Each object must have a `name` property
   * representing the name of the service, and then one of `provider`, `constant`,
   * `value`, `factory`, or `service`. Alternately, you can supply an object matching
   * names to factories as this is typically the most default use case.
   *
   * @example
   * // Mock MyModule and provide the passed fake services to it.
   * EasyTest.mockModule('MyModule', [{
   *   name: 'ServiceName',
   *   factory: function() {}
   * },{
   *   name: 'MyFakeConstant',
   *   constant: 1337
   * }]);
   *
   * @example
   * // Mock MyModule and provide the passed fake services to it.
   * EasyTest.mockModule('MyOtherModule', {
   *   FooFactory: { foo: function() {} },
   *   BarFactory: { bar: function() {} }
   * });
   */
  EasyTest.mockModule = function mockModule(moduleName, services) {
    services = services || [];

    // if services is just a hash, convert to an array of all factories
    services = Array.isArray(services) ? services :
      Object.keys(services).map(function(name) {
        return {
          name: name,
          factory: services[name]
        };
      });

    // iterate over each service, mocking it by type
    var mockTypes = [ 'factory', 'service', 'provider', 'value', 'constant' ];
    angular.mock.module(moduleName, function($provide) {
      services.forEach(function serviceProvide(service) {
        mockTypes.forEach(function mockIfExists(mocktype) {
          if (service[mocktype]) {
            if (mocktype === 'factory' || mocktype === 'service') {
              // if mocktype is factory or service, mock it into $getFn
              $provide[mocktype](service.name, [ function() {
                return service[mocktype];
              }]);
            }
            else {
              // else, mock normally (?)
              $provide[mocktype](service.name, service[mocktype]);
            }
          }
        });
      });
    });
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Mocks a series of modules. The arguments that are passed into this function
   * can be passed in a very flexible manner. String arguments will be split
   * on their spaces and mocked directly, object arguments will be expected to
   * have a `name` property and `values` property which is an array of the
   * values to be provided for the module. See the {@link EasyTest.mockModule}
   * function for more information on how that works.
   *
   * @param {string|object} arguments A series of string or object parameters.
   *
   * @example
   * // Simply mock three modules
   * EasyTest.mockModules('one two three');
   * // or
   * EasyTest.mockModules('one two', 'three');
   * // etc, can mix and match.
   *
   * @example
   * // Mock two simple modules and a third with a fake service.
   * EasyTest.mockModules('one two', { name: 'three', values: [{
   *   name: 'FakeService',
   *   service: { get: function() {} }
   * }]});
   */
  EasyTest.mockModules = function mockModules() {
    var modules =  Array.prototype.slice.call(arguments, 0);
    modules.forEach(function(arg) {
      if (typeof arg === 'string') {
        return arg.split(' ').forEach(function(moduleName) {
          angular.mock.module(moduleName); // Go directly to angular.mock
        });
      }
      EasyTest.mockModule(arg.name, arg.values);
    });
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Injects a specific service and returns a reference to it.
   *
   * @param {string} serviceName The name of the service to inject.
   *
   * @returns {object} The service that was injected.
   *
   * @example
   * var $q = EasyTest.getService('$q');
   */
  EasyTest.getService = function getService(serviceName) {
    return EasyTest.injectify([ serviceName ])[serviceName];
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Creates a 'test context' for a particular controller. Note that currently
   * you need to mock any modules that this controller may need beforehand.
   * See the {@link EasyTest.mockModule} function.
   *
   * @param {string} controller The name of the controller to load and create a context for.
   *
   * @returns {object} An object with a $scope and controller property representing the
   * controller that has been loaded and its scope.
   *
   * @example
   * var context = EasyTest.createTextContext('MyControllerName');
   * expect(context.$scope).to.have.property('MyExpectedProperty');
   * expect(context.controller.myFunc).to.be.a('function');
   */
  EasyTest.createTestContext = function createTestContext(controller) {
    var testContext = {};
    inject(function($rootScope, $controller) {
      var $scope = $rootScope.$new();
      testContext = {
        $scope: $scope,
        controller: $controller(controller, {
          $scope: $scope
        })
      };
    });
    return testContext;
  };

  /**
  * @memberof EasyTest
  * @description
  *
  * Gets a specific controller and returns a reference to it.
  *
  * @param {string} controllerName The name of the controller to get.
  *
  * @returns {object} The controller that was retreived.
  *
  * @example
  * var myController = EasyTest.getController('myController');
  */
  EasyTest.getController = function getController(controllerName) {
    return EasyTest.createTestContext(controllerName).controller;
  };

  /**
  * @memberof EasyTest
  * @description
  *
  * Gets a specific controller and returns a reference to it. Use this
  * instead of `getController` if the controller belongs to a directive
  * that has `bindToController` set to true.
  *
  * @param {string} controllerName The name of the controller to get.
  *
  * @param {object} scope a object that will be inserted into the controller's
  * scope.
  *
  * @returns {object} The controller that was retreived.
  *
  * @example
  * var scope = { value: 'some value' };
  * var myController = EasyTest.getBoundController('myController', scope);
  */
  EasyTest.getBoundController = function getBoundController(controllerName, scope) {
    var controller = {};

    inject(function($rootScope, $controller) {
      // The third arg for $controller is an undocumented parameter
      // that allows us to delay the instantiation of the controller.
      // This approach may break in a future version of angular, but it is
      // currently the only way to deal with `bindToController` controllers.
      controller = $controller(controllerName, {
        $scope: $rootScope.$new()
      }, true);
    });

    if (scope) {
      angular.extend(controller.instance, scope);
    }

    // Instantiate the controller.
    return controller();
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Compiles a directive and returns the top level element in the compiled
   * directive's HTML. Note that currently you need to mock any modules that
   * this directive may need beforehand. See the {@link EasyTest.mockModule} function.
   *
   * @param {string} directiveHTML The HTML of the directive to be compiled.
   * @param {object} scope a dictionary of variables that will be inserted into the element's scope.
   *
   * @returns {HTMLElement} The top level HTML element of the compiled directive.
   *
   * @example
   * var element = EasyTest.compileDirective('

');
   * expect(element.length).to.equal(expectedLength);
   *
   * @example
   * var scope = { a: 1, b: 'something' };
   * var element = EasyTest.compileDirective('
{{a}} {{b}}
', scope);
   * expect(element.text()).to.equal('1 something');
   */
  EasyTest.compileDirective = function compileDirective(directiveHTML, scope) {
    var $el;
    inject(function($compile, $rootScope) {
      var $scope = $rootScope.$new();
      if (scope) {
        angular.extend($scope, scope);
      }
      $el = angular.element(directiveHTML);
      $compile($el)($scope);
      $scope.$digest();
    });
    return $el;
  };

  /**
  * @memberof EasyTest
  * @description
  *
  * Allows access to the controller scope for directive that has
  * `bindToController` set to true.
  *
  * @param {object} angularElement An angular element gotten from compiled
  * HTML.
  *
  * @returns {object} The scope of the bound controller.
  */
  EasyTest.getBoundScope = function getBoundScope(angularElement) {
    return angularElement.data('$scope').$$childHead.ctrl;
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Tests an HTMLElement or Angular/jQuery element to see if it contains
   * some attribute, optionally with some given value.
   *
   * @param {HTMLElement|angular.element} element the element to be tested.
   * @param {string} attr the name of the attribute being tested.
   * @param {string} val optional: the value of the attribute being tested.
   *
   * @returns {boolean} Whether the element contains the attribute `attr`.
   * If `val` is provided, returns true if and only if the value of `attr` is
   * also `val`.
   *
   * @example
   * var element = EasyTest.compileDirective('

');
   * expect(EasyTest.hasAttr(element[0], 'foo')).to.be.true;
   * expect(EasyTest.hasAttr(element[0], 'foo', 'bar')).to.be.true;
   */
  EasyTest.hasAttr = function hasAttr(element, attr, val) {
    element = angular.element.prototype.isPrototypeOf(element) ?
      element[0] : element;

    var attrs = element.attributes;
    for (var k in attrs) {
      var objAttr = attrs[k];
      if ((objAttr.name === attr) && (typeof val === 'undefined' || objAttr.value === val)) {
        return true;
      }
    }
    return false;
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Tests an object against a JSON specification object.
   *
   * @param {object} object The object to test.
   * @param {object} spec The 'specification' to test the object against. Should
   * have the expected types as properties and the names of the properties with
   * that type as their value, separated by spaces.
   *
   * @returns {object} An error object if anything failed, nothing if it passed.
   *
   * @example
   * // Using Chai you could pass the result directly back to 'done'
   * it('test object', function(done) {
   *   var res = EasyTest.looksLike(object, {
   *     'function': 'funcOne funcTwo',
   *     'number': 'numberOne numberTwo numberThree'
   *   });
   *   done(res);
   * });
   */
  EasyTest.looksLike = function looksLike(object, spec) {
    var types = Object.getOwnPropertyNames(spec);
    for (var i = 0; i < types.length; i++) {
      var type = types[i];
      var properties = spec[type].split(' ');
      for (var y = 0; y < properties.length; y++) {
        var property = properties[y];
        if (!(property in object)) {
          return new Error('Expected object to have the property \'' +
                           property + '\'.');
        }
        if (typeof object[property] !== type) {
          return new Error('Expected property \'' + property + '\' to be of ' +
                           'type ' + type + '.');
        }
      }
    }
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Tests a controller's scope by seeing if it conforms to a JSON specficiation
   * object. See {@link EasyTest.looksLike} for more information.
   *
   * @param {string|object} context The name of the controller to create a test
   * context for or the context itself.
   * @param {array} scopeSpec The scope's spec. An object with the names of the
   * expected types as its keys and a string for its value of all the properties,
   * separated by spaces.
   *
   * @example
   * // Will load the controller and test it using the passed spec.
   * EasyTest.testScope('MyControllerName', {
   *   'function': 'funcOne funcTwo funcThree',
   *   'number': 'numberOne numberTwo',
   *   'boolean': 'hasStuff'
   * });
   */
  EasyTest.testScope = function testScope(context, scopeSpec) {
    if (typeof context === 'string') {
      context = EasyTest.createTestContext(context);
    }
    return EasyTest.looksLike(context.$scope, scopeSpec);
  };

  /**
   * @memberof EasyTest
   * @description
   *
   * Tests a controller to see if it conforms to a JSON specification object.
   * See {@link EasyTest.looksLike} for more information.
   *
   * @param {string|object} controller The name of the controller to create or a
   * reference to a controller.
   * @param {array} spec The controller's spec. An object with the names of the
   * expected types as its keys and a string for its value of all the properties,
   * separated by spaces.
   *
   * @example
   * // Will load the controller and test it using the passed spec.
   * EasyTest.testController('MyControllerName', {
   *   'function': 'funcOne funcTwo funcThree',
   *   'number': 'numberOne numberTwo',
   *   'boolean': 'hasStuff'
   * });
   */
  EasyTest.testController = function testScope(controller, spec) {
    if (typeof controller === 'string') {
      controller = EasyTest.getController(controller);
    }
    return EasyTest.looksLike(controller, spec);
  };

  /**
  * @memberof EasyTest
  * @description
  *
  * Tests a service to see if it conforms to a JSON specification object.
  * See {@link EasyTest.looksLike} for more information.
  *
  * @param {string|object} service The name of the service to create or a
  * reference to a service.
  * @param {array} spec  The service's spec. An object with the names of the
  * expected types as its keys and a string for its value of all the properties,
  * separated by spaces.
  *
  * @example
  * // Will load the service and test it using the passed spec.
  * EasyTest.testService('MyServiceName', {
  *   'function': 'funcOne funcTwo funcThree',
  *   'number': 'numberOne numberTwo',
  *   'boolean': 'hasStuff'
  * });
  */
  EasyTest.testService = function testScope(service, spec) {
    if (typeof service === 'string') {
      service = EasyTest.getService(service);
    }
    return EasyTest.looksLike(service, spec);
  };

  window.EasyTest = EasyTest;

}());
<|endoftext|>


<commit_before>/**
 * List compiled by mystix on the extjs.com forums.
 * Thank you Mystix!
 *
 * English Translations
 */

if(Ext.UpdateManager && Ext.UpdateManager.defaults){
    Ext.UpdateManager.defaults.indicatorText = '
'+trlVps('Loading...')+'
';
}
if(Ext.View){
  Ext.View.prototype.emptyText = "";
}

if(Ext.grid.Grid){
  Ext.grid.Grid.prototype.ddText = trlVps("{0} selected row(s)");
}

if(Ext.TabPanelItem){
  Ext.TabPanelItem.prototype.closeText = trlVps("Close this tab");
}

if(Ext.form.Field){
  Ext.form.Field.prototype.invalidText = trlVps("The value in this field is invalid");
}

if(Ext.LoadMask){
  Ext.LoadMask.prototype.msg = trlVps("Loading...");
}

Date.monthNames = [
  trlVps("January"),
  trlVps("February"),
  trlVps("March"),
  trlVps("April"),
  trlVps("May"),
  trlVps("June"),
  trlVps("July"),
  trlVps("August"),
  trlVps("September"),
  trlVps("October"),
  trlVps("November"),
  trlVps("December")
];

Date.getShortMonthName = function(month) {
  return Date.monthNames[month].substring(0, 3);
};

Date.monthNumbers = {
  Jan : 0,
  Feb : 1,
  Mar : 2,
  Apr : 3,
  May : 4,
  Jun : 5,
  Jul : 6,
  Aug : 7,
  Sep : 8,
  Oct : 9,
  Nov : 10,
  Dec : 11
};

Date.getMonthNumber = function(name) {
  return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
};

Date.dayNames = [
  trlVps("Sunday"),
  trlVps("Monday"),
  trlVps("Tuesday"),
  trlVps("Wednesday"),
  trlVps("Thursday"),
  trlVps("Friday"),
  "Saturday"
];

Date.getShortDayName = function(day) {
  return Date.dayNames[day].substring(0, 3);
};

if(Ext.MessageBox){
  Ext.MessageBox.buttonText = {
    ok     : trlVps("OK"),
    cancel : trlVps("Cancel"),
    yes    : trlVps("Yes"),
    no     : trlVps("No")
  };
}

//Auskommentiert da es beim Datum Probleme gegeben hat
/*if(Ext.util.Format){
  Ext.util.Format.date = function(v, format){
    if(!v) return "";
    if(!(v instanceof Date)) v = new Date(Date.parse(v));
    return v.dateFormat(format || "m/d/Y");
  };
}*/

if(Ext.DatePicker){
  Ext.apply(Ext.DatePicker.prototype, {
    todayText         : trlVps("Today"),
    minText           : trlVps("This date is before the minimum date"),
    maxText           : trlVps("This date is after the maximum date"),
    disabledDaysText  : "",
    disabledDatesText : "",
    monthNames        : Date.monthNames,
    dayNames          : Date.dayNames,
    nextText          : trlVps('Next Month (Control+Right)'),
    prevText          : trlVps('Previous Month (Control+Left)'),
    monthYearText     : trlVps('Choose a month (Control+Up/Down to move years)'),
    todayTip          : trlVps("{0} (Spacebar)"),
    format            : trlVps("m/d/y"),
    okText            : " "+trlVps('OK')+" ",
    cancelText        : trlVps("Cancel"),
    startDay          : 0
  });
}

if(Ext.PagingToolbar){
  Ext.apply(Ext.PagingToolbar.prototype, {
    beforePageText : trlVps("Page"),
    afterPageText  : trlVps("of {0}"),
    firstText      : trlVps("First Page"),
    prevText       : trlVps("Previous Page"),
    nextText       : trlVps("Next Page"),
    lastText       : trlVps("Last Page"),
    refreshText    : trlVps("Refresh"),
    displayMsg     : trlVps("Displaying {0} - {1} of {2}"),
    emptyMsg       : trlVps('No data to display')
  });
}

if(Ext.form.TextField){
  Ext.apply(Ext.form.TextField.prototype, {
    minLengthText : trlVps("The minimum length for this field is {0}"),
    maxLengthText : trlVps("The maximum length for this field is {0}"),
    blankText     : trlVps("This field is required"),
    regexText     : "",
    emptyText     : null
  });
}

if(Ext.form.NumberField){
  Ext.apply(Ext.form.NumberField.prototype, {
    minText : trlVps("The minimum value for this field is {0}"),
    maxText : trlVps("The maximum value for this field is {0}"),
    nanText : trlVps("{0} is not a valid number")
  });
}

if(Ext.form.DateField){
  Ext.apply(Ext.form.DateField.prototype, {
    disabledDaysText  : trlVps("Disabled"),
    disabledDatesText : trlVps("Disabled"),
    minText           : trlVps("The date in this field must be after {0}"),
    maxText           : trlVps("The date in this field must be before {0}"),
    invalidText       : trlVps("{0} is not a valid date - it must be in the format {1}"),
    format            : trlVps("m/d/y"),
    altFormats        : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d"
  });
}

if(Ext.form.ComboBox){
  Ext.apply(Ext.form.ComboBox.prototype, {
    loadingText       : trlVps("Loading..."),
    valueNotFoundText : undefined
  });
}

if(Ext.form.VTypes){
  Ext.apply(Ext.form.VTypes, {
    emailText    : trlVps('This field should be an e-mail address in the format "user@domain.com"'),
    urlText      : trlVps('This field should be a URL in the format "http:/'+'/www.domain.com"'),
    alphaText    : trlVps('This field should only contain letters and _'),
    alphanumText : trlVps('This field should only contain letters, numbers and _')
  });
}

if(Ext.form.HtmlEditor){
  Ext.apply(Ext.form.HtmlEditor.prototype, {
    createLinkText : trlVps('Please enter the URL for the link:'),
    buttonTips : {
      bold : {
        title: trlVps('Bold (Ctrl+B)'),
        text:  trlVps('Make the selected text bold.'),
        cls: 'x-html-editor-tip'
      },
      italic : {
        title: trlVps('Italic (Ctrl+I)'),
        text: trlVps('Make the selected text italic.'),
        cls: 'x-html-editor-tip'
      },
      underline : {
        title: trlVps('Underline (Ctrl+U)'),
        text: trlVps('Underline the selected text.'),
        cls: 'x-html-editor-tip'
      },
      increasefontsize : {
        title: trlVps('Grow Text'),
        text: trlVps('Increase the font size.'),
        cls: 'x-html-editor-tip'
      },
      decreasefontsize : {
        title: trlVps('Shrink Text'),
        text: trlVps('Decrease the font size.'),
        cls: 'x-html-editor-tip'
      },
      backcolor : {
        title: trlVps('Text Highlight Color'),
        text: trlVps('Change the background color of the selected text.'),
        cls: 'x-html-editor-tip'
      },
      forecolor : {
        title: trlVps('Font Color'),
        text: trlVps('Change the color of the selected text.'),
        cls: 'x-html-editor-tip'
      },
      justifyleft : {
        title: trlVps('Align Text Left'),
        text: trlVps('Align text to the left.'),
        cls: 'x-html-editor-tip'
      },
      justifycenter : {
        title: trlVps('Center Text'),
        text: trlVps('Center text in the editor.'),
        cls: 'x-html-editor-tip'
      },
      justifyright : {
        title: trlVps('Align Text Right'),
        text: trlVps('Align text to the right.'),
        cls: 'x-html-editor-tip'
      },
      insertunorderedlist : {
        title: trlVps('Bullet List'),
        text: trlVps('Start a bulleted list.'),
        cls: 'x-html-editor-tip'
      },
      insertorderedlist : {
        title: trlVps('Numbered List'),
        text: trlVps('Start a numbered list.'),
        cls: 'x-html-editor-tip'
      },
      createlink : {
        title: trlVps('Hyperlink'),
        text: trlVps('Make the selected text a hyperlink.'),
        cls: 'x-html-editor-tip'
      },
      sourceedit : {
        title: trlVps('Source Edit'),
        text: trlVps('Switch to source editing mode.'),
        cls: 'x-html-editor-tip'
      }
    }
  });
}

if(Ext.grid.GridView){
  Ext.apply(Ext.grid.GridView.prototype, {
    sortAscText  : trlVps("Sort Ascending"),
    sortDescText : trlVps("Sort Descending"),
    lockText     : trlVps("Lock Column"),
    unlockText   : trlVps("Unlock Column"),
    columnsText  : trlVps("Columns")
  });
}

if(Ext.grid.GroupingView){
  Ext.apply(Ext.grid.GroupingView.prototype, {
    emptyGroupText : trlVps('(None)'),
    groupByText    : trlVps('Group By This Field'),
    showGroupsText : trlVps('Show in Groups')
  });
}

if(Ext.grid.PropertyColumnModel){
  Ext.apply(Ext.grid.PropertyColumnModel.prototype, {
    nameText   : trlVps("Name"),
    valueText  : trlVps("Value"),
    dateFormat : "m/j/Y"
  });
}

if(Ext.layout && Ext.layout.BorderLayout && Ext.layout.BorderLayout.SplitRegion){
  Ext.apply(Ext.layout.BorderLayout.SplitRegion.prototype, {
    splitTip            : trlVps("Drag to resize."),
    collapsibleSplitTip : trlVps("Drag to resize. Double click to hide.")
  });
}
<commit_msg>js fehler vermeiden wenn ext gar nicht da. diese datei wird ja immer hinzugefügt - auch wenn kein ext da ist. kommt davon wenn man einen hack macht :D<commit_after>/**
 * List compiled by mystix on the extjs.com forums.
 * Thank you Mystix!
 *
 * English Translations
 */

if(Ext && Ext.UpdateManager && Ext.UpdateManager.defaults){
    Ext.UpdateManager.defaults.indicatorText = '
'+trlVps('Loading...')+'
';
}
if(Ext && Ext.View){
  Ext.View.prototype.emptyText = "";
}

if(Ext && Ext.grid.Grid){
  Ext.grid.Grid.prototype.ddText = trlVps("{0} selected row(s)");
}

if(Ext && Ext.TabPanelItem){
  Ext.TabPanelItem.prototype.closeText = trlVps("Close this tab");
}

if(Ext && Ext.form.Field){
  Ext.form.Field.prototype.invalidText = trlVps("The value in this field is invalid");
}

if(Ext && Ext.LoadMask){
  Ext.LoadMask.prototype.msg = trlVps("Loading...");
}

Date.monthNames = [
  trlVps("January"),
  trlVps("February"),
  trlVps("March"),
  trlVps("April"),
  trlVps("May"),
  trlVps("June"),
  trlVps("July"),
  trlVps("August"),
  trlVps("September"),
  trlVps("October"),
  trlVps("November"),
  trlVps("December")
];

Date.getShortMonthName = function(month) {
  return Date.monthNames[month].substring(0, 3);
};

Date.monthNumbers = {
  Jan : 0,
  Feb : 1,
  Mar : 2,
  Apr : 3,
  May : 4,
  Jun : 5,
  Jul : 6,
  Aug : 7,
  Sep : 8,
  Oct : 9,
  Nov : 10,
  Dec : 11
};

Date.getMonthNumber = function(name) {
  return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
};

Date.dayNames = [
  trlVps("Sunday"),
  trlVps("Monday"),
  trlVps("Tuesday"),
  trlVps("Wednesday"),
  trlVps("Thursday"),
  trlVps("Friday"),
  "Saturday"
];

Date.getShortDayName = function(day) {
  return Date.dayNames[day].substring(0, 3);
};

if(Ext && Ext.MessageBox){
  Ext.MessageBox.buttonText = {
    ok     : trlVps("OK"),
    cancel : trlVps("Cancel"),
    yes    : trlVps("Yes"),
    no     : trlVps("No")
  };
}

//Auskommentiert da es beim Datum Probleme gegeben hat
/*if(Ext.util.Format){
  Ext.util.Format.date = function(v, format){
    if(!v) return "";
    if(!(v instanceof Date)) v = new Date(Date.parse(v));
    return v.dateFormat(format || "m/d/Y");
  };
}*/

if(Ext && Ext.DatePicker){
  Ext.apply(Ext.DatePicker.prototype, {
    todayText         : trlVps("Today"),
    minText           : trlVps("This date is before the minimum date"),
    maxText           : trlVps("This date is after the maximum date"),
    disabledDaysText  : "",
    disabledDatesText : "",
    monthNames        : Date.monthNames,
    dayNames          : Date.dayNames,
    nextText          : trlVps('Next Month (Control+Right)'),
    prevText          : trlVps('Previous Month (Control+Left)'),
    monthYearText     : trlVps('Choose a month (Control+Up/Down to move years)'),
    todayTip          : trlVps("{0} (Spacebar)"),
    format            : trlVps("m/d/y"),
    okText            : " "+trlVps('OK')+" ",
    cancelText        : trlVps("Cancel"),
    startDay          : 0
  });
}

if(Ext && Ext.PagingToolbar){
  Ext.apply(Ext.PagingToolbar.prototype, {
    beforePageText : trlVps("Page"),
    afterPageText  : trlVps("of {0}"),
    firstText      : trlVps("First Page"),
    prevText       : trlVps("Previous Page"),
    nextText       : trlVps("Next Page"),
    lastText       : trlVps("Last Page"),
    refreshText    : trlVps("Refresh"),
    displayMsg     : trlVps("Displaying {0} - {1} of {2}"),
    emptyMsg       : trlVps('No data to display')
  });
}

if(Ext && Ext.form.TextField){
  Ext.apply(Ext.form.TextField.prototype, {
    minLengthText : trlVps("The minimum length for this field is {0}"),
    maxLengthText : trlVps("The maximum length for this field is {0}"),
    blankText     : trlVps("This field is required"),
    regexText     : "",
    emptyText     : null
  });
}

if(Ext && Ext.form.NumberField){
  Ext.apply(Ext.form.NumberField.prototype, {
    minText : trlVps("The minimum value for this field is {0}"),
    maxText : trlVps("The maximum value for this field is {0}"),
    nanText : trlVps("{0} is not a valid number")
  });
}

if(Ext && Ext.form.DateField){
  Ext.apply(Ext.form.DateField.prototype, {
    disabledDaysText  : trlVps("Disabled"),
    disabledDatesText : trlVps("Disabled"),
    minText           : trlVps("The date in this field must be after {0}"),
    maxText           : trlVps("The date in this field must be before {0}"),
    invalidText       : trlVps("{0} is not a valid date - it must be in the format {1}"),
    format            : trlVps("m/d/y"),
    altFormats        : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d"
  });
}

if(Ext && Ext.form.ComboBox){
  Ext.apply(Ext.form.ComboBox.prototype, {
    loadingText       : trlVps("Loading..."),
    valueNotFoundText : undefined
  });
}

if(Ext && Ext.form.VTypes){
  Ext.apply(Ext.form.VTypes, {
    emailText    : trlVps('This field should be an e-mail address in the format "user@domain.com"'),
    urlText      : trlVps('This field should be a URL in the format "http:/'+'/www.domain.com"'),
    alphaText    : trlVps('This field should only contain letters and _'),
    alphanumText : trlVps('This field should only contain letters, numbers and _')
  });
}

if(Ext && Ext.form.HtmlEditor){
  Ext.apply(Ext.form.HtmlEditor.prototype, {
    createLinkText : trlVps('Please enter the URL for the link:'),
    buttonTips : {
      bold : {
        title: trlVps('Bold (Ctrl+B)'),
        text:  trlVps('Make the selected text bold.'),
        cls: 'x-html-editor-tip'
      },
      italic : {
        title: trlVps('Italic (Ctrl+I)'),
        text: trlVps('Make the selected text italic.'),
        cls: 'x-html-editor-tip'
      },
      underline : {
        title: trlVps('Underline (Ctrl+U)'),
        text: trlVps('Underline the selected text.'),
        cls: 'x-html-editor-tip'
      },
      increasefontsize : {
        title: trlVps('Grow Text'),
        text: trlVps('Increase the font size.'),
        cls: 'x-html-editor-tip'
      },
      decreasefontsize : {
        title: trlVps('Shrink Text'),
        text: trlVps('Decrease the font size.'),
        cls: 'x-html-editor-tip'
      },
      backcolor : {
        title: trlVps('Text Highlight Color'),
        text: trlVps('Change the background color of the selected text.'),
        cls: 'x-html-editor-tip'
      },
      forecolor : {
        title: trlVps('Font Color'),
        text: trlVps('Change the color of the selected text.'),
        cls: 'x-html-editor-tip'
      },
      justifyleft : {
        title: trlVps('Align Text Left'),
        text: trlVps('Align text to the left.'),
        cls: 'x-html-editor-tip'
      },
      justifycenter : {
        title: trlVps('Center Text'),
        text: trlVps('Center text in the editor.'),
        cls: 'x-html-editor-tip'
      },
      justifyright : {
        title: trlVps('Align Text Right'),
        text: trlVps('Align text to the right.'),
        cls: 'x-html-editor-tip'
      },
      insertunorderedlist : {
        title: trlVps('Bullet List'),
        text: trlVps('Start a bulleted list.'),
        cls: 'x-html-editor-tip'
      },
      insertorderedlist : {
        title: trlVps('Numbered List'),
        text: trlVps('Start a numbered list.'),
        cls: 'x-html-editor-tip'
      },
      createlink : {
        title: trlVps('Hyperlink'),
        text: trlVps('Make the selected text a hyperlink.'),
        cls: 'x-html-editor-tip'
      },
      sourceedit : {
        title: trlVps('Source Edit'),
        text: trlVps('Switch to source editing mode.'),
        cls: 'x-html-editor-tip'
      }
    }
  });
}

if(Ext && Ext.grid.GridView){
  Ext.apply(Ext.grid.GridView.prototype, {
    sortAscText  : trlVps("Sort Ascending"),
    sortDescText : trlVps("Sort Descending"),
    lockText     : trlVps("Lock Column"),
    unlockText   : trlVps("Unlock Column"),
    columnsText  : trlVps("Columns")
  });
}

if(Ext && Ext.grid.GroupingView){
  Ext.apply(Ext.grid.GroupingView.prototype, {
    emptyGroupText : trlVps('(None)'),
    groupByText    : trlVps('Group By This Field'),
    showGroupsText : trlVps('Show in Groups')
  });
}

if(Ext && Ext.grid.PropertyColumnModel){
  Ext.apply(Ext.grid.PropertyColumnModel.prototype, {
    nameText   : trlVps("Name"),
    valueText  : trlVps("Value"),
    dateFormat : "m/j/Y"
  });
}

if(Ext && Ext.layout && Ext.layout.BorderLayout && Ext.layout.BorderLayout.SplitRegion){
  Ext.apply(Ext.layout.BorderLayout.SplitRegion.prototype, {
    splitTip            : trlVps("Drag to resize."),
    collapsibleSplitTip : trlVps("Drag to resize. Double click to hide.")
  });
}
<|endoftext|>


<commit_before>'use strict';

//Executes the command. Exits with errorMessage on failed execution
require('shelljs/global');
//var PleasantProgress = require('pleasant-progress');
var chalk = require('chalk');

module.exports = {
  sync: function(cmd, errorMessage, silent) {
    errorMessage = errorMessage || 'Error: command ' + cmd + ' failed.';
    if (silent === undefined) {
      silent = true;
    }
    var execCmd = exec(cmd, { silent: silent });
    if (execCmd.code !== 0) {
      console.log(errorMessage);
      //Note: Implement --force flag.
      //echo('Exiting. If you want to ignore that error provide the --force flag.');
      console.log('Please report this error to https://github.com/artificialio/sane/issues.');
      console.log(chalk.yellow('\nFurther output from the run command:'));
      console.log(execCmd.output);
      exit(1);
    }
  } //,
  // async: function (cmd, silent, errorMessage, buildMessage, successMessage) {
  //   errorMessage = errorMessage || 'Error: command ' + cmd + ' failed.';
  //   if (silent === undefined) {
  //     silent = true;
  //   }

  //   if (buildMessage === undefined) {
  //     var progress = null;
  //   } else {
  //     var progress = new PleasantProgress();
  //     progress.start(chalk.green(buildMessage));
  //   }

  //   return new Promise(function(resolve, reject){
  //     exec(cmd, { silent: silent, async: true }, function(code, output){
  //       if (progress) {
  //         progress.stop();
  //         console.log(chalk.green(successMessage));
  //       }
  //       if (code !== 0) {
  //         console.log(errorMessage);
  //         //Note: Implement --force flag.
  //         //echo('Exiting. If you want to ignore that error provide the --force flag.');
  //         console.log('Please report this error to https://github.com/artificialio/sane/issues.');
  //         console.log(chalk.yellow('\nFurther output from the run command:'));
  //         console.log(output);
  //         //reject('reject');
  //         exit(1);
  //       }
  //       resolve();
  //     })
  //   });
  // }
};<commit_msg>Code style consistency<commit_after>'use strict';

//Executes the command. Exits with errorMessage on failed execution
require('shelljs/global');
//var PleasantProgress = require('pleasant-progress');
var chalk = require('chalk');

module.exports = {
  sync: function (cmd, errorMessage, silent) {
    errorMessage = errorMessage || 'Error: command ' + cmd + ' failed.';
    if (silent === undefined) {
      silent = true;
    }
    var execCmd = exec(cmd, { silent: silent });
    if (execCmd.code !== 0) {
      console.log(errorMessage);
      //Note: Implement --force flag.
      //echo('Exiting. If you want to ignore that error provide the --force flag.');
      console.log('Please report this error to https://github.com/artificialio/sane/issues.');
      console.log(chalk.yellow('\nFurther output from the run command:'));
      console.log(execCmd.output);
      exit(1);
    }
  } //,
  // async: function (cmd, silent, errorMessage, buildMessage, successMessage) {
  //   errorMessage = errorMessage || 'Error: command ' + cmd + ' failed.';
  //   if (silent === undefined) {
  //     silent = true;
  //   }

  //   if (buildMessage === undefined) {
  //     var progress = null;
  //   } else {
  //     var progress = new PleasantProgress();
  //     progress.start(chalk.green(buildMessage));
  //   }

  //   return new Promise(function(resolve, reject){
  //     exec(cmd, { silent: silent, async: true }, function(code, output){
  //       if (progress) {
  //         progress.stop();
  //         console.log(chalk.green(successMessage));
  //       }
  //       if (code !== 0) {
  //         console.log(errorMessage);
  //         //Note: Implement --force flag.
  //         //echo('Exiting. If you want to ignore that error provide the --force flag.');
  //         console.log('Please report this error to https://github.com/artificialio/sane/issues.');
  //         console.log(chalk.yellow('\nFurther output from the run command:'));
  //         console.log(output);
  //         //reject('reject');
  //         exit(1);
  //       }
  //       resolve();
  //     })
  //   });
  // }
};
<|endoftext|>


<commit_before>'use strict';

const path = require('path');

const ffmpeg = require('fluent-ffmpeg');
const Stream = require('stream');

const minmax = require('../gutil').minmax;

const playfile = {
  handle: 'audio::playfile',
  exec: function playfile (data) {
    if (this.io.input) return false;

    ffmpeg.ffprobe(data.filename, (error, meta) => {
      if (error) {
        if (data.done) data.done(error, true);
        return;
      }

      if (!this.io.input) {
        const conf = this.config.extensions.audio;
        const stream = meta.streams[0];
        const samples = 48000;

        this.space.set('audio.streaming', true);

        this.io.input = this.client.connection.inputStream({
          channels: stream.channels,
          sampleRate: samples,
          gain: conf.gain
        });

        const pass = new Stream.PassThrough();
        // ffmpeg's PassThrough is missing an unpipe method.
        // (╯ರ ~ ರ）╯︵ ┻━┻
        // Another pipe ought to fix it.

        ffmpeg(data.filename)
          .withAudioBitrate(stream.bit_rate)
          .withAudioFrequency(samples)
          .withAudioChannels(stream.channels)
          .format('s16le')
          .pipe(pass);

        let lived = true;

        pass
        .on('end', () => {
          if (lived) setTimeout(() => {
            this.execute('audio::stopfile');

            if (data.done) data.done(null, lived);
          }, 250);
        })
        .on('kill', () => {
          lived = false;

          if (data.done) data.done({
            code: 'APKILL',
            message: 'Audio playback was killed.',
          }, lived);

          pass.end();
        })
        .pipe(this.io.input);

        this.space.set('audio.pass', pass);
      }
    });

    return true;
  }
};

const stopfile = {
  handle: 'audio::stopfile',
  exec: function stopfile (data) {
    if (this.space.get('audio.streaming')) {
      const pass = this.space.get('audio.pass');

      // Work around for noisey bits.
      this.io.input.setGain(0.01);

      pass.unpipe(this.io.input);
      if (data && data.force) pass.emit('kill');
      else pass.end();

      this.io.nullify('i');

      this.space.set('audio.streaming', false);
      this.space.set('audio.pass', null);
    }
  }
};

const feedback = {
  handle: 'audio::feedback',
  init: stumble => stumble.space.set('audio.feedback', false),
  term: stumble => stumble.space.delete('audio.feedback'),
  exec: function feedback () {
    const feeding = this.space.get('audio.feedback');

    if (feeding) {
      this.io.output.unpipe(this.io.input);
      this.io.nullify('io');
      this.space.set('audio.feedback', false);
    } else if (!this.io.input && !this.io.output) {
      this.space.set('audio.feedback', true);
      this.io.input = this.client.connection.inputStream();
      this.io.output = this.client.connection.outputStream();
      this.io.output.pipe(this.io.input);
    }
  },
  commands: [{
    handle: 'feedback',
    exec: function feedback () {
      this.execute('audio::feedback');
    },
    info: () => `

USAGE: feedback

Toggles audio feedback, causing the bot
to repeat everything it hears.

`
  }]
};

const mute = {
  handle: 'audio::mute',
  exec: function mute () {
    const value = !this.space.get('audio.muted');

    this.client.connection.sendMessage('UserState', {
      'self_mute': value
    });

    this.space.set('audio.muted', value);
  },
  commands: [{
    handle: 'mute',
    exec: function mute () {
      this.execute('audio::mute');
    },
    info: () => `

USAGE: mute

Toggles a self-mute on the bot.

`
  }]
};

const gain = {
  handle: 'audio::gain',
  exec: function gain (data) {
    const conf = this.config.extensions.audio;

    if (Number.isInteger(data.gain)) {
      const ngain = minmax(1, data.gain, 100) / 100;

      if (this.space.get('audio.streaming'))
        this.io.input.setGain(ngain);

      conf.gain = ngain;
    }

    return conf.gain;
  },
  commands: [{
    handle: 'gain',
    exec: function gaincmd (data) {
      const level = this.execute('audio::gain', {
        gain: parseInt(data.message, 10)
      });

      data.user.sendMessage(`Current gain: [ ${(level * 100) >> 0} ].`);
    },
    info: () => `

USAGE: gain INTEGER

Sets the audio gain to an integer,
between 1 and 100 inclusive.
Going outside these bounds will set
the gain to the nearest extreme.

`
  }]
};

module.exports = {
  handle: 'audio',
  init: stumble => {
    const conf = stumble.config.extensions.audio;
    conf.directory = path.resolve(conf.directory);

    stumble.space.set('audio.streaming', false);
    stumble.space.set('audio.muted', false);
    stumble.space.set('audio.pass', null);
  },
  term: stumble => {
    stumble.space.delete('audio.streaming');
    stumble.space.delete('audio.muted');
    stumble.space.delete('audio.pass');
  },
  extensions: [playfile, stopfile, feedback, mute, gain]
};
<commit_msg>aggressive audio gain clamps<commit_after>'use strict';

const path = require('path');

const ffmpeg = require('fluent-ffmpeg');
const Stream = require('stream');

const minmax = require('../gutil').minmax;

const playfile = {
  handle: 'audio::playfile',
  exec: function playfile (data) {
    if (this.io.input) return false;

    ffmpeg.ffprobe(data.filename, (error, meta) => {
      if (error) {
        if (data.done) data.done(error, true);
        return;
      }

      if (!this.io.input) {
        const conf = this.config.extensions.audio;
        const stream = meta.streams[0];
        const samples = 48000;

        this.space.set('audio.streaming', true);

        this.io.input = this.client.connection.inputStream({
          channels: stream.channels,
          sampleRate: samples,
          gain: minmax(0.01, conf.gain, 1.0)
        });

        const pass = new Stream.PassThrough();
        // ffmpeg's PassThrough is missing an unpipe method.
        // (╯ರ ~ ರ）╯︵ ┻━┻
        // Another pipe ought to fix it.

        ffmpeg(data.filename)
          .withAudioBitrate(stream.bit_rate)
          .withAudioFrequency(samples)
          .withAudioChannels(stream.channels)
          .format('s16le')
          .pipe(pass);

        let lived = true;

        pass
        .on('end', () => {
          if (lived) setTimeout(() => {
            this.execute('audio::stopfile');

            if (data.done) data.done(null, lived);
          }, 250);
        })
        .on('kill', () => {
          lived = false;

          if (data.done) data.done({
            code: 'APKILL',
            message: 'Audio playback was killed.',
          }, lived);

          pass.end();
        })
        .pipe(this.io.input);

        this.space.set('audio.pass', pass);
      }
    });

    return true;
  }
};

const stopfile = {
  handle: 'audio::stopfile',
  exec: function stopfile (data) {
    if (this.space.get('audio.streaming')) {
      const pass = this.space.get('audio.pass');

      // Work around for noisey bits.
      this.io.input.setGain(0.01);

      pass.unpipe(this.io.input);
      if (data && data.force) pass.emit('kill');
      else pass.end();

      this.io.nullify('i');

      this.space.set('audio.streaming', false);
      this.space.set('audio.pass', null);
    }
  }
};

const feedback = {
  handle: 'audio::feedback',
  init: stumble => stumble.space.set('audio.feedback', false),
  term: stumble => stumble.space.delete('audio.feedback'),
  exec: function feedback () {
    const feeding = this.space.get('audio.feedback');

    if (feeding) {
      this.io.output.unpipe(this.io.input);
      this.io.nullify('io');
      this.space.set('audio.feedback', false);
    } else if (!this.io.input && !this.io.output) {
      this.space.set('audio.feedback', true);
      this.io.input = this.client.connection.inputStream();
      this.io.output = this.client.connection.outputStream();
      this.io.output.pipe(this.io.input);
    }
  },
  commands: [{
    handle: 'feedback',
    exec: function feedback () {
      this.execute('audio::feedback');
    },
    info: () => `

USAGE: feedback

Toggles audio feedback, causing the bot
to repeat everything it hears.

`
  }]
};

const mute = {
  handle: 'audio::mute',
  exec: function mute () {
    const value = !this.space.get('audio.muted');

    this.client.connection.sendMessage('UserState', {
      'self_mute': value
    });

    this.space.set('audio.muted', value);
  },
  commands: [{
    handle: 'mute',
    exec: function mute () {
      this.execute('audio::mute');
    },
    info: () => `

USAGE: mute

Toggles a self-mute on the bot.

`
  }]
};

const gain = {
  handle: 'audio::gain',
  exec: function gain (data) {
    const conf = this.config.extensions.audio;

    if (Number.isInteger(data.gain)) {
      const ngain = minmax(1, data.gain, 100) / 100;

      if (this.space.get('audio.streaming'))
        this.io.input.setGain(ngain);

      conf.gain = ngain;
    }

    return conf.gain;
  },
  commands: [{
    handle: 'gain',
    exec: function gaincmd (data) {
      const level = this.execute('audio::gain', {
        gain: parseInt(data.message, 10)
      });

      data.user.sendMessage(`Current gain: [ ${(level * 100) >> 0} ].`);
    },
    info: () => `

USAGE: gain INTEGER

Sets the audio gain to an integer,
between 1 and 100 inclusive.
Going outside these bounds will set
the gain to the nearest extreme.

`
  }]
};

module.exports = {
  handle: 'audio',
  init: stumble => {
    const conf = stumble.config.extensions.audio;
    conf.directory = path.resolve(conf.directory);
    conf.gain = minmax(0.01, conf.gain, 1.0);

    stumble.space.set('audio.streaming', false);
    stumble.space.set('audio.muted', false);
    stumble.space.set('audio.pass', null);
  },
  term: stumble => {
    stumble.space.delete('audio.streaming');
    stumble.space.delete('audio.muted');
    stumble.space.delete('audio.pass');
  },
  extensions: [playfile, stopfile, feedback, mute, gain]
};
<|endoftext|>