<commit_before>app.controller('ApiEditController', ['$scope', '$routeParams', '$http', 'viewFactory', 'toast',
    function ($scope, $routeParams, $http, viewFactory, toast) {

    $scope.apiId = $routeParams.apiId;
    $scope.formInput = {};

    viewFactory.title = 'Edit API';
    $scope.pluginList = [];

    $scope.fetchPluginList = function (url) {
        $http({
            method: 'GET',
            url: url
        }).then(function (response) {
            $scope.nextPluginUrl = response.data.next || '';

            for (var i=0; i<response.data.data.length; i++ ) {
                $scope.pluginList.push(response.data.data[i]);
            }

        }, function () {
            toast.error('Could not load plugin list');
        })
    };

    $http({
        method: 'GET',
        url: buildUrl('/apis/' + $scope.apiId)
    }).then(function (response) {
        $scope.formInput.upstreamUrl = response.data.upstream_url;
        $scope.formInput.requestPath = (typeof response.data.request_path == 'undefined') ? '' : response.data.request_path;
        $scope.formInput.requestHost = (typeof response.data.request_host == 'undefined') ? '' : response.data.request_host;
        $scope.formInput.apiName = (typeof response.data.name == 'undefined') ? '' : response.data.name;
        $scope.formInput.preserveHost = response.data.preserve_host;
        $scope.formInput.stripRequestPath = response.data.strip_request_path;

        viewFactory.deleteAction = {target: 'API', url: '/apis/' + $scope.apiId, redirect: '#/api'};

    }, function (response) {
        toast.error('Could not load API details');

        if (response && response.status === 404) window.location.href = '#/api';
    });

    var apiForm = angular.element('form#formEdit');

    apiForm.on('submit', function (event) {
        event.preventDefault();

        var payload = {};

        if ($scope.formInput.apiName.trim().length > 1) {
            payload.name = $scope.formInput.apiName;
        }

        if ($scope.formInput.requestHost.trim().length > 1) {
            payload.request_host = $scope.formInput.requestHost;
        }

        if (typeof payload.name === 'undefined' &&
            typeof payload.request_host === 'undefined') {
            apiForm.find('input[name="apiName"]').focus();
            return false;
        }

        if ($scope.formInput.requestPath.trim().length > 1) {
            payload.request_path = $scope.formInput.requestPath;
        }

        if ( typeof payload.request_path === 'undefined' &&
            typeof payload.request_host === 'undefined') {
            apiForm.find('input[name="requestPath"]').focus();
            return false;
        }

        if ($scope.formInput.upstreamUrl.trim().length > 1) {
            payload.upstream_url = $scope.formInput.upstreamUrl;
        } else {
            apiForm.find('input[name="upstreamUrl"]').focus();
            return false;
        }

        payload.strip_request_path = $scope.formInput.stripRequestPath;
        payload.preserve_host = $scope.formInput.preserveHost;

        $http({
            method: 'PATCH',
            url: buildUrl('/apis/' + $scope.apiId),
            data: payload,
            headers: {'Content-Type': 'application/json'}
        }).then(function () {
            toast.success('API details updated');

        }, function (response) {
            toast.error(response.data);
        });

        return false
    });

    angular.element('table#pluginListTable').on('click', 'input[type="checkbox"].plugin-state', function (event) {
        var state = 'enabled';

        if (event.target.checked) state = 'enabled';
        else state = 'disabled';

        $http({
            method: 'PATCH',
            url: buildUrl('/apis/' + $scope.apiId + '/plugins/' + event.target.value),
            data: { enabled: (state == 'enabled') },
            headers: {'Content-Type': 'application/json'}
        }).then(function () {
            toast.success('Plugin ' + event.target.dataset.name + ' ' + state)

        }, function () {
            toast.error('Status could not not be changed')
        })
    });

    $scope.fetchPluginList(buildUrl('/apis/' + $scope.apiId + '/plugins'))
}]);<commit_msg>[added] ajax and toast dependency<commit_after>app.controller('ApiEditController', ['$scope', '$routeParams', 'ajax', 'toast' ,'viewFactory',
    function ($scope, $routeParams, ajax, toast, viewFactory) {

    $scope.apiId = $routeParams.apiId;
    $scope.formInput = {};

    viewFactory.title = 'Edit API';
    $scope.pluginList = [];

    $scope.fetchPluginList = function (url) {
        ajax.get({
            resource: url
        }).then(function (response) {
            $scope.nextPluginUrl = response.data.next || '';

            for (var i=0; i<response.data.data.length; i++ ) {
                $scope.pluginList.push(response.data.data[i]);
            }

        }, function () {
            toast.error('Could not load plugin list');
        })
    };

    ajax.get({
        resource: '/apis/' + $scope.apiId
    }).then(function (response) {
        $scope.formInput.upstreamUrl = response.data.upstream_url;
        $scope.formInput.requestPath = (typeof response.data.request_path == 'undefined') ? '' : response.data.request_path;
        $scope.formInput.requestHost = (typeof response.data.request_host == 'undefined') ? '' : response.data.request_host;
        $scope.formInput.apiName = (typeof response.data.name == 'undefined') ? '' : response.data.name;
        $scope.formInput.preserveHost = response.data.preserve_host;
        $scope.formInput.stripRequestPath = response.data.strip_request_path;

        viewFactory.deleteAction = {target: 'API', url: '/apis/' + $scope.apiId, redirect: '#/api'};

    }, function (response) {
        toast.error('Could not load API details');

        if (response && response.status === 404) window.location.href = '#/api';
    });

    var apiForm = angular.element('form#formEdit');

    apiForm.on('submit', function (event) {
        event.preventDefault();

        var payload = {};

        if ($scope.formInput.apiName.trim().length > 1) {
            payload.name = $scope.formInput.apiName;
        }

        if ($scope.formInput.requestHost.trim().length > 1) {
            payload.request_host = $scope.formInput.requestHost;
        }

        if (typeof payload.name === 'undefined' &&
            typeof payload.request_host === 'undefined') {
            apiForm.find('input[name="apiName"]').focus();
            return false;
        }

        if ($scope.formInput.requestPath.trim().length > 1) {
            payload.request_path = $scope.formInput.requestPath;
        }

        if ( typeof payload.request_path === 'undefined' &&
            typeof payload.request_host === 'undefined') {
            apiForm.find('input[name="requestPath"]').focus();
            return false;
        }

        if ($scope.formInput.upstreamUrl.trim().length > 1) {
            payload.upstream_url = $scope.formInput.upstreamUrl;
        } else {
            apiForm.find('input[name="upstreamUrl"]').focus();
            return false;
        }

        payload.strip_request_path = $scope.formInput.stripRequestPath;
        payload.preserve_host = $scope.formInput.preserveHost;

        ajax.patch({
            resource: '/apis/' + $scope.apiId,
            data: payload
        }).then(function () {
            toast.success('API details updated');

        }, function (response) {
            toast.error(response.data);
        });

        return false
    });

    angular.element('table#pluginListTable').on('click', 'input[type="checkbox"].plugin-state', function (event) {
        var state = 'enabled';

        if (event.target.checked) state = 'enabled';
        else state = 'disabled';

        ajax.patch({
            url: '/apis/' + $scope.apiId + '/plugins/' + event.target.value,
            data: { enabled: (state == 'enabled') },
        }).then(function () {
            toast.success('Plugin ' + event.target.dataset.name + ' ' + state)

        }, function () {
            toast.error('Status could not not be changed')
        })
    });

    $scope.fetchPluginList('/apis/' + $scope.apiId + '/plugins')
}]);<|endoftext|>


<commit_before>import { Vector4 } from '../math/Vector4';
import { Vector3 } from '../math/Vector3';
import { Vector2 } from '../math/Vector2';
import { Color } from '../math/Color';
import { _Math } from '../math/Math';

/**
 * @author mrdoob / http://mrdoob.com/
 */

function BufferAttribute( array, itemSize, normalized ) {

	if (array.buffer instanceof ArrayBuffer === false) {

		throw new TypeError( 'THREE.BufferAttribute: array should be an instance of Uint8Array, Int8Array, Uint16Array, Int16Array, or Float32Array.' );

	}

	this.uuid = _Math.generateUUID();

	this.array = array;
	this.itemSize = itemSize;

	this.dynamic = false;
	this.updateRange = { offset: 0, count: - 1 };

	this.version = 0;
	this.normalized = normalized === true;

}

BufferAttribute.prototype = {

	constructor: BufferAttribute,

	isBufferAttribute: true,

	get count() {

		return this.array.length / this.itemSize;

	},

	set needsUpdate( value ) {

		if ( value === true ) this.version ++;

	},

	setDynamic: function ( value ) {

		this.dynamic = value;

		return this;

	},

	copy: function ( source ) {

		this.array = new source.array.constructor( source.array );
		this.itemSize = source.itemSize;

		this.dynamic = source.dynamic;

		return this;

	},

	copyAt: function ( index1, attribute, index2 ) {

		index1 *= this.itemSize;
		index2 *= attribute.itemSize;

		for ( var i = 0, l = this.itemSize; i < l; i ++ ) {

			this.array[ index1 + i ] = attribute.array[ index2 + i ];

		}

		return this;

	},

	copyArray: function ( array ) {

		this.array.set( array );

		return this;

	},

	copyColorsArray: function ( colors ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = colors.length; i < l; i ++ ) {

			var color = colors[ i ];

			if ( color === undefined ) {

				console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );
				color = new Color();

			}

			array[ offset ++ ] = color.r;
			array[ offset ++ ] = color.g;
			array[ offset ++ ] = color.b;

		}

		return this;

	},

	copyIndicesArray: function ( indices ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = indices.length; i < l; i ++ ) {

			var index = indices[ i ];

			array[ offset ++ ] = index.a;
			array[ offset ++ ] = index.b;
			array[ offset ++ ] = index.c;

		}

		return this;

	},

	copyVector2sArray: function ( vectors ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = vectors.length; i < l; i ++ ) {

			var vector = vectors[ i ];

			if ( vector === undefined ) {

				console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );
				vector = new Vector2();

			}

			array[ offset ++ ] = vector.x;
			array[ offset ++ ] = vector.y;

		}

		return this;

	},

	copyVector3sArray: function ( vectors ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = vectors.length; i < l; i ++ ) {

			var vector = vectors[ i ];

			if ( vector === undefined ) {

				console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );
				vector = new Vector3();

			}

			array[ offset ++ ] = vector.x;
			array[ offset ++ ] = vector.y;
			array[ offset ++ ] = vector.z;

		}

		return this;

	},

	copyVector4sArray: function ( vectors ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = vectors.length; i < l; i ++ ) {

			var vector = vectors[ i ];

			if ( vector === undefined ) {

				console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );
				vector = new Vector4();

			}

			array[ offset ++ ] = vector.x;
			array[ offset ++ ] = vector.y;
			array[ offset ++ ] = vector.z;
			array[ offset ++ ] = vector.w;

		}

		return this;

	},

	set: function ( value, offset ) {

		if ( offset === undefined ) offset = 0;

		this.array.set( value, offset );

		return this;

	},

	getX: function ( index ) {

		return this.array[ index * this.itemSize ];

	},

	setX: function ( index, x ) {

		this.array[ index * this.itemSize ] = x;

		return this;

	},

	getY: function ( index ) {

		return this.array[ index * this.itemSize + 1 ];

	},

	setY: function ( index, y ) {

		this.array[ index * this.itemSize + 1 ] = y;

		return this;

	},

	getZ: function ( index ) {

		return this.array[ index * this.itemSize + 2 ];

	},

	setZ: function ( index, z ) {

		this.array[ index * this.itemSize + 2 ] = z;

		return this;

	},

	getW: function ( index ) {

		return this.array[ index * this.itemSize + 3 ];

	},

	setW: function ( index, w ) {

		this.array[ index * this.itemSize + 3 ] = w;

		return this;

	},

	setXY: function ( index, x, y ) {

		index *= this.itemSize;

		this.array[ index + 0 ] = x;
		this.array[ index + 1 ] = y;

		return this;

	},

	setXYZ: function ( index, x, y, z ) {

		index *= this.itemSize;

		this.array[ index + 0 ] = x;
		this.array[ index + 1 ] = y;
		this.array[ index + 2 ] = z;

		return this;

	},

	setXYZW: function ( index, x, y, z, w ) {

		index *= this.itemSize;

		this.array[ index + 0 ] = x;
		this.array[ index + 1 ] = y;
		this.array[ index + 2 ] = z;
		this.array[ index + 3 ] = w;

		return this;

	},

	clone: function () {

		return new this.constructor().copy( this );

	}

};

//

function Int8Attribute( array, itemSize ) {

	return new BufferAttribute( new Int8Array( array ), itemSize );

}

function Uint8Attribute( array, itemSize ) {

	return new BufferAttribute( new Uint8Array( array ), itemSize );

}

function Uint8ClampedAttribute( array, itemSize ) {

	return new BufferAttribute( new Uint8ClampedArray( array ), itemSize );

}

function Int16Attribute( array, itemSize ) {

	return new BufferAttribute( new Int16Array( array ), itemSize );

}

function Uint16Attribute( array, itemSize ) {

	return new BufferAttribute( new Uint16Array( array ), itemSize );

}

function Int32Attribute( array, itemSize ) {

	return new BufferAttribute( new Int32Array( array ), itemSize );

}

function Uint32Attribute( array, itemSize ) {

	return new BufferAttribute( new Uint32Array( array ), itemSize );

}

function Float32Attribute( array, itemSize ) {

	return new BufferAttribute( new Float32Array( array ), itemSize );

}

function Float64Attribute( array, itemSize ) {

	return new BufferAttribute( new Float64Array( array ), itemSize );

}

// Deprecated

function DynamicBufferAttribute( array, itemSize ) {

	console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' );
	return new BufferAttribute( array, itemSize ).setDynamic( true );

}

export {
  DynamicBufferAttribute,
  Float64Attribute,
  Float32Attribute,
  Uint32Attribute,
  Int32Attribute,
  Uint16Attribute,
  Int16Attribute,
  Uint8ClampedAttribute,
  Uint8Attribute,
  Int8Attribute,
  BufferAttribute
};
<commit_msg>Clean up.<commit_after>import { Vector4 } from '../math/Vector4';
import { Vector3 } from '../math/Vector3';
import { Vector2 } from '../math/Vector2';
import { Color } from '../math/Color';
import { _Math } from '../math/Math';

/**
 * @author mrdoob / http://mrdoob.com/
 */

function BufferAttribute( array, itemSize, normalized ) {

	if ( array.buffer instanceof ArrayBuffer === false ) {

		throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );

	}

	this.uuid = _Math.generateUUID();

	this.array = array;
	this.itemSize = itemSize;

	this.dynamic = false;
	this.updateRange = { offset: 0, count: - 1 };

	this.version = 0;
	this.normalized = normalized === true;

}

BufferAttribute.prototype = {

	constructor: BufferAttribute,

	isBufferAttribute: true,

	get count() {

		return this.array.length / this.itemSize;

	},

	set needsUpdate( value ) {

		if ( value === true ) this.version ++;

	},

	setDynamic: function ( value ) {

		this.dynamic = value;

		return this;

	},

	copy: function ( source ) {

		this.array = new source.array.constructor( source.array );
		this.itemSize = source.itemSize;

		this.dynamic = source.dynamic;

		return this;

	},

	copyAt: function ( index1, attribute, index2 ) {

		index1 *= this.itemSize;
		index2 *= attribute.itemSize;

		for ( var i = 0, l = this.itemSize; i < l; i ++ ) {

			this.array[ index1 + i ] = attribute.array[ index2 + i ];

		}

		return this;

	},

	copyArray: function ( array ) {

		this.array.set( array );

		return this;

	},

	copyColorsArray: function ( colors ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = colors.length; i < l; i ++ ) {

			var color = colors[ i ];

			if ( color === undefined ) {

				console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );
				color = new Color();

			}

			array[ offset ++ ] = color.r;
			array[ offset ++ ] = color.g;
			array[ offset ++ ] = color.b;

		}

		return this;

	},

	copyIndicesArray: function ( indices ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = indices.length; i < l; i ++ ) {

			var index = indices[ i ];

			array[ offset ++ ] = index.a;
			array[ offset ++ ] = index.b;
			array[ offset ++ ] = index.c;

		}

		return this;

	},

	copyVector2sArray: function ( vectors ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = vectors.length; i < l; i ++ ) {

			var vector = vectors[ i ];

			if ( vector === undefined ) {

				console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );
				vector = new Vector2();

			}

			array[ offset ++ ] = vector.x;
			array[ offset ++ ] = vector.y;

		}

		return this;

	},

	copyVector3sArray: function ( vectors ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = vectors.length; i < l; i ++ ) {

			var vector = vectors[ i ];

			if ( vector === undefined ) {

				console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );
				vector = new Vector3();

			}

			array[ offset ++ ] = vector.x;
			array[ offset ++ ] = vector.y;
			array[ offset ++ ] = vector.z;

		}

		return this;

	},

	copyVector4sArray: function ( vectors ) {

		var array = this.array, offset = 0;

		for ( var i = 0, l = vectors.length; i < l; i ++ ) {

			var vector = vectors[ i ];

			if ( vector === undefined ) {

				console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );
				vector = new Vector4();

			}

			array[ offset ++ ] = vector.x;
			array[ offset ++ ] = vector.y;
			array[ offset ++ ] = vector.z;
			array[ offset ++ ] = vector.w;

		}

		return this;

	},

	set: function ( value, offset ) {

		if ( offset === undefined ) offset = 0;

		this.array.set( value, offset );

		return this;

	},

	getX: function ( index ) {

		return this.array[ index * this.itemSize ];

	},

	setX: function ( index, x ) {

		this.array[ index * this.itemSize ] = x;

		return this;

	},

	getY: function ( index ) {

		return this.array[ index * this.itemSize + 1 ];

	},

	setY: function ( index, y ) {

		this.array[ index * this.itemSize + 1 ] = y;

		return this;

	},

	getZ: function ( index ) {

		return this.array[ index * this.itemSize + 2 ];

	},

	setZ: function ( index, z ) {

		this.array[ index * this.itemSize + 2 ] = z;

		return this;

	},

	getW: function ( index ) {

		return this.array[ index * this.itemSize + 3 ];

	},

	setW: function ( index, w ) {

		this.array[ index * this.itemSize + 3 ] = w;

		return this;

	},

	setXY: function ( index, x, y ) {

		index *= this.itemSize;

		this.array[ index + 0 ] = x;
		this.array[ index + 1 ] = y;

		return this;

	},

	setXYZ: function ( index, x, y, z ) {

		index *= this.itemSize;

		this.array[ index + 0 ] = x;
		this.array[ index + 1 ] = y;
		this.array[ index + 2 ] = z;

		return this;

	},

	setXYZW: function ( index, x, y, z, w ) {

		index *= this.itemSize;

		this.array[ index + 0 ] = x;
		this.array[ index + 1 ] = y;
		this.array[ index + 2 ] = z;
		this.array[ index + 3 ] = w;

		return this;

	},

	clone: function () {

		return new this.constructor().copy( this );

	}

};

//

function Int8Attribute( array, itemSize ) {

	return new BufferAttribute( new Int8Array( array ), itemSize );

}

function Uint8Attribute( array, itemSize ) {

	return new BufferAttribute( new Uint8Array( array ), itemSize );

}

function Uint8ClampedAttribute( array, itemSize ) {

	return new BufferAttribute( new Uint8ClampedArray( array ), itemSize );

}

function Int16Attribute( array, itemSize ) {

	return new BufferAttribute( new Int16Array( array ), itemSize );

}

function Uint16Attribute( array, itemSize ) {

	return new BufferAttribute( new Uint16Array( array ), itemSize );

}

function Int32Attribute( array, itemSize ) {

	return new BufferAttribute( new Int32Array( array ), itemSize );

}

function Uint32Attribute( array, itemSize ) {

	return new BufferAttribute( new Uint32Array( array ), itemSize );

}

function Float32Attribute( array, itemSize ) {

	return new BufferAttribute( new Float32Array( array ), itemSize );

}

function Float64Attribute( array, itemSize ) {

	return new BufferAttribute( new Float64Array( array ), itemSize );

}

// Deprecated

function DynamicBufferAttribute( array, itemSize ) {

	console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' );
	return new BufferAttribute( array, itemSize ).setDynamic( true );

}

export {
  DynamicBufferAttribute,
  Float64Attribute,
  Float32Attribute,
  Uint32Attribute,
  Int32Attribute,
  Uint16Attribute,
  Int16Attribute,
  Uint8ClampedAttribute,
  Uint8Attribute,
  Int8Attribute,
  BufferAttribute
};
<|endoftext|>


<commit_before>/**
 * https://github.com/mrdoob/eventdispatcher.js/
 */

THREE.EventDispatcher = function () {};

THREE.EventDispatcher.prototype = {

	constructor: THREE.EventDispatcher,

	apply: function ( object ) {

		object.addEventListener = THREE.EventDispatcher.prototype.addEventListener;
		object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener;
		object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener;
		object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent;

	},

	addEventListener: function ( type, listener ) {

		if ( this._listeners === undefined ) this._listeners = {};

		var listeners = this._listeners;

		if ( listeners[ type ] === undefined ) {

			listeners[ type ] = [];

		}

		if ( listeners[ type ].indexOf( listener ) === - 1 ) {

			listeners[ type ].push( listener );

		}

	},

	hasEventListener: function ( type, listener ) {

		if ( this._listeners === undefined ) return false;

		var listeners = this._listeners;

		if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {

			return true;

		}

		return false;

	},

	removeEventListener: function ( type, listener ) {

		if ( this._listeners === undefined ) return;

		var listeners = this._listeners;
		var listenerArray = listeners[ type ];

		if ( listenerArray !== undefined ) {

			var index = listenerArray.indexOf( listener );

			if ( index !== - 1 ) {

				listenerArray.splice( index, 1 );

			}

		}

	},

	dispatchEvent: function ( event ) {

		if ( this._listeners === undefined ) return;

		var listenerArray = this._listeners[ event.type ];

		if ( listenerArray !== undefined ) {

			event.target = this;

			var length = listenerArray.length;

			for ( var i = 0; i < length; i ++ ) {

				listenerArray[ i ].call( this, event );

			}

		}

	}

};
<commit_msg>Restores EventDispatcher tweak, which has moved to a new issue and branch for it.<commit_after>/**
 * https://github.com/mrdoob/eventdispatcher.js/
 */

THREE.EventDispatcher = function () {};

THREE.EventDispatcher.prototype = {

	constructor: THREE.EventDispatcher,

	apply: function ( object ) {

		object.addEventListener = THREE.EventDispatcher.prototype.addEventListener;
		object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener;
		object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener;
		object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent;

	},

	addEventListener: function ( type, listener ) {

		if ( this._listeners === undefined ) this._listeners = {};

		var listeners = this._listeners;

		if ( listeners[ type ] === undefined ) {

			listeners[ type ] = [];

		}

		if ( listeners[ type ].indexOf( listener ) === - 1 ) {

			listeners[ type ].push( listener );

		}

	},

	hasEventListener: function ( type, listener ) {

		if ( this._listeners === undefined ) return false;

		var listeners = this._listeners;

		if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {

			return true;

		}

		return false;

	},

	removeEventListener: function ( type, listener ) {

		if ( this._listeners === undefined ) return;

		var listeners = this._listeners;
		var listenerArray = listeners[ type ];

		if ( listenerArray !== undefined ) {

			var index = listenerArray.indexOf( listener );

			if ( index !== - 1 ) {

				listenerArray.splice( index, 1 );

			}

		}

	},

	dispatchEvent: function ( event ) {

		if ( this._listeners === undefined ) return;

		var listeners = this._listeners;
		var listenerArray = listeners[ event.type ];

		if ( listenerArray !== undefined ) {

			event.target = this;

			var array = [];
			var length = listenerArray.length;

			for ( var i = 0; i < length; i ++ ) {

				array[ i ] = listenerArray[ i ];

			}

			for ( var i = 0; i < length; i ++ ) {

				array[ i ].call( this, event );

			}

		}

	}
};
<|endoftext|>


<commit_before>import _ from 'lodash';
const logger = require('../util/logger')(__filename);
const {knex} = require('../util/database').connect();
import {createFeedItem} from './feed-core';
import {markAsAggregated} from './action-core';

function queryStats() {
  let sqlString = `SELECT
      actions.id as id,
      actions.location as location,
      actions.aggregated as aggregated,
      action_types.code as action_type_code,
      users.id as user_id,
      users.name as user_name,
      teams.id as team_id,
      teams.name as team_name
    FROM actions
    JOIN action_types ON action_types.id = actions.action_type_id
    JOIN users ON users.id = actions.user_id
    JOIN teams ON teams.id = actions.team_id
    WHERE action_types.code = 'SIMA'
    ORDER BY id`;

  return knex.raw(sqlString)
    .then(result => {
      const rows = result.rows;

      const stats = buildStats(rows);

      return stats;
    });
}

function buildStats(rows) {
  const getStats = function(stats, key, name) {
    const existing = stats[key];
    if (existing) {
      return existing;
    }

    const newStats = {
      name: name,
      drinksAggregated: 0,
      newDrinks: 0,
      newActions: []
    };

    stats[key] = newStats;
    return newStats;
  }

  const teamStats = {};
  const userStats = {};

  rows.forEach(row => {
    const team = getStats(teamStats, row.team_id, row.team_name);
    const user = getStats(userStats, row.user_id, row.user_name);

    if (row.aggregated) {
      team.drinksAggregated++;
      user.drinksAggregated++;
    } else {
      team.newDrinks++;
      user.newDrinks++;

      team.newActions.push(row);
      user.newActions.push(row);
    }
  });

  return {
    teamStats,
    userStats
  };
}

function handleAction(action, trx) {
  if (action.type === 'IMAGE' || action.type === 'TEXT') {
    return createFeedItem(action, trx)
      .then(() => markAsAggregated(action.id, trx));
  }

  return Promise.resolve();
}

function feedItemParam(action, text) {
  const item = {
    text: text,
    type: 'TEXT'
  };

  if (action.location) {
    item.location = {
      longitude: action.location.x,
      latitude: action.location.y
    };
  }

  return item;
}

function integerDivide(num, denominator) {
  return Math.floor(num / denominator);
}

function createFeedItemForUser(feedItem, userId, newActions) {
  const maxId = _.last(newActions).id;

  return knex.transaction(function(trx) {
    return createFeedItem(feedItem, trx)
      .then(() => {
        return trx('actions')
          .update('aggregated', true)
          .where('id', '<=', maxId)
          .andWhere('aggregated', false)
          .andWhere('user_id', userId);
      });
  });
}

function createFeedItemForTeam(feedItem, teamId, newActions) {
  const maxId = _.last(newActions).id;

  return knex.transaction(function(trx) {
    return createFeedItem(feedItem, trx)
      .then(() => {
        return trx('actions')
          .update('aggregated', true)
          .where('id', '<=', maxId)
          .andWhere('aggregated', false)
          .andWhere('team_id', teamId);
      });
  });
}

function createDrinkAggregates(stats) {
  const feedItemsToCreate = [];

  _.forEach(stats.userStats, (userStats, userId) => {
    if (userStats.drinksAggregated === userStats.newDrinks) {
      return;
    }

    const username = userStats.name;
    const drinksBefore = userStats.drinksAggregated;
    const drinksAfter  = drinksBefore + userStats.newDrinks;
    let feedItem;

    if (drinksBefore === 0) {
      const text = `${ username } starts wappu! Congratulations on the first sima!`;
      feedItem = feedItemParam(userStats.newActions[0], text);
    }
    else if (integerDivide(drinksBefore, 100) !== integerDivide(drinksAfter, 100)) {
      const text = `Such wow. ${ username } has had already ${ integerDivide(drinksAfter, 100) * 100 } simas.`;
      const idx = 100 - drinksBefore % 100;
      feedItem = feedItemParam(userStats.newActions[idx - 1], text);
    }

    if (feedItem) {
      feedItemsToCreate.push(
        createFeedItemForUser(feedItem, userId, userStats.newActions)
      )
    }
  });

  _.forEach(stats.teamStats, (teamStats, teamId) => {
    if (teamStats.drinksAggregated === teamStats.newDrinks) {
      return;
    }

    const name = teamStats.name;
    const drinksBefore = teamStats.drinksAggregated;
    const drinksAfter  = drinksBefore + teamStats.newDrinks;
    let feedItem;

    if (drinksBefore === 0) {
      const text = `${ name } starts wappu! Congratulations on the first sima!`;
      feedItem = feedItemParam(teamStats.newActions[0], text);
    }
    else if (integerDivide(drinksBefore, 100) !== integerDivide(drinksAfter, 100)) {
      const text = `Such wow. ${ name } has had already ${ integerDivide(drinksAfter, 100) * 100 } simas.`;
      const idx = 100 - drinksBefore % 100;
      feedItem = feedItemParam(teamStats.newActions[idx - 1], text);
    }

    if (feedItem) {
      feedItemsToCreate.push(
        createFeedItemForTeam(feedItem, teamId, teamStats.newActions)
      )
    }
  });

  return Promise.all(feedItemsToCreate);
}

function aggregate() {
  // TODO: It's suboptimal to query all actions on every poll.
  // Should use caching here.
  queryStats()
    .then(createDrinkAggregates);
}

let isRunning = false;

function start() {
  if (isRunning) {
    throw new Error("Already running");
  }

  isRunning = true;

  function aggregatePoll() {
    try {
      aggregate();
    } catch (error) {
      logger.error(error);
    }

    setTimeout(aggregatePoll, 60 * 1000);
  }

  aggregatePoll();
}

export {
  start,
  handleAction
};
<commit_msg>Implemented aggregating for score<commit_after>import _ from 'lodash';
const BPromise = require("bluebird");
const logger = require('../util/logger')(__filename);
const {knex} = require('../util/database').connect();
import {createFeedItem} from './feed-core';
import {markAsAggregated} from './action-core';

const SIMA_REPORT_INTERVAL = 100;
const SCORE_REPORT_INTERVAL = 1000;
const POLL_INTERVAL = 60 * 1000; // 1 min

function toInt(n) {
  return parseInt(n, 10) || 0;
}

function roundTo(n, target) {
  return integerDivide(n, target) * target;
}

function passesPoint(start, end, point) {
  return integerDivide(start, point) !== integerDivide(end, point)
}

function Stats(name) {
  const newActions = [];
  let scoreBefore  = 0;
  let drinksBefore = 0;
  let newDrinks    = 0;
  let newScore     = 0;

  function addOldActionStats(type, count, score) {
    if (type === 'SIMA') {
      drinksBefore += count;
    }

    scoreBefore += toInt(score);
  }

  function addNewAction(action) {
    if (action.type === 'SIMA') {
      newDrinks++;
    }

    newScore += action.score;
    newActions.push(action);
  }

  function getFirstAction() {
    return newActions[0];
  }

  function getLastAction() {
    return newActions[newActions.length - 1];
  }

  function getActionIds() {
    return newActions.map(a => a.id);
  }

  return Object.defineProperties({
      addNewAction,
      addOldActionStats,
      getActionIds,
      getFirstAction,
      getLastAction,
      toString: function() {
        return JSON.stringify({
          name: name,
          drinksBefore: drinksBefore,
          drinksAfter: drinksBefore + newDrinks,
          scoreBefore: scoreBefore,
          scoreAfter: scoreBefore + newScore,
          newActions: newActions
        }, null, 2);
      }
    },
    {
      name:         { get: () => name },
      drinksBefore: { get: () => drinksBefore },
      drinksAfter:  { get: () => drinksBefore + newDrinks },
      scoreBefore:  { get: () => scoreBefore },
      scoreAfter:   { get: () => scoreBefore + newScore }
    });
}

function queryNewActions(stats) {
  const sqlString = `SELECT
      actions.id as id,
      actions.location as location,
      action_types.code as type,
      action_types.value as score,
      users.id as user_id,
      users.name as user_name,
      teams.id as team_id,
      teams.name as team_name
    FROM actions
    JOIN action_types ON action_types.id = actions.action_type_id
    JOIN users ON users.id = actions.user_id
    JOIN teams ON teams.id = actions.team_id
    WHERE
      actions.aggregated = false
    ORDER BY id
  `;

  return knex.raw(sqlString)
    .then(result => {
      const rows = result.rows.map(row => ({
        id:       toInt(row.id),
        location: row.location,
        type:     row.type,
        score:    toInt(row.score),
        userId:   toInt(row.user_id),
        userName: row.user_name,
        teamId:   toInt(row.team_id),
        teamName: row.team_name
      }));

      return readNewActions(stats, rows);
    });
}

function readNewActions(stats, rows) {
  const teamStats = stats.teamStats;
  const userStats = stats.userStats;

  rows.forEach(row => {
    const team = getStats(teamStats, row.teamId, row.teamName);
    const user = getStats(userStats, row.userId, row.userName);

    team.addNewAction(row);
    user.addNewAction(row);
  });

  return stats
}

function queryStats() {
  let sqlString = `
    SELECT
      SUM(action_types.value) AS score,
      COUNT(*)          AS count,
      action_types.code AS type,
      users.id          AS user_id,
      users.name        AS user_name,
      teams.id          AS team_id,
      teams.name        AS team_name
    FROM actions
    JOIN action_types ON action_types.id = actions.action_type_id
    JOIN users ON users.id = actions.user_id
    JOIN teams ON teams.id = actions.team_id
    WHERE
      actions.aggregated = true
    GROUP BY
      action_types.code,
      users.id, users.name,
      teams.id, teams.name
  `;

  return knex.raw(sqlString)
    .then(result => {
      const rows = result.rows.map(row => ({
        score:    toInt(row.score),
        count:    toInt(row.count),
        type:     row.type,
        userId:   toInt(row.user_id),
        userName: row.user_name,
        teamId:   toInt(row.team_id),
        teamName: row.team_name
      }));

      const stats = buildStats(rows);

      return stats;
    });
}

function getStats(stats, key, name) {
  const existing = stats[key];
  if (existing) {
    return existing;
  }

  const newStats = Stats(name);
  stats[key] = newStats;
  return newStats;
}

function buildStats(rows) {

  const teamStats = {};
  const userStats = {};

  rows.forEach(row => {
    const team = getStats(teamStats, row.teamId, row.teamName);
    const user = getStats(userStats, row.userId, row.userName);

    team.addOldActionStats(row.type, row.count, row.score);
    user.addOldActionStats(row.type, row.count, row.score);
  });

  return {
    teamStats,
    userStats
  };
}

function handleAction(action, trx) {
  if (action.type === 'IMAGE' || action.type === 'TEXT') {
    // Don't mark it as aggregated so that score can be counted
    return createFeedItem(action, trx);
  }

  return Promise.resolve();
}

function feedItemParam(action, text) {
  const item = {
    text: text,
    type: 'TEXT'
  };

  if (action.location) {
    item.location = {
      longitude: action.location.x,
      latitude: action.location.y
    };
  }

  return item;
}

function integerDivide(num, denominator) {
  return Math.floor(num / denominator);
}

function createDrinkAggregates(allStats) {
  const feedItems = [];
  const actionIds = [];

  const createDrinkFeedItems = function(stats) {
    _.forEach(stats, itemStats => {
      const drinksBefore = itemStats.drinksBefore;
      const drinksAfter  = itemStats.drinksAfter;
      const username     = itemStats.name;

      if (drinksBefore === drinksAfter) {
        return;
      }

      let feedItem;

      if (drinksBefore === 0) {
        const text = `${ username } starts wappu! Congratulations on the first sima!`;
        feedItem = feedItemParam(itemStats.getFirstAction(), text);
      }
      else if (passesPoint(drinksBefore, drinksAfter, SIMA_REPORT_INTERVAL)) {
        const drinks = roundTo(drinksAfter, SIMA_REPORT_INTERVAL);
        const text = `Such wow. ${ username } has had already ${ drinks } simas.`;
        // Let's cheat a little and assume last sima was the one
        feedItem = feedItemParam(itemStats.getLastAction(), text);
      }

      if (feedItem) {
        feedItems.push(feedItem);
        actionIds.push(itemStats.getActionIds());
      }
    });
  };

  createDrinkFeedItems(allStats.teamStats);
  createDrinkFeedItems(allStats.userStats);

  return {
    feedItems,
    actionIds: _.flatten(actionIds)
  };
}

function createScoreAggregates(allStats) {
  const feedItems = [];
  const actionIds = [];

  const createScoreFeedItems = function(stats) {
    _.forEach(stats, itemStats => {
      const scoreBefore = itemStats.scoreBefore;
      const scoreAfter  = itemStats.scoreAfter;
      const username    = itemStats.name;

      if (scoreBefore === scoreAfter) {
        return;
      }

      let feedItem;

      if (passesPoint(scoreBefore, scoreAfter, SCORE_REPORT_INTERVAL)) {
        const points = roundTo(scoreAfter, SCORE_REPORT_INTERVAL);
        const text = `Oh my. ${ username } has already gathered ${ points } points.`;
        // Let's cheat a little and assume last action was the one
        feedItem = feedItemParam(itemStats.getLastAction(), text);
      }

      if (feedItem) {
        feedItems.push(feedItem);
        actionIds.push(itemStats.getActionIds());
      }
    });
  }

  createScoreFeedItems(allStats.userStats);
  createScoreFeedItems(allStats.teamStats);

  return {
    feedItems,
    actionIds: _.flatten(actionIds)
  };
}

function insertFeedItems(feedItems, actionIds) {
  // Update in database id order
  const uniqueIds = _(actionIds).uniq().sortBy().value();

  return knex.transaction(function(trx) {
    return BPromise.mapSeries(feedItems, item => createFeedItem(item, trx))
      .then(() => BPromise.mapSeries(uniqueIds, id => markAsAggregated(id, trx)));
  });
}

function aggregate() {
  // TODO: It's suboptimal to query all actions on every poll.
  // Should use caching here.
  queryStats()
    .then(queryNewActions)
    .then(stats => {
      const items = {
        drinkItems: createDrinkAggregates(stats),
        scoreItems: createScoreAggregates(stats)
      };

      return insertFeedItems(
        items.drinkItems.feedItems.concat(items.scoreItems.feedItems),
        items.drinkItems.actionIds.concat(items.scoreItems.actionIds)
      );
    });
}

let isRunning = false;

function start() {
  if (isRunning) {
    throw new Error("Already running");
  }

  isRunning = true;

  function aggregatePoll() {
    try {
      aggregate();
    } catch (error) {
      logger.error(error);
    }

    setTimeout(aggregatePoll, POLL_INTERVAL);
  }

  aggregatePoll();
}

export {
  start,
  handleAction
};
<|endoftext|>


<commit_before>/**
 * @constructor
 * @param {function(string, string)} activeCallback
 * @param {function(string, string)} inactiveCallback
 * @param {webfont.DomHelper} domHelper
 * @param {Object.<string, function(Object): {width: number, height: number}>} fontSizer
 * @param {function(function(), number=)} asyncCall
 * @param {function(): number} getTime
 * @param {string} fontFamily
 * @param {string} fontDescription
 * @param {boolean} hasWebkitFallbackBug
 * @param {string=} opt_fontTestString
 */
webfont.FontWatchRunner = function(activeCallback, inactiveCallback, domHelper,
    fontSizer, asyncCall, getTime, fontFamily, fontDescription, hasWebkitFallbackBug, opt_fontTestString) {
  this.activeCallback_ = activeCallback;
  this.inactiveCallback_ = inactiveCallback;
  this.domHelper_ = domHelper;
  this.fontSizer_ = fontSizer;
  this.asyncCall_ = asyncCall;
  this.getTime_ = getTime;
  this.fontFamily_ = fontFamily;
  this.fontDescription_ = fontDescription;
  this.fontTestString_ = opt_fontTestString || webfont.FontWatchRunner.DEFAULT_TEST_STRING;
  this.hasWebkitFallbackBug_ = hasWebkitFallbackBug;
  this.lastObservedSizeA_ = this.getDefaultFontSize_(
      webfont.FontWatchRunner.DEFAULT_FONTS_A);
  this.lastObservedSizeB_ = this.getDefaultFontSize_(
      webfont.FontWatchRunner.DEFAULT_FONTS_B);
  this.sizeChangeCount_ = 0;
  this.requestedFontA_ = new webfont.FontRuler(this.domHelper_, this.fontSizer_,
      this.fontFamily_ + ',' + webfont.FontWatchRunner.DEFAULT_FONTS_A,
      this.fontDescription_, this.fontTestString_);
  this.requestedFontB_ = new webfont.FontRuler(this.domHelper_, this.fontSizer_,
      this.fontFamily_ + ',' + webfont.FontWatchRunner.DEFAULT_FONTS_B,
      this.fontDescription_, this.fontTestString_);
};

/**
 * A set of sans-serif fonts and a generic family that cover most platforms:
 * Windows - arial - 99.71%
 * Mac - arial - 97.67%
 * Linux - 97.67%
 * (Based on http://www.codestyle.org/css/font-family/sampler-CombinedResults.shtml)
 * @type {string}
 * @const
 */
webfont.FontWatchRunner.DEFAULT_FONTS_A = "arial,'URW Gothic L',sans-serif";

/**
 * A set of serif fonts and a generic family that cover most platforms. We
 * want each of these fonts to have a different width when rendering the test
 * string than each of the fonts in DEFAULT_FONTS_A:
 * Windows - Georgia - 98.98%
 * Mac - Georgia - 95.60%
 * Linux - Century Schoolbook L - 97.97%
 * (Based on http://www.codestyle.org/css/font-family/sampler-CombinedResults.shtml)
 * @type {string}
 * @const
 */
webfont.FontWatchRunner.DEFAULT_FONTS_B = "Georgia,'Century Schoolbook L',serif";

/**
 * Default test string. Characters are chosen so that their widths vary a lot
 * between the fonts in the default stacks. We want each fallback stack
 * to always start out at a different width than the other.
 * @type {string}
 * @const
 */
webfont.FontWatchRunner.DEFAULT_TEST_STRING = 'BESbswy';

webfont.FontWatchRunner.prototype.start = function() {
  this.started_ = this.getTime_();
  this.check_();
};

/**
 * Checks the size of the two spans against their original sizes during each
 * async loop. If the size of one of the spans is different than the original
 * size, then we know that the font is rendering and finish with the active
 * callback. If we wait more than 5 seconds and nothing has changed, we finish
 * with the inactive callback.
 *
 * @private
 */
webfont.FontWatchRunner.prototype.check_ = function() {
  var sizeA = this.requestedFontA_.getSize();
  var sizeB = this.requestedFontB_.getSize();

  if (this.lastObservedSizeA_.width != sizeA.width || this.lastObservedSizeB_.width != sizeB.width ||
      this.lastObservedSizeA_.height != sizeB.height || this.lastObservedSizeB_.height != sizeB.height) {
    if ((this.hasWebkitFallbackBug_ && this.sizeChangeCount_ === 1) ||
        (!this.hasWebkitFallbackBug_ && this.sizeChangeCount_ === 0)) {
      this.finish_(this.activeCallback_);
    } else {
      this.lastObservedSizeA_ = sizeA;
      this.lastObservedSizeB_ = sizeB;
      this.sizeChangeCount_ += 1;
      this.asyncCheck_();
    }
  } else if (this.getTime_() - this.started_ >= 5000) {
    if (this.hasWebkitFallbackBug_ && this.sizeChangeCount_ === 1) {
      // If we reach the timeout and we are in a Webkit browser with the
      // fallback and we observed at least one size change, hope for the
      // best and assume that the font has loaded and has identical font
      // metrics compared to the browser's last resort font.
      this.finish_(this.activeCallback_);
    } else {
      this.finish_(this.inactiveCallback_);
    }
  } else {
    this.asyncCheck_();
  }
};

/**
 * @private
 */
webfont.FontWatchRunner.prototype.asyncCheck_ = function() {
  this.asyncCall_(function(context, func) {
    return function() {
      func.call(context);
    }
  }(this, this.check_), 25);
};

/**
 * @private
 * @param {function(string, string)} callback
 */
webfont.FontWatchRunner.prototype.finish_ = function(callback) {
  this.requestedFontA_.remove();
  this.requestedFontB_.remove();
  callback(this.fontFamily_, this.fontDescription_);
};

/**
 * @private
 * @param {string} defaultFonts
 * @return {{width: number, height: number}}
 */
webfont.FontWatchRunner.prototype.getDefaultFontSize_ = function(defaultFonts) {
  var defaultFont = new webfont.FontRuler(this.domHelper_, this.fontSizer_, defaultFonts, this.fontDescription_, this.fontTestString_);
  var size = defaultFont.getSize();
  defaultFont.remove();
  return size;
};
<commit_msg>Extracted size comparison method.<commit_after>/**
 * @constructor
 * @param {function(string, string)} activeCallback
 * @param {function(string, string)} inactiveCallback
 * @param {webfont.DomHelper} domHelper
 * @param {Object.<string, function(Object): {width: number, height: number}>} fontSizer
 * @param {function(function(), number=)} asyncCall
 * @param {function(): number} getTime
 * @param {string} fontFamily
 * @param {string} fontDescription
 * @param {boolean} hasWebkitFallbackBug
 * @param {string=} opt_fontTestString
 */
webfont.FontWatchRunner = function(activeCallback, inactiveCallback, domHelper,
    fontSizer, asyncCall, getTime, fontFamily, fontDescription, hasWebkitFallbackBug, opt_fontTestString) {
  this.activeCallback_ = activeCallback;
  this.inactiveCallback_ = inactiveCallback;
  this.domHelper_ = domHelper;
  this.fontSizer_ = fontSizer;
  this.asyncCall_ = asyncCall;
  this.getTime_ = getTime;
  this.fontFamily_ = fontFamily;
  this.fontDescription_ = fontDescription;
  this.fontTestString_ = opt_fontTestString || webfont.FontWatchRunner.DEFAULT_TEST_STRING;
  this.hasWebkitFallbackBug_ = hasWebkitFallbackBug;
  this.lastObservedSizeA_ = this.getDefaultFontSize_(
      webfont.FontWatchRunner.DEFAULT_FONTS_A);
  this.lastObservedSizeB_ = this.getDefaultFontSize_(
      webfont.FontWatchRunner.DEFAULT_FONTS_B);
  this.sizeChangeCount_ = 0;
  this.requestedFontA_ = new webfont.FontRuler(this.domHelper_, this.fontSizer_,
      this.fontFamily_ + ',' + webfont.FontWatchRunner.DEFAULT_FONTS_A,
      this.fontDescription_, this.fontTestString_);
  this.requestedFontB_ = new webfont.FontRuler(this.domHelper_, this.fontSizer_,
      this.fontFamily_ + ',' + webfont.FontWatchRunner.DEFAULT_FONTS_B,
      this.fontDescription_, this.fontTestString_);
};

/**
 * A set of sans-serif fonts and a generic family that cover most platforms:
 * Windows - arial - 99.71%
 * Mac - arial - 97.67%
 * Linux - 97.67%
 * (Based on http://www.codestyle.org/css/font-family/sampler-CombinedResults.shtml)
 * @type {string}
 * @const
 */
webfont.FontWatchRunner.DEFAULT_FONTS_A = "arial,'URW Gothic L',sans-serif";

/**
 * A set of serif fonts and a generic family that cover most platforms. We
 * want each of these fonts to have a different width when rendering the test
 * string than each of the fonts in DEFAULT_FONTS_A:
 * Windows - Georgia - 98.98%
 * Mac - Georgia - 95.60%
 * Linux - Century Schoolbook L - 97.97%
 * (Based on http://www.codestyle.org/css/font-family/sampler-CombinedResults.shtml)
 * @type {string}
 * @const
 */
webfont.FontWatchRunner.DEFAULT_FONTS_B = "Georgia,'Century Schoolbook L',serif";

/**
 * Default test string. Characters are chosen so that their widths vary a lot
 * between the fonts in the default stacks. We want each fallback stack
 * to always start out at a different width than the other.
 * @type {string}
 * @const
 */
webfont.FontWatchRunner.DEFAULT_TEST_STRING = 'BESbswy';

webfont.FontWatchRunner.prototype.start = function() {
  this.started_ = this.getTime_();
  this.check_();
};

/**
 * Returns true if two metrics are the same.
 * @param {{width: number, height: number}} a
 * @param {{width: number, height: number}} b
 * @return {boolean}
 */
webfont.FontWatchRunner.prototype.sizeEquals = function(a, b) {
  return a.width === b.width && a.height === b.height;
};

/**
 * Checks the size of the two spans against their original sizes during each
 * async loop. If the size of one of the spans is different than the original
 * size, then we know that the font is rendering and finish with the active
 * callback. If we wait more than 5 seconds and nothing has changed, we finish
 * with the inactive callback.
 *
 * @private
 */
webfont.FontWatchRunner.prototype.check_ = function() {
  var sizeA = this.requestedFontA_.getSize();
  var sizeB = this.requestedFontB_.getSize();

  if (!this.sizeEquals(this.lastObservedSizeA_, sizeA) || !this.sizeEquals(this.lastObservedSizeB_, sizeB)) {
    if ((this.hasWebkitFallbackBug_ && this.sizeChangeCount_ === 1) ||
        (!this.hasWebkitFallbackBug_ && this.sizeChangeCount_ === 0)) {
      this.finish_(this.activeCallback_);
    } else {
      this.lastObservedSizeA_ = sizeA;
      this.lastObservedSizeB_ = sizeB;
      this.sizeChangeCount_ += 1;
      this.asyncCheck_();
    }
  } else if (this.getTime_() - this.started_ >= 5000) {
    if (this.hasWebkitFallbackBug_ && this.sizeChangeCount_ === 1) {
      // If we reach the timeout and we are in a Webkit browser with the
      // fallback and we observed at least one size change, hope for the
      // best and assume that the font has loaded and has identical font
      // metrics compared to the browser's last resort font.
      this.finish_(this.activeCallback_);
    } else {
      this.finish_(this.inactiveCallback_);
    }
  } else {
    this.asyncCheck_();
  }
};

/**
 * @private
 */
webfont.FontWatchRunner.prototype.asyncCheck_ = function() {
  this.asyncCall_(function(context, func) {
    return function() {
      func.call(context);
    }
  }(this, this.check_), 25);
};

/**
 * @private
 * @param {function(string, string)} callback
 */
webfont.FontWatchRunner.prototype.finish_ = function(callback) {
  this.requestedFontA_.remove();
  this.requestedFontB_.remove();
  callback(this.fontFamily_, this.fontDescription_);
};

/**
 * @private
 * @param {string} defaultFonts
 * @return {{width: number, height: number}}
 */
webfont.FontWatchRunner.prototype.getDefaultFontSize_ = function(defaultFonts) {
  var defaultFont = new webfont.FontRuler(this.domHelper_, this.fontSizer_, defaultFonts, this.fontDescription_, this.fontTestString_);
  var size = defaultFont.getSize();
  defaultFont.remove();
  return size;
};
<|endoftext|>


<commit_before>//----------------------------------------------------------------------
//
// This source file is part of the Folktale project.
//
// See LICENCE for licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------

/*~
 * Returns pairs of (key, value) for all own enumerable properties in an object.
 *
 * Objects in JavaScript are commonly used as dictionaries, but natively
 * there are no operations to work with them in that way. This function
 * allows one to extract the (key, value) pairs from an object:
 *
 *     const pair = { x: 10, y: 20 };
 *     toPairs(pair);
 *     // => [['x', 10], ['y', 20]]  or  [['y', 20], ['x', 10]]
 *
 * Inherited properties, and those that are not marked as enumerable, are
 * not returned in the resulting array:
 *
 *     const p1 = { z: 2 };
 *     const pair = Object.create(p1);
 *     pair.x = 10; pair.y = 20;
 *
 *     toPairs(pair);
 *     // => [['x', 10], ['y', 20]]  or  [['y', 20], ['x', 10]]
 *
 *     // non-enumerable property x
 *     Object.defineProperty(p1, 'x', { value: 1 });
 *
 *     toPairs(p1);
 *     // => [['z', 2]]
 *
 *
 * > **NOTE**
 * > While ECMAScript 2015 specifies that objects are ordered using
 * > insertion order, you're not guaranteed to get that behaviour in
 * > any non-ES2015 engine, so for all effects it's better to treat
 * > the result of this operation as an unordered collection.
 *
 * ---------------------------------------------------------------------
 * name        : toPairs
 * module      : folktale/core/object/to-pairs
 * copyright   : (c) 2015-2016 Quildreen Motta, and CONTRIBUTORS
 * licence     : MIT
 * repository  : https://github.com/origamitower/folktale
 *
 * category    : Combining
 * stability   : stable
 * portability : portable
 * platforms:
 *   - ECMAScript 5
 *   - ECMAScript 3, with es5-shim
 *
 * maintainers:
 *   - Quildreen Motta <queen@robotlolita.me>
 *
 * authors:
 *   - Quildreen Motta
 *
 * complexity : O(n), n is the number of own enumerable properties
 * signature  : toPairs(object)
 * type: |
 *   (Object 'a) => Array (String or Symbol, 'a)
 */
const toPairs = (object) => Object.keys(object).map(k => [k, object[k]]);

module.exports = toPairs;
<commit_msg>(- doc object) Documentation for to-pairs<commit_after>//----------------------------------------------------------------------
//
// This source file is part of the Folktale project.
//
// See LICENCE for licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------

/*~
 * Returns pairs of `(key, value)` for all own enumerable properties in an object.
 *
 *
 * ## Example::
 *
 *     const pair = { x: 10, y: 20 };
 *     toPairs(pair);
 *     // ==> [['x', 10], ['y', 20]]
 *     // (in ES5- VMs this may be [['y', 20], ['x', 10]])
 *
 *
 * ## Why?
 *
 * Objects in JavaScript are commonly used as dictionaries, but natively
 * there are no operations to work with them in that way. This function
 * allows one to extract the `(key, value)` pairs from an object::
 *
 *     const pair = { x: 10, y: 20 };
 *     toPairs(pair);
 *     // ==> [['x', 10], ['y', 20]]
 *     // or  [['y', 20], ['x', 10]]
 *
 * Inherited properties, and those that are not marked as enumerable, are
 * not returned in the resulting array::
 *
 *     const p1 = { z: 2 };
 *     const pair = Object.create(p1);
 *     pair.x = 10; pair.y = 20;
 *
 *     toPairs(pair);
 *     // ==> [['x', 10], ['y', 20]]
 *     // or  [['y', 20], ['x', 10]]
 *
 *     // non-enumerable property x
 *     Object.defineProperty(p1, 'x', { value: 1 });
 *
 *     toPairs(p1);
 *     // ==> [['z', 2]]
 *
 *
 * ## Caveats
 *
 * While ECMAScript 2015 specifies that objects are ordered using
 * insertion order, you're not guaranteed to get that behaviour in
 * any non-ES2015 engine, so for all effects it's better to treat
 * the result of this operation as an unordered collection.
 *
 * ---
 * category  : Combining
 * stability : stable
 * authors:
 *   - Quildreen Motta
 *
 * complexity : O(n), n is the number of own enumerable properties
 * type: |
 *   (Object 'a) => Array (String or Symbol, 'a)
 */
const toPairs = (object) => Object.keys(object).map(k => [k, object[k]]);

// --[ Exports ]-------------------------------------------------------
module.exports = toPairs;
<|endoftext|>


<commit_before>import Marionette from 'backbone.marionette';

const template = require('./ModalView.hbs');
// require('./ModalView.css');

const PanelView = Marionette.LayoutView.extend(/** @lends core/views.PanelView# */{
  template,

  className: 'modal fade',

  regions: {
    content: '.modal-body',
  },

  events() {
    const baseEvents = {
      'click .close': 'close',
      'shown.bs.modal': 'onModalShown',
    };

    const buttons = this.options.buttons || [];
    for (let i = 0; i < buttons.length; ++i) {
      baseEvents[`click .modal-footer button:eq(${i})`] = buttons[i][1];
    }

    return baseEvents;
  },

  initialize(options) {
    this.view = options.view;
    this.title = options.title;
    this.icon = options.icon;
    this.closed = options.closed;
    this.closeable = typeof options.closeable === 'undefined' ? true : options.closeable;
    this.buttonDescs = options.buttons || [];
  },

  templateHelpers() {
    return {
      title: this.title,
      icon: this.icon,
      closeable: this.closeable,
      hasButtons: this.buttonDescs.length > 0,
      buttons: this.buttonDescs.map(buttonDesc => buttonDesc[0]),
    };
  },

  onAttach() {
    this.initialDisplay = this.$el.css('display');
    this.$el.modal('show');
  },

  onModalShown() {
    this.showChildView('content', this.view);
  },

  /**
    Show the Panel when it was not visible before.
   */
  open() {
    // this.$el.css('display', this.initialDisplay);
    this.$el.modal('show');
    this.closed = false;
  },

  /**
    Close the Panel when open before.
   */
  close() {
    // this.$el.css('display', 'none');
    this.$el.modal('hide');
    this.closed = true;
  },

  /**
    Toggle the visibility of the Panel.
   */
  toggleOpen() {
    if (this.closed) {
      this.open();
    } else {
      this.close();
    }
  },

  getView() {
    return this.view;
  },
});

export default PanelView;
<commit_msg>Cleanup.<commit_after>import Marionette from 'backbone.marionette';

const template = require('./ModalView.hbs');
// require('./ModalView.css');

const PanelView = Marionette.LayoutView.extend(/** @lends core/views.PanelView# */{
  template,

  className: 'modal fade',

  regions: {
    content: '.modal-body',
  },

  events() {
    const baseEvents = {
      'click .close': 'close',
      'shown.bs.modal': 'onModalShown',
    };

    const buttons = this.options.buttons || [];
    for (let i = 0; i < buttons.length; ++i) {
      baseEvents[`click .modal-footer button:eq(${i})`] = buttons[i][1];
    }

    return baseEvents;
  },

  initialize(options) {
    this.view = options.view;
    this.title = options.title;
    this.icon = options.icon;
    this.closed = options.closed;
    this.closeable = typeof options.closeable === 'undefined' ? true : options.closeable;
    this.buttonDescs = options.buttons || [];
  },

  templateHelpers() {
    return {
      title: this.title,
      icon: this.icon,
      closeable: this.closeable,
      hasButtons: this.buttonDescs.length > 0,
      buttons: this.buttonDescs.map(buttonDesc => buttonDesc[0]),
    };
  },

  onAttach() {
    this.initialDisplay = this.$el.css('display');
    this.$el.modal('show');
  },

  onModalShown() {
    this.showChildView('content', this.view);
  },

  /**
    Show the Panel when it was not visible before.
   */
  open() {
    this.$el.modal('show');
    this.closed = false;
  },

  /**
    Close the Panel when open before.
   */
  close() {
    this.$el.modal('hide');
    this.closed = true;
  },

  /**
    Toggle the visibility of the Panel.
   */
  toggleOpen() {
    if (this.closed) {
      this.open();
    } else {
      this.close();
    }
  },

  getView() {
    return this.view;
  },
});

export default PanelView;
<|endoftext|>


<commit_before>/**
 * Copyright 2020-2022 The Cotonic Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var cotonic = cotonic || {};

(function(cotonic) {
"use strict";

    function init() {
        cotonic.broker.publish("model/window/event/ping", "pong", { retain: true });
        cotonic.broker.publish("model/location/event/ui-status", {
            status: {
                is_opener: !!window.opener
            }
        }, { retain: true });
    }

    cotonic.broker.subscribe("model/window/post/close",
        function(msg) {
            let result;

            if (window.opener) {
                window.close();
                result = true;
            } else if (msg.payload.url) {
                cotonic.broker.publish("model/location/post/redirect", { url: msg.payload.url });
                result = true;
            } else {
                result = false;
            }
            if(msg.properties.response_topic) {
                cotonic.broker.publish(msg.properties.response_topic, result);
            }
        });

    cotonic.broker.subscribe("model/window/post/open",
        function(msg) {
            let options = {
                full:0,             // set the height/width to the current window, show scrollbars etc.
                centerBrowser:1,    // center window over browser window? {1 (YES) or 0 (NO)}. overrides top and left
                centerScreen:0,     // center window over entire screen? {1 (YES) or 0 (NO)}. overrides top and left
                height:500,         // sets the height in pixels of the window.
                left:0,             // left position when the window appears.
                location:0,         // determines whether the address bar is displayed {1 (YES) or 0 (NO)}.
                menubar:0,          // determines whether the menu bar is displayed {1 (YES) or 0 (NO)}.
                resizable:0,        // whether the window can be resized {1 (YES) or 0 (NO)}. Can also be overloaded using resizable.
                scrollbars:0,       // determines whether scrollbars appear on the window {1 (YES) or 0 (NO)}.
                status:0,           // whether a status line appears at the bottom of the window {1 (YES) or 0 (NO)}.
                width:500,          // sets the width in pixels of the window.
                name:null,          // name of window
                top:0,              // top position when the window appears.
                toolbar:0           // determines whether a toolbar (includes the forward and back buttons) is displayed {1 (YES) or 0 (NO)}.
            };

            if (typeof msg.payload.message == "object") {
                let attrs = msg.payload.message;
                if (attrs.href) {
                    options.url = msg.payload.message.href;
                    if (msg.payload.message['data-window']) {
                        if (typeof msg.payload.message['data-window'] == "string") {
                            attrs = JSON.parse(msg.payload.message['data-window']);
                        }
                    } else {
                        attrs = {};
                    }
                }
                let keys = Object.keys(attrs);
                for (let k in keys) {
                    options[k] = attrs[k];
                }
                let features = 'height=' + options.height +
                               ',width=' + options.width +
                               ',toolbar=' + (options.toolbar?'yes':'no') +
                               ',scrollbars=' + (options.scrollbars?'yes':'no') +
                               ',status=' + (options.status?'yes':'no') +
                               ',resizable=' + (options.resizable?'yes':'no') +
                               ',location=' + (options.location?'yes':'no') +
                               ',menubar=' + (options.menubar?'yes':'no');

                let top, left;

                if (options.centerBrowser && !options.centerScreen) {
                    top = window.screenY + (((window.outerHeight/2) - (options.height/2)));
                    left = window.screenX + (((window.outerWidth/2) - (options.width/2)));
                } else if (options.centerScreen) {
                    top = (screen.height - options.height)/2;
                    left = (screen.width - options.width)/2;
                } else {
                    top = options.top;
                    left = options.left;
                }
                if (options.name) {
                    options.name = options.name.replace(/[^a-zA-Z0-9]/g,'_');
                }
                let w = window.open(options.url, options.name, features+',left='+Math.ceil(left)+',top='+Math.ceil(top));
                // setTimeout(
                //     function() {
                //         if (w.innerWidth != undefined && w.innerWidth > 0) {
                //             w.resizeBy(options.width - w.innerWidth, options.height - w.innerHeight);
                //         }
                //     }, 500);
                w.focus();
            }
        });

    init();

}(cotonic));

<commit_msg>Let the model/window/post/close fallback to the href of the clicked element.<commit_after>/**
 * Copyright 2020-2022 The Cotonic Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var cotonic = cotonic || {};

(function(cotonic) {
"use strict";

    function init() {
        cotonic.broker.publish("model/window/event/ping", "pong", { retain: true });
        cotonic.broker.publish("model/location/event/ui-status", {
            status: {
                is_opener: !!window.opener
            }
        }, { retain: true });
    }

    cotonic.broker.subscribe("model/window/post/close",
        function(msg) {
            let result;

            if (window.opener) {
                window.close();
                result = true;
            } else if (msg.payload.url) {
                cotonic.broker.publish("model/location/post/redirect", { url: msg.payload.url });
                result = true;
            } else if (msg.payload.message && msg.payload.message.href) {
                cotonic.broker.publish("model/location/post/redirect", { url: msg.payload.message.href });
                result = true;
            } else {
                result = false;
            }
            if(msg.properties.response_topic) {
                cotonic.broker.publish(msg.properties.response_topic, result);
            }
        });

    cotonic.broker.subscribe("model/window/post/open",
        function(msg) {
            let options = {
                full:0,             // set the height/width to the current window, show scrollbars etc.
                centerBrowser:1,    // center window over browser window? {1 (YES) or 0 (NO)}. overrides top and left
                centerScreen:0,     // center window over entire screen? {1 (YES) or 0 (NO)}. overrides top and left
                height:500,         // sets the height in pixels of the window.
                left:0,             // left position when the window appears.
                location:0,         // determines whether the address bar is displayed {1 (YES) or 0 (NO)}.
                menubar:0,          // determines whether the menu bar is displayed {1 (YES) or 0 (NO)}.
                resizable:0,        // whether the window can be resized {1 (YES) or 0 (NO)}. Can also be overloaded using resizable.
                scrollbars:0,       // determines whether scrollbars appear on the window {1 (YES) or 0 (NO)}.
                status:0,           // whether a status line appears at the bottom of the window {1 (YES) or 0 (NO)}.
                width:500,          // sets the width in pixels of the window.
                name:null,          // name of window
                top:0,              // top position when the window appears.
                toolbar:0           // determines whether a toolbar (includes the forward and back buttons) is displayed {1 (YES) or 0 (NO)}.
            };

            if (typeof msg.payload.message == "object") {
                let attrs = msg.payload.message;
                if (attrs.href) {
                    options.url = msg.payload.message.href;
                    if (msg.payload.message['data-window']) {
                        if (typeof msg.payload.message['data-window'] == "string") {
                            attrs = JSON.parse(msg.payload.message['data-window']);
                        }
                    } else {
                        attrs = {};
                    }
                }
                let keys = Object.keys(attrs);
                for (let k in keys) {
                    options[k] = attrs[k];
                }
                let features = 'height=' + options.height +
                               ',width=' + options.width +
                               ',toolbar=' + (options.toolbar?'yes':'no') +
                               ',scrollbars=' + (options.scrollbars?'yes':'no') +
                               ',status=' + (options.status?'yes':'no') +
                               ',resizable=' + (options.resizable?'yes':'no') +
                               ',location=' + (options.location?'yes':'no') +
                               ',menubar=' + (options.menubar?'yes':'no');

                let top, left;

                if (options.centerBrowser && !options.centerScreen) {
                    top = window.screenY + (((window.outerHeight/2) - (options.height/2)));
                    left = window.screenX + (((window.outerWidth/2) - (options.width/2)));
                } else if (options.centerScreen) {
                    top = (screen.height - options.height)/2;
                    left = (screen.width - options.width)/2;
                } else {
                    top = options.top;
                    left = options.left;
                }
                if (options.name) {
                    options.name = options.name.replace(/[^a-zA-Z0-9]/g,'_');
                }
                let w = window.open(options.url, options.name, features+',left='+Math.ceil(left)+',top='+Math.ceil(top));
                // setTimeout(
                //     function() {
                //         if (w.innerWidth != undefined && w.innerWidth > 0) {
                //             w.resizeBy(options.width - w.innerWidth, options.height - w.innerHeight);
                //         }
                //     }, 500);
                w.focus();
            }
        });

    init();

}(cotonic));

<|endoftext|>


<commit_before>var tm = process.binding('tm');
var http_parser = process.binding('http_parser');

var url = require('url');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var net = require('net');
var dns = require('dns');

/**
 * ServerResponse
 */

function ServerResponse (req, socket) {
  this.req = req;
  this.socket = socket;
  this.headers = {};
  this._header = false;
  this._closed = false;

  var self = this;
  this.req.on('end', function () {
    self._closed = true;
  })
  this.req.on('close', function () {
    self._closed = true;
  })
}

// TODO stream
util.inherits(ServerResponse, EventEmitter);

ServerResponse.prototype.setHeader = function (name, value) {
  if (this._header) {
    throw "Already wrote HEAD";
  }
  this.headers[String(name).toLowerCase()] = value;
};

ServerResponse.prototype.setHeaders = function (headers) {
  for (var key in headers) {
    this.setHeader(key, headers[key]);
  }
};

ServerResponse.prototype.writeHead = function (status, headers) {
  if (this._header) {
    throw "Already wrote HEAD";
  }

  if (headers) {
    this.setHeaders(headers);
  }
  this.socket.write('HTTP/1.1 ' + status + ' OK\r\n');
  this._usesContentType = false;
  for (var key in this.headers) {
    if (key.toLowerCase() == 'content-length') {
      this._usesContentType = true;
    }
    this.socket.write(key + ': ' + this.headers[key] + '\r\n');
  }
  if (!this._usesContentType) {
    this.socket.write('Transfer-Encoding: chunked\r\n');
  }
  this.socket.write('\r\n');
  this._header = true;
  // console.log('writing headers', headers);
};

ServerResponse.prototype.write = function (data) {
  if (!this._header) {
    this.writeHead(200);
  }

  if (!this._usesContentType) {
    this.socket.write(Number(data.length).toString(16));
    this.socket.write('\r\n');
    this.socket.write(data);
    this.socket.write('\r\n');
  } else {
    this.socket.write(data);
  }
}

ServerResponse.prototype.getHeader = function (key) {
  return this.headers[key.toLowerCase()];
};

ServerResponse.prototype.end = function (data) {
  if (!this._header) {
    this.writeHead(200);
  }

  if (data != null) {
    this.write(data);
  }
  this._closed = true;
  if (!this._usesContentType) {
    this.socket.write('0\r\n\r\n');
  }
  this.socket.close();
};

/**
 * ServerRequest
 */

function ServerRequest (socket) {
  var self = this;

  this.headers = {};
  this.socket = socket;
  this.url = null;

  function js_wrap_function (fn) {
    return function () {
      return fn.apply(null, [this].concat(arguments));
    }
  }

  var lastheader;
  var parser = http_parser.new('request', {
    onMessageBegin: js_wrap_function(function () {
    }),
    onUrl: js_wrap_function(function (url) {
      self.url = url;
    }),
    onHeaderField: js_wrap_function(function (field) {
      lastheader = field;
    }),
    onHeaderValue: js_wrap_function(function (value) {
      self.headers[lastheader.toLowerCase()] = value;
    }),
    onHeadersComplete: js_wrap_function(function (info) {
      self.method = info.method;
      self.emit('request', self);
    }),
    onBody: js_wrap_function(function (body) {
      self.emit('data', body);
    }),
    onMessageComplete: js_wrap_function(function () {
      self.emit('end');
      self._closed = true;
      // TODO close
    }),
    onError: js_wrap_function(function (err) {
      self.socket.emit('error', err)
    })
  })
  this.socket.on('data', function (data) {
    // console.log('received', data.length, data.substr(0, 15));
    parser.execute(data, 0, data.length);
  })
}

util.inherits(ServerRequest, EventEmitter);

function HTTPServer () {
  var self = this;
  this.socket = net.createServer(function (socket) {
    var request = new ServerRequest(socket);
    var response = new ServerResponse(request, socket);
    request.on('request', function () {
      self.emit('request', request, response)
    });
  });
}

util.inherits(HTTPServer, EventEmitter);

HTTPServer.prototype.listen = function (port, ip) {
  this.socket.listen(port, ip);
};

/**
 * HTTPIncomingResponse
 */

function HTTPIncomingResponse (data) {
  this.headers = {};
}

// TODO stream
util.inherits(HTTPIncomingResponse, EventEmitter);

HTTPIncomingResponse.prototype.setEncoding = function () {
  // TODO
};

/**
 * HTTPOutgoingRequest
 */

function isIP (host) {
  return host.match(/^[0-9.]+$/);
}

function HTTPOutgoingRequest (port, host, path, method, headers, _secure) {
  var self = this;

  if (!isIP(host)) {
    dns.resolve(host, onhost);
  } else {
    onhost(null, [host]);
  }

  function onhost (err, ips) {
    if (err) throw err;
    var ip = ips[0];

    if (path[0] != '/') {
      path = '/' + path;
    }

    self._connected = false;
    self._contentLength = 0;
    self.socket = net.connect(port, ip, function () {
      self._connected = true;
      var header = '';
      if (method != 'GET') {
        header = 'Content-Length: ' + self._contentLength + '\r\n';
      }
      var usedHost = false, usedUserAgent;
      for (var key in headers) {
        if (key.toLowerCase() == 'host') {
          usedHost = true;
        } else if (key.toLowerCase() == 'user-agent') {
          usedUserAgent = true;
        }
        header = header + key + ': ' + headers[key] + '\r\n';
      }
      if (!usedHost) {
        header = header + 'Host: ' + host + '\r\n'
      }
      if (!usedUserAgent) {
        header = header + 'User-Agent: tessel\r\n';
      }
      self.socket.write(method + ' ' + path + ' HTTP/1.1\r\n' + header + '\r\n');
      // self.emit('connect');
    }, _secure)

    self.socket.on('error', function (err) {
      self.emit('error', err);
    })

    function js_wrap_function (fn) {
      return function () {
        return fn.apply(null, [this].concat(arguments));
      }
    }

    var response, lastheader;
    var parser = http_parser.new('response', {
      onMessageBegin: js_wrap_function(function () {
        response = new HTTPIncomingResponse();
      }),
      onUrl: js_wrap_function(function (url) {
        // nop
      }),
      onHeaderField: js_wrap_function(function (field) {
        lastheader = field;
      }),
      onHeaderValue: js_wrap_function(function (value) {
        response.headers[lastheader.toLowerCase()] = value;
      }),
      onHeadersComplete: js_wrap_function(function (obj) {
        response.statusCode = obj.status_code
        self.emit('response', response);
      }),
      onBody: js_wrap_function(function (body) {
        response.emit('data', body);
      }),
      onMessageComplete: js_wrap_function(function () {
        response.emit('end');
        // TODO close
        self.socket.close();
      })
    })
    self.socket.on('data', function (data) {
      var nparsed = parser.execute(data, 0, data.length);
      if (nparsed != data.length) {
        self.socket.emit('error', 'Could not parse tokens at character #' + String(nparsed));
      }
    })
  };
}

util.inherits(HTTPOutgoingRequest, EventEmitter);

HTTPOutgoingRequest.prototype.write = function (data) {
  this._contentLength += data.length;
  if (this._connected) {
    this.socket.write(data);
  } else {
    this.socket.once('connect', function () {
      this.socket.write(data);
    }.bind(this))
  }
};

HTTPOutgoingRequest.prototype.end = function () {
  // TODO
  // console.log('end');
};

/**
 * Public API
 */

exports.request = function (opts, onresponse) {
  var host = opts.hostname || opts.host || 'localhost';
  var req = new HTTPOutgoingRequest(opts.port || (this._secure ? 443 : 80), host, opts.path || '', opts.method || 'GET', opts.headers || {}, this._secure);
  onresponse && req.on('response', onresponse);
  return req;
};

exports.get = function (opts, onresponse) {
  if (typeof opts == 'string') {
    opts = url.parse(opts);
    if (opts.search) {
      opts.path += opts.search;
    }
  }
  opts.method = 'GET';

  var req = this.request(opts, onresponse);
  req.end();
  return req;
};

exports.createServer = function (onrequest) {
  var server = new HTTPServer();
  onrequest && server.on('request', onrequest);
  return server;
};

exports.ServerResponse = ServerResponse;
exports.ServerRequest = ServerRequest;

exports.IncomingMessage = ServerResponse;
<commit_msg>http: emit the `socket` event when the socket is created.<commit_after>var tm = process.binding('tm');
var http_parser = process.binding('http_parser');

var url = require('url');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var net = require('net');
var dns = require('dns');

/**
 * ServerResponse
 */

function ServerResponse (req, socket) {
  this.req = req;
  this.socket = socket;
  this.headers = {};
  this._header = false;
  this._closed = false;

  var self = this;
  this.req.on('end', function () {
    self._closed = true;
  })
  this.req.on('close', function () {
    self._closed = true;
  })
}

// TODO stream
util.inherits(ServerResponse, EventEmitter);

ServerResponse.prototype.setHeader = function (name, value) {
  if (this._header) {
    throw "Already wrote HEAD";
  }
  this.headers[String(name).toLowerCase()] = value;
};

ServerResponse.prototype.setHeaders = function (headers) {
  for (var key in headers) {
    this.setHeader(key, headers[key]);
  }
};

ServerResponse.prototype.writeHead = function (status, headers) {
  if (this._header) {
    throw "Already wrote HEAD";
  }

  if (headers) {
    this.setHeaders(headers);
  }
  this.socket.write('HTTP/1.1 ' + status + ' OK\r\n');
  this._usesContentType = false;
  for (var key in this.headers) {
    if (key.toLowerCase() == 'content-length') {
      this._usesContentType = true;
    }
    this.socket.write(key + ': ' + this.headers[key] + '\r\n');
  }
  if (!this._usesContentType) {
    this.socket.write('Transfer-Encoding: chunked\r\n');
  }
  this.socket.write('\r\n');
  this._header = true;
  // console.log('writing headers', headers);
};

ServerResponse.prototype.write = function (data) {
  if (!this._header) {
    this.writeHead(200);
  }

  if (!this._usesContentType) {
    this.socket.write(Number(data.length).toString(16));
    this.socket.write('\r\n');
    this.socket.write(data);
    this.socket.write('\r\n');
  } else {
    this.socket.write(data);
  }
}

ServerResponse.prototype.getHeader = function (key) {
  return this.headers[key.toLowerCase()];
};

ServerResponse.prototype.end = function (data) {
  if (!this._header) {
    this.writeHead(200);
  }

  if (data != null) {
    this.write(data);
  }
  this._closed = true;
  if (!this._usesContentType) {
    this.socket.write('0\r\n\r\n');
  }
  this.socket.close();
};

/**
 * ServerRequest
 */

function ServerRequest (socket) {
  var self = this;

  this.headers = {};
  this.socket = socket;
  this.url = null;

  function js_wrap_function (fn) {
    return function () {
      return fn.apply(null, [this].concat(arguments));
    }
  }

  var lastheader;
  var parser = http_parser.new('request', {
    onMessageBegin: js_wrap_function(function () {
    }),
    onUrl: js_wrap_function(function (url) {
      self.url = url;
    }),
    onHeaderField: js_wrap_function(function (field) {
      lastheader = field;
    }),
    onHeaderValue: js_wrap_function(function (value) {
      self.headers[lastheader.toLowerCase()] = value;
    }),
    onHeadersComplete: js_wrap_function(function (info) {
      self.method = info.method;
      self.emit('request', self);
    }),
    onBody: js_wrap_function(function (body) {
      self.emit('data', body);
    }),
    onMessageComplete: js_wrap_function(function () {
      self.emit('end');
      self._closed = true;
      // TODO close
    }),
    onError: js_wrap_function(function (err) {
      self.socket.emit('error', err)
    })
  })
  this.socket.on('data', function (data) {
    // console.log('received', data.length, data.substr(0, 15));
    parser.execute(data, 0, data.length);
  })
}

util.inherits(ServerRequest, EventEmitter);

function HTTPServer () {
  var self = this;
  this.socket = net.createServer(function (socket) {
    var request = new ServerRequest(socket);
    var response = new ServerResponse(request, socket);
    request.on('request', function () {
      self.emit('request', request, response)
    });
  });
}

util.inherits(HTTPServer, EventEmitter);

HTTPServer.prototype.listen = function (port, ip) {
  this.socket.listen(port, ip);
};

/**
 * HTTPIncomingResponse
 */

function HTTPIncomingResponse (data) {
  this.headers = {};
}

// TODO stream
util.inherits(HTTPIncomingResponse, EventEmitter);

HTTPIncomingResponse.prototype.setEncoding = function () {
  // TODO
};

/**
 * HTTPOutgoingRequest
 */

function isIP (host) {
  return host.match(/^[0-9.]+$/);
}

function HTTPOutgoingRequest (port, host, path, method, headers, _secure) {
  var self = this;

  if (!isIP(host)) {
    dns.resolve(host, onhost);
  } else {
    onhost(null, [host]);
  }

  function onhost (err, ips) {
    if (err) throw err;
    var ip = ips[0];

    if (path[0] != '/') {
      path = '/' + path;
    }

    self._connected = false;
    self._contentLength = 0;
    self.socket = net.connect(port, ip, function () {
      self._connected = true;
      var header = '';
      if (method != 'GET') {
        header = 'Content-Length: ' + self._contentLength + '\r\n';
      }
      var usedHost = false, usedUserAgent;
      for (var key in headers) {
        if (key.toLowerCase() == 'host') {
          usedHost = true;
        } else if (key.toLowerCase() == 'user-agent') {
          usedUserAgent = true;
        }
        header = header + key + ': ' + headers[key] + '\r\n';
      }
      if (!usedHost) {
        header = header + 'Host: ' + host + '\r\n'
      }
      if (!usedUserAgent) {
        header = header + 'User-Agent: tessel\r\n';
      }
      self.socket.write(method + ' ' + path + ' HTTP/1.1\r\n' + header + '\r\n');
      // self.emit('connect');
    }, _secure)

    self.socket.on('error', function (err) {
      self.emit('error', err);
    })

    function js_wrap_function (fn) {
      return function () {
        return fn.apply(null, [this].concat(arguments));
      }
    }

    var response, lastheader;
    var parser = http_parser.new('response', {
      onMessageBegin: js_wrap_function(function () {
        response = new HTTPIncomingResponse();
      }),
      onUrl: js_wrap_function(function (url) {
        // nop
      }),
      onHeaderField: js_wrap_function(function (field) {
        lastheader = field;
      }),
      onHeaderValue: js_wrap_function(function (value) {
        response.headers[lastheader.toLowerCase()] = value;
      }),
      onHeadersComplete: js_wrap_function(function (obj) {
        response.statusCode = obj.status_code
        self.emit('response', response);
      }),
      onBody: js_wrap_function(function (body) {
        response.emit('data', body);
      }),
      onMessageComplete: js_wrap_function(function () {
        response.emit('end');
        // TODO close
        self.socket.close();
      })
    })
    self.socket.on('data', function (data) {
      var nparsed = parser.execute(data, 0, data.length);
      if (nparsed != data.length) {
        self.socket.emit('error', 'Could not parse tokens at character #' + String(nparsed));
      }
    })

    self.emit('socket', self.socket);
  };
}

util.inherits(HTTPOutgoingRequest, EventEmitter);

HTTPOutgoingRequest.prototype.write = function (data) {
  this._contentLength += data.length;
  if (this._connected) {
    this.socket.write(data);
  } else {
    this.socket.once('connect', function () {
      this.socket.write(data);
    }.bind(this))
  }
};

HTTPOutgoingRequest.prototype.end = function () {
  // TODO
  // console.log('end');
};

/**
 * Public API
 */

exports.request = function (opts, onresponse) {
  var host = opts.hostname || opts.host || 'localhost';
  var req = new HTTPOutgoingRequest(opts.port || (this._secure ? 443 : 80), host, opts.path || '', opts.method || 'GET', opts.headers || {}, this._secure);
  onresponse && req.on('response', onresponse);
  return req;
};

exports.get = function (opts, onresponse) {
  if (typeof opts == 'string') {
    opts = url.parse(opts);
    if (opts.search) {
      opts.path += opts.search;
    }
  }
  opts.method = 'GET';

  var req = this.request(opts, onresponse);
  req.end();
  return req;
};

exports.createServer = function (onrequest) {
  var server = new HTTPServer();
  onrequest && server.on('request', onrequest);
  return server;
};

exports.ServerResponse = ServerResponse;
exports.ServerRequest = ServerRequest;

exports.IncomingMessage = ServerResponse;
<|endoftext|>


<commit_before>(function(wysihtml5) {
  var undef,
      NODE_NAME = "A",
      dom       = wysihtml5.dom;

  function _removeFormat(composer, anchors) {
    var length  = anchors.length,
        i       = 0,
        anchor,
        codeElement,
        textContent;
    for (; i<length; i++) {
      anchor      = anchors[i];
      codeElement = dom.getParentElement(anchor, { nodeName: "code" });
      textContent = dom.getTextContent(anchor);

      // if <a> contains url-like text content, rename it to <code> to prevent re-autolinking
      // else replace <a> with its childNodes
      if (textContent.match(dom.autoLink.URL_REG_EXP) && !codeElement) {
        // <code> element is used to prevent later auto-linking of the content
        codeElement = dom.renameElement(anchor, "code");
      } else {
        dom.replaceWithChildNodes(anchor);
      }
    }
  }

  function _format(composer, attributes) {
    var doc             = composer.doc,
        tempClass       = "_wysihtml5-temp-" + (+new Date()),
        tempClassRegExp = /non-matching-class/g,
        i               = 0,
        length,
        anchors,
        anchor,
        hasElementChild,
        isEmpty,
        elementToSetCaretAfter,
        textContent,
        whiteSpace,
        j;
    wysihtml5.commands.formatInline.exec(composer, undef, NODE_NAME, tempClass, tempClassRegExp);
    anchors = doc.querySelectorAll(NODE_NAME + "." + tempClass);
    length  = anchors.length;
    for (; i<length; i++) {
      anchor = anchors[i];
      anchor.removeAttribute("class");
      for (j in attributes) {
        anchor.setAttribute(j, attributes[j]);
      }
    }

    elementToSetCaretAfter = anchor;
    if (length === 1) {
      textContent = dom.getTextContent(anchor);
      hasElementChild = !!anchor.querySelector("*");
      isEmpty = textContent === "" || textContent === wysihtml5.INVISIBLE_SPACE;
      if (!hasElementChild && isEmpty) {
        dom.setTextContent(anchor, attributes.text || anchor.href);
        whiteSpace = doc.createTextNode(" ");
        composer.selection.setAfter(anchor);
        composer.selection.insertNode(whiteSpace);
        elementToSetCaretAfter = whiteSpace;
      }
    }
    composer.selection.setAfter(elementToSetCaretAfter);
  }

  wysihtml5.commands.createLink = {
    /**
     * TODO: Use HTMLApplier or formatInline here
     *
     * Turns selection into a link
     * If selection is already a link, it removes the link and wraps it with a <code> element
     * The <code> element is needed to avoid auto linking
     *
     * @example
     *    // either ...
     *    wysihtml5.commands.createLink.exec(composer, "createLink", "http://www.google.de");
     *    // ... or ...
     *    wysihtml5.commands.createLink.exec(composer, "createLink", { href: "http://www.google.de", target: "_blank" });
     */
    exec: function(composer, command, value) {
      var anchors = this.state(composer, command);
      if (anchors) {
        // Selection contains links
        composer.selection.executeAndRestore(function() {
          _removeFormat(composer, anchors);
        });
      } else {
        // Create links
        value = typeof(value) === "object" ? value : { href: value };
        _format(composer, value);
      }
    },

    state: function(composer, command) {
      return wysihtml5.commands.formatInline.state(composer, command, "A");
    }
  };
})(wysihtml5);<commit_msg>createLink command: Make sure that space is correctly inserted after <a> (fixes #171)<commit_after>(function(wysihtml5) {
  var undef,
      NODE_NAME = "A",
      dom       = wysihtml5.dom;

  function _removeFormat(composer, anchors) {
    var length  = anchors.length,
        i       = 0,
        anchor,
        codeElement,
        textContent;
    for (; i<length; i++) {
      anchor      = anchors[i];
      codeElement = dom.getParentElement(anchor, { nodeName: "code" });
      textContent = dom.getTextContent(anchor);

      // if <a> contains url-like text content, rename it to <code> to prevent re-autolinking
      // else replace <a> with its childNodes
      if (textContent.match(dom.autoLink.URL_REG_EXP) && !codeElement) {
        // <code> element is used to prevent later auto-linking of the content
        codeElement = dom.renameElement(anchor, "code");
      } else {
        dom.replaceWithChildNodes(anchor);
      }
    }
  }

  function _format(composer, attributes) {
    var doc             = composer.doc,
        tempClass       = "_wysihtml5-temp-" + (+new Date()),
        tempClassRegExp = /non-matching-class/g,
        i               = 0,
        length,
        anchors,
        anchor,
        hasElementChild,
        isEmpty,
        elementToSetCaretAfter,
        textContent,
        whiteSpace,
        j;
    wysihtml5.commands.formatInline.exec(composer, undef, NODE_NAME, tempClass, tempClassRegExp);
    anchors = doc.querySelectorAll(NODE_NAME + "." + tempClass);
    length  = anchors.length;
    for (; i<length; i++) {
      anchor = anchors[i];
      anchor.removeAttribute("class");
      for (j in attributes) {
        anchor.setAttribute(j, attributes[j]);
      }
    }

    elementToSetCaretAfter = anchor;
    if (length === 1) {
      textContent = dom.getTextContent(anchor);
      hasElementChild = !!anchor.querySelector("*");
      isEmpty = textContent === "" || textContent === wysihtml5.INVISIBLE_SPACE;
      if (!hasElementChild && isEmpty) {
        dom.setTextContent(anchor, attributes.text || anchor.href);
        whiteSpace = doc.createTextNode(" ");
        composer.selection.setAfter(anchor);
        dom.insert(whiteSpace).after(anchor);
        elementToSetCaretAfter = whiteSpace;
      }
    }
    composer.selection.setAfter(elementToSetCaretAfter);
  }

  wysihtml5.commands.createLink = {
    /**
     * TODO: Use HTMLApplier or formatInline here
     *
     * Turns selection into a link
     * If selection is already a link, it removes the link and wraps it with a <code> element
     * The <code> element is needed to avoid auto linking
     *
     * @example
     *    // either ...
     *    wysihtml5.commands.createLink.exec(composer, "createLink", "http://www.google.de");
     *    // ... or ...
     *    wysihtml5.commands.createLink.exec(composer, "createLink", { href: "http://www.google.de", target: "_blank" });
     */
    exec: function(composer, command, value) {
      var anchors = this.state(composer, command);
      if (anchors) {
        // Selection contains links
        composer.selection.executeAndRestore(function() {
          _removeFormat(composer, anchors);
        });
      } else {
        // Create links
        value = typeof(value) === "object" ? value : { href: value };
        _format(composer, value);
      }
    },

    state: function(composer, command) {
      return wysihtml5.commands.formatInline.state(composer, command, "A");
    }
  };
})(wysihtml5);<|endoftext|>