<commit_before>import Map from './Map';
import Point from 'geo/Point';
import { mat4 } from '@mapbox/gl-matrix';
import { clamp, interpolate, wrap } from 'core/util';
import { applyMatrix, matrixToQuaternion, quaternionToMatrix, lookAt, setPosition } from 'core/util/math';
import Browser from 'core/Browser';

const RADIAN = Math.PI / 180;
const DEFAULT_FOV = 0.6435011087932844;

/*!
 * contains code from mapbox-gl-js
 * https://github.com/mapbox/mapbox-gl-js
 * LICENSE : MIT
 * (c) mapbox
 *
 */

Map.include(/** @lends Map.prototype */{
    /**
     * Get map's fov (field of view);
     * @return {Number} fov in degree
     */
    getFov() {
        if (!this._fov) {
            this._fov = DEFAULT_FOV;
        }
        return this._fov / RADIAN;
    },

    /**
     * Set a new fov to map
     * @param {Number} fov new fov in degree
     * @return {Map} this
     */
    setFov(fov) {
        if (this.isZooming()) {
            return this;
        }
        fov = Math.max(0.01, Math.min(60, fov));
        if (this._fov === fov) return this;
        const from = this.getFov();
        this._fov = fov * RADIAN;
        this._calcMatrices();
        this._renderLayers();
        /*
          * fovchange event
          * @event Map#fovchange
          * @type {Object}
          * @property {String} type                    - fovchange
          * @property {Map} target                     - the map fires event
          * @property {Number} from                    - fovchange from
          * @property {Number} to                      - fovchange to
        */
        this._fireEvent('fovchange', { 'from' : from, 'to': this.getFov() });
        return this;
    },

    /**
     * Get map's bearing
     * @return {Number} bearing in degree
     */
    getBearing() {
        if (!this._angle) {
            return 0;
        }
        return -this._angle / RADIAN;
    },

    /**
     * Set a new bearing to map
     * @param {Number} bearing new bearing in degree
     * @return {Map} this
     */
    setBearing(bearing) {
        if (Browser.ie9) {
            throw new Error('map can\'t rotate in IE9.');
        }
        const b = -wrap(bearing, -180, 180) * RADIAN;
        if (this._angle === b) return this;
        const from = this.getBearing();
        this._angle = b;
        this._calcMatrices();
        this._renderLayers();
        /*
          * rotate event
          * @event Map#rotate
          * @type {Object}
          * @property {String} type                    - rotate
          * @property {Map} target                     - the map fires event
          * @property {Number} from                    - bearing rotate from
          * @property {Number} to                      - bearing rotate to
        */
        this._fireEvent('rotate', { 'from' : from, 'to': b });
        return this;
    },

    /**
     * Get map's pitch
     * @return {Number} pitch in degree
     */
    getPitch() {
        if (!this._pitch) {
            return 0;
        }
        return this._pitch / Math.PI * 180;
    },

    /**
     * Set a new pitch to map
     * @param {Number} pitch new pitch in degree
     * @return {Map} this
     */
    setPitch(pitch) {
        if (Browser.ie9) {
            throw new Error('map can\'t tilt in IE9.');
        }
        const p = clamp(pitch, 0, this.options['maxPitch']) * RADIAN;
        if (this._pitch === p) return this;
        const from = this.getPitch();
        this._pitch = p;
        this._calcMatrices();
        this._renderLayers();
        /**
          * pitch event
          * @event Map#pitch
          * @type {Object}
          * @property {String} type                    - pitch
          * @property {Map} target                     - the map fires event
          * @property {Number} from                    - pitch from
          * @property {Number} to                      - pitch to
          */
        this._fireEvent('pitch', { 'from' : from, 'to': p });
        return this;
    },

    /**
     * Whether the map is rotating or tilting.
     * @return {Boolean}
     * @private
     */
    isTransforming() {
        return !!(this._pitch || this._angle);
    },

    /**
     * Convert 2d point at target zoom to containerPoint at current zoom
     * @param  {Point} point 2d point at target zoom
     * @param  {Number} zoom  point's zoom
     * @param  {Number} [altitude=0]  target's altitude in 2d point system at target zoom
     * @return {Point}       containerPoint at current zoom
     * @private
     */
    _pointToContainerPoint(point, zoom, altitude = 0) {
        point = this._pointToPoint(point, zoom);
        if (this.isTransforming() || altitude) {
            //convert altitude at zoom to current zoom
            altitude *= this.getResolution(zoom) / this.getResolution();
            const scale = this._glScale;
            const t = [point.x * scale, point.y * scale, altitude * scale];
            applyMatrix(t, t, this.projViewMatrix);

            const w2 = this.width / 2, h2 = this.height / 2;
            t[0] = (t[0] * w2) + w2;
            t[1] = -(t[1] * h2) + h2;
            return new Point(t[0], t[1]);
        } else {
            const centerPoint = this._prjToPoint(this._getPrjCenter());
            return point._sub(centerPoint)._add(this.width / 2, this.height / 2);
        }
    },

    /**
     * Convert containerPoint at current zoom to 2d point at target zoom
     * @param  {Point} p    container point at current zoom
     * @param  {Number} zoom target zoom, current zoom in default
     * @return {Point}      2d point at target zoom
     * @private
     */
    _containerPointToPoint(p, zoom) {
        if (this.isTransforming()) {
            const w2 = this.width / 2, h2 = this.height / 2;
            const cp = [(p.x - w2) / w2, (h2 - p.y) / h2];

            const coord0 = [cp[0], cp[1], 0, 1];
            const coord1 = [cp[0], cp[1], 1, 1];

            applyMatrix(coord0, coord0, this.projViewMatrixInverse);
            applyMatrix(coord1, coord1, this.projViewMatrixInverse);
            const x0 = coord0[0];
            const x1 = coord1[0];
            const y0 = coord0[1];
            const y1 = coord1[1];
            const z0 = coord0[2];
            const z1 = coord1[2];

            const t = z0 === z1 ? 0 : (0 - z0) / (z1 - z0);

            const point = new Point(interpolate(x0, x1, t), interpolate(y0, y1, t))._multi(1 / this._glScale);
            return ((zoom === undefined || this.getZoom() === zoom) ? point : this._pointToPointAtZoom(point, zoom));
        }
        const centerPoint = this._prjToPoint(this._getPrjCenter(), zoom),
            scale = (zoom !== undefined ? this._getResolution() / this._getResolution(zoom) : 1);
        const x = scale * (p.x - this.width / 2),
            y = scale * (p.y - this.height / 2);
        return centerPoint._add(x, y);
    },

    /**
     * GL Matrices in maptalks (based on THREE):
     * this.cameraCenter
     * this.cameraWorldMatrix
     * this.projMatrix
     * this.viewMatrix = cameraWorldMatrix.inverse()
     * this.projViewMatrix = projMatrix * viewMatrix
     * this.projViewMatrixInverse = projViewMatrix.inverse()
     */
    _calcMatrices: function () {
        // closure matrixes to reuse
        const m0 = createMat4(),
            m1 = createMat4();
        return function () {
            // get pixel size of map
            const size = this.getSize();
            if (size.width === 0 || size.height === 0) {
                return;
            }
            this._glScale = this.getGLScale();
            // get field of view
            const fov = this.getFov() * Math.PI / 180;
            const maxScale = this.getScale(this.getMinZoom()) / this.getScale(this.getMaxNativeZoom());
            const farZ = maxScale * size.height / 2 / this._getFovRatio() + 1;
            // camera projection matrix
            const projMatrix = this.projMatrix || createMat4();
            mat4.perspective(projMatrix, fov, size.width / size.height, 1, farZ);
            mat4.scale(projMatrix, projMatrix, [1, -1, 1]);
            // camera world matrix
            const worldMatrix = this._getCameraWorldMatrix();
            // view matrix
            this.viewMatrix = mat4.invert(m0, worldMatrix);
            // matrix for world point => screen point
            this.projViewMatrix = mat4.multiply(this.projViewMatrix || createMat4(), projMatrix, this.viewMatrix);
            // matrix for screen point => world point
            this.projViewMatrixInverse = mat4.multiply(this.projViewMatrixInverse || createMat4(), worldMatrix, mat4.invert(m1, projMatrix));
        };
    }(),

    _getCameraWorldMatrix() {
        const targetZ = this.getGLZoom();

        const size = this.getSize(),
            scale = this.getGLScale();
        const center2D = this.cameraCenter = this._prjToPoint(this._prjCenter, targetZ);

        const pitch = this.getPitch() * RADIAN;
        const bearing = -this.getBearing() * RADIAN;

        const ratio = this._getFovRatio();
        const z = scale * size.height / 2 / ratio;
        const cz = z * Math.cos(pitch);
        this.cameraAltitude = cz;
        // and [dist] away from map's center on XY plane to tilt the scene.
        const dist = Math.sin(pitch) * z;
        // when map rotates, the camera's xy position is rotating with the given bearing and still keeps [dist] away from map's center
        const cx = center2D.x + dist * Math.sin(bearing);
        const cy = center2D.y + dist * Math.cos(bearing);

        // when map rotates, camera's up axis is pointing to bearing from south direction of map
        // default [0,1,0] is the Y axis while the angle of inclination always equal 0
        // if you want to rotate the map after up an incline,please rotateZ like this:
        // let up = new vec3(0,1,0);
        // up.rotateZ(target,radians);
        const d = dist || 1;
        const up = [Math.sin(bearing) * d, Math.cos(bearing) * d, 0];
        const m = this.cameraWorldMatrix || createMat4();
        lookAt(m, [cx, cy, cz], [center2D.x, center2D.y, 0], up);

        // math from THREE.js
        const q = {};
        matrixToQuaternion(q, m);
        quaternionToMatrix(m, q);
        setPosition(m, [cx, cy, cz]);

        return m;
    },

    _getFovRatio() {
        const fov = this.getFov();
        return Math.tan(fov / 2 * RADIAN);
    },

    _renderLayers() {
        if (this.isInteracting()) {
            return;
        }
        const layers = this._getLayers();
        // clear canvas layers to prevent unsync painting with tile layers.
        layers.forEach(layer => {
            if (!layer) {
                return;
            }
            const renderer = layer._getRenderer();
            if (renderer && renderer.setToRedraw) {
                renderer.setToRedraw();
            }
        });
    }
});

function createMat4() {
    const out = new Float64Array(16);
    out[0] = out[5] = out[10] = out[15] = 1;
    return out;
}
<commit_msg>fixed near/far of camera, close #540<commit_after>import Map from './Map';
import Point from 'geo/Point';
import { mat4 } from '@mapbox/gl-matrix';
import { clamp, interpolate, wrap } from 'core/util';
import { applyMatrix, matrixToQuaternion, quaternionToMatrix, lookAt, setPosition } from 'core/util/math';
import Browser from 'core/Browser';

const RADIAN = Math.PI / 180;
const DEFAULT_FOV = 0.6435011087932844;

/*!
 * contains code from mapbox-gl-js
 * https://github.com/mapbox/mapbox-gl-js
 * LICENSE : MIT
 * (c) mapbox
 *
 */

Map.include(/** @lends Map.prototype */{
    /**
     * Get map's fov (field of view);
     * @return {Number} fov in degree
     */
    getFov() {
        if (!this._fov) {
            this._fov = DEFAULT_FOV;
        }
        return this._fov / RADIAN;
    },

    /**
     * Set a new fov to map
     * @param {Number} fov new fov in degree
     * @return {Map} this
     */
    setFov(fov) {
        if (this.isZooming()) {
            return this;
        }
        fov = Math.max(0.01, Math.min(60, fov));
        if (this._fov === fov) return this;
        const from = this.getFov();
        this._fov = fov * RADIAN;
        this._calcMatrices();
        this._renderLayers();
        /*
          * fovchange event
          * @event Map#fovchange
          * @type {Object}
          * @property {String} type                    - fovchange
          * @property {Map} target                     - the map fires event
          * @property {Number} from                    - fovchange from
          * @property {Number} to                      - fovchange to
        */
        this._fireEvent('fovchange', { 'from' : from, 'to': this.getFov() });
        return this;
    },

    /**
     * Get map's bearing
     * @return {Number} bearing in degree
     */
    getBearing() {
        if (!this._angle) {
            return 0;
        }
        return -this._angle / RADIAN;
    },

    /**
     * Set a new bearing to map
     * @param {Number} bearing new bearing in degree
     * @return {Map} this
     */
    setBearing(bearing) {
        if (Browser.ie9) {
            throw new Error('map can\'t rotate in IE9.');
        }
        const b = -wrap(bearing, -180, 180) * RADIAN;
        if (this._angle === b) return this;
        const from = this.getBearing();
        this._angle = b;
        this._calcMatrices();
        this._renderLayers();
        /*
          * rotate event
          * @event Map#rotate
          * @type {Object}
          * @property {String} type                    - rotate
          * @property {Map} target                     - the map fires event
          * @property {Number} from                    - bearing rotate from
          * @property {Number} to                      - bearing rotate to
        */
        this._fireEvent('rotate', { 'from' : from, 'to': b });
        return this;
    },

    /**
     * Get map's pitch
     * @return {Number} pitch in degree
     */
    getPitch() {
        if (!this._pitch) {
            return 0;
        }
        return this._pitch / Math.PI * 180;
    },

    /**
     * Set a new pitch to map
     * @param {Number} pitch new pitch in degree
     * @return {Map} this
     */
    setPitch(pitch) {
        if (Browser.ie9) {
            throw new Error('map can\'t tilt in IE9.');
        }
        const p = clamp(pitch, 0, this.options['maxPitch']) * RADIAN;
        if (this._pitch === p) return this;
        const from = this.getPitch();
        this._pitch = p;
        this._calcMatrices();
        this._renderLayers();
        /**
          * pitch event
          * @event Map#pitch
          * @type {Object}
          * @property {String} type                    - pitch
          * @property {Map} target                     - the map fires event
          * @property {Number} from                    - pitch from
          * @property {Number} to                      - pitch to
          */
        this._fireEvent('pitch', { 'from' : from, 'to': p });
        return this;
    },

    /**
     * Whether the map is rotating or tilting.
     * @return {Boolean}
     * @private
     */
    isTransforming() {
        return !!(this._pitch || this._angle);
    },

    /**
     * Convert 2d point at target zoom to containerPoint at current zoom
     * @param  {Point} point 2d point at target zoom
     * @param  {Number} zoom  point's zoom
     * @param  {Number} [altitude=0]  target's altitude in 2d point system at target zoom
     * @return {Point}       containerPoint at current zoom
     * @private
     */
    _pointToContainerPoint(point, zoom, altitude = 0) {
        point = this._pointToPoint(point, zoom);
        if (this.isTransforming() || altitude) {
            //convert altitude at zoom to current zoom
            altitude *= this.getResolution(zoom) / this.getResolution();
            const scale = this._glScale;
            const t = [point.x * scale, point.y * scale, altitude * scale];
            applyMatrix(t, t, this.projViewMatrix);

            const w2 = this.width / 2, h2 = this.height / 2;
            t[0] = (t[0] * w2) + w2;
            t[1] = -(t[1] * h2) + h2;
            return new Point(t[0], t[1]);
        } else {
            const centerPoint = this._prjToPoint(this._getPrjCenter());
            return point._sub(centerPoint)._add(this.width / 2, this.height / 2);
        }
    },

    /**
     * Convert containerPoint at current zoom to 2d point at target zoom
     * @param  {Point} p    container point at current zoom
     * @param  {Number} zoom target zoom, current zoom in default
     * @return {Point}      2d point at target zoom
     * @private
     */
    _containerPointToPoint(p, zoom) {
        if (this.isTransforming()) {
            const w2 = this.width / 2, h2 = this.height / 2;
            const cp = [(p.x - w2) / w2, (h2 - p.y) / h2];

            const coord0 = [cp[0], cp[1], 0, 1];
            const coord1 = [cp[0], cp[1], 1, 1];

            applyMatrix(coord0, coord0, this.projViewMatrixInverse);
            applyMatrix(coord1, coord1, this.projViewMatrixInverse);
            const x0 = coord0[0];
            const x1 = coord1[0];
            const y0 = coord0[1];
            const y1 = coord1[1];
            const z0 = coord0[2];
            const z1 = coord1[2];

            const t = z0 === z1 ? 0 : (0 - z0) / (z1 - z0);

            const point = new Point(interpolate(x0, x1, t), interpolate(y0, y1, t))._multi(1 / this._glScale);
            return ((zoom === undefined || this.getZoom() === zoom) ? point : this._pointToPointAtZoom(point, zoom));
        }
        const centerPoint = this._prjToPoint(this._getPrjCenter(), zoom),
            scale = (zoom !== undefined ? this._getResolution() / this._getResolution(zoom) : 1);
        const x = scale * (p.x - this.width / 2),
            y = scale * (p.y - this.height / 2);
        return centerPoint._add(x, y);
    },

    /**
     * GL Matrices in maptalks (based on THREE):
     * this.cameraCenter
     * this.cameraWorldMatrix
     * this.projMatrix
     * this.viewMatrix = cameraWorldMatrix.inverse()
     * this.projViewMatrix = projMatrix * viewMatrix
     * this.projViewMatrixInverse = projViewMatrix.inverse()
     */
    _calcMatrices: function () {
        // closure matrixes to reuse
        const m0 = createMat4(),
            m1 = createMat4();
        return function () {
            // get pixel size of map
            const size = this.getSize();
            if (size.width === 0 || size.height === 0) {
                return;
            }
            this._glScale = this.getGLScale();
            // get field of view
            const fov = this.getFov() * Math.PI / 180;
            const maxScale = this.getScale(this.getMinZoom()) / this.getScale(this.getMaxNativeZoom());
            const farZ = maxScale * size.height / 2 / this._getFovRatio() + 1;
            // camera projection matrix
            const projMatrix = this.projMatrix || createMat4();
            mat4.perspective(projMatrix, fov, size.width / size.height, 0.1, farZ);
            mat4.scale(projMatrix, projMatrix, [1, -1, 1]);
            // camera world matrix
            const worldMatrix = this._getCameraWorldMatrix();
            // view matrix
            this.viewMatrix = mat4.invert(m0, worldMatrix);
            // matrix for world point => screen point
            this.projViewMatrix = mat4.multiply(this.projViewMatrix || createMat4(), projMatrix, this.viewMatrix);
            // matrix for screen point => world point
            this.projViewMatrixInverse = mat4.multiply(this.projViewMatrixInverse || createMat4(), worldMatrix, mat4.invert(m1, projMatrix));
        };
    }(),

    _getCameraWorldMatrix() {
        const targetZ = this.getGLZoom();

        const size = this.getSize(),
            scale = this.getGLScale();
        const center2D = this.cameraCenter = this._prjToPoint(this._prjCenter, targetZ);

        const pitch = this.getPitch() * RADIAN;
        const bearing = -this.getBearing() * RADIAN;

        const ratio = this._getFovRatio();
        const z = scale * size.height / 2 / ratio;
        const cz = z * Math.cos(pitch);
        this.cameraAltitude = cz;
        // and [dist] away from map's center on XY plane to tilt the scene.
        const dist = Math.sin(pitch) * z;
        // when map rotates, the camera's xy position is rotating with the given bearing and still keeps [dist] away from map's center
        const cx = center2D.x + dist * Math.sin(bearing);
        const cy = center2D.y + dist * Math.cos(bearing);

        // when map rotates, camera's up axis is pointing to bearing from south direction of map
        // default [0,1,0] is the Y axis while the angle of inclination always equal 0
        // if you want to rotate the map after up an incline,please rotateZ like this:
        // let up = new vec3(0,1,0);
        // up.rotateZ(target,radians);
        const d = dist || 1;
        const up = [Math.sin(bearing) * d, Math.cos(bearing) * d, 0];
        const m = this.cameraWorldMatrix || createMat4();
        lookAt(m, [cx, cy, cz], [center2D.x, center2D.y, 0], up);

        // math from THREE.js
        const q = {};
        matrixToQuaternion(q, m);
        quaternionToMatrix(m, q);
        setPosition(m, [cx, cy, cz]);

        return m;
    },

    _getFovRatio() {
        const fov = this.getFov();
        return Math.tan(fov / 2 * RADIAN);
    },

    _renderLayers() {
        if (this.isInteracting()) {
            return;
        }
        const layers = this._getLayers();
        // clear canvas layers to prevent unsync painting with tile layers.
        layers.forEach(layer => {
            if (!layer) {
                return;
            }
            const renderer = layer._getRenderer();
            if (renderer && renderer.setToRedraw) {
                renderer.setToRedraw();
            }
        });
    }
});

function createMat4() {
    const out = new Float64Array(16);
    out[0] = out[5] = out[10] = out[15] = 1;
    return out;
}
<|endoftext|>


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

import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import '@polymer/iron-ajax/iron-ajax.js';
import '@polymer/paper-fab/paper-fab.js';
import '@polymer/iron-icons/iron-icons.js';
import '@vaadin/vaadin-grid/theme/material/vaadin-grid.js';
import '@vaadin/vaadin-grid/vaadin-grid.js';
import '@vaadin/vaadin-grid/vaadin-grid-filter.js';
import './style-element.js';

class userList extends PolymerElement {
	static get template() {
		return html`

			<vaadin-grid
					id="userGrid"
					loading="{{loading}}"
					active-item="{{activeItem}}"
					theme="no-border">
				<vaadin-grid-column>
					<template class="header">
						<vaadin-grid-filter
								aria-label="User Name"
								path="id"
								value="{{_filterId}}">
							<input
									slot="filter"
									placeholder="Username"
									value="{{_filterId::input}}"
									focus-target>
						</vaadin-grid-filter>
					</template>
					<template>
						[[item.id]]
					</template>
				</vaadin-grid-column>
				<vaadin-grid-column>
					<template class="header">
						<vaadin-grid-filter
								aria-label="Language"
								path="language"
								value="{{_filterLanguage}}">
							<input
									slot="filter"
									placeholder="Language"
									value="{{_filterLanguage::input}}"
									focus-target>
						</vaadin-grid-filter>
					</template>
					<template>
						[[item.language]]
					</template>
				</vaadin-grid-column>
			</vaadin-grid>

				<paper-fab
					icon="add"
					on-tap = "showAddUserModal">
				</paper-fab>

			<iron-ajax
				id="getUserAjax"
				url="/partyManagement/v1/individual"
				rejectWithRequest>
			</iron-ajax>
		`;
	}

	static get properties() {
		return {
			loading: {
				type: Boolean,
				notify: true
			},
			activeItem: {
				type: Object,
				notify: true
			},
			etag: {
				type: String,
				value: null
			}
		}
	}

	ready() {
		super.ready();
		var grid = this.shadowRoot.getElementById('userGrid');
		grid.dataProvider = this._getUsers;
	}

	_getUsers(params, callback) {
		var grid = this;
		if(!grid.size) {
				grid.size = 0;
		}
		var userList = document.body.querySelector('sig-app').shadowRoot.querySelector('sig-user-list');
		var ajax = userList.shadowRoot.getElementById('getUserAjax');
		delete ajax.params['filter'];
		function checkHead(param) {
			return param.path == "id";
		}
		params.filters.filter(checkHead).forEach(function(filter) {
			if (filter.value) {
				ajax.params['filter'] = "\"[{id.like=[" + filter.value + "%";
			}
		});
		function checkChar(param) {
			return param.path != "id";
		}
		params.filters.filter(checkChar).forEach(function(filter) {
			if (filter.value) {
				if (!ajax.params['filter']) {
					ajax.params['filter'] = "\"[{";
				} else {
					ajax.params['filter'] += "],";
				}
				ajax.params['filter'] += "characteristic.contains=[{name=language" + ",value.like=[" + filter.value + "%]}";
			}
		});
		if (ajax.params['filter']) {
			ajax.params['filter'] += "]}]\"";
		}
		if(userList.etag && params.page > 0) {
			ajax.headers['If-Range'] = userList.etag;
		}
		var handleAjaxResponse = function(request) {
			if(request) {
				userList.etag = request.xhr.getResponseHeader('ETag');
				var range = request.xhr.getResponseHeader('Content-Range');
				var range1 = range.split("/");
				var range2 = range1[0].split("-");
				if (range1[1] != "*") {
					grid.size = Number(range1[1]);
				} else {
					grid.size = Number(range2[1]) + grid.pageSize * 2;
				}
				var vaadinItems = new Array();
				function checkChar(characteristic){
					return characteristic.name == "locale";
				}
				for(var index in request.response) {
					var newRecord = new Object();
					newRecord.id = request.response[index].id;
					var langChar = request.response[index].characteristic.find(checkChar);
					if(langChar != undefined) {
						newRecord.language = langChar.value;
					}
					vaadinItems[index] = newRecord;
				}
				callback(vaadinItems);
			} else {
				callback([]);
			}
		};
		var handleAjaxError = function(error) {
			userList.etag = null;
			var toast = document.body.querySelector('sig-app').shadowRoot.getElementById('restError');
			toast.text = "Error";
			toast.open();
			callback([]);
		}
		if(ajax.loading) {
			ajax.lastRequest.completes.then(function(request) {
				var startRange = params.page * params.pageSize + 1;
				ajax.headers['Range'] = "items=" + startRange + "-" + endRange;
				if (userList.etag && params.page > 0) {
					ajax.headers['If-Range'] = userList.etag;
				} else {
					delete ajax.headers['If-Range'];
				}
				return ajax.generateRequest().completes;
			}, handleAjaxError).then(handleAjaxResponse, handleAjaxError);
		} else {
			var startRange = params.page * params.pageSize + 1;
			var endRange = startRange + params.pageSize - 1;
			ajax.headers['Range'] = "items=" + startRange + "-" + endRange;
			if (userList.etag && params.page > 0) {
				ajax.headers['If-Range'] = userList.etag;
			} else {
				delete ajax.headers['If-Range'];
			}
			ajax.generateRequest().completes.then(handleAjaxResponse, handleAjaxError);
		}
	}

	showAddUserModal(event) {
		document.body.querySelector('sig-app').shadowRoot.querySelector('sig-user-add').shadowRoot.getElementById('addUserModal').open();
	}
}

window.customElements.define('sig-user-list', userList);
<commit_msg>Handle rating role in get view<commit_after>/**
 * Copyright 2016 - 2021 SigScale Global Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import '@polymer/iron-ajax/iron-ajax.js';
import '@polymer/paper-fab/paper-fab.js';
import '@polymer/iron-icons/iron-icons.js';
import '@vaadin/vaadin-grid/theme/material/vaadin-grid.js';
import '@vaadin/vaadin-grid/vaadin-grid.js';
import '@vaadin/vaadin-grid/vaadin-grid-filter.js';
import './style-element.js';

class userList extends PolymerElement {
	static get template() {
		return html`

			<vaadin-grid
					id="userGrid"
					loading="{{loading}}"
					active-item="{{activeItem}}"
					theme="no-border">
				<vaadin-grid-column>
					<template class="header">
						<vaadin-grid-filter
								aria-label="User Name"
								path="id"
								value="{{_filterId}}">
							<input
									slot="filter"
									placeholder="Username"
									value="{{_filterId::input}}"
									focus-target>
						</vaadin-grid-filter>
					</template>
					<template>
						[[item.id]]
					</template>
				</vaadin-grid-column>
				<vaadin-grid-column>
					<template class="header">
						<vaadin-grid-filter
								aria-label="Language"
								path="language"
								value="{{_filterLanguage}}">
							<input
									slot="filter"
									placeholder="Language"
									value="{{_filterLanguage::input}}"
									focus-target>
						</vaadin-grid-filter>
					</template>
					<template>
						[[item.language]]
					</template>
				</vaadin-grid-column>
				<vaadin-grid-column>
					<template class="header">
						<vaadin-grid-filter
								aria-label="Rating"
								path="rating"
								value="{{_filterRating}}">
							<input
									slot="filter"
									placeholder="Rating"
									value="{{_filterRating::input}}"
									focus-target>
						</vaadin-grid-filter>
					</template>
					<template>
						[[item.rating]]
					</template>
				</vaadin-grid-column>
			</vaadin-grid>

				<paper-fab
					icon="add"
					on-tap = "showAddUserModal">
				</paper-fab>

			<iron-ajax
				id="getUserAjax"
				url="/partyManagement/v1/individual"
				rejectWithRequest>
			</iron-ajax>
		`;
	}

	static get properties() {
		return {
			loading: {
				type: Boolean,
				notify: true
			},
			activeItem: {
				type: Object,
				notify: true
			},
			etag: {
				type: String,
				value: null
			}
		}
	}

	ready() {
		super.ready();
		var grid = this.shadowRoot.getElementById('userGrid');
		grid.dataProvider = this._getUsers;
	}

	_getUsers(params, callback) {
		var grid = this;
		if(!grid.size) {
				grid.size = 0;
		}
		var userList = document.body.querySelector('sig-app').shadowRoot.querySelector('sig-user-list');
		var ajax = userList.shadowRoot.getElementById('getUserAjax');
		delete ajax.params['filter'];
		function checkHead(param) {
			return param.path == "id";
		}
		params.filters.filter(checkHead).forEach(function(filter) {
			if (filter.value) {
				ajax.params['filter'] = "\"[{id.like=[" + filter.value + "%";
			}
		});
		function checkChar(param) {
			return param.path != "id";
		}
		params.filters.filter(checkChar).forEach(function(filter) {
			if (filter.value) {
				if (!ajax.params['filter']) {
					ajax.params['filter'] = "\"[{";
				} else {
					ajax.params['filter'] += "],";
				}
				ajax.params['filter'] += "characteristic.contains=[{name=language" + ",value.like=[" + filter.value + "%]}";
			}
		});
		if (ajax.params['filter']) {
			ajax.params['filter'] += "]}]\"";
		}
		if(userList.etag && params.page > 0) {
			ajax.headers['If-Range'] = userList.etag;
		}
		var handleAjaxResponse = function(request) {
			if(request) {
				userList.etag = request.xhr.getResponseHeader('ETag');
				var range = request.xhr.getResponseHeader('Content-Range');
				var range1 = range.split("/");
				var range2 = range1[0].split("-");
				if (range1[1] != "*") {
					grid.size = Number(range1[1]);
				} else {
					grid.size = Number(range2[1]) + grid.pageSize * 2;
				}
				var vaadinItems = new Array();
				function checkChar(characteristic){
					return characteristic.name == "locale";
				}
				function checkCharRate(characteristicRa){
					return characteristicRa.name == "rating";
				}
				for(var index in request.response) {
					var newRecord = new Object();
					newRecord.id = request.response[index].id;
					var langChar = request.response[index].characteristic.find(checkChar);
					if(langChar != undefined) {
						newRecord.language = langChar.value;
					}
					var rateChar = request.response[index].characteristic.find(checkCharRate);
					if(rateChar != undefined) {
						newRecord.rating = rateChar.value;
					}
					vaadinItems[index] = newRecord;
				}
				callback(vaadinItems);
			} else {
				callback([]);
			}
		};
		var handleAjaxError = function(error) {
			userList.etag = null;
			var toast = document.body.querySelector('sig-app').shadowRoot.getElementById('restError');
			toast.text = "Error";
			toast.open();
			callback([]);
		}
		if(ajax.loading) {
			ajax.lastRequest.completes.then(function(request) {
				var startRange = params.page * params.pageSize + 1;
				ajax.headers['Range'] = "items=" + startRange + "-" + endRange;
				if (userList.etag && params.page > 0) {
					ajax.headers['If-Range'] = userList.etag;
				} else {
					delete ajax.headers['If-Range'];
				}
				return ajax.generateRequest().completes;
			}, handleAjaxError).then(handleAjaxResponse, handleAjaxError);
		} else {
			var startRange = params.page * params.pageSize + 1;
			var endRange = startRange + params.pageSize - 1;
			ajax.headers['Range'] = "items=" + startRange + "-" + endRange;
			if (userList.etag && params.page > 0) {
				ajax.headers['If-Range'] = userList.etag;
			} else {
				delete ajax.headers['If-Range'];
			}
			ajax.generateRequest().completes.then(handleAjaxResponse, handleAjaxError);
		}
	}

	showAddUserModal(event) {
		document.body.querySelector('sig-app').shadowRoot.querySelector('sig-user-add').shadowRoot.getElementById('addUserModal').open();
	}
}

window.customElements.define('sig-user-list', userList);
<|endoftext|>


<commit_before>'use strict';

class MaterialInput extends HTMLElement {

    constructor() {
        super(); // always call super() first in the ctor. This also calls the extended class' ctor.
    }

    createdCallback() {
        var value = '';
        // set value of material-input
        Object.defineProperty( this, "value", {
            configurable: true,
            enumerable: true,
            get: function(){
                return value;
            },
            set: function(newValue){
                value = !newValue ? '' : newValue;
                this._value(value);
                this.$input.value = value;
            }
        });

        this.createShadowRoot().innerHTML = `

                <content></content>
                <input class="material-input__input" tabindex="-1" />

        `;
        this.attributesExceptions = [
            'name',
            'id',
            'style',
            'label',
            'tabindex',
            'placeholder',
            'autofocus',
            'autocomplete',
            'autovalidate'
        ];
        // set tab index to make element focussable
        this.setAttribute('tabindex',0);
        // shim shadowDOM styling
        if(WebComponents !== undefined && WebComponents.flags.shadow === true){
            WebComponents.ShadowCSS.shimStyling( this.shadowRoot, 'material-input' )
        }
        // add hidden input
        this.insertAdjacentHTML('afterend','<input tabindex="-1" class="material-input__hidden-input" style="pointer-events: none; margin:0; border: 0; height: 0; opacity: 0; position: absolute; top: '+(this.offsetTop + this.offsetHeight)+'px; left: '+this.offsetLeft+'px;" name="'+this.getAttribute('name')+'"/>');
        this.$hiddenInput = document.querySelector('.material-input__hidden-input[name='+this.getAttribute('name')+']');
        // elements
        this.$container = this.shadowRoot.querySelector('.material-input__container');
        this.$input = this.$container.querySelector('.material-input__input');
        this.$label = this.$container.querySelector('.material-input__label');
        this.$message = this.$container.querySelector('.material-input__message');
        this.$form = this._getParentForm(this);
        //
        this.validity = this.hasAttribute('valid') ? true : this.hasAttribute('invalid') ? false : undefined;
        // add events
        this._addEvents();
        // transfer attribtues to input & hiddenInput
        this._transferAttributes();
        // set value, label, etc.
        this._setValue(this.getAttribute('value'));
        this.$input.value = this.value;
        this._setLabel(this.getAttribute('label'));
        this._setPlaceholder(this.getAttribute('placeholder'));
        this._setValid(this.validity);
        this._setMessage(this.getAttribute('message'));
        // remove no-animation loading class
        setTimeout(function(){
            this.$container.classList.remove('no-animation');
        }.bind(this),100);
    }
    /**
     * when an attribute is changed
     */
    attributeChangedCallback(attrName, oldVal, newVal) {
        // define callbacks
        var callbacks = {
            'value': this._setValue,
            'label': this._setLabel,
            'placeholder': this._setPlaceholder,
            'name': this._setName,
            'message': this._setMessage
        };
        // call callback if it exists
        if(callbacks.hasOwnProperty(attrName)) {
            callbacks[attrName].call(this, newVal, oldVal);
        }
        else{
            // if other attributes are updated, transfer updates to hidden input field
            this._transferAttribute(attrName, newVal, this.attributesExceptions);
        }
    }
    /**
     * set the custom validity of the input
     */
    setCustomValidity(msg){
        this.$input.setCustomValidity(msg);
        this.$hiddenInput.setCustomValidity(msg)
    }
    /**
     * add events for all items
     */
    _addEvents(){
        // on focuse pass to input
        this.addEventListener('focus', function(){
            this.$input.focus();
        });
        // set validation status when hiddenInput is invalid
        this.$hiddenInput.addEventListener('invalid', function(){
            this._setValid(false);
        }.bind(this));
        // submit on enters
        this.$input.addEventListener('keydown', function(e){
            if(e.keyCode === 13){
                if(this.$form.checkValidity()){
                    this.$form.submit();
                }else if(this.$form.querySelector('[type="submit"]') !== null){
                    // needed to trigger validation
                    this.$form.querySelector('[type="submit"]').click();
                }
                return;
            }
        }.bind(this));
        // pass on value when user enters content
        this.$input.addEventListener('input', function(e){
            this._value(this.$input.value);
        }.bind(this));
        // pass in value and validate when user exits input field
        this.$input.addEventListener('blur', function(){
            this._value(this.$input.value);
            if(this.hasAttribute('autovalidate') && String(this.getAttribute('autovalidate')) !== 'false'){
                // check if is valid
                this._checkValidity();
            }
        }.bind(this));
        // if autovalidate is set to true, validate on key event
        if(this.hasAttribute('autovalidate') && String(this.getAttribute('autovalidate')) !== 'false'){
            this.$input.addEventListener('input', function(){
                // check if is valid
                this._checkValidity();
            }.bind(this));
        } else {
            this.$input.addEventListener('input', function(){
                // check if is valid
                if(this.$container.classList.contains('invalid') && this.$input.value !== '' && this.$input.validity.valid === true){
                    this._setValid(true);
                }
            }.bind(this));
        }
    }
    /**
     * get parent form
     */
    _getParentForm(current){
        current = current.parentElement;
        // return form
        if( current.constructor === HTMLFormElement ) return current;
        // return false on body
        if(current.constructor === HTMLBodyElement) return false;
        // dig one level deeper
        return this._getParentForm(current);
    }
    /**
     * check validity
     */
    _checkValidity(){
        if(this.$input.value !== '' && (this.$input.validity.valid === true || this.$input.validity.valid === false) ){
            this._setValid(this.$input.validity.valid);
        }else{
            this._setValid(undefined);
        }
    }
    /**
     * set value
     */
    _setValue(newValue){
        this.value = newValue;
    }
    /**
     * set message
     */
    _setMessage(msg){
        this.$message.innerHTML = msg;
    }
    /**
     * set name
     */
    _setName(newName){
        this.$hiddenInput.setAttribute('name', newName);
    }
    /**
     * set field to valid or invalid
     */
    _setValid(validity = undefined){
        // valid is not set
        if(validity === undefined){
            this.valid = undefined;
            this.$container.classList.remove('valid');
            this.$container.classList.remove('invalid');
        }
        // valid is true
        if(validity === true){
            this.valid = true;
            this.$container.classList.add('valid');
            this.$container.classList.remove('invalid');
        }
        // valid is false
        if(validity === false) {
            this.valid = false;
            this.$container.classList.add('invalid');
            this.$container.classList.remove('valid');
        }
    }
    /**
     * transfer attributes to input
     */
    _transferAttributes(){
        for(var key of Object.keys(this.attributes)){
            if (this.attributes.hasOwnProperty(key)) {
                this._transferAttribute(this.attributes[key].name, this.attributes[key].value, this.attributesExceptions);
            }
        }
    }
    /**
     * transfer attribute to input
     */
    _transferAttribute(attrName, val, attributesExceptions){
        if(attributesExceptions.indexOf(attrName) === -1){
            this.$hiddenInput.setAttribute(attrName,val);
            this.$input.setAttribute(attrName,val);
        }
    }
    /**
     * update value and toggle is-empty class
     */
    _value(val){
        // set value of hidden input for form submission
        this.$hiddenInput.value = val;
        this.setAttribute('value',val);
        // set state depending on value
        this._toggle(this.$container, 'is-empty', val === '');
    }
    /**
     * add label to material-input
     */
    _setLabel(label){
        if(label !== undefined && label !== null){
            return this.$label.innerHTML = label;
        }
        this.$label.innerHTML = '';
    }
    /**
     * set placeholder and add label-always-floats class
     */
    _setPlaceholder(placeholder){
        if(placeholder !== null && placeholder !== undefined){
            this.$input.setAttribute('placeholder', placeholder);
            this.$container.classList.add('label-always-floats');
            return;
        }
        this.$input.removeAttribute('placeholder');
        this.$container.classList.remove('label-always-floats');
    }
    /**
     * since classList.toggle with a second param is not supported in IE11 and below
     */
    _toggle(el, cls, condition){
        if(condition === true){
            el.classList.add(cls);
        }else{
            el.classList.remove(cls);
        }
    }
}

document.registerElement('material-input', MaterialInput);
<commit_msg>add placeholder color variabel to label<commit_after>'use strict';

class MaterialInput extends HTMLElement {

    constructor() {
        super(); // always call super() first in the ctor. This also calls the extended class' ctor.
    }

    createdCallback() {
        var value = '';
        // set value of material-input
        Object.defineProperty( this, "value", {
            configurable: true,
            enumerable: true,
            get: function(){
                return value;
            },
            set: function(newValue){
                value = !newValue ? '' : newValue;
                this._value(value);
                this.$input.value = value;
            }
        });

        this.createShadowRoot().innerHTML = `

                <content></content>
                <input class="material-input__input" tabindex="-1" />

        `;
        this.attributesExceptions = [
            'name',
            'id',
            'style',
            'label',
            'tabindex',
            'placeholder',
            'autofocus',
            'autocomplete',
            'autovalidate'
        ];
        // set tab index to make element focussable
        this.setAttribute('tabindex',0);
        // shim shadowDOM styling
        if(WebComponents !== undefined && WebComponents.flags.shadow === true){
            WebComponents.ShadowCSS.shimStyling( this.shadowRoot, 'material-input' )
        }
        // add hidden input
        this.insertAdjacentHTML('afterend','<input tabindex="-1" class="material-input__hidden-input" style="pointer-events: none; margin:0; border: 0; height: 0; opacity: 0; position: absolute; top: '+(this.offsetTop + this.offsetHeight)+'px; left: '+this.offsetLeft+'px;" name="'+this.getAttribute('name')+'"/>');
        this.$hiddenInput = document.querySelector('.material-input__hidden-input[name='+this.getAttribute('name')+']');
        // elements
        this.$container = this.shadowRoot.querySelector('.material-input__container');
        this.$input = this.$container.querySelector('.material-input__input');
        this.$label = this.$container.querySelector('.material-input__label');
        this.$message = this.$container.querySelector('.material-input__message');
        this.$form = this._getParentForm(this);
        //
        this.validity = this.hasAttribute('valid') ? true : this.hasAttribute('invalid') ? false : undefined;
        // add events
        this._addEvents();
        // transfer attribtues to input & hiddenInput
        this._transferAttributes();
        // set value, label, etc.
        this._setValue(this.getAttribute('value'));
        this.$input.value = this.value;
        this._setLabel(this.getAttribute('label'));
        this._setPlaceholder(this.getAttribute('placeholder'));
        this._setValid(this.validity);
        this._setMessage(this.getAttribute('message'));
        // remove no-animation loading class
        setTimeout(function(){
            this.$container.classList.remove('no-animation');
        }.bind(this),100);
    }
    /**
     * when an attribute is changed
     */
    attributeChangedCallback(attrName, oldVal, newVal) {
        // define callbacks
        var callbacks = {
            'value': this._setValue,
            'label': this._setLabel,
            'placeholder': this._setPlaceholder,
            'name': this._setName,
            'message': this._setMessage
        };
        // call callback if it exists
        if(callbacks.hasOwnProperty(attrName)) {
            callbacks[attrName].call(this, newVal, oldVal);
        }
        else{
            // if other attributes are updated, transfer updates to hidden input field
            this._transferAttribute(attrName, newVal, this.attributesExceptions);
        }
    }
    /**
     * set the custom validity of the input
     */
    setCustomValidity(msg){
        this.$input.setCustomValidity(msg);
        this.$hiddenInput.setCustomValidity(msg)
    }
    /**
     * add events for all items
     */
    _addEvents(){
        // on focuse pass to input
        this.addEventListener('focus', function(){
            this.$input.focus();
        });
        // set validation status when hiddenInput is invalid
        this.$hiddenInput.addEventListener('invalid', function(){
            this._setValid(false);
        }.bind(this));
        // submit on enters
        this.$input.addEventListener('keydown', function(e){
            if(e.keyCode === 13){
                if(this.$form.checkValidity()){
                    this.$form.submit();
                }else if(this.$form.querySelector('[type="submit"]') !== null){
                    // needed to trigger validation
                    this.$form.querySelector('[type="submit"]').click();
                }
                return;
            }
        }.bind(this));
        // pass on value when user enters content
        this.$input.addEventListener('input', function(e){
            this._value(this.$input.value);
        }.bind(this));
        // pass in value and validate when user exits input field
        this.$input.addEventListener('blur', function(){
            this._value(this.$input.value);
            if(this.hasAttribute('autovalidate') && String(this.getAttribute('autovalidate')) !== 'false'){
                // check if is valid
                this._checkValidity();
            }
        }.bind(this));
        // if autovalidate is set to true, validate on key event
        if(this.hasAttribute('autovalidate') && String(this.getAttribute('autovalidate')) !== 'false'){
            this.$input.addEventListener('input', function(){
                // check if is valid
                this._checkValidity();
            }.bind(this));
        } else {
            this.$input.addEventListener('input', function(){
                // check if is valid
                if(this.$container.classList.contains('invalid') && this.$input.value !== '' && this.$input.validity.valid === true){
                    this._setValid(true);
                }
            }.bind(this));
        }
    }
    /**
     * get parent form
     */
    _getParentForm(current){
        current = current.parentElement;
        // return form
        if( current.constructor === HTMLFormElement ) return current;
        // return false on body
        if(current.constructor === HTMLBodyElement) return false;
        // dig one level deeper
        return this._getParentForm(current);
    }
    /**
     * check validity
     */
    _checkValidity(){
        if(this.$input.value !== '' && (this.$input.validity.valid === true || this.$input.validity.valid === false) ){
            this._setValid(this.$input.validity.valid);
        }else{
            this._setValid(undefined);
        }
    }
    /**
     * set value
     */
    _setValue(newValue){
        this.value = newValue;
    }
    /**
     * set message
     */
    _setMessage(msg){
        this.$message.innerHTML = msg;
    }
    /**
     * set name
     */
    _setName(newName){
        this.$hiddenInput.setAttribute('name', newName);
    }
    /**
     * set field to valid or invalid
     */
    _setValid(validity = undefined){
        // valid is not set
        if(validity === undefined){
            this.valid = undefined;
            this.$container.classList.remove('valid');
            this.$container.classList.remove('invalid');
        }
        // valid is true
        if(validity === true){
            this.valid = true;
            this.$container.classList.add('valid');
            this.$container.classList.remove('invalid');
        }
        // valid is false
        if(validity === false) {
            this.valid = false;
            this.$container.classList.add('invalid');
            this.$container.classList.remove('valid');
        }
    }
    /**
     * transfer attributes to input
     */
    _transferAttributes(){
        for(var key of Object.keys(this.attributes)){
            if (this.attributes.hasOwnProperty(key)) {
                this._transferAttribute(this.attributes[key].name, this.attributes[key].value, this.attributesExceptions);
            }
        }
    }
    /**
     * transfer attribute to input
     */
    _transferAttribute(attrName, val, attributesExceptions){
        if(attributesExceptions.indexOf(attrName) === -1){
            this.$hiddenInput.setAttribute(attrName,val);
            this.$input.setAttribute(attrName,val);
        }
    }
    /**
     * update value and toggle is-empty class
     */
    _value(val){
        // set value of hidden input for form submission
        this.$hiddenInput.value = val;
        this.setAttribute('value',val);
        // set state depending on value
        this._toggle(this.$container, 'is-empty', val === '');
    }
    /**
     * add label to material-input
     */
    _setLabel(label){
        if(label !== undefined && label !== null){
            return this.$label.innerHTML = label;
        }
        this.$label.innerHTML = '';
    }
    /**
     * set placeholder and add label-always-floats class
     */
    _setPlaceholder(placeholder){
        if(placeholder !== null && placeholder !== undefined){
            this.$input.setAttribute('placeholder', placeholder);
            this.$container.classList.add('label-always-floats');
            return;
        }
        this.$input.removeAttribute('placeholder');
        this.$container.classList.remove('label-always-floats');
    }
    /**
     * since classList.toggle with a second param is not supported in IE11 and below
     */
    _toggle(el, cls, condition){
        if(condition === true){
            el.classList.add(cls);
        }else{
            el.classList.remove(cls);
        }
    }
}

document.registerElement('material-input', MaterialInput);
<|endoftext|>


<commit_before>/*jslint forin:true sub:true anon:true, sloppy:true, stupid:true nomen:true, node:true continue:true*/

/*
 * Copyright (c) 2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

var fs = require('fs');
var colors = require("colors");
var UserAgent = require('useragent');

/**
 *
 * @param reportObj includes all information about testSessions, wsSessions and the test report itself
 * @constructor
 */
function ReportManager(reportObj) {
    var stats;
    try {
        if (reportObj.arrTestSessions) {
            this.testSessions = reportObj.arrTestSessions;
        } else {
            this.testSessions = [];
        }

        if (reportObj.arrWDSessions) {
            this.wdSessions = reportObj.arrWDSessions;
        } else {
            this.wdSessions = [];
        }
        this.reportObj = reportObj;
        if (reportObj.reportFolder) {
            stats = fs.lstatSync(reportObj.reportFolder);
            if (stats.isDirectory()) {
                this.reportFolder = reportObj.reportFolder;
            }
        }
    } catch (ex) {
        //console.log(ex);
    }
}

ReportManager.prototype.showReportOnConsole = function (result, verbose) {

//    console.log("Result: " + JSON.stringify(result));
//    console.log("Verbose " + verbose);

    var lastSuite, suite, k, k1, test, msg, m, browser,
        log = console.log;

    log("");
    browser = result.ua;
    if (result.failed) {
        log("Failed ".red +  result.name.blue + " on"  + browser);
    } else {
        log("Passed ".green + result.name.blue + " on"  + browser);
    }
    log((result.passed + " Passed").green + ", " + (result.failed + " Failed ").red + ", " + (result.ignored + " skipped ").grey);
    if (result.failed) {
        for (k in result) {
            suite = result[k];
            if ("object" === typeof suite) {
                if (suite.failed) {
                    for (k1 in suite) {
                        test = suite[k1];
                        if ("object" === typeof test) {
                            if ("fail" === test.result) {
                                if (!lastSuite || lastSuite !== suite.name) {
                                    log(" in " +  suite.name.bold);
                                    lastSuite = suite.name;
                                }
                                msg = test.message.split('\n');
                                log(" " +  test.name.bold.red + ":" +  msg[0]);
                                for (m = 1; m < msg.length; m = m + 1) {
                                    log(" " + msg[m]);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    log("");
};

ReportManager.prototype.writeReports = function () {

    var i, j, k, strJunitReport, strReport, rep, results, testJson, yuiUserAgent, yuiSuiteName, browser, descTestName, sessionId, yuiTestCaseName, timePerTest,
        className, testName, totalTimeTaken, errorCount, testSuiteName, arrReport, xmlReportName, jsonReportName, errorRep,
        self = this,
        wdSessions = self.wdSessions,
        testSessions = self.testSessions,
        passCount = 0,
        failCount = 0,
        skipCount = 0,
        timeCount = 0,
        totalCount = 0,
        jsonReport = {},
        log = console.log;
        resultPresent = false;
        errorCount = 0;

    function writeFile(fileName, data) {

        if (self.reportFolder) {
            fileName = self.reportFolder  + fileName;
        }
        fs.writeFileSync(fileName, data, 'utf8');
        log(("Report Created: " + fileName).inverse);
    }

    function addProperty(name, val) {
        if (val) {
            strReport += "<property name='" + name + "' value='" + val + "' />";
        }
    }
    function addTest(time, className, name, failureMessage, log) {
        strReport += "<testcase time='" + (time / 1000).toFixed(2) + "' classname='" + className  + "' name='" + name + "'";
        if (failureMessage) {
            if (failureMessage === "skip") {
                strReport += " ' executed='false' />";
            } else {
                strReport += "><failure><![CDATA[" + failureMessage + "]]></failure>" + log + "</testcase>";
            }
        } else {
            strReport += "/>";
        }
    }

    function parseYUIResults(testJson, dtName) {
        var l, m;

        yuiUserAgent = testJson.ua;
        yuiSuiteName = testJson.name;
        browser = UserAgent.parse(yuiUserAgent).toAgent();
        if (browser === "Other 0.0.0") {
            browser = "NodeJS";
        }
        descTestName = dtName;
        sessionId = testJson.sessionId;
        for (l in testJson) {
            if (testJson[l]) {
                if (testJson[l].type === "testcase") {
                    yuiTestCaseName = testJson[l].name;
                    timePerTest = testJson[l].duration / testJson[l].total;
                    passCount += testJson[l].passed;
                    failCount += testJson[l].failed;
                    skipCount += testJson[l].ignored;
                    timeCount += testJson[l].duration;
                    totalCount += testJson[l].total;

                    for (m in testJson[l]) {
                        if (testJson[l][m].type === "test") {
                            className = browser + "." + descTestName;
                            testName = yuiSuiteName + "::" + yuiTestCaseName + "::" + testJson[l][m].name;
                            if (testJson[l][m].result === "fail") {
                                addTest(timePerTest, className, testName, testJson[l][m].message);
                            } else if (testJson[l][m].result === "pass") {
                                addTest(timePerTest, className, testName);
                            } else if (testJson[l][m].result === "ignore") {
                                addTest(timePerTest, className, testName,"skip");
                            }
                        }
                    }
                }
            }
        }
    }
    strReport = "<properties>";
    addProperty("descriptor", this.reportObj.descriptor);
    addProperty("parallel", this.reportObj.parallel);
    addProperty("driver", this.reportObj.driver);
    addProperty("browser", this.reportObj.browser);
    addProperty("reuseSession", this.reportObj.reuseSession);
    addProperty("activeSeleniumSessionCount", wdSessions.length);
    addProperty("seleniumSessionIds", wdSessions.join());
    addProperty("group", this.reportObj.group);
    addProperty("testName", this.reportObj.testName);
    addProperty("reportFolder", this.reportObj.reportFolder);
    strReport += "</properties>";

    for (i = 0; i < testSessions.length; i = i + 1) {
        rep = testSessions[i].driver.getReports();

        if (rep.scenario) {
            arrReport = rep.scenario;
        } else {
            arrReport = rep.results;
        }

        resultPresent = false;

        for (j = 0; j < arrReport.length; j = j + 1) {
            if (rep.scenario) {
                results = arrReport[j].results;
                for (k = 0; k < results.length; k = k + 1) {
                    testJson =  results[k];
                    if(testJson.type === "report") {
                        resultPresent = true;
                        parseYUIResults(testJson, testSessions[i].driver.testName);
                    }
                }

            } else {
                testJson = arrReport[j];
                if(testJson.type === "report") {
                    resultPresent = true;
                    parseYUIResults(testJson, testSessions[i].driver.testName);
                }
            }
        }

        if(!resultPresent) {
            errorCount = errorCount + 1;
           var errorRep = arrReport[arrReport.length-1];

           if(errorRep.results) {
               //console.log("Error :" + errorRep.results[0].error);
               addTest(0, "Error." +  testSessions[i].driver.testName,"Error" , errorRep.results[0].error);

           } else if (errorRep.error) {
              // console.log("Error : " +errorRep.error)
               addTest(0, "Error." +  testSessions[i].driver.testName,"Error" , errorRep.error);
           } else{
               console.log("Error : UNKOWN");
           }

            console.log("Test Session dint have any report");
        }
    }
    log();
    log("Test Report Summary".bold.magenta);
    log();
    log(("Total Number of Executed Tests: " + (totalCount + errorCount) ).blue);
    log(("Total Number of Passed Tests: " + passCount).green);
    log(("Total Number of Failed Tests: " + ( errorCount + failCount) ).red);
    log(("Total Number of Skipped Tests: " + skipCount).cyan);
    log();
    log(("Total Test Execution Time Inside Browser: " + (timeCount / 1000).toFixed(2) + " seconds").green.bold);
    totalTimeTaken = Date.now() - global.startTime;
    log(("Total Test Execution Time:" + (totalTimeTaken / 1000).toFixed(2) + " seconds").green.bold);
    log();

    errorCount = totalCount - passCount - failCount - skipCount;

    testSuiteName = "";
    if (this.reportObj.testSuiteName) {
        testSuiteName = this.reportObj.testSuiteName;
    } else {
        testSuiteName = "TestSuite";
    }

    //writing xml file
    strJunitReport = "<testsuite failures='" + failCount + "' time='" + (totalTimeTaken / 1000).toFixed(2) + "' errors='" + errorCount + "' skipped='" + skipCount + "' tests='" + totalCount + "' name='" + testSuiteName + "'>";
    strJunitReport += strReport;
    strJunitReport += "</testsuite>";
    if (this.reportObj.descriptor) {

        xmlReportName = this.reportObj.descriptor.split(".")[0] + "-report.xml";
        jsonReportName = this.reportObj.descriptor.split(".")[0] + "-report.json";
    } else {
        xmlReportName = "report.xml";
        jsonReportName = "report.json";
    }
    writeFile(xmlReportName, strJunitReport);

    //writing json file
    for (i = 0; i < testSessions.length; i = i + 1) {
        jsonReport[testSessions[i].driver.testName] = JSON.parse(JSON.stringify(testSessions[i].driver.getReports()));
    }
    jsonReport.testSuiteName =  testSuiteName;
    writeFile(jsonReportName, JSON.stringify(jsonReport));
};

module.exports = ReportManager;
<commit_msg>fixing reporting issues<commit_after>/*jslint forin:true sub:true anon:true, sloppy:true, stupid:true nomen:true, node:true continue:true*/

/*
 * Copyright (c) 2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

var fs = require('fs');
var colors = require("colors");
var UserAgent = require('useragent');

/**
 *
 * @param reportObj includes all information about testSessions, wsSessions and the test report itself
 * @constructor
 */
function ReportManager(reportObj) {
    var stats;
    try {
        if (reportObj.arrTestSessions) {
            this.testSessions = reportObj.arrTestSessions;
        } else {
            this.testSessions = [];
        }

        if (reportObj.arrWDSessions) {
            this.wdSessions = reportObj.arrWDSessions;
        } else {
            this.wdSessions = [];
        }
        this.reportObj = reportObj;
        if (reportObj.reportFolder) {
            stats = fs.lstatSync(reportObj.reportFolder);
            if (stats.isDirectory()) {
                this.reportFolder = reportObj.reportFolder;
            }
        }
    } catch (ex) {
        //console.log(ex);
    }
}

ReportManager.prototype.showReportOnConsole = function (result, verbose) {

//    console.log("Result: " + JSON.stringify(result));
//    console.log("Verbose " + verbose);

    var lastSuite, suite, k, k1, test, msg, m, browser,
        log = console.log;

    log("");
    browser = result.ua;
    if (result.failed) {
        log("Failed ".red +  result.name.blue + " on"  + browser);
    } else {
        log("Passed ".green + result.name.blue + " on"  + browser);
    }
    log((result.passed + " Passed").green + ", " + (result.failed + " Failed ").red + ", " + (result.ignored + " skipped ").grey);
    if (result.failed) {
        for (k in result) {
            suite = result[k];
            if ("object" === typeof suite) {
                if (suite.failed) {
                    for (k1 in suite) {
                        test = suite[k1];
                        if ("object" === typeof test) {
                            if ("fail" === test.result) {
                                if (!lastSuite || lastSuite !== suite.name) {
                                    log(" in " +  suite.name.bold);
                                    lastSuite = suite.name;
                                }
                                msg = test.message.split('\n');
                                log(" " +  test.name.bold.red + ":" +  msg[0]);
                                for (m = 1; m < msg.length; m = m + 1) {
                                    log(" " + msg[m]);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    log("");
};

ReportManager.prototype.writeReports = function () {

    var i, j, k, strJunitReport, strReport, rep, results, testJson, yuiUserAgent, yuiSuiteName, browser, descTestName, sessionId, yuiTestCaseName, timePerTest,
        className, testName, totalTimeTaken, errorCount, testSuiteName, arrReport, xmlReportName, jsonReportName, errorRep,
        self = this,
        wdSessions = self.wdSessions,
        testSessions = self.testSessions,
        passCount = 0,
        failCount = 0,
        skipCount = 0,
        timeCount = 0,
        totalCount = 0,
        jsonReport = {},
        log = console.log;
        resultPresent = false;
        missingErrorCount = 0;

    function writeFile(fileName, data) {

        if (self.reportFolder) {
            fileName = self.reportFolder  + fileName;
        }
        fs.writeFileSync(fileName, data, 'utf8');
        log(("Report Created: " + fileName).inverse);
    }

    function addProperty(name, val) {
        if (val) {
            strReport += "<property name='" + name + "' value='" + val + "' />";
        }
    }
    function addTest(time, className, name, failureMessage) {
        strReport += "<testcase time='" + (time / 1000).toFixed(2) + "' classname='" + className  + "' name='" + name + "'";
        if (failureMessage) {
            if (failureMessage === "skip") {
                strReport += " ' executed='false' />";
            } else {
                strReport += "><failure><![CDATA[" + failureMessage + "]]></failure>" + "</testcase>";
            }
        } else {
            strReport += "/>";
        }
    }

    function parseYUIResults(testJson, dtName) {
        var l, m;

        yuiUserAgent = testJson.ua;
        yuiSuiteName = testJson.name;
        browser = UserAgent.parse(yuiUserAgent).toAgent();
        if (browser === "Other 0.0.0") {
            browser = "NodeJS";
        }
        descTestName = dtName;
        sessionId = testJson.sessionId;
        for (l in testJson) {
            if (testJson[l]) {
                if (testJson[l].type === "testcase") {
                    yuiTestCaseName = testJson[l].name;
                    timePerTest = testJson[l].duration / testJson[l].total;
                    passCount += testJson[l].passed;
                    failCount += testJson[l].failed;
                    skipCount += testJson[l].ignored;
                    timeCount += testJson[l].duration;
                    totalCount += testJson[l].total;

                    for (m in testJson[l]) {
                        if (testJson[l][m].type === "test") {
                            className = browser + "." + descTestName;
                            testName = yuiSuiteName + "::" + yuiTestCaseName + "::" + testJson[l][m].name;
                            if (testJson[l][m].result === "fail") {
                                addTest(timePerTest, className, testName, testJson[l][m].message);
                            } else if (testJson[l][m].result === "pass") {
                                addTest(timePerTest, className, testName);
                            } else if (testJson[l][m].result === "ignore") {
                                addTest(timePerTest, className, testName,"skip");
                            }
                        }
                    }
                }
            }
        }
    }
    strReport = "<properties>";
    addProperty("descriptor", this.reportObj.descriptor);
    addProperty("parallel", this.reportObj.parallel);
    addProperty("driver", this.reportObj.driver);
    addProperty("browser", this.reportObj.browser);
    addProperty("reuseSession", this.reportObj.reuseSession);
    addProperty("activeSeleniumSessionCount", wdSessions.length);
    addProperty("seleniumSessionIds", wdSessions.join());
    addProperty("group", this.reportObj.group);
    addProperty("testName", this.reportObj.testName);
    addProperty("reportFolder", this.reportObj.reportFolder);
    strReport += "</properties>";

    for (i = 0; i < testSessions.length; i = i + 1) {
        rep = testSessions[i].driver.getReports();

        if (rep.scenario) {
            arrReport = rep.scenario;
        } else {
            arrReport = rep.results;
        }

        resultPresent = false;

        for (j = 0; j < arrReport.length; j = j + 1) {
            if (rep.scenario) {
                results = arrReport[j].results;
                for (k = 0; k < results.length; k = k + 1) {
                    testJson =  results[k];
                    if(testJson.type === "report") {
                        resultPresent = true;
                        parseYUIResults(testJson, testSessions[i].driver.testName);
                    }
                }

            } else {
                testJson = arrReport[j];
                if(testJson.type === "report") {
                    resultPresent = true;
                    parseYUIResults(testJson, testSessions[i].driver.testName);
                }
            }
        }
        if(!resultPresent) {
            missingErrorCount = missingErrorCount + 1;
           var errorRep = arrReport[arrReport.length-1];

           if(errorRep.results) {
               //console.log("Error :" + errorRep.results[0].error);
               addTest(0, "Error." +  testSessions[i].driver.testName,"Error" , errorRep.results[0].error);

           } else if (errorRep.error) {
              // console.log("Error : " +errorRep.error)
               addTest(0, "Error." +  testSessions[i].driver.testName,"Error" , errorRep.error);
           } else{
               console.log("Error : UNKOWN");
           }

         //   console.log("Test Session dint have any report");
        }
    }

    totalCount = totalCount + missingErrorCount;
    failCount = failCount + missingErrorCount;

    log();
    log("Test Report Summary".bold.magenta);
    log();
    log(("Total Number of Executed Tests: " + totalCount ).blue);
    log(("Total Number of Passed Tests: " + passCount ).green);
    log(("Total Number of Failed Tests: " + failCount ).red);
    log(("Total Number of Skipped Tests: " + skipCount).cyan);
    log();
    log(("Total Test Execution Time Inside Browser: " + (timeCount / 1000).toFixed(2) + " seconds").green.bold);
    totalTimeTaken = Date.now() - global.startTime;
    log(("Total Test Execution Time:" + (totalTimeTaken / 1000).toFixed(2) + " seconds").green.bold);
    log();

    errorCount = totalCount - passCount - failCount - skipCount;

    testSuiteName = "";
    if (this.reportObj.testSuiteName) {
        testSuiteName = this.reportObj.testSuiteName;
    } else {
        testSuiteName = "TestSuite";
    }
    //console.log(totalCount + ":" + failCount + ":" + missingErrorCount + ":" + skipCount);
    //writing xml file
    strJunitReport = "<testsuite failures='" + failCount + "' time='" + (totalTimeTaken / 1000).toFixed(2) + "' errors='" + errorCount + "' skipped='" + skipCount + "' tests='" + totalCount + "' name='" + testSuiteName + "'>";
    strJunitReport += strReport;
    strJunitReport += "</testsuite>";
    if (this.reportObj.descriptor) {

        xmlReportName = this.reportObj.descriptor.split(".")[0] + "-report.xml";
        jsonReportName = this.reportObj.descriptor.split(".")[0] + "-report.json";
    } else {
        xmlReportName = "report.xml";
        jsonReportName = "report.json";
    }
    writeFile(xmlReportName, strJunitReport);

    //writing json file
    for (i = 0; i < testSessions.length; i = i + 1) {
        jsonReport[testSessions[i].driver.testName] = JSON.parse(JSON.stringify(testSessions[i].driver.getReports()));
    }
    jsonReport.testSuiteName =  testSuiteName;
    writeFile(jsonReportName, JSON.stringify(jsonReport));
};

module.exports = ReportManager;
<|endoftext|>


<commit_before>import invariant from 'invariant';
import twitter from 'twitter-text';
import closest from 'dom-closest';
import removeNode from 'dom-remove';
import last from 'mention/utils/last';
import getKeyCode from 'mention/utils/getKeyCode';

import {
  getEditorContent,
  prevCharIsSpace
} from 'mention/utils/tinyMCEUtils';

import {
  moveDown,
  moveUp,
  query,
  remove,
  resetMentions,
  resetQuery,
  select
} from 'mention/actions/mentionActions';

const keyMap = {
  BACKSPACE: 8,
  DOWN: 40,
  ENTER: 13,
  TAB: 9,
  UP: 38
};

/**
 * Reference to the TinyMCE editor.
 * @type {Object}
 */
let editor = null;

/**
 * The delimiter we're using to trigger @mentions. Defaults to @.
 * @type {String}
 */
let delimiter = '@';

/**
 * Checks if we're currently focused on @mention lookup with bound event handlers.
 * @type {Boolean}
 */
let isFocused = false;

/**
 * The Redux store for handling lookups, mentions and tracking.
 * @type {Object}
 */
let store = null;

export function initializePlugin(reduxStore, dataSource, delimiterConfig = delimiter) {

  invariant(reduxStore,
    'Plugin must be initialized with a Redux store.'
  );

  invariant(dataSource,
    'Plugin must be initialized with a dataSource.  Datasource can be an array or promise.'
  );

  return new Promise((resolve, reject) => {

    if (typeof window.tinymce === 'undefined') {
      return reject('Error initializing Mention plugin: `tinymce` is undefined.');
    }

    window.tinymce.create('tinymce.plugins.Mention', {

      /**
       * Callback when the Editor has been registered and is ready
       * to accept plugin initialization.
       * @param  {Object} editor The editor
       */
      init(ed) {
        editor = ed;
        store = Object.freeze(reduxStore);
        delimiter = Object.freeze(delimiterConfig);

        // Check if we're using a promise the dataSource or a
        // raw array.  If promise, wait for it to resolve before
        // resolving the outer promise and initializing the app.
        if (typeof dataSource.then === 'function') {

          dataSource.then(response => {
            resolve({
              editor,
              resolvedDataSource: response.data
            });

            start();
          }).catch(error => {
            throw new Error(error);
          });

        } else {
          resolve({
            editor,
            resolvedDataSource: dataSource
          });
          start();
        }
      }
    });

    window.tinymce.PluginManager.add('mention', window.tinymce.plugins.Mention);
  });
}

function start() {
  editor.on('keypress', handleTopLevelEditorInput);
  editor.on('keyup', handleEditorBackspace);
}

/**
 * Add persistent top-level listener for delegating handlers for
 * binding and unbinding events related to UI interaction / querying.
 * @param  {jQuery.Event} event
 */
function handleTopLevelEditorInput(event) {
  const character = String.fromCharCode(getKeyCode(event));
  const delimiterIndex = delimiter.indexOf(character);

  // User has typed `@`; begin tracking
  if (delimiterIndex > -1 && prevCharIsSpace(editor)) {
    !isFocused && startListeningForInput();

  // User has exited mentions, stop tracking
  } else if (prevCharIsSpace(editor) || character === ' ') {
    isFocused && stopListeningAndCleanup();
  }
}

function startListeningForInput() {
  if (toggleFocus()) {
    editor.on('keydown', handleKeyPress);
  }
}

function stopListeningAndCleanup() {
  if (!toggleFocus()) {
    store.dispatch(resetQuery());
    editor.off('keydown', handleKeyPress);
  }
}

/**
 * Validates input and checks to see if any intermediate actions should
 * be performed.
 *
 * @param  {Number} keyCode The current key being pressed
 * @param  {jQuery.Event} event
 * @return {Function}
 */
function performIntermediateActions(keyCode, event) {
  Object.keys(keyMap).forEach(key => {
    const keyValue = keyMap[key];

    // Override default behavior if we're using anything from our keyMap.
    if (keyCode === keyValue && keyValue !== keyMap.BACKSPACE) {
      event.preventDefault();
    }
  });

  return shouldSelectOrMove(keyCode);
}

function shouldSelectOrMove(keyCode) {
  switch(keyCode) {
  case keyMap.TAB:
    return store.dispatch(select());
  case keyMap.ENTER:
    return store.dispatch(select());
  case keyMap.DOWN:
    return store.dispatch(moveDown());
  case keyMap.UP:
    return store.dispatch(moveUp());
  default:
    return false;
  }
}

function isNearMention(content) {
  const re = /@\w+\b(?! *.)/;
  return re.exec(content);
}

function removeMentionFromEditor(mentionNode) {
  const mention = mentionNode
    .innerText
    .replace(/(?:@|_)/g, ' ')
    .trim();

  removeNode(mentionNode);

  return mention;
}

/**
 * Handler for internal key-presses. Parses the input and dispatches
 * queries back to the store for list view and selection.
 */
function handleKeyPress(event) {
  const keyCode = getKeyCode(event);

  if (performIntermediateActions(keyCode, event)) {
    return false;
  };

  setTimeout(() => {
    const content = getEditorContent(editor);

    if (isNearMention(content)) {
      const mention = last(twitter.extractMentionsWithIndices(content));

      if (mention && isFocused) {
        store.dispatch(query(mention.screenName));
      }
    }
  }, 0);
}

/**
 * Handler for backspace presses. Dispatches back to store with request
 * to reset the current query and matches.
 */
function handleEditorBackspace(event) {
  const keyCode = getKeyCode(event);

  if (keyCode === keyMap.BACKSPACE) {
    const foundMentionNode = closest(editor.selection.getNode(), '.mention');

    if (foundMentionNode) {
      const mention = removeMentionFromEditor(foundMentionNode)
      store.dispatch(remove(mention));
    } else if (!getEditorContent(editor).trim().length) {
      store.dispatch(resetMentions());
    }
  }
}

function toggleFocus() {
  return isFocused = !isFocused;
}

// Export methods for testing
export default {
  _performIntermediateActions: performIntermediateActions,
  _isNearMention: isNearMention,
  _removeMentionFromEditor: removeMentionFromEditor,
  _handleKeyPress: handleKeyPress,
  _handleEditorBackspace: handleEditorBackspace
}
<commit_msg>Renamed var<commit_after>import invariant from 'invariant';
import twitter from 'twitter-text';
import closest from 'dom-closest';
import removeNode from 'dom-remove';
import last from 'mention/utils/last';
import getKeyCode from 'mention/utils/getKeyCode';

import {
  getEditorContent,
  prevCharIsSpace
} from 'mention/utils/tinyMCEUtils';

import {
  moveDown,
  moveUp,
  query,
  remove,
  resetMentions,
  resetQuery,
  select
} from 'mention/actions/mentionActions';

const keyMap = {
  BACKSPACE: 8,
  DOWN: 40,
  ENTER: 13,
  TAB: 9,
  UP: 38
};

/**
 * Reference to the TinyMCE editor.
 * @type {Object}
 */
let editor = null;

/**
 * The delimiter we're using to trigger @mentions. Defaults to @.
 * @type {String}
 */
let delimiter = '@';

/**
 * Checks if we're currently focused on @mention lookup with bound event handlers.
 * @type {Boolean}
 */
let isFocused = false;

/**
 * The Redux store for handling lookups, mentions and tracking.
 * @type {Object}
 */
let store = null;

export function initializePlugin(reduxStore, dataSource, delimiterConfig = delimiter) {

  invariant(reduxStore,
    'Plugin must be initialized with a Redux store.'
  );

  invariant(dataSource,
    'Plugin must be initialized with a dataSource.  Datasource can be an array or promise.'
  );

  return new Promise((resolve, reject) => {

    if (typeof window.tinymce === 'undefined') {
      return reject('Error initializing Mention plugin: `tinymce` is undefined.');
    }

    window.tinymce.create('tinymce.plugins.Mention', {

      /**
       * Callback when the Editor has been registered and is ready
       * to accept plugin initialization.
       * @param  {Object} editor The editor
       */
      init(activeEditor) {
        editor = activeEditor;
        store = Object.freeze(reduxStore);
        delimiter = Object.freeze(delimiterConfig);

        // Check if we're using a promise the dataSource or a
        // raw array.  If promise, wait for it to resolve before
        // resolving the outer promise and initializing the app.
        if (typeof dataSource.then === 'function') {

          dataSource.then(response => {
            resolve({
              editor,
              resolvedDataSource: response.data
            });

            start();
          }).catch(error => {
            throw new Error(error);
          });

        } else {
          resolve({
            editor,
            resolvedDataSource: dataSource
          });

          start();
        }
      }
    });

    window.tinymce.PluginManager.add('mention', window.tinymce.plugins.Mention);
  });
}

function start() {
  editor.on('keypress', handleTopLevelEditorInput);
  editor.on('keyup', handleEditorBackspace);
}

/**
 * Add persistent top-level listener for delegating handlers for
 * binding and unbinding events related to UI interaction / querying.
 * @param  {jQuery.Event} event
 */
function handleTopLevelEditorInput(event) {
  const character = String.fromCharCode(getKeyCode(event));
  const delimiterIndex = delimiter.indexOf(character);

  // User has typed `@`; begin tracking
  if (delimiterIndex > -1 && prevCharIsSpace(editor)) {
    !isFocused && startListeningForInput();

  // User has exited mentions, stop tracking
  } else if (prevCharIsSpace(editor) || character === ' ') {
    isFocused && stopListeningAndCleanup();
  }
}

function startListeningForInput() {
  if (toggleFocus()) {
    editor.on('keydown', handleKeyPress);
  }
}

function stopListeningAndCleanup() {
  if (!toggleFocus()) {
    store.dispatch(resetQuery());
    editor.off('keydown', handleKeyPress);
  }
}

/**
 * Validates input and checks to see if any intermediate actions should
 * be performed.
 *
 * @param  {Number} keyCode The current key being pressed
 * @param  {jQuery.Event} event
 * @return {Function}
 */
function performIntermediateActions(keyCode, event) {
  Object.keys(keyMap).forEach(key => {
    const keyValue = keyMap[key];

    // Override default behavior if we're using anything from our keyMap.
    if (keyCode === keyValue && keyValue !== keyMap.BACKSPACE) {
      event.preventDefault();
    }
  });

  return shouldSelectOrMove(keyCode);
}

function shouldSelectOrMove(keyCode) {
  switch(keyCode) {
  case keyMap.TAB:
    return store.dispatch(select());
  case keyMap.ENTER:
    return store.dispatch(select());
  case keyMap.DOWN:
    return store.dispatch(moveDown());
  case keyMap.UP:
    return store.dispatch(moveUp());
  default:
    return false;
  }
}

function isNearMention(content) {
  const re = /@\w+\b(?! *.)/;
  return re.exec(content);
}

function removeMentionFromEditor(mentionNode) {
  const mention = mentionNode
    .innerText
    .replace(/(?:@|_)/g, ' ')
    .trim();

  removeNode(mentionNode);

  return mention;
}

/**
 * Handler for internal key-presses. Parses the input and dispatches
 * queries back to the store for list view and selection.
 */
function handleKeyPress(event) {
  const keyCode = getKeyCode(event);

  if (performIntermediateActions(keyCode, event)) {
    return false;
  };

  setTimeout(() => {
    const content = getEditorContent(editor);

    if (isNearMention(content)) {
      const mention = last(twitter.extractMentionsWithIndices(content));

      if (mention && isFocused) {
        store.dispatch(query(mention.screenName));
      }
    }
  }, 0);
}

/**
 * Handler for backspace presses. Dispatches back to store with request
 * to reset the current query and matches.
 */
function handleEditorBackspace(event) {
  const keyCode = getKeyCode(event);

  if (keyCode === keyMap.BACKSPACE) {
    const foundMentionNode = closest(editor.selection.getNode(), '.mention');

    if (foundMentionNode) {
      const mention = removeMentionFromEditor(foundMentionNode)
      store.dispatch(remove(mention));
    } else if (!getEditorContent(editor).trim().length) {
      store.dispatch(resetMentions());
    }
  }
}

function toggleFocus() {
  return isFocused = !isFocused;
}

// Export methods for testing
export default {
  _performIntermediateActions: performIntermediateActions,
  _isNearMention: isNearMention,
  _removeMentionFromEditor: removeMentionFromEditor,
  _handleKeyPress: handleKeyPress,
  _handleEditorBackspace: handleEditorBackspace
}
<|endoftext|>


<commit_before><commit_msg>Disable discard/undo context menu items when appropriate<commit_after><|endoftext|>


<commit_before>'use strict';

var request = require('superagent');
var _ = require('lodash');
var agentQ = require('qagent');
var genDeferred = require('./gen-deferred');

function MethodFactory(url, method, endpoint) {
  this.method = method;
  this.endpoint = endpoint;
  this.headers = {};

  this._url = url || this.endpoint._url;

  return this;
}

MethodFactory.prototype.header = function(headerKey, headerValue) {
  this.headers[headerKey] = headerValue;

  return this;
};

MethodFactory.prototype.url = function(url) {
  this._url = url;

  return this;
};

MethodFactory.prototype.merge = function(defaults, overrides) {
  return _.extend({}, defaults, overrides);
};

MethodFactory.prototype.data = function(data) {
  this.endpoint.data = data;

  return this;
};

MethodFactory.prototype.send = function() {
  var d = genDeferred();
  var requestObject = this.createRequestObject();

  var handleResponse = function(data) {
    var responseBody;
    var response;
    var status;

    if(data.xhr) {
      /* Browser like response */
      response = data.xhr;
      responseBody = data.xhr.response;
      status = data.xhr.status;
    } else {
      /* Node like response */
      response = data.res;
      responseBody = data.res.body;
      status = data.res.statusCode;
    }

    if(status > 299) {
      d.reject(response);
      return;
    }

    if(data.xhr) {
      responseBody = JSON.parse(responseBody);
    }

    if(_.contains(['get', 'post', 'put', 'patch'], this.method)) {
      this.endpoint.data = responseBody;
    }

    d.resolve(this.endpoint);
  };

  var reject = function(error) {
    d.reject(error);
  };

  agentQ.end(requestObject)
  .then(_.bind(handleResponse, this), reject)
  .done();

  return d.promise;
};

MethodFactory.prototype.createRequestObject = function() {
  var requestObject = request[this.method](this._url);
  var headers = this.merge(this.endpoint.headers, this.headers);

  if(_.contains(['post', 'patch', 'put'], this.method)) {
    requestObject.send(this.endpoint.data);
  }

  _.each(headers, function(headerValue, headerName) {
    requestObject.set(headerName, headerValue);
  }, this);

  return requestObject;
};

module.exports = MethodFactory;
<commit_msg>Factor out response data massaging<commit_after>'use strict';

var request = require('superagent');
var _ = require('lodash');
var agentQ = require('qagent');
var genDeferred = require('./gen-deferred');

function MethodFactory(url, method, endpoint) {
  this.method = method;
  this.endpoint = endpoint;
  this.headers = {};

  this._url = url || this.endpoint._url;

  return this;
}

MethodFactory.prototype.header = function(headerKey, headerValue) {
  this.headers[headerKey] = headerValue;

  return this;
};

MethodFactory.prototype.url = function(url) {
  this._url = url;

  return this;
};

MethodFactory.prototype.merge = function(defaults, overrides) {
  return _.extend({}, defaults, overrides);
};

MethodFactory.prototype.data = function(data) {
  this.endpoint.data = data;

  return this;
};

MethodFactory.prototype.massageResponse = function(_response) {
  var response = {};

  if(_response.xhr) {
    /* Browser like response */
    response.all = _response.xhr;
    response.status = _response.xhr.status;

    if(response.status < 300) {
      response.body = JSON.parse(_response.xhr.response);
    }
  } else {
    /* Node like response */
    response.all = _response.res;
    response.body = _response.res.body;
    response.status = _response.res.statusCode;
  }

  return response;
};

MethodFactory.prototype.send = function() {
  var d = genDeferred();
  var requestObject = this.createRequestObject();

  var handleResponse = function(data) {
    var responseBody;
    var response = this.massageResponse(data);
    var status;

    if(response.status > 299) {
      d.reject(response.all);
      return;
    }

    if(_.contains(['get', 'post', 'put', 'patch'], this.method)) {
      this.endpoint.data = response.body;
    }

    d.resolve(this.endpoint);
  };

  var reject = function(error) {
    d.reject(error);
  };

  agentQ.end(requestObject)
  .then(_.bind(handleResponse, this), reject)
  .done();

  return d.promise;
};

MethodFactory.prototype.createRequestObject = function() {
  var requestObject = request[this.method](this._url);
  var headers = this.merge(this.endpoint.headers, this.headers);

  if(_.contains(['post', 'patch', 'put'], this.method)) {
    requestObject.send(this.endpoint.data);
  }

  _.each(headers, function(headerValue, headerName) {
    requestObject.set(headerName, headerValue);
  }, this);

  return requestObject;
};

module.exports = MethodFactory;
<|endoftext|>