<commit_before>/*
Copyright 2017 Vector Creations Ltd

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.
*/

'use strict';

import url from 'url';
import qs from 'querystring';
import React from 'react';
import MatrixClientPeg from '../../../MatrixClientPeg';
import PlatformPeg from '../../../PlatformPeg';
import ScalarAuthClient from '../../../ScalarAuthClient';
import SdkConfig from '../../../SdkConfig';
import Modal from '../../../Modal';
import { _t, _td } from '../../../languageHandler';
import sdk from '../../../index';
import AppPermission from './AppPermission';
import AppWarning from './AppWarning';
import MessageSpinner from './MessageSpinner';
import WidgetUtils from '../../../WidgetUtils';
import dis from '../../../dispatcher';

const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];

export default React.createClass({
    displayName: 'AppTile',

    propTypes: {
        id: React.PropTypes.string.isRequired,
        url: React.PropTypes.string.isRequired,
        name: React.PropTypes.string.isRequired,
        room: React.PropTypes.object.isRequired,
        type: React.PropTypes.string.isRequired,
        // Specifying 'fullWidth' as true will render the app tile to fill the width of the app drawer continer.
        // This should be set to true when there is only one widget in the app drawer, otherwise it should be false.
        fullWidth: React.PropTypes.bool,
        // UserId of the current user
        userId: React.PropTypes.string.isRequired,
        // UserId of the entity that added / modified the widget
        creatorUserId: React.PropTypes.string,
    },

    getDefaultProps() {
        return {
            url: "",
        };
    },

    /**
     * Set initial component state when the App wUrl (widget URL) is being updated.
     * Component props *must* be passed (rather than relying on this.props).
     * @param  {Object} newProps The new properties of the component
     * @return {Object} Updated component state to be set with setState
     */
    _getNewState(newProps) {
        const widgetPermissionId = [newProps.room.roomId, encodeURIComponent(newProps.url)].join('_');
        const hasPermissionToLoad = localStorage.getItem(widgetPermissionId);
        return {
            initialising: true,   // True while we are mangling the widget URL
            loading: true,        // True while the iframe content is loading
            widgetUrl: newProps.url,
            widgetPermissionId: widgetPermissionId,
            // Assume that widget has permission to load if we are the user who
            // added it to the room, or if explicitly granted by the user
            hasPermissionToLoad: hasPermissionToLoad === 'true' || newProps.userId === newProps.creatorUserId,
            error: null,
            deleting: false,
        };
    },

    getInitialState() {
        return this._getNewState(this.props);
    },

    /**
     * Returns true if specified url is a scalar URL, typically https://scalar.vector.im/api
     * @param  {[type]}  url URL to check
     * @return {Boolean} True if specified URL is a scalar URL
     */
    isScalarUrl(url) {
        if (!url) {
            console.error('Scalar URL check failed. No URL specified');
            return false;
        }

        let scalarUrls = SdkConfig.get().integrations_widgets_urls;
        if (!scalarUrls || scalarUrls.length == 0) {
            scalarUrls = [SdkConfig.get().integrations_rest_url];
        }

        for (let i = 0; i < scalarUrls.length; i++) {
            if (url.startsWith(scalarUrls[i])) {
                return true;
            }
        }
        return false;
    },

    isMixedContent() {
        const parentContentProtocol = window.location.protocol;
        const u = url.parse(this.props.url);
        const childContentProtocol = u.protocol;
        if (parentContentProtocol === 'https:' && childContentProtocol !== 'https:') {
            console.warn("Refusing to load mixed-content app:",
            parentContentProtocol, childContentProtocol, window.location, this.props.url);
            return true;
        }
        return false;
    },

    componentWillMount() {
        window.addEventListener('message', this._onMessage, false);
        this.setScalarToken();
    },

    /**
     * Adds a scalar token to the widget URL, if required
     * Component initialisation is only complete when this function has resolved
     */
    setScalarToken() {
        this.setState({initialising: true});

        if (!this.isScalarUrl(this.props.url)) {
            console.warn('Non-scalar widget, not setting scalar token!', url);
            this.setState({
                error: null,
                widgetUrl: this.props.url,
                initialising: false,
            });
            return;
        }

        // Fetch the token before loading the iframe as we need it to mangle the URL
        if (!this._scalarClient) {
            this._scalarClient = new ScalarAuthClient();
        }
        this._scalarClient.getScalarToken().done((token) => {
            // Append scalar_token as a query param if not already present
            this._scalarClient.scalarToken = token;
            const u = url.parse(this.props.url);
            const params = qs.parse(u.query);
            if (!params.scalar_token) {
                params.scalar_token = encodeURIComponent(token);
                // u.search must be set to undefined, so that u.format() uses query paramerters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
                u.search = undefined;
                u.query = params;
            }

            this.setState({
                error: null,
                widgetUrl: u.format(),
                initialising: false,
            });
        }, (err) => {
            console.error("Failed to get scalar_token", err);
            this.setState({
                error: err.message,
                initialising: false,
            });
        });
    },

    componentWillUnmount() {
        window.removeEventListener('message', this._onMessage);
    },

    componentWillReceiveProps(nextProps) {
        if (nextProps.url !== this.props.url) {
            this._getNewState(nextProps);
            this.setScalarToken();
        } else if (nextProps.show && !this.props.show) {
            this.setState({
                loading: true,
            });
        }
    },

    _onMessage(event) {
        if (this.props.type !== 'jitsi') {
            return;
        }
        if (!event.origin) {
            event.origin = event.originalEvent.origin;
        }

        if (!this.state.widgetUrl.startsWith(event.origin)) {
            return;
        }

        if (event.data.widgetAction === 'jitsi_iframe_loaded') {
            const iframe = this.refs.appFrame.contentWindow
                .document.querySelector('iframe[id^="jitsiConferenceFrame"]');
            PlatformPeg.get().setupScreenSharingForIframe(iframe);
        }
    },

    _canUserModify() {
        return WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
    },

    _onEditClick(e) {
        console.log("Edit widget ID ", this.props.id);
        const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
        const src = this._scalarClient.getScalarInterfaceUrlForRoom(
            this.props.room.roomId, 'type_' + this.props.type, this.props.id);
        Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, {
            src: src,
        }, "mx_IntegrationsManager");
    },

    /* If user has permission to modify widgets, delete the widget,
     * otherwise revoke access for the widget to load in the user's browser
    */
    _onDeleteClick() {
        if (this._canUserModify()) {
            // Show delete confirmation dialog
            const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
            Modal.createTrackedDialog('Delete Widget', '', QuestionDialog, {
                title: _t("Delete Widget"),
                description: _t(
                    "Deleting a widget removes it for all users in this room." +
                    " Are you sure you want to delete this widget?"),
                button: _t("Delete widget"),
                onFinished: (confirmed) => {
                    if (!confirmed) {
                        return;
                    }
                    this.setState({deleting: true});
                    MatrixClientPeg.get().sendStateEvent(
                        this.props.room.roomId,
                        'im.vector.modular.widgets',
                        {}, // empty content
                        this.props.id,
                    ).catch((e) => {
                        console.error('Failed to delete widget', e);
                        this.setState({deleting: false});
                    });
                },
            });
        } else {
            console.log("Revoke widget permissions - %s", this.props.id);
            this._revokeWidgetPermission();
        }
    },

    _onLoaded() {
        this.setState({loading: false});
    },

    // Widget labels to render, depending upon user permissions
    // These strings are translated at the point that they are inserted in to the DOM, in the render method
    _deleteWidgetLabel() {
        if (this._canUserModify()) {
            return _td('Delete widget');
        }
        return _td('Revoke widget access');
    },

    /* TODO -- Store permission in account data so that it is persisted across multiple devices */
    _grantWidgetPermission() {
        console.warn('Granting permission to load widget - ', this.state.widgetUrl);
        localStorage.setItem(this.state.widgetPermissionId, true);
        this.setState({hasPermissionToLoad: true});
    },

    _revokeWidgetPermission() {
        console.warn('Revoking permission to load widget - ', this.state.widgetUrl);
        localStorage.removeItem(this.state.widgetPermissionId);
        this.setState({hasPermissionToLoad: false});
    },

    formatAppTileName() {
        let appTileName = "No name";
        if(this.props.name && this.props.name.trim()) {
            appTileName = this.props.name.trim();
        }
        return appTileName;
    },

    onClickMenuBar(ev) {
        ev.preventDefault();

        // Ignore clicks on menu bar children
        if (ev.target !== this.refs.menu_bar) {
            return;
        }

        // Toggle the view state of the apps drawer
        dis.dispatch({
            action: 'appsDrawer',
            show: !this.props.show,
        });
    },

    render() {
        let appTileBody;

        // Don't render widget if it is in the process of being deleted
        if (this.state.deleting) {
            return

;
        }

        // Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
        // because that would allow the iframe to prgramatically remove the sandbox attribute, but
        // this would only be for content hosted on the same origin as the riot client: anything
        // hosted on the same origin as the client will get the same access as if you clicked
        // a link to it.
        const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+
            "allow-same-origin allow-scripts allow-presentation";
        const parsedWidgetUrl = url.parse(this.state.widgetUrl);
        let safeWidgetUrl = '';
        if (ALLOWED_APP_URL_SCHEMES.indexOf(parsedWidgetUrl.protocol) !== -1) {
            safeWidgetUrl = url.format(parsedWidgetUrl);
        }

        if (this.props.show) {
            const loadingElement = (

                    <MessageSpinner msg='Loading...' />

            );
            if (this.state.initialising) {
                appTileBody = loadingElement;
            } else if (this.state.hasPermissionToLoad == true) {
                if (this.isMixedContent()) {
                    appTileBody = (

                            <AppWarning errorMsg="Error - Mixed content" />

                    );
                } else {
                    appTileBody = (

                            { this.state.loading && loadingElement }

                    );
                }
            } else {
                const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
                appTileBody = (

                        <AppPermission
                            isRoomEncrypted={isRoomEncrypted}
                            url={this.state.widgetUrl}
                            onPermissionGranted={this._grantWidgetPermission}
                        />

                );
            }
        }

        // editing is done in scalar
        const showEditButton = Boolean(this._scalarClient && this._canUserModify());
        const deleteWidgetLabel = this._deleteWidgetLabel();
        let deleteIcon = 'img/cancel.svg';
        let deleteClasses = 'mx_filterFlipColor mx_AppTileMenuBarWidget';
        if(this._canUserModify()) {
            deleteIcon = 'img/icon-delete-pink.svg';
            deleteClasses += ' mx_AppTileMenuBarWidgetDelete';
        }

        return (

                    { this.formatAppTileName() }

                        { /* Edit widget */ }
                        { showEditButton &&  }

                        { /* Delete widget */ }

                { appTileBody }

        );
    },
});
<commit_msg>Use TintableSvgButtons for widget menubar icons.<commit_after>/*
Copyright 2017 Vector Creations Ltd

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.
*/

'use strict';

import url from 'url';
import qs from 'querystring';
import React from 'react';
import MatrixClientPeg from '../../../MatrixClientPeg';
import PlatformPeg from '../../../PlatformPeg';
import ScalarAuthClient from '../../../ScalarAuthClient';
import TintableSvgButton from './TintableSvgButton';
import SdkConfig from '../../../SdkConfig';
import Modal from '../../../Modal';
import { _t, _td } from '../../../languageHandler';
import sdk from '../../../index';
import AppPermission from './AppPermission';
import AppWarning from './AppWarning';
import MessageSpinner from './MessageSpinner';
import WidgetUtils from '../../../WidgetUtils';
import dis from '../../../dispatcher';

const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];

export default React.createClass({
    displayName: 'AppTile',

    propTypes: {
        id: React.PropTypes.string.isRequired,
        url: React.PropTypes.string.isRequired,
        name: React.PropTypes.string.isRequired,
        room: React.PropTypes.object.isRequired,
        type: React.PropTypes.string.isRequired,
        // Specifying 'fullWidth' as true will render the app tile to fill the width of the app drawer continer.
        // This should be set to true when there is only one widget in the app drawer, otherwise it should be false.
        fullWidth: React.PropTypes.bool,
        // UserId of the current user
        userId: React.PropTypes.string.isRequired,
        // UserId of the entity that added / modified the widget
        creatorUserId: React.PropTypes.string,
    },

    getDefaultProps() {
        return {
            url: "",
        };
    },

    /**
     * Set initial component state when the App wUrl (widget URL) is being updated.
     * Component props *must* be passed (rather than relying on this.props).
     * @param  {Object} newProps The new properties of the component
     * @return {Object} Updated component state to be set with setState
     */
    _getNewState(newProps) {
        const widgetPermissionId = [newProps.room.roomId, encodeURIComponent(newProps.url)].join('_');
        const hasPermissionToLoad = localStorage.getItem(widgetPermissionId);
        return {
            initialising: true,   // True while we are mangling the widget URL
            loading: true,        // True while the iframe content is loading
            widgetUrl: newProps.url,
            widgetPermissionId: widgetPermissionId,
            // Assume that widget has permission to load if we are the user who
            // added it to the room, or if explicitly granted by the user
            hasPermissionToLoad: hasPermissionToLoad === 'true' || newProps.userId === newProps.creatorUserId,
            error: null,
            deleting: false,
        };
    },

    getInitialState() {
        return this._getNewState(this.props);
    },

    /**
     * Returns true if specified url is a scalar URL, typically https://scalar.vector.im/api
     * @param  {[type]}  url URL to check
     * @return {Boolean} True if specified URL is a scalar URL
     */
    isScalarUrl(url) {
        if (!url) {
            console.error('Scalar URL check failed. No URL specified');
            return false;
        }

        let scalarUrls = SdkConfig.get().integrations_widgets_urls;
        if (!scalarUrls || scalarUrls.length == 0) {
            scalarUrls = [SdkConfig.get().integrations_rest_url];
        }

        for (let i = 0; i < scalarUrls.length; i++) {
            if (url.startsWith(scalarUrls[i])) {
                return true;
            }
        }
        return false;
    },

    isMixedContent() {
        const parentContentProtocol = window.location.protocol;
        const u = url.parse(this.props.url);
        const childContentProtocol = u.protocol;
        if (parentContentProtocol === 'https:' && childContentProtocol !== 'https:') {
            console.warn("Refusing to load mixed-content app:",
            parentContentProtocol, childContentProtocol, window.location, this.props.url);
            return true;
        }
        return false;
    },

    componentWillMount() {
        window.addEventListener('message', this._onMessage, false);
        this.setScalarToken();
    },

    /**
     * Adds a scalar token to the widget URL, if required
     * Component initialisation is only complete when this function has resolved
     */
    setScalarToken() {
        this.setState({initialising: true});

        if (!this.isScalarUrl(this.props.url)) {
            console.warn('Non-scalar widget, not setting scalar token!', url);
            this.setState({
                error: null,
                widgetUrl: this.props.url,
                initialising: false,
            });
            return;
        }

        // Fetch the token before loading the iframe as we need it to mangle the URL
        if (!this._scalarClient) {
            this._scalarClient = new ScalarAuthClient();
        }
        this._scalarClient.getScalarToken().done((token) => {
            // Append scalar_token as a query param if not already present
            this._scalarClient.scalarToken = token;
            const u = url.parse(this.props.url);
            const params = qs.parse(u.query);
            if (!params.scalar_token) {
                params.scalar_token = encodeURIComponent(token);
                // u.search must be set to undefined, so that u.format() uses query paramerters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
                u.search = undefined;
                u.query = params;
            }

            this.setState({
                error: null,
                widgetUrl: u.format(),
                initialising: false,
            });
        }, (err) => {
            console.error("Failed to get scalar_token", err);
            this.setState({
                error: err.message,
                initialising: false,
            });
        });
    },

    componentWillUnmount() {
        window.removeEventListener('message', this._onMessage);
    },

    componentWillReceiveProps(nextProps) {
        if (nextProps.url !== this.props.url) {
            this._getNewState(nextProps);
            this.setScalarToken();
        } else if (nextProps.show && !this.props.show) {
            this.setState({
                loading: true,
            });
        }
    },

    _onMessage(event) {
        if (this.props.type !== 'jitsi') {
            return;
        }
        if (!event.origin) {
            event.origin = event.originalEvent.origin;
        }

        if (!this.state.widgetUrl.startsWith(event.origin)) {
            return;
        }

        if (event.data.widgetAction === 'jitsi_iframe_loaded') {
            const iframe = this.refs.appFrame.contentWindow
                .document.querySelector('iframe[id^="jitsiConferenceFrame"]');
            PlatformPeg.get().setupScreenSharingForIframe(iframe);
        }
    },

    _canUserModify() {
        return WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
    },

    _onEditClick(e) {
        console.log("Edit widget ID ", this.props.id);
        const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
        const src = this._scalarClient.getScalarInterfaceUrlForRoom(
            this.props.room.roomId, 'type_' + this.props.type, this.props.id);
        Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, {
            src: src,
        }, "mx_IntegrationsManager");
    },

    /* If user has permission to modify widgets, delete the widget,
     * otherwise revoke access for the widget to load in the user's browser
    */
    _onDeleteClick() {
        if (this._canUserModify()) {
            // Show delete confirmation dialog
            const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
            Modal.createTrackedDialog('Delete Widget', '', QuestionDialog, {
                title: _t("Delete Widget"),
                description: _t(
                    "Deleting a widget removes it for all users in this room." +
                    " Are you sure you want to delete this widget?"),
                button: _t("Delete widget"),
                onFinished: (confirmed) => {
                    if (!confirmed) {
                        return;
                    }
                    this.setState({deleting: true});
                    MatrixClientPeg.get().sendStateEvent(
                        this.props.room.roomId,
                        'im.vector.modular.widgets',
                        {}, // empty content
                        this.props.id,
                    ).catch((e) => {
                        console.error('Failed to delete widget', e);
                        this.setState({deleting: false});
                    });
                },
            });
        } else {
            console.log("Revoke widget permissions - %s", this.props.id);
            this._revokeWidgetPermission();
        }
    },

    _onLoaded() {
        this.setState({loading: false});
    },

    // Widget labels to render, depending upon user permissions
    // These strings are translated at the point that they are inserted in to the DOM, in the render method
    _deleteWidgetLabel() {
        if (this._canUserModify()) {
            return _td('Delete widget');
        }
        return _td('Revoke widget access');
    },

    /* TODO -- Store permission in account data so that it is persisted across multiple devices */
    _grantWidgetPermission() {
        console.warn('Granting permission to load widget - ', this.state.widgetUrl);
        localStorage.setItem(this.state.widgetPermissionId, true);
        this.setState({hasPermissionToLoad: true});
    },

    _revokeWidgetPermission() {
        console.warn('Revoking permission to load widget - ', this.state.widgetUrl);
        localStorage.removeItem(this.state.widgetPermissionId);
        this.setState({hasPermissionToLoad: false});
    },

    formatAppTileName() {
        let appTileName = "No name";
        if(this.props.name && this.props.name.trim()) {
            appTileName = this.props.name.trim();
        }
        return appTileName;
    },

    onClickMenuBar(ev) {
        ev.preventDefault();

        // Ignore clicks on menu bar children
        if (ev.target !== this.refs.menu_bar) {
            return;
        }

        // Toggle the view state of the apps drawer
        dis.dispatch({
            action: 'appsDrawer',
            show: !this.props.show,
        });
    },

    render() {
        let appTileBody;

        // Don't render widget if it is in the process of being deleted
        if (this.state.deleting) {
            return

;
        }

        // Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
        // because that would allow the iframe to prgramatically remove the sandbox attribute, but
        // this would only be for content hosted on the same origin as the riot client: anything
        // hosted on the same origin as the client will get the same access as if you clicked
        // a link to it.
        const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+
            "allow-same-origin allow-scripts allow-presentation";
        const parsedWidgetUrl = url.parse(this.state.widgetUrl);
        let safeWidgetUrl = '';
        if (ALLOWED_APP_URL_SCHEMES.indexOf(parsedWidgetUrl.protocol) !== -1) {
            safeWidgetUrl = url.format(parsedWidgetUrl);
        }

        if (this.props.show) {
            const loadingElement = (

                    <MessageSpinner msg='Loading...' />

            );
            if (this.state.initialising) {
                appTileBody = loadingElement;
            } else if (this.state.hasPermissionToLoad == true) {
                if (this.isMixedContent()) {
                    appTileBody = (

                            <AppWarning errorMsg="Error - Mixed content" />

                    );
                } else {
                    appTileBody = (

                            { this.state.loading && loadingElement }

                    );
                }
            } else {
                const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
                appTileBody = (

                        <AppPermission
                            isRoomEncrypted={isRoomEncrypted}
                            url={this.state.widgetUrl}
                            onPermissionGranted={this._grantWidgetPermission}
                        />

                );
            }
        }

        // editing is done in scalar
        const showEditButton = Boolean(this._scalarClient && this._canUserModify());
        const deleteWidgetLabel = this._deleteWidgetLabel();
        let deleteIcon = 'img/cancel_green.svg';
        let deleteClasses = 'mx_AppTileMenuBarWidget';
        if(this._canUserModify()) {
            deleteIcon = 'img/icon-delete-pink.svg';
            deleteClasses += ' mx_AppTileMenuBarWidgetDelete';
        }

        return (

                    { this.formatAppTileName() }

                        { /* Edit widget */ }
                        { showEditButton && <TintableSvgButton
                            src="img/edit_green.svg"
                            className="mx_AppTileMenuBarWidget mx_AppTileMenuBarWidgetPadding"
                            title={_t('Edit')}
                            onClick={this._onEditClick}
                        /> }

                        { /* Delete widget */ }
                        <TintableSvgButton
                            src={deleteIcon}
                            className={deleteClasses}
                            title={_t(deleteWidgetLabel)}
                            onClick={this._onDeleteClick}
                        />

                { appTileBody }

        );
    },
});
<|endoftext|>


<commit_before>this.cloudopacity = 0.0;
this.dataopacity = 1;

var createEarth	= function(){
	var geometry	= new THREE.SphereGeometry(0.50, 32, 32)
	var material	= new THREE.MeshPhongMaterial({
		map		: THREE.ImageUtils.loadTexture('img/earthmap4k.jpg'),
		bumpMap		: THREE.ImageUtils.loadTexture('img/earthbump1k.jpg'),
		bumpScale	: 0.05,
	})
	var mesh	= new THREE.Mesh(geometry, material)
	return mesh;
}

var createEarthCloud	= function(){
	// create destination canvas
	var canvasResult	= document.createElement('canvas')
	canvasResult.width	= 1024
	canvasResult.height	= 512
	var contextResult	= canvasResult.getContext('2d')

	// load earthcloudmap
	var imageMap	= new Image();
	imageMap.addEventListener("load", function() {

		// create dataMap ImageData for earthcloudmap1
		var canvasMap	= document.createElement('canvas')
		canvasMap.width	= imageMap.width
		canvasMap.height= imageMap.height
		var contextMap	= canvasMap.getContext('2d')
		contextMap.drawImage(imageMap, 0, 0)
		var dataMap	= contextMap.getImageData(0, 0, canvasMap.width, canvasMap.height)

		// load earthcloudmaptrans
		var imageTrans	= new Image();
		imageTrans.addEventListener("load", function(){
			// create dataTrans ImageData for earthcloudmaptrans
			var canvasTrans		= document.createElement('canvas')
			canvasTrans.width	= imageTrans.width
			canvasTrans.height	= imageTrans.height
			var contextTrans	= canvasTrans.getContext('2d')
			contextTrans.drawImage(imageTrans, 0, 0)
			var dataTrans		= contextTrans.getImageData(0, 0, canvasTrans.width, canvasTrans.height)
			// merge dataMap + dataTrans into dataResult
			var dataResult		= contextMap.createImageData(canvasMap.width, canvasMap.height)
			for(var y = 0, offset = 0; y < imageMap.height; y++){
				for(var x = 0; x < imageMap.width; x++, offset += 4){
					dataResult.data[offset+0]	= dataMap.data[offset+0]
					dataResult.data[offset+1]	= dataMap.data[offset+1]
					dataResult.data[offset+2]	= dataMap.data[offset+2]
					dataResult.data[offset+3]	= 255 - dataTrans.data[offset+0]
				}
			}
			// update texture with result
			contextResult.putImageData(dataResult,0,0)
			material.map.needsUpdate = true;
		})
		imageTrans.src	= 'img/earthcloudmaptrans.jpg';
	}, false);
	imageMap.src	= 'img/earthcloudmap.jpg';

	var geometry	= new THREE.SphereGeometry(0.51, 32, 32)
	var material	= new THREE.MeshPhongMaterial({
		map		: new THREE.Texture(canvasResult),
		side		: THREE.DoubleSide,
		transparent	: true,
		opacity		: this.cloudopacity,
	})
	var mesh	= new THREE.Mesh(geometry, material)
	return mesh;
}

var createStarfield	= function(){
	var texture	= THREE.ImageUtils.loadTexture('img/starfield.png')
	var material	= new THREE.MeshBasicMaterial({
		map	: texture,
		side	: THREE.BackSide
	})
	var geometry	= new THREE.SphereGeometry(90, 32, 32)
	var mesh	= new THREE.Mesh(geometry, material)
	return mesh
}

var createAtmosphereMaterial	= function(){
	var vertexShader	= [
		'varying vec3	vVertexWorldPosition;',
		'varying vec3	vVertexNormal;',

		'void main(){',
		'	vVertexNormal	= normalize(normalMatrix * normal);',

		'	vVertexWorldPosition	= (modelMatrix * vec4(position, 1.0)).xyz;',

		'	// set gl_Position',
		'	gl_Position	= projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
		'}',

		].join('\n')
	var fragmentShader	= [
		'uniform vec3	glowColor;',
		'uniform float	coeficient;',
		'uniform float	power;',

		'varying vec3	vVertexNormal;',
		'varying vec3	vVertexWorldPosition;',

		'void main(){',
		'	vec3 worldCameraToVertex= vVertexWorldPosition - cameraPosition;',
		'	vec3 viewCameraToVertex	= (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;',
		'	viewCameraToVertex	= normalize(viewCameraToVertex);',
		'	float intensity		= pow(coeficient + dot(vVertexNormal, viewCameraToVertex), power);',
		'	gl_FragColor		= vec4(glowColor, intensity);',
		'}',
	].join('\n')

	// create custom material from the shader code above
	//   that is within specially labeled script tags
	var material	= new THREE.ShaderMaterial({
		uniforms: {
			coeficient	: {
				type	: "f",
				value	: 1.0
			},
			power		: {
				type	: "f",
				value	: 2
			},
			glowColor	: {
				type	: "c",
				value	: new THREE.Color('pink')
			},
		},
		vertexShader	: vertexShader,
		fragmentShader	: fragmentShader,
		//blending	: THREE.AdditiveBlending,
		transparent	: true,
		depthWrite	: false,
	});
	return material
}<commit_msg>Downgrade Texture<commit_after>this.cloudopacity = 0.0;
this.dataopacity = 1;

var createEarth	= function(){
	var geometry	= new THREE.SphereGeometry(0.50, 32, 32)
	var material	= new THREE.MeshPhongMaterial({
		map		: THREE.ImageUtils.loadTexture('img/earthmap1k.jpg'),
		bumpMap		: THREE.ImageUtils.loadTexture('img/earthbump1k.jpg'),
		bumpScale	: 0.05,
	})
	var mesh	= new THREE.Mesh(geometry, material)
	return mesh;
}

var createEarthCloud	= function(){
	// create destination canvas
	var canvasResult	= document.createElement('canvas')
	canvasResult.width	= 1024
	canvasResult.height	= 512
	var contextResult	= canvasResult.getContext('2d')

	// load earthcloudmap
	var imageMap	= new Image();
	imageMap.addEventListener("load", function() {

		// create dataMap ImageData for earthcloudmap1
		var canvasMap	= document.createElement('canvas')
		canvasMap.width	= imageMap.width
		canvasMap.height= imageMap.height
		var contextMap	= canvasMap.getContext('2d')
		contextMap.drawImage(imageMap, 0, 0)
		var dataMap	= contextMap.getImageData(0, 0, canvasMap.width, canvasMap.height)

		// load earthcloudmaptrans
		var imageTrans	= new Image();
		imageTrans.addEventListener("load", function(){
			// create dataTrans ImageData for earthcloudmaptrans
			var canvasTrans		= document.createElement('canvas')
			canvasTrans.width	= imageTrans.width
			canvasTrans.height	= imageTrans.height
			var contextTrans	= canvasTrans.getContext('2d')
			contextTrans.drawImage(imageTrans, 0, 0)
			var dataTrans		= contextTrans.getImageData(0, 0, canvasTrans.width, canvasTrans.height)
			// merge dataMap + dataTrans into dataResult
			var dataResult		= contextMap.createImageData(canvasMap.width, canvasMap.height)
			for(var y = 0, offset = 0; y < imageMap.height; y++){
				for(var x = 0; x < imageMap.width; x++, offset += 4){
					dataResult.data[offset+0]	= dataMap.data[offset+0]
					dataResult.data[offset+1]	= dataMap.data[offset+1]
					dataResult.data[offset+2]	= dataMap.data[offset+2]
					dataResult.data[offset+3]	= 255 - dataTrans.data[offset+0]
				}
			}
			// update texture with result
			contextResult.putImageData(dataResult,0,0)
			material.map.needsUpdate = true;
		})
		imageTrans.src	= 'img/earthcloudmaptrans.jpg';
	}, false);
	imageMap.src	= 'img/earthcloudmap.jpg';

	var geometry	= new THREE.SphereGeometry(0.51, 32, 32)
	var material	= new THREE.MeshPhongMaterial({
		map		: new THREE.Texture(canvasResult),
		side		: THREE.DoubleSide,
		transparent	: true,
		opacity		: this.cloudopacity,
	})
	var mesh	= new THREE.Mesh(geometry, material)
	return mesh;
}

var createStarfield	= function(){
	var texture	= THREE.ImageUtils.loadTexture('img/starfield.png')
	var material	= new THREE.MeshBasicMaterial({
		map	: texture,
		side	: THREE.BackSide
	})
	var geometry	= new THREE.SphereGeometry(90, 32, 32)
	var mesh	= new THREE.Mesh(geometry, material)
	return mesh
}

var createAtmosphereMaterial	= function(){
	var vertexShader	= [
		'varying vec3	vVertexWorldPosition;',
		'varying vec3	vVertexNormal;',

		'void main(){',
		'	vVertexNormal	= normalize(normalMatrix * normal);',

		'	vVertexWorldPosition	= (modelMatrix * vec4(position, 1.0)).xyz;',

		'	// set gl_Position',
		'	gl_Position	= projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
		'}',

		].join('\n')
	var fragmentShader	= [
		'uniform vec3	glowColor;',
		'uniform float	coeficient;',
		'uniform float	power;',

		'varying vec3	vVertexNormal;',
		'varying vec3	vVertexWorldPosition;',

		'void main(){',
		'	vec3 worldCameraToVertex= vVertexWorldPosition - cameraPosition;',
		'	vec3 viewCameraToVertex	= (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;',
		'	viewCameraToVertex	= normalize(viewCameraToVertex);',
		'	float intensity		= pow(coeficient + dot(vVertexNormal, viewCameraToVertex), power);',
		'	gl_FragColor		= vec4(glowColor, intensity);',
		'}',
	].join('\n')

	// create custom material from the shader code above
	//   that is within specially labeled script tags
	var material	= new THREE.ShaderMaterial({
		uniforms: {
			coeficient	: {
				type	: "f",
				value	: 1.0
			},
			power		: {
				type	: "f",
				value	: 2
			},
			glowColor	: {
				type	: "c",
				value	: new THREE.Color('pink')
			},
		},
		vertexShader	: vertexShader,
		fragmentShader	: fragmentShader,
		//blending	: THREE.AdditiveBlending,
		transparent	: true,
		depthWrite	: false,
	});
	return material
}<|endoftext|>


<commit_before>var dnode  = require('dnode'),
    fs     = require('fs'),
    log4js = require('log4js'),
    util   = require('util'),
    path   = require('path'),
    _      = require('underscore'),
    config = require('./config.js'),
    Batch  = require('./batch.js');
var logger  = log4js.getLogger('appserver');

function Client(parent, pool, sock) {
  this.parent = parent;
  this.pool   = pool;
  this.socket = sock;
  this.pending_search = null;
  this.last_search = null;
  this.active_search = null;
  this.remote_address = sock.handshake.address;
  this.fast_streak = 0;
  this.last_slow   = null;
}

Client.prototype.debug = function() {
  logger.debug("[%s:%d] %s",
               this.remote_address.address,
               this.remote_address.port,
               util.format.apply(null, arguments));
}

Client.prototype.new_search = function (line, file, id) {
  this.debug('new search: (%s) (%j)', id, {line:line, file:file});
  if (this.last_search &&
      line === this.last_search.line &&
      file === this.last_search.file)
    return;
  this.pending_search = {
    line: line,
    file: file,
    id: id
  };
  this.dispatch_search();
}

Client.prototype.search_done = function() {
  this.active_search = null;
  process.nextTick(this.dispatch_search.bind(this));
}

Client.prototype.switch_pool = function(pool) {
  if (this.pool === pool)
    return;
  this.debug("Switching to %s pool",
             pool === this.parent.slow_pool ? "slow" : "fast");
  this.pool = pool;
}

Client.prototype.slow_query = function() {
  this.last_slow = new Date();
  this.fast_streak = 0;
  this.switch_pool(this.parent.slow_pool);
}

Client.prototype.fast_query = function() {
  this.fast_streak++;
  if ((new Date() - this.last_slow) < this.parent.config.MIN_SLOW_TIME)
    return;
  if (this.fast_streak >= this.parent.config.QUERY_STREAK)
    this.switch_pool(this.parent.fast_pool);
}

Client.prototype.dispatch_search = function() {
  if (this.pending_search !== null &&
      !this.active_search &&
      this.pool.remotes.length) {
    if (this.last_slow &&
        (new Date() - this.last_slow) >= this.parent.config.MAX_SLOW_TIME)
      this.switch_pool(this.parent.fast_pool);

    var codesearch = this.pool.remotes.pop();
    console.assert(codesearch.cs_ready);
    var start = new Date();
    this.last_search = this.pending_search;
    this.debug('dispatching: (%j)...', this.pending_search);

    var search = this.pending_search;
    this.pending_search = null;
    this.active_search  = search;
    var self   = this;
    var sock   = this.socket;
    var batch  = new Batch(function (m) {
                             sock.emit('match', search.id, m);
                           }, 50);
    var cbs = {
      not_ready: function() {
        logger.info('Remote reports not ready for %j', search);
        if (self.pending_search === null)
          self.pending_search = search;
        self.search_done();
        codesearch.cs_client = null;
      },
      error: function (err) {
        sock.emit('regex_error', search.id, err);
        self.search_done();
        codesearch.cs_client = null;
      },
      match: function (match) {
        match = JSON.parse(match);
        logger.trace("Reporting match %j for %j.", match, search);
        batch.send(match);
      },
      done: function (stats) {
        stats = JSON.parse(stats);
        var time = (new Date()) - start;
        batch.flush();
        sock.emit('search_done', search.id, time, stats.why);
        self.debug("Search done: (%j): %s", search, time);
        if (time > self.parent.config.SLOW_THRESHOLD) {
          self.slow_query();
        } else {
          self.fast_query();
        }
        self.search_done();
        codesearch.cs_client = null;
      }
    }
    codesearch.try_search(search.line, search.file, cbs);
    codesearch.cs_ready = false;
    codesearch.cs_client = this;
  }
}

function ConnectionPool(server, config) {
  var parent = this;
  this.server  = server
  this.remotes = [];
  this.connections = [];

  config.BACKENDS.forEach(
    function (bk) {
      for (var i = 0; i < config.BACKEND_CONNECTIONS; i++) {
        (function() {
           var remote = null;
           var connection = null;

           function ready() {
             logger.debug('Remote ready!');
             if (remote.cs_ready) {
               logger.debug('(already queued)!');
               return;
             }
             remote.cs_ready = true;
             parent.remotes.push(remote);
             parent.dispatch();
           }

           function disconnected() {
             logger.info("Lost connection to backend")
             parent.remotes = parent.remotes.filter(function (r) {return r !== remote});
             parent.connections = parent.connections.filter(
               function (c) { return c !== connection});
             if (remote.cs_client)
               remote.cs_client.search_done();
           }

           dnode({ ready: ready }).
             connect(
               bk[0], bk[1],
               function (r, conn) {
                 parent.connections.push(conn);
                 remote = r;
                 connection = conn;
                 remote.cs_ready  = false;
                 remote.cs_client = null;
                 logger.info("Connected to codesearch daemon.");
                 conn.on('ready',     ready);
                 conn.on('reconnect', ready);
                 conn.on('close',     disconnected);
                 conn.on('end',       disconnected);
               }, {
                 reconnect: 200
               });
         })();
      }
    });
}

ConnectionPool.prototype.dispatch = function () {
  var clients = this.clients;
  _.shuffle(_.values(this.clients)).forEach(
    function (client) {
      client.dispatch_search();
    });
}

function SearchServer(config, io) {
  var parent = this;
  this.config  = config;
  this.clients = {};
  this.fast_pool = new ConnectionPool(this, config);
  this.slow_pool = new ConnectionPool(this, config);

  var Server = function (sock) {
    logger.info("New client (%s)[%j]", sock.id, sock.handshake.address);
    parent.clients[sock.id] = new Client(parent, parent.fast_pool, sock);
    sock.on('new_search', function(line, file, id) {
              if (id == null)
                id = line;
              parent.clients[sock.id].new_search(line, file, id);
    });
    sock.on('disconnect', function() {
              logger.info("Disconnected (%d)[%j]", sock.id, sock.handshake.address);
              delete parent.clients[sock.id];
            });
  };

  io.sockets.on('connection', function(sock) {
    new Server(sock);
  });
  setInterval(function() {
                parent.dump_stats();
              }, 5000);
}

SearchServer.prototype.dump_stats = function() {
  var clients = 0, slow = 0, fast = 0;
  var self = this;
  _.values(this.clients).forEach(
    function (c){
      clients++;
      if (c.pool === self.slow_pool)
        slow++;
      else if (c.pool === self.fast_pool)
        fast++;
      else
        console.log("WTF pool %j", c);
    });
  logger.info("Clients/slow/fast: %d %d %d", clients, slow, fast);
  fs.writeFile(path.join(__dirname, "log/stats.json"),
               JSON.stringify({
                                clients: clients,
                                slow: slow,
                                fast: fast
                              }) + "\n",
               "utf8");
}

module.exports = SearchServer;
<commit_msg>Write out query statistics to stats.json.<commit_after>var dnode  = require('dnode'),
    fs     = require('fs'),
    log4js = require('log4js'),
    util   = require('util'),
    path   = require('path'),
    _      = require('underscore'),
    config = require('./config.js'),
    Batch  = require('./batch.js'),
    QueryStats  = require('../lib/query-stats.js');
var logger  = log4js.getLogger('appserver');

function Client(parent, pool, sock) {
  this.parent = parent;
  this.pool   = pool;
  this.socket = sock;
  this.pending_search = null;
  this.last_search = null;
  this.active_search = null;
  this.remote_address = sock.handshake.address;
  this.fast_streak = 0;
  this.last_slow   = null;
}

Client.prototype.debug = function() {
  logger.debug("[%s:%d] %s",
               this.remote_address.address,
               this.remote_address.port,
               util.format.apply(null, arguments));
}

Client.prototype.new_search = function (line, file, id) {
  this.debug('new search: (%s) (%j)', id, {line:line, file:file});
  if (this.last_search &&
      line === this.last_search.line &&
      file === this.last_search.file)
    return;
  this.pending_search = {
    line: line,
    file: file,
    id: id
  };
  this.dispatch_search();
}

Client.prototype.search_done = function() {
  this.active_search = null;
  process.nextTick(this.dispatch_search.bind(this));
}

Client.prototype.switch_pool = function(pool) {
  if (this.pool === pool)
    return;
  this.debug("Switching to %s pool",
             pool === this.parent.slow_pool ? "slow" : "fast");
  this.pool = pool;
}

Client.prototype.slow_query = function() {
  this.last_slow = new Date();
  this.fast_streak = 0;
  this.switch_pool(this.parent.slow_pool);
}

Client.prototype.fast_query = function() {
  this.fast_streak++;
  if ((new Date() - this.last_slow) < this.parent.config.MIN_SLOW_TIME)
    return;
  if (this.fast_streak >= this.parent.config.QUERY_STREAK)
    this.switch_pool(this.parent.fast_pool);
}

Client.prototype.dispatch_search = function() {
  if (this.pending_search !== null &&
      !this.active_search &&
      this.pool.remotes.length) {
    if (this.last_slow &&
        (new Date() - this.last_slow) >= this.parent.config.MAX_SLOW_TIME)
      this.switch_pool(this.parent.fast_pool);

    var codesearch = this.pool.remotes.pop();
    console.assert(codesearch.cs_ready);
    var start = new Date();
    this.last_search = this.pending_search;
    this.debug('dispatching: (%j)...', this.pending_search);

    var search = this.pending_search;
    this.pending_search = null;
    this.active_search  = search;
    var self   = this;
    var sock   = this.socket;
    var batch  = new Batch(function (m) {
                             sock.emit('match', search.id, m);
                           }, 50);
    var cbs = {
      not_ready: function() {
        logger.info('Remote reports not ready for %j', search);
        if (self.pending_search === null)
          self.pending_search = search;
        self.search_done();
        codesearch.cs_client = null;
      },
      error: function (err) {
        sock.emit('regex_error', search.id, err);
        self.search_done();
        codesearch.cs_client = null;
      },
      match: function (match) {
        match = JSON.parse(match);
        logger.trace("Reporting match %j for %j.", match, search);
        batch.send(match);
      },
      done: function (stats) {
        stats = JSON.parse(stats);
        var time = (new Date()) - start;
        self.parent.stats.done(search.id, start, time);
        batch.flush();
        sock.emit('search_done', search.id, time, stats.why);
        self.debug("Search done: (%j): %s", search, time);
        if (time > self.parent.config.SLOW_THRESHOLD) {
          self.slow_query();
        } else {
          self.fast_query();
        }
        self.search_done();
        codesearch.cs_client = null;
      }
    }
    codesearch.try_search(search.line, search.file, cbs);
    codesearch.cs_ready = false;
    codesearch.cs_client = this;
  }
}

function ConnectionPool(server, config) {
  var parent = this;
  this.server  = server
  this.remotes = [];
  this.connections = [];

  config.BACKENDS.forEach(
    function (bk) {
      for (var i = 0; i < config.BACKEND_CONNECTIONS; i++) {
        (function() {
           var remote = null;
           var connection = null;

           function ready() {
             logger.debug('Remote ready!');
             if (remote.cs_ready) {
               logger.debug('(already queued)!');
               return;
             }
             remote.cs_ready = true;
             parent.remotes.push(remote);
             parent.dispatch();
           }

           function disconnected() {
             logger.info("Lost connection to backend")
             parent.remotes = parent.remotes.filter(function (r) {return r !== remote});
             parent.connections = parent.connections.filter(
               function (c) { return c !== connection});
             if (remote.cs_client)
               remote.cs_client.search_done();
           }

           dnode({ ready: ready }).
             connect(
               bk[0], bk[1],
               function (r, conn) {
                 parent.connections.push(conn);
                 remote = r;
                 connection = conn;
                 remote.cs_ready  = false;
                 remote.cs_client = null;
                 logger.info("Connected to codesearch daemon.");
                 conn.on('ready',     ready);
                 conn.on('reconnect', ready);
                 conn.on('close',     disconnected);
                 conn.on('end',       disconnected);
               }, {
                 reconnect: 200
               });
         })();
      }
    });
}

ConnectionPool.prototype.dispatch = function () {
  var clients = this.clients;
  _.shuffle(_.values(this.clients)).forEach(
    function (client) {
      client.dispatch_search();
    });
}

function SearchServer(config, io) {
  var parent = this;
  this.config  = config;
  this.clients = {};
  this.fast_pool = new ConnectionPool(this, config);
  this.slow_pool = new ConnectionPool(this, config);
  this.stats     = new QueryStats('appserver', {timeout: 60*1000});
  this.stats.start();

  var Server = function (sock) {
    logger.info("New client (%s)[%j]", sock.id, sock.handshake.address);
    parent.clients[sock.id] = new Client(parent, parent.fast_pool, sock);
    sock.on('new_search', function(line, file, id) {
              if (id == null)
                id = line;
              parent.clients[sock.id].new_search(line, file, id);
    });
    sock.on('disconnect', function() {
              logger.info("Disconnected (%d)[%j]", sock.id, sock.handshake.address);
              delete parent.clients[sock.id];
            });
  };

  io.sockets.on('connection', function(sock) {
    new Server(sock);
  });
  setInterval(function() {
                parent.dump_stats();
              }, 5000);
}

SearchServer.prototype.dump_stats = function() {
  var clients = 0, slow = 0, fast = 0;
  var self = this;
  _.values(this.clients).forEach(
    function (c){
      clients++;
      if (c.pool === self.slow_pool)
        slow++;
      else if (c.pool === self.fast_pool)
        fast++;
      else
        console.log("WTF pool %j", c);
    });
  logger.info("Clients/slow/fast: %d %d %d", clients, slow, fast);
  var stats = this.stats.stats();
  stats.server = {
    clients: clients,
    slow: slow,
    fast: fast
  };
  fs.writeFile(path.join(__dirname, "log/stats.json"),
               JSON.stringify(stats) + "\n",
               "utf8");
}

module.exports = SearchServer;
<|endoftext|>


<commit_before>'use strict';

const playerBar = '.player';

Connector.playerSelector = playerBar;

Connector.artistSelector = `${playerBar} .title-field`;

Connector.trackSelector = `${playerBar} .description-field`;

Connector.currentTimeSelector = `${playerBar} .start-time`;

Connector.durationSelector = `${playerBar} .end-time`;

Connector.trackArtSelector = `${playerBar} .cover img`;

Connector.pauseButtonSelector = `${playerBar} .ico-stop`;

// function removeYearFromArtist(text) {
// 	const regexp = new RegExp(/\s+\(\d{4}\)(?=[^\s+(\d{4})]*$)/gm);
// 	return text.replace(regexp, '');
// }
<commit_msg>Fix fip connector<commit_after>'use strict';

const playerBar = '.RFplayer';

Connector.playerSelector = playerBar;

Connector.artistSelector = `${playerBar} .media-informations a:first-child`;

Connector.trackSelector = `${playerBar} .media-informations a:last-child`;

Connector.currentTimeSelector = `${playerBar} .start-time`;

Connector.durationSelector = `${playerBar} .end-time`;

Connector.trackArtSelector = `${playerBar} .cover img`;

Connector.pauseButtonSelector = `${playerBar} .ico-stop`;

// function removeYearFromArtist(text) {
// 	const regexp = new RegExp(/\s+\(\d{4}\)(?=[^\s+(\d{4})]*$)/gm);
// 	return text.replace(regexp, '');
// }
<|endoftext|>


<commit_before>/*

This is a custom js for the TutorStudy website.

By: Ryan Ngo

*/

/* Generic Functions */
function getUserInfo(){
    loadMajors();
    loadClasses();
    $.ajax({
        type: "GET",
        async: false,
        dataType: "JSON",
        url: "http://52.38.218.199/TutorStudyServlet/GetUserInfo",
        success: function(data){
            var result = data;
            switch(result.UTID){
                case 2:
                    $("#tutorInfoForm").hide();
                    $("#studentInfoForm input[name='name']").val(result.name);
                    $("#studentInfoForm input[name='email']").val(result.email);
                    $("#studentMajorSelect").val(result.MID);
                    $("#studentMajorSelect").trigger('change');
                    break;
                case 3:
                    $("#classSelect").val(result.classes).trigger('change');
                    $("#studentInfoForm").hide();
                    $("#tutorInfoForm input[name='name']").val(result.name);
                    $("#tutorInfoForm input[name='email']").val(result.email);
                    $("#tutorInfoForm textarea[name='description']").val(result.Description);
                    $("#tutorInfoForm input[name='rate']").val(result.Rate);
                    $("#tutorMajorSelect").val(result.MID).trigger('change');
                    console.log($("#classSelect").val());
                    break;
                default:
                    break;
            }
        },
        error: function(){
            window.location.replace("http://52.38.218.199/TutorStudy/");
        }
    });
}

function loadMajors(){
     $.ajax({
        type: "GET",
        async: false,
        dataType: "json",
        url: "http://52.38.218.199/TutorStudyServlet/GetMajors",
        success:function(data){
            $("#studentMajorSelect").select2({
                data :  $.map(data, function(data){
                            return{
                                id: data.id,
                                text: data.major
                            }
                        }),
                createTag: function(){
                    return null;
                }

            });
            $("#tutorMajorSelect").select2({
                data :  $.map(data, function(data){
                            return{
                                id: data.id,
                                text: data.major
                            }
                        }),
                createTag: function(){
                    return null;
                }
            });
        }
    });
}

function loadClasses(){
    $.ajax({
        type: "GET",
        async: false,
        dataType: "json",
        url: "http://52.38.218.199/TutorStudyServlet/GetClasses",
        success :function(response){
            response = $.map(response, function(data){
                            return{
                                id: data.CID,
                                text: data.Abrv+" "+data.CNum+"-"+data.CName
                            }
                        });
            $("#classSelect").select2({
                data :  response,
                createTag: function(){
                    return null;
                }

            });
        }
    });
}
/* End Generic Functions */

/* Update User Functions */
function updateTutor(){
    var fields = $("#tutorInfoForm").serializeArray();
    if (!checkPassword(fields)){
        return;
    }
    if (!isInteger(fields[6].value)||
        fields[6].value > 150 ||
        fields[6].value < 0){
        $("#userInfo").removeClass("alert-success");
        $("#userInfo").removeClass("alert-danger");
        $("#userInfo").addClass("alert-info");
        $("#alertUserInfo").text("Please Enter a fee/hour that is a whole number greater than 0 but less than 150.");
        $("#userInfo").show('fast');
    }
    fields = $("#tutorInfoForm").serialize();
    $.ajax({
        type: "POST",
        url: "http://52.38.218.199/TutorStudyServlet/UpdateTutor",
        data: fields,
        success: function(data){
            $("#userInfo").removeClass("alert-success");
            $("#userInfo").removeClass("alert-danger");
            $("#userInfo").addClass("alert-info");
            $("#alertUserInfo").text(data);
            $("#userInfo").show('fast');
            setTimeout(function(){$("#userInfo").hide('fast');}, 2000);
        }
    });
}

function updateStudent(){
    var fields = $("#studentInfoForm").serializeArray();
    if (!checkPassword(fields)){
        return;
    }
    fields = $("#studentInfoForm").serialize();
    $.ajax({
        type: "POST",
        url: "http://52.38.218.199/TutorStudyServlet/UpdateStudent",
        data: fields,
        success: function(data){
            $("#userInfo").removeClass("alert-success");
            $("#userInfo").removeClass("alert-danger");
            $("#userInfo").addClass("alert-info");
            $("#alertUserInfo").text(data);
            $("#userInfo").show('fast');
            setTimeout(function(){$("#userInfo").hide('fast');}, 2000);
        }
    });
}

function checkPassword(fields){
    if(fields[2].value!=fields[3].value){
        $("#userInfo").removeClass("alert-success");
        $("#userInfo").addClass("alert-danger");
        $("#alertUserInfo").text("Passwords do not match.");
        $("#userInfo").show('fast');
        setTimeout(function(){$("#userInfo").hide('fast');}, 2000);
        return false;
    }
    return true;
}
/* End Update User Functions */

/* Document Ready Functions */
$(document).ready(function(){
    getUserInfo();
});
/* End Document Ready Functions */<commit_msg>aasdf<commit_after>/*

This is a custom js for the TutorStudy website.

By: Ryan Ngo

*/

/* Generic Functions */
function getUserInfo(){
    loadMajors();
    loadClasses();
    $.ajax({
        type: "GET",
        async: false,
        dataType: "JSON",
        url: "http://52.38.218.199/TutorStudyServlet/GetUserInfo",
        success: function(data){
            var result = data;
            switch(result.UTID){
                case 2:
                    $("#tutorInfoForm").hide();
                    $("#studentInfoForm input[name='name']").val(result.name);
                    $("#studentInfoForm input[name='email']").val(result.email);
                    $("#studentMajorSelect").val(result.MID);
                    $("#studentMajorSelect").trigger('change');
                    break;
                case 3:
                    $("#classSelect").val(result.classes).trigger('change');
                    $("#studentInfoForm").hide();
                    $("#tutorInfoForm input[name='name']").val(result.name);
                    $("#tutorInfoForm input[name='email']").val(result.email);
                    $("#tutorInfoForm textarea[name='description']").val(result.Description);
                    $("#tutorInfoForm input[name='rate']").val(result.Rate);
                    $("#tutorMajorSelect").val(result.MID).trigger('change');
                    console.log($("#classSelect").val());
                    break;
                default:
                    break;
            }
        },
        error: function(){
            window.location.replace("http://52.38.218.199/TutorStudy/");
        }
    });
}

function loadMajors(){
     $.ajax({
        type: "GET",
        async: false,
        dataType: "json",
        url: "http://52.38.218.199/TutorStudyServlet/GetMajors",
        success:function(data){
            $("#studentMajorSelect").select2({
                data :  $.map(data, function(data){
                            return{
                                id: data.id,
                                text: data.major
                            }
                        }),
                createTag: function(){
                    return null;
                }

            });
            $("#tutorMajorSelect").select2({
                data :  $.map(data, function(data){
                            return{
                                id: data.id,
                                text: data.major
                            }
                        }),
                createTag: function(){
                    return null;
                }
            });
        }
    });
}

function loadClasses(){
    $.ajax({
        type: "GET",
        async: false,
        dataType: "json",
        url: "http://52.38.218.199/TutorStudyServlet/GetClasses",
        success :function(response){
            response = $.map(response, function(data){
                            return{
                                id: data.CID,
                                text: data.Abrv+" "+data.CNum+"-"+data.CName
                            }
                        });
            $("#classSelect").select2({
                data :  response,
                createTag: function(){
                    return null;
                }

            });
        }
    });
}
/* End Generic Functions */

/* Update User Functions */
function updateTutor(){
    var fields = $("#tutorInfoForm").serializeArray();
    if (!checkPassword(fields)){
        return;
    }
    if (!isInteger(fields[6].value)||
        fields[6].value > 150 ||
        fields[6].value < 0){
        $("#userInfo").removeClass("alert-success");
        $("#userInfo").addClass("alert-danger");
        $("#alertUserInfo").text("Please Enter a fee/hour that is a whole number greater than 0 but less than 150.");
        $("#userInfo").show('fast');
        return;
    }
    fields = $("#tutorInfoForm").serialize();
    $.ajax({
        type: "POST",
        url: "http://52.38.218.199/TutorStudyServlet/UpdateTutor",
        data: fields,
        success: function(data){
            $("#userInfo").removeClass("alert-success");
            $("#userInfo").removeClass("alert-danger");
            $("#userInfo").addClass("alert-info");
            $("#alertUserInfo").text(data);
            $("#userInfo").show('fast');
            setTimeout(function(){$("#userInfo").hide('fast');}, 2000);
        }
    });
}

function updateStudent(){
    var fields = $("#studentInfoForm").serializeArray();
    if (!checkPassword(fields)){
        return;
    }
    fields = $("#studentInfoForm").serialize();
    $.ajax({
        type: "POST",
        url: "http://52.38.218.199/TutorStudyServlet/UpdateStudent",
        data: fields,
        success: function(data){
            $("#userInfo").removeClass("alert-success");
            $("#userInfo").removeClass("alert-danger");
            $("#userInfo").addClass("alert-info");
            $("#alertUserInfo").text(data);
            $("#userInfo").show('fast');
            setTimeout(function(){$("#userInfo").hide('fast');}, 2000);
        }
    });
}

function checkPassword(fields){
    if(fields[2].value!=fields[3].value){
        $("#userInfo").removeClass("alert-success");
        $("#userInfo").addClass("alert-danger");
        $("#alertUserInfo").text("Passwords do not match.");
        $("#userInfo").show('fast');
        setTimeout(function(){$("#userInfo").hide('fast');}, 2000);
        return false;
    }
    return true;
}
/* End Update User Functions */

/* Document Ready Functions */
$(document).ready(function(){
    getUserInfo();
});
/* End Document Ready Functions */<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var classNames = require('classnames');
var Matrix = require("matrix-js-sdk");
var q = require('q');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
var Entities = require("../../../Entities");
var sdk = require('../../../index');
var GeminiScrollbar = require('react-gemini-scrollbar');
var rate_limited_func = require('../../../ratelimitedfunc');

var INITIAL_LOAD_NUM_MEMBERS = 30;
var SHARE_HISTORY_WARNING =

        Newly invited users will see the history of this room.

        If you'd prefer invited users not to see messages that were sent before they joined,

        turn off, 'Share message history with new users' in the settings for this room.

var shown_invite_warning_this_session = false;
// global promise so people can bulk invite and they all get resolved
var invite_defer = q.defer();

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

    getInitialState: function() {
        var state = {
            members: [],
            // ideally we'd size this to the page height, but
            // in practice I find that a little constraining
            truncateAt: INITIAL_LOAD_NUM_MEMBERS,
        };
        if (!this.props.roomId) return state;
        var cli = MatrixClientPeg.get();
        var room = cli.getRoom(this.props.roomId);
        if (!room) return state;

        this.memberDict = this.getMemberDict();

        state.members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS);
        return state;
    },

    componentWillMount: function() {
        var cli = MatrixClientPeg.get();
        cli.on("RoomState.members", this.onRoomStateMember);
        cli.on("RoomMember.name", this.onRoomMemberName);
        cli.on("RoomState.events", this.onRoomStateEvent);
        cli.on("Room", this.onRoom); // invites
    },

    componentWillUnmount: function() {
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("Room", this.onRoom);
            MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
            MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName);
            MatrixClientPeg.get().removeListener("User.presence", this.userPresenceFn);
            MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvent);
        }
    },

    componentDidMount: function() {
        var self = this;

        // Lazy-load in more than the first N members
        setTimeout(function() {
            if (!self.isMounted()) return;
            // lazy load to prevent it blocking the first render
            self.setState({
                members: self.roomMembers()
            });
        }, 50);

        // Attach a SINGLE listener for global presence changes then locate the
        // member tile and re-render it. This is more efficient than every tile
        // evar attaching their own listener.
        function updateUserState(event, user) {
            // XXX: evil hack to track the age of this presence info.
            // this should be removed once syjs-28 is resolved in the JS SDK itself.
            user.lastPresenceTs = Date.now();

            var tile = self.refs[user.userId];

            if (tile) {
                self._updateList(); // reorder the membership list
            }
        }
        // FIXME: we should probably also reset 'lastActiveAgo' to zero whenever
        // we see a typing notif from a user, as we don't get presence updates for those.
        MatrixClientPeg.get().on("User.presence", updateUserState);
        this.userPresenceFn = updateUserState;
    },

    onRoom: function(room) {
        if (room.roomId !== this.props.roomId) {
            return;
        }
        // We listen for room events because when we accept an invite
        // we need to wait till the room is fully populated with state
        // before refreshing the member list else we get a stale list.
        this._updateList();
    },

    onRoomStateMember: function(ev, state, member) {
        this._updateList();
    },

    onRoomMemberName: function(ev, member) {
        this._updateList();
    },

    onRoomStateEvent: function(event, state) {
        if (event.getType() === "m.room.third_party_invite") {
            this._updateList();
        }
    },

    _updateList: new rate_limited_func(function() {
        this.memberDict = this.getMemberDict();

        var self = this;
        this.setState({
            members: self.roomMembers()
        });
    }, 500),

    onInvite: function(inputText) {
        var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
        var self = this;
        inputText = inputText.trim(); // react requires es5-shim so we know trim() exists

        // email addresses and user IDs do not allow space, comma, semicolon so split
        // on them for bulk inviting.
        var separators =[ ";", " ", "," ];
        for (var i = 0; i < separators.length; i++) {
            if (inputText.indexOf(separators[i]) >= 0) {
                var inputs = inputText.split(separators[i]);
                inputs.forEach(function(input) {
                    self.onInvite(input);
                });
                return;
            }
        }

        var isEmailAddress = /^\S+@\S+\.\S+$/.test(inputText);

        // sanity check the input for user IDs
        if (!isEmailAddress && (inputText[0] !== '@' || inputText.indexOf(":") === -1)) {
            console.error("Bad ID to invite: %s", inputText);
            Modal.createDialog(ErrorDialog, {
                title: "Invite Error",
                description: "Malformed ID. Should be an email address or a Matrix ID like '@localpart:domain'"
            });
            return;
        }

        var inviteWarningDefer = q.defer();

        var room = MatrixClientPeg.get().getRoom(this.props.roomId);
        var history_visibility = room.currentState.getStateEvents('m.room.history_visibility', '');
        if (history_visibility) history_visibility = history_visibility.getContent().history_visibility;

        if (history_visibility == 'shared' && !shown_invite_warning_this_session) {
            inviteWarningDefer = invite_defer; // whether we continue depends on this defer
            var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
            Modal.createDialog(QuestionDialog, {
                title: "Warning",
                description: SHARE_HISTORY_WARNING,
                button: "Invite",
                onFinished: function(should_invite) {
                    if (should_invite) {
                        shown_invite_warning_this_session = true;
                        invite_defer.resolve();
                    } else {
                        invite_defer.reject(null);
                        // reset the promise so we don't auto-reject all invites from
                        // now on.
                        invite_defer = q.defer();
                    }
                }
            });
        } else {
            inviteWarningDefer.resolve();
        }

        var promise = inviteWarningDefer.promise;
        if (isEmailAddress) {
            promise = promise.then(function() {
                 return MatrixClientPeg.get().inviteByEmail(self.props.roomId, inputText);
            });
        }
        else {
            promise = promise.then(function() {
                return MatrixClientPeg.get().invite(self.props.roomId, inputText);
            });
        }

        self.setState({
            inviting: true
        });
        console.log(
            "Invite %s to %s - isEmail=%s", inputText, this.props.roomId, isEmailAddress
        );
        promise.done(function(res) {
            console.log("Invited %s", inputText);
            self.setState({
                inviting: false
            });
        }, function(err) {
            if (err !== null) {
                console.error("Failed to invite: %s", JSON.stringify(err));
                Modal.createDialog(ErrorDialog, {
                    title: "Server error whilst inviting",
                    description: err.message
                });
            }
            self.setState({
                inviting: false
            });
        });
    },

    getMemberDict: function() {
        if (!this.props.roomId) return {};
        var cli = MatrixClientPeg.get();
        var room = cli.getRoom(this.props.roomId);
        if (!room) return {};

        var all_members = room.currentState.members;

        // XXX: evil hack until SYJS-28 is fixed
        Object.keys(all_members).map(function(userId) {
            // work around a race where you might have a room member object
            // before the user object exists.  This may or may not cause
            // https://github.com/vector-im/vector-web/issues/186
            if (all_members[userId].user === null) {
                all_members[userId].user = MatrixClientPeg.get().getUser(userId);
            }

            if (all_members[userId].user && !all_members[userId].user.lastPresenceTs) {
                all_members[userId].user.lastPresenceTs = Date.now();
            }
        });

        return all_members;
    },

    roomMembers: function(limit) {
        var all_members = this.memberDict || {};
        var all_user_ids = Object.keys(all_members);

        if (this.memberSort) all_user_ids.sort(this.memberSort);

        var to_display = [];
        var count = 0;
        for (var i = 0; i < all_user_ids.length && (limit === undefined || count < limit); ++i) {
            var user_id = all_user_ids[i];
            var m = all_members[user_id];

            if (m.membership == 'join' || m.membership == 'invite') {
                to_display.push(user_id);
                ++count;
            }
        }
        return to_display;
    },

    _createOverflowTile: function(overflowCount, totalCount) {
        // For now we'll pretend this is any entity. It should probably be a separate tile.
        var EntityTile = sdk.getComponent("rooms.EntityTile");
        var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
        var text = "and " + overflowCount + " other" + (overflowCount > 1 ? "s" : "") +  "...";
        return (
            <EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
                <BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
            } name={text} presenceState="online" suppressOnHover={true}
            onClick={this._showFullMemberList} />
        );
    },

    _showFullMemberList: function() {
        this.setState({
            truncateAt: -1
        });
    },

    memberSort: function(userIdA, userIdB) {
        var userA = this.memberDict[userIdA].user;
        var userB = this.memberDict[userIdB].user;

        var presenceMap = {
            online: 3,
            unavailable: 2,
            offline: 1
        };

        var presenceOrdA = userA ? presenceMap[userA.presence] : 0;
        var presenceOrdB = userB ? presenceMap[userB.presence] : 0;

        if (presenceOrdA != presenceOrdB) {
            return presenceOrdB - presenceOrdA;
        }

        var latA = userA ? (userA.lastPresenceTs - (userA.lastActiveAgo || userA.lastPresenceTs)) : 0;
        var latB = userB ? (userB.lastPresenceTs - (userB.lastActiveAgo || userB.lastPresenceTs)) : 0;

        return latB - latA;
    },

    onSearchQueryChanged: function(input) {
        this.setState({
            searchQuery: input
        });
    },

    makeMemberTiles: function(membership, query) {
        var MemberTile = sdk.getComponent("rooms.MemberTile");
        query = (query || "").toLowerCase();

        var self = this;

        var memberList = self.state.members.filter(function(userId) {
            var m = self.memberDict[userId];
            if (query && m.name.toLowerCase().indexOf(query) === -1) {
                return false;
            }
            return m.membership == membership;
        }).map(function(userId) {
            var m = self.memberDict[userId];
            return (
                <MemberTile key={userId} member={m} ref={userId} />
            );
        });

        if (membership === "invite") {
            // include 3pid invites (m.room.third_party_invite) state events.
            // The HS may have already converted these into m.room.member invites so
            // we shouldn't add them if the 3pid invite state key (token) is in the
            // member invite (content.third_party_invite.signed.token)
            var room = MatrixClientPeg.get().getRoom(this.props.roomId);
            var EntityTile = sdk.getComponent("rooms.EntityTile");
            if (room) {
                room.currentState.getStateEvents("m.room.third_party_invite").forEach(
                function(e) {
                    // discard all invites which have a m.room.member event since we've
                    // already added them.
                    var memberEvent = room.currentState.getInviteForThreePidToken(e.getStateKey());
                    if (memberEvent) {
                        return;
                    }
                    memberList.push(
                        <EntityTile key={e.getStateKey()} name={e.getContent().display_name} />
                    )
                })
            }
        }

        return memberList;
    },

    render: function() {
        var InviteMemberList = sdk.getComponent("rooms.InviteMemberList");

        var invitedSection = null;
        var invitedMemberTiles = this.makeMemberTiles('invite', this.state.searchQuery);
        if (invitedMemberTiles.length > 0) {
            invitedSection = (

Invited

                        {invitedMemberTiles}

            );
        }

        var inviteMemberListSection;
        if (this.state.inviting) {
            var Loader = sdk.getComponent("elements.Spinner");
            inviteMemberListSection = (
                <Loader />
            );
        }
        else {
            inviteMemberListSection = (
                <InviteMemberList roomId={this.props.roomId}
                    onSearchQueryChanged={this.onSearchQueryChanged}
                    onInvite={this.onInvite} />
            );
        }

        var TruncatedList = sdk.getComponent("elements.TruncatedList");
        return (

                    {inviteMemberListSection}
                    <GeminiScrollbar autoshow={true} className="mx_MemberList_joined mx_MemberList_outerWrapper">
                        <TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
                                createOverflowElement={this._createOverflowTile}>
                            {this.makeMemberTiles('join', this.state.searchQuery)}
                        </TruncatedList>
                        {invitedSection}
                    </GeminiScrollbar>

        );
    }
});

<commit_msg>Don't crash on redacted (or otherwise invalid) 3pid invites<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var classNames = require('classnames');
var Matrix = require("matrix-js-sdk");
var q = require('q');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
var Entities = require("../../../Entities");
var sdk = require('../../../index');
var GeminiScrollbar = require('react-gemini-scrollbar');
var rate_limited_func = require('../../../ratelimitedfunc');

var INITIAL_LOAD_NUM_MEMBERS = 30;
var SHARE_HISTORY_WARNING =

        Newly invited users will see the history of this room.

        If you'd prefer invited users not to see messages that were sent before they joined,

        turn off, 'Share message history with new users' in the settings for this room.

var shown_invite_warning_this_session = false;
// global promise so people can bulk invite and they all get resolved
var invite_defer = q.defer();

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

    getInitialState: function() {
        var state = {
            members: [],
            // ideally we'd size this to the page height, but
            // in practice I find that a little constraining
            truncateAt: INITIAL_LOAD_NUM_MEMBERS,
        };
        if (!this.props.roomId) return state;
        var cli = MatrixClientPeg.get();
        var room = cli.getRoom(this.props.roomId);
        if (!room) return state;

        this.memberDict = this.getMemberDict();

        state.members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS);
        return state;
    },

    componentWillMount: function() {
        var cli = MatrixClientPeg.get();
        cli.on("RoomState.members", this.onRoomStateMember);
        cli.on("RoomMember.name", this.onRoomMemberName);
        cli.on("RoomState.events", this.onRoomStateEvent);
        cli.on("Room", this.onRoom); // invites
    },

    componentWillUnmount: function() {
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("Room", this.onRoom);
            MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
            MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName);
            MatrixClientPeg.get().removeListener("User.presence", this.userPresenceFn);
            MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvent);
        }
    },

    componentDidMount: function() {
        var self = this;

        // Lazy-load in more than the first N members
        setTimeout(function() {
            if (!self.isMounted()) return;
            // lazy load to prevent it blocking the first render
            self.setState({
                members: self.roomMembers()
            });
        }, 50);

        // Attach a SINGLE listener for global presence changes then locate the
        // member tile and re-render it. This is more efficient than every tile
        // evar attaching their own listener.
        function updateUserState(event, user) {
            // XXX: evil hack to track the age of this presence info.
            // this should be removed once syjs-28 is resolved in the JS SDK itself.
            user.lastPresenceTs = Date.now();

            var tile = self.refs[user.userId];

            if (tile) {
                self._updateList(); // reorder the membership list
            }
        }
        // FIXME: we should probably also reset 'lastActiveAgo' to zero whenever
        // we see a typing notif from a user, as we don't get presence updates for those.
        MatrixClientPeg.get().on("User.presence", updateUserState);
        this.userPresenceFn = updateUserState;
    },

    onRoom: function(room) {
        if (room.roomId !== this.props.roomId) {
            return;
        }
        // We listen for room events because when we accept an invite
        // we need to wait till the room is fully populated with state
        // before refreshing the member list else we get a stale list.
        this._updateList();
    },

    onRoomStateMember: function(ev, state, member) {
        this._updateList();
    },

    onRoomMemberName: function(ev, member) {
        this._updateList();
    },

    onRoomStateEvent: function(event, state) {
        if (event.getType() === "m.room.third_party_invite") {
            this._updateList();
        }
    },

    _updateList: new rate_limited_func(function() {
        this.memberDict = this.getMemberDict();

        var self = this;
        this.setState({
            members: self.roomMembers()
        });
    }, 500),

    onInvite: function(inputText) {
        var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
        var self = this;
        inputText = inputText.trim(); // react requires es5-shim so we know trim() exists

        // email addresses and user IDs do not allow space, comma, semicolon so split
        // on them for bulk inviting.
        var separators =[ ";", " ", "," ];
        for (var i = 0; i < separators.length; i++) {
            if (inputText.indexOf(separators[i]) >= 0) {
                var inputs = inputText.split(separators[i]);
                inputs.forEach(function(input) {
                    self.onInvite(input);
                });
                return;
            }
        }

        var isEmailAddress = /^\S+@\S+\.\S+$/.test(inputText);

        // sanity check the input for user IDs
        if (!isEmailAddress && (inputText[0] !== '@' || inputText.indexOf(":") === -1)) {
            console.error("Bad ID to invite: %s", inputText);
            Modal.createDialog(ErrorDialog, {
                title: "Invite Error",
                description: "Malformed ID. Should be an email address or a Matrix ID like '@localpart:domain'"
            });
            return;
        }

        var inviteWarningDefer = q.defer();

        var room = MatrixClientPeg.get().getRoom(this.props.roomId);
        var history_visibility = room.currentState.getStateEvents('m.room.history_visibility', '');
        if (history_visibility) history_visibility = history_visibility.getContent().history_visibility;

        if (history_visibility == 'shared' && !shown_invite_warning_this_session) {
            inviteWarningDefer = invite_defer; // whether we continue depends on this defer
            var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
            Modal.createDialog(QuestionDialog, {
                title: "Warning",
                description: SHARE_HISTORY_WARNING,
                button: "Invite",
                onFinished: function(should_invite) {
                    if (should_invite) {
                        shown_invite_warning_this_session = true;
                        invite_defer.resolve();
                    } else {
                        invite_defer.reject(null);
                        // reset the promise so we don't auto-reject all invites from
                        // now on.
                        invite_defer = q.defer();
                    }
                }
            });
        } else {
            inviteWarningDefer.resolve();
        }

        var promise = inviteWarningDefer.promise;
        if (isEmailAddress) {
            promise = promise.then(function() {
                 return MatrixClientPeg.get().inviteByEmail(self.props.roomId, inputText);
            });
        }
        else {
            promise = promise.then(function() {
                return MatrixClientPeg.get().invite(self.props.roomId, inputText);
            });
        }

        self.setState({
            inviting: true
        });
        console.log(
            "Invite %s to %s - isEmail=%s", inputText, this.props.roomId, isEmailAddress
        );
        promise.done(function(res) {
            console.log("Invited %s", inputText);
            self.setState({
                inviting: false
            });
        }, function(err) {
            if (err !== null) {
                console.error("Failed to invite: %s", JSON.stringify(err));
                Modal.createDialog(ErrorDialog, {
                    title: "Server error whilst inviting",
                    description: err.message
                });
            }
            self.setState({
                inviting: false
            });
        });
    },

    getMemberDict: function() {
        if (!this.props.roomId) return {};
        var cli = MatrixClientPeg.get();
        var room = cli.getRoom(this.props.roomId);
        if (!room) return {};

        var all_members = room.currentState.members;

        // XXX: evil hack until SYJS-28 is fixed
        Object.keys(all_members).map(function(userId) {
            // work around a race where you might have a room member object
            // before the user object exists.  This may or may not cause
            // https://github.com/vector-im/vector-web/issues/186
            if (all_members[userId].user === null) {
                all_members[userId].user = MatrixClientPeg.get().getUser(userId);
            }

            if (all_members[userId].user && !all_members[userId].user.lastPresenceTs) {
                all_members[userId].user.lastPresenceTs = Date.now();
            }
        });

        return all_members;
    },

    roomMembers: function(limit) {
        var all_members = this.memberDict || {};
        var all_user_ids = Object.keys(all_members);

        if (this.memberSort) all_user_ids.sort(this.memberSort);

        var to_display = [];
        var count = 0;
        for (var i = 0; i < all_user_ids.length && (limit === undefined || count < limit); ++i) {
            var user_id = all_user_ids[i];
            var m = all_members[user_id];

            if (m.membership == 'join' || m.membership == 'invite') {
                to_display.push(user_id);
                ++count;
            }
        }
        return to_display;
    },

    _createOverflowTile: function(overflowCount, totalCount) {
        // For now we'll pretend this is any entity. It should probably be a separate tile.
        var EntityTile = sdk.getComponent("rooms.EntityTile");
        var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
        var text = "and " + overflowCount + " other" + (overflowCount > 1 ? "s" : "") +  "...";
        return (
            <EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
                <BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
            } name={text} presenceState="online" suppressOnHover={true}
            onClick={this._showFullMemberList} />
        );
    },

    _showFullMemberList: function() {
        this.setState({
            truncateAt: -1
        });
    },

    memberSort: function(userIdA, userIdB) {
        var userA = this.memberDict[userIdA].user;
        var userB = this.memberDict[userIdB].user;

        var presenceMap = {
            online: 3,
            unavailable: 2,
            offline: 1
        };

        var presenceOrdA = userA ? presenceMap[userA.presence] : 0;
        var presenceOrdB = userB ? presenceMap[userB.presence] : 0;

        if (presenceOrdA != presenceOrdB) {
            return presenceOrdB - presenceOrdA;
        }

        var latA = userA ? (userA.lastPresenceTs - (userA.lastActiveAgo || userA.lastPresenceTs)) : 0;
        var latB = userB ? (userB.lastPresenceTs - (userB.lastActiveAgo || userB.lastPresenceTs)) : 0;

        return latB - latA;
    },

    onSearchQueryChanged: function(input) {
        this.setState({
            searchQuery: input
        });
    },

    makeMemberTiles: function(membership, query) {
        var MemberTile = sdk.getComponent("rooms.MemberTile");
        query = (query || "").toLowerCase();

        var self = this;

        var memberList = self.state.members.filter(function(userId) {
            var m = self.memberDict[userId];
            if (query && m.name.toLowerCase().indexOf(query) === -1) {
                return false;
            }
            return m.membership == membership;
        }).map(function(userId) {
            var m = self.memberDict[userId];
            return (
                <MemberTile key={userId} member={m} ref={userId} />
            );
        });

        // XXX: surely this is not the right home for this logic.
        if (membership === "invite") {
            // include 3pid invites (m.room.third_party_invite) state events.
            // The HS may have already converted these into m.room.member invites so
            // we shouldn't add them if the 3pid invite state key (token) is in the
            // member invite (content.third_party_invite.signed.token)
            var room = MatrixClientPeg.get().getRoom(this.props.roomId);
            var EntityTile = sdk.getComponent("rooms.EntityTile");
            if (room) {
                room.currentState.getStateEvents("m.room.third_party_invite").forEach(
                function(e) {
                    // any events without these keys are not valid 3pid invites, so we ignore them
                    var required_keys = ['key_validity_url', 'public_key', 'display_name'];
                    for (var i = 0; i < required_keys.length; ++i) {
                        if (e.getContent()[required_keys[i]] === undefined) return;
                    }

                    // discard all invites which have a m.room.member event since we've
                    // already added them.
                    var memberEvent = room.currentState.getInviteForThreePidToken(e.getStateKey());
                    if (memberEvent) {
                        return;
                    }
                    memberList.push(
                        <EntityTile key={e.getStateKey()} name={e.getContent().display_name} />
                    )
                })
            }
        }

        return memberList;
    },

    render: function() {
        var InviteMemberList = sdk.getComponent("rooms.InviteMemberList");

        var invitedSection = null;
        var invitedMemberTiles = this.makeMemberTiles('invite', this.state.searchQuery);
        if (invitedMemberTiles.length > 0) {
            invitedSection = (

Invited

                        {invitedMemberTiles}

            );
        }

        var inviteMemberListSection;
        if (this.state.inviting) {
            var Loader = sdk.getComponent("elements.Spinner");
            inviteMemberListSection = (
                <Loader />
            );
        }
        else {
            inviteMemberListSection = (
                <InviteMemberList roomId={this.props.roomId}
                    onSearchQueryChanged={this.onSearchQueryChanged}
                    onInvite={this.onInvite} />
            );
        }

        var TruncatedList = sdk.getComponent("elements.TruncatedList");
        return (

                    {inviteMemberListSection}
                    <GeminiScrollbar autoshow={true} className="mx_MemberList_joined mx_MemberList_outerWrapper">
                        <TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
                                createOverflowElement={this._createOverflowTile}>
                            {this.makeMemberTiles('join', this.state.searchQuery)}
                        </TruncatedList>
                        {invitedSection}
                    </GeminiScrollbar>

        );
    }
});

<|endoftext|>


<commit_before><commit_msg>- first commit<commit_after>import config from '../config';

export default Constants = {
  // Anchor constants
  TOP_LEFT: 'topleft',
  TOP_RIGHT: 'topright',
  BOTTOM_LEFT: 'bottomleft',
  BOTTOM_RIGHT: 'bottomright',
  TOP: 'top',
  BOTTOM: 'bottom',
  LEFT: 'left',
  RIGHT: 'right',
  CENTER: 'center',
  MIDDLE: 'middle',

  // Axis constants
  HORIZONTAL: 'horizontal',
  VERTICAL: 'vertical',

  // Direction constants
  LEFT_TO_RIGHT: 12,
  RIGHT_TO_LEFT: 13,
  TOP_TO_BOTTOM: 14,
  BOTTOM_TO_TOP: 15,

  // Resizing constants
  STRETCH: 16,
  FIT: 17,
  FILL: 18,
  NOSCALE: 19,

  // Math constants
  DEGREES: 57.2957795,
  RADIANS: 0.0174532925,
  PI2: 6.2831853071,

  // Creatine constants
  VERSION: config.version,
  DEFAULT_CONFIG: {
    environment: {
      debug: false,
      canvas: {
        selector: '.game-canvas',
        width: 1080,
        height: 1700,
        showFPS: true,
      },
      ticker: {
        FPS: 60,
        timingMode: EaselJS.Ticker.RAF,
      },
    },
    game: {}
  }
}

<|endoftext|>