<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017, 2018, 2019 New Vector 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.
*/

import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import sdk from '../../../index';
import Modal from "../../../Modal";
import MatrixClientPeg from "../../../MatrixClientPeg";
import SdkConfig from "../../../SdkConfig";

import PasswordReset from "../../../PasswordReset";

// Phases
// Show controls to configure server details
const PHASE_SERVER_DETAILS = 0;
// Show the forgot password inputs
const PHASE_FORGOT = 1;
// Email is in the process of being sent
const PHASE_SENDING_EMAIL = 2;
// Email has been sent
const PHASE_EMAIL_SENT = 3;
// User has clicked the link in email and completed reset
const PHASE_DONE = 4;

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

    propTypes: {
        // The default server name to use when the user hasn't specified
        // one. If set, `defaultHsUrl` and `defaultHsUrl` were derived for this
        // via `.well-known` discovery. The server name is used instead of the
        // HS URL when talking about "your account".
        defaultServerName: PropTypes.string,
        // An error passed along from higher up explaining that something
        // went wrong when finding the defaultHsUrl.
        defaultServerDiscoveryError: PropTypes.string,

        defaultHsUrl: PropTypes.string,
        defaultIsUrl: PropTypes.string,
        customHsUrl: PropTypes.string,
        customIsUrl: PropTypes.string,

        onLoginClick: PropTypes.func,
        onComplete: PropTypes.func.isRequired,
    },

    getInitialState: function() {
        return {
            enteredHsUrl: this.props.customHsUrl || this.props.defaultHsUrl,
            enteredIsUrl: this.props.customIsUrl || this.props.defaultIsUrl,
            phase: PHASE_FORGOT,
            email: "",
            password: "",
            password2: "",
            errorText: null,
        };
    },

    submitPasswordReset: function(hsUrl, identityUrl, email, password) {
        this.setState({
            phase: PHASE_SENDING_EMAIL,
        });
        this.reset = new PasswordReset(hsUrl, identityUrl);
        this.reset.resetPassword(email, password).done(() => {
            this.setState({
                phase: PHASE_EMAIL_SENT,
            });
        }, (err) => {
            this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
            this.setState({
                phase: PHASE_FORGOT,
            });
        });
    },

    onVerify: function(ev) {
        ev.preventDefault();
        if (!this.reset) {
            console.error("onVerify called before submitPasswordReset!");
            return;
        }
        this.reset.checkEmailLinkClicked().done((res) => {
            this.setState({ phase: PHASE_DONE });
        }, (err) => {
            this.showErrorDialog(err.message);
        });
    },

    onSubmitForm: function(ev) {
        ev.preventDefault();

        // Don't allow the user to register if there's a discovery error
        // Without this, the user could end up registering on the wrong homeserver.
        if (this.props.defaultServerDiscoveryError) {
            this.setState({errorText: this.props.defaultServerDiscoveryError});
            return;
        }

        if (!this.state.email) {
            this.showErrorDialog(_t('The email address linked to your account must be entered.'));
        } else if (!this.state.password || !this.state.password2) {
            this.showErrorDialog(_t('A new password must be entered.'));
        } else if (this.state.password !== this.state.password2) {
            this.showErrorDialog(_t('New passwords must match each other.'));
        } else {
            const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
            Modal.createTrackedDialog('Forgot Password Warning', '', QuestionDialog, {
                title: _t('Warning!'),
                description:

                        { _t(
                            "Changing your password will reset any end-to-end encryption keys " +
                            "on all of your devices, making encrypted chat history unreadable. Set up " +
                            "Key Backup or export your room keys from another device before resetting your " +
                            "password.",
                        ) }

,
                button: _t('Continue'),
                onFinished: (confirmed) => {
                    if (confirmed) {
                        this.submitPasswordReset(
                            this.state.enteredHsUrl, this.state.enteredIsUrl,
                            this.state.email, this.state.password,
                        );
                    }
                },
            });
        }
    },

    onInputChanged: function(stateKey, ev) {
        this.setState({
            [stateKey]: ev.target.value,
        });
    },

    onServerConfigChange: function(config) {
        const newState = {};
        if (config.hsUrl !== undefined) {
            newState.enteredHsUrl = config.hsUrl;
        }
        if (config.isUrl !== undefined) {
            newState.enteredIsUrl = config.isUrl;
        }
        this.setState(newState);
    },

    onServerDetailsNextPhaseClick(ev) {
        ev.stopPropagation();
        this.setState({
            phase: PHASE_FORGOT,
        });
    },

    onEditServerDetailsClick(ev) {
        ev.preventDefault();
        ev.stopPropagation();
        this.setState({
            phase: PHASE_SERVER_DETAILS,
        });
    },

    onLoginClick: function(ev) {
        ev.preventDefault();
        ev.stopPropagation();
        this.props.onLoginClick();
    },

    showErrorDialog: function(body, title) {
        const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
        Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
            title: title,
            description: body,
        });
    },

    renderServerDetails() {
        const ServerConfig = sdk.getComponent("auth.ServerConfig");
        const AccessibleButton = sdk.getComponent("elements.AccessibleButton");

        if (SdkConfig.get()['disable_custom_urls']) {
            return null;
        }

        return

            <ServerConfig
                defaultHsUrl={this.props.defaultHsUrl}
                defaultIsUrl={this.props.defaultIsUrl}
                customHsUrl={this.state.enteredHsUrl}
                customIsUrl={this.state.enteredIsUrl}
                onServerConfigChange={this.onServerConfigChange}
                delayTimeMs={0} />
            <AccessibleButton className="mx_Login_submit"
                onClick={this.onServerDetailsNextPhaseClick}
            >
                {_t("Next")}
            </AccessibleButton>

;
    },

    renderForgot() {
        const Field = sdk.getComponent('elements.Field');

        let errorText = null;
        const err = this.state.errorText || this.props.defaultServerDiscoveryError;
        if (err) {
            errorText =
{ err }
;
        }

        let yourMatrixAccountText = _t('Your Matrix account');
        if (this.state.enteredHsUrl === this.props.defaultHsUrl) {
            yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
                serverName: this.props.defaultServerName,
            });
        } else {
            try {
                const parsedHsUrl = new URL(this.state.enteredHsUrl);
                yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
                    serverName: parsedHsUrl.hostname,
                });
            } catch (e) {
                errorText =
{_t(
                    "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please " +
                    "enter a valid URL including the protocol prefix.",
                {
                    hsUrl: this.state.enteredHsUrl,
                })}
;
            }
        }

        // If custom URLs are allowed, wire up the server details edit link.
        let editLink = null;
        if (!SdkConfig.get()['disable_custom_urls']) {
            editLink =
                {_t('Change')}
            ;
        }

        return

                {yourMatrixAccountText}
                {editLink}

            {errorText}
            <form onSubmit={this.onSubmitForm}>

                    <Field
                        id="mx_ForgotPassword_email"
                        name="reset_email" // define a name so browser's password autofill gets less confused
                        type="text"
                        label={_t('Email')}
                        value={this.state.email}
                        onChange={this.onInputChanged.bind(this, "email")}
                        autoFocus
                    />

                    <Field
                        id="mx_ForgotPassword_password"
                        name="reset_password"
                        type="password"
                        label={_t('Password')}
                        value={this.state.password}
                        onChange={this.onInputChanged.bind(this, "password")}
                    />
                    <Field
                        id="mx_ForgotPassword_passwordConfirm"
                        name="reset_password_confirm"
                        type="password"
                        label={_t('Confirm')}
                        value={this.state.password2}
                        onChange={this.onInputChanged.bind(this, "password2")}
                    />

                {_t(
                    'A verification email will be sent to your inbox to confirm ' +
                    'setting your new password.',
                )}
                <input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
            </form>

                {_t('Sign in instead')}

;
    },

    renderSendingEmail() {
        const Spinner = sdk.getComponent("elements.Spinner");
        return <Spinner />;
    },

    renderEmailSent() {
        return

            {_t("An email has been sent to %(emailAddress)s. Once you've followed the " +
                "link it contains, click below.", { emailAddress: this.state.email })}

            <input className="mx_Login_submit" type="button" onClick={this.onVerify}
                value={_t('I have verified my email address')} />

;
    },

    renderDone() {
        return

{_t("Your password has been reset.")}

{_t(
                "You have been logged out of all devices and will no longer receive " +
                "push notifications. To re-enable notifications, sign in again on each " +
                "device.",
            )}

            <input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
                value={_t('Return to login screen')} />

;
    },

    render: function() {
        const AuthPage = sdk.getComponent("auth.AuthPage");
        const AuthHeader = sdk.getComponent("auth.AuthHeader");
        const AuthBody = sdk.getComponent("auth.AuthBody");

        let resetPasswordJsx;
        switch (this.state.phase) {
            case PHASE_SERVER_DETAILS:
                resetPasswordJsx = this.renderServerDetails();
                break;
            case PHASE_FORGOT:
                resetPasswordJsx = this.renderForgot();
                break;
            case PHASE_SENDING_EMAIL:
                resetPasswordJsx = this.renderSendingEmail();
                break;
            case PHASE_EMAIL_SENT:
                resetPasswordJsx = this.renderEmailSent();
                break;
            case PHASE_DONE:
                resetPasswordJsx = this.renderDone();
                break;
        }

        return (
            <AuthPage>
                <AuthHeader />
                <AuthBody>

 { _t('Set a new password') }

                    {resetPasswordJsx}
                </AuthBody>
            </AuthPage>
        );
    },
});
<commit_msg>Clean up unused imports<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017, 2018, 2019 New Vector 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.
*/

import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import sdk from '../../../index';
import Modal from "../../../Modal";
import SdkConfig from "../../../SdkConfig";

import PasswordReset from "../../../PasswordReset";

// Phases
// Show controls to configure server details
const PHASE_SERVER_DETAILS = 0;
// Show the forgot password inputs
const PHASE_FORGOT = 1;
// Email is in the process of being sent
const PHASE_SENDING_EMAIL = 2;
// Email has been sent
const PHASE_EMAIL_SENT = 3;
// User has clicked the link in email and completed reset
const PHASE_DONE = 4;

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

    propTypes: {
        // The default server name to use when the user hasn't specified
        // one. If set, `defaultHsUrl` and `defaultHsUrl` were derived for this
        // via `.well-known` discovery. The server name is used instead of the
        // HS URL when talking about "your account".
        defaultServerName: PropTypes.string,
        // An error passed along from higher up explaining that something
        // went wrong when finding the defaultHsUrl.
        defaultServerDiscoveryError: PropTypes.string,

        defaultHsUrl: PropTypes.string,
        defaultIsUrl: PropTypes.string,
        customHsUrl: PropTypes.string,
        customIsUrl: PropTypes.string,

        onLoginClick: PropTypes.func,
        onComplete: PropTypes.func.isRequired,
    },

    getInitialState: function() {
        return {
            enteredHsUrl: this.props.customHsUrl || this.props.defaultHsUrl,
            enteredIsUrl: this.props.customIsUrl || this.props.defaultIsUrl,
            phase: PHASE_FORGOT,
            email: "",
            password: "",
            password2: "",
            errorText: null,
        };
    },

    submitPasswordReset: function(hsUrl, identityUrl, email, password) {
        this.setState({
            phase: PHASE_SENDING_EMAIL,
        });
        this.reset = new PasswordReset(hsUrl, identityUrl);
        this.reset.resetPassword(email, password).done(() => {
            this.setState({
                phase: PHASE_EMAIL_SENT,
            });
        }, (err) => {
            this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
            this.setState({
                phase: PHASE_FORGOT,
            });
        });
    },

    onVerify: function(ev) {
        ev.preventDefault();
        if (!this.reset) {
            console.error("onVerify called before submitPasswordReset!");
            return;
        }
        this.reset.checkEmailLinkClicked().done((res) => {
            this.setState({ phase: PHASE_DONE });
        }, (err) => {
            this.showErrorDialog(err.message);
        });
    },

    onSubmitForm: function(ev) {
        ev.preventDefault();

        // Don't allow the user to register if there's a discovery error
        // Without this, the user could end up registering on the wrong homeserver.
        if (this.props.defaultServerDiscoveryError) {
            this.setState({errorText: this.props.defaultServerDiscoveryError});
            return;
        }

        if (!this.state.email) {
            this.showErrorDialog(_t('The email address linked to your account must be entered.'));
        } else if (!this.state.password || !this.state.password2) {
            this.showErrorDialog(_t('A new password must be entered.'));
        } else if (this.state.password !== this.state.password2) {
            this.showErrorDialog(_t('New passwords must match each other.'));
        } else {
            const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
            Modal.createTrackedDialog('Forgot Password Warning', '', QuestionDialog, {
                title: _t('Warning!'),
                description:

                        { _t(
                            "Changing your password will reset any end-to-end encryption keys " +
                            "on all of your devices, making encrypted chat history unreadable. Set up " +
                            "Key Backup or export your room keys from another device before resetting your " +
                            "password.",
                        ) }

,
                button: _t('Continue'),
                onFinished: (confirmed) => {
                    if (confirmed) {
                        this.submitPasswordReset(
                            this.state.enteredHsUrl, this.state.enteredIsUrl,
                            this.state.email, this.state.password,
                        );
                    }
                },
            });
        }
    },

    onInputChanged: function(stateKey, ev) {
        this.setState({
            [stateKey]: ev.target.value,
        });
    },

    onServerConfigChange: function(config) {
        const newState = {};
        if (config.hsUrl !== undefined) {
            newState.enteredHsUrl = config.hsUrl;
        }
        if (config.isUrl !== undefined) {
            newState.enteredIsUrl = config.isUrl;
        }
        this.setState(newState);
    },

    onServerDetailsNextPhaseClick(ev) {
        ev.stopPropagation();
        this.setState({
            phase: PHASE_FORGOT,
        });
    },

    onEditServerDetailsClick(ev) {
        ev.preventDefault();
        ev.stopPropagation();
        this.setState({
            phase: PHASE_SERVER_DETAILS,
        });
    },

    onLoginClick: function(ev) {
        ev.preventDefault();
        ev.stopPropagation();
        this.props.onLoginClick();
    },

    showErrorDialog: function(body, title) {
        const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
        Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
            title: title,
            description: body,
        });
    },

    renderServerDetails() {
        const ServerConfig = sdk.getComponent("auth.ServerConfig");
        const AccessibleButton = sdk.getComponent("elements.AccessibleButton");

        if (SdkConfig.get()['disable_custom_urls']) {
            return null;
        }

        return

            <ServerConfig
                defaultHsUrl={this.props.defaultHsUrl}
                defaultIsUrl={this.props.defaultIsUrl}
                customHsUrl={this.state.enteredHsUrl}
                customIsUrl={this.state.enteredIsUrl}
                onServerConfigChange={this.onServerConfigChange}
                delayTimeMs={0} />
            <AccessibleButton className="mx_Login_submit"
                onClick={this.onServerDetailsNextPhaseClick}
            >
                {_t("Next")}
            </AccessibleButton>

;
    },

    renderForgot() {
        const Field = sdk.getComponent('elements.Field');

        let errorText = null;
        const err = this.state.errorText || this.props.defaultServerDiscoveryError;
        if (err) {
            errorText =
{ err }
;
        }

        let yourMatrixAccountText = _t('Your Matrix account');
        if (this.state.enteredHsUrl === this.props.defaultHsUrl) {
            yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
                serverName: this.props.defaultServerName,
            });
        } else {
            try {
                const parsedHsUrl = new URL(this.state.enteredHsUrl);
                yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
                    serverName: parsedHsUrl.hostname,
                });
            } catch (e) {
                errorText =
{_t(
                    "The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please " +
                    "enter a valid URL including the protocol prefix.",
                {
                    hsUrl: this.state.enteredHsUrl,
                })}
;
            }
        }

        // If custom URLs are allowed, wire up the server details edit link.
        let editLink = null;
        if (!SdkConfig.get()['disable_custom_urls']) {
            editLink =
                {_t('Change')}
            ;
        }

        return

                {yourMatrixAccountText}
                {editLink}

            {errorText}
            <form onSubmit={this.onSubmitForm}>

                    <Field
                        id="mx_ForgotPassword_email"
                        name="reset_email" // define a name so browser's password autofill gets less confused
                        type="text"
                        label={_t('Email')}
                        value={this.state.email}
                        onChange={this.onInputChanged.bind(this, "email")}
                        autoFocus
                    />

                    <Field
                        id="mx_ForgotPassword_password"
                        name="reset_password"
                        type="password"
                        label={_t('Password')}
                        value={this.state.password}
                        onChange={this.onInputChanged.bind(this, "password")}
                    />
                    <Field
                        id="mx_ForgotPassword_passwordConfirm"
                        name="reset_password_confirm"
                        type="password"
                        label={_t('Confirm')}
                        value={this.state.password2}
                        onChange={this.onInputChanged.bind(this, "password2")}
                    />

                {_t(
                    'A verification email will be sent to your inbox to confirm ' +
                    'setting your new password.',
                )}
                <input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
            </form>

                {_t('Sign in instead')}

;
    },

    renderSendingEmail() {
        const Spinner = sdk.getComponent("elements.Spinner");
        return <Spinner />;
    },

    renderEmailSent() {
        return

            {_t("An email has been sent to %(emailAddress)s. Once you've followed the " +
                "link it contains, click below.", { emailAddress: this.state.email })}

            <input className="mx_Login_submit" type="button" onClick={this.onVerify}
                value={_t('I have verified my email address')} />

;
    },

    renderDone() {
        return

{_t("Your password has been reset.")}

{_t(
                "You have been logged out of all devices and will no longer receive " +
                "push notifications. To re-enable notifications, sign in again on each " +
                "device.",
            )}

            <input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
                value={_t('Return to login screen')} />

;
    },

    render: function() {
        const AuthPage = sdk.getComponent("auth.AuthPage");
        const AuthHeader = sdk.getComponent("auth.AuthHeader");
        const AuthBody = sdk.getComponent("auth.AuthBody");

        let resetPasswordJsx;
        switch (this.state.phase) {
            case PHASE_SERVER_DETAILS:
                resetPasswordJsx = this.renderServerDetails();
                break;
            case PHASE_FORGOT:
                resetPasswordJsx = this.renderForgot();
                break;
            case PHASE_SENDING_EMAIL:
                resetPasswordJsx = this.renderSendingEmail();
                break;
            case PHASE_EMAIL_SENT:
                resetPasswordJsx = this.renderEmailSent();
                break;
            case PHASE_DONE:
                resetPasswordJsx = this.renderDone();
                break;
        }

        return (
            <AuthPage>
                <AuthHeader />
                <AuthBody>

 { _t('Set a new password') }

                    {resetPasswordJsx}
                </AuthBody>
            </AuthPage>
        );
    },
});
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Color = this.Color = Base.extend(new function() {

	var colorCache = {},
		colorContext;

	function nameToRGBColor(name) {
		var color = colorCache[name];
		if (!color) {
			// Use a 1x1 Canvas to draw to with the given name and then
			// retrieve the rgb values from. Build a cache for all these
			// colors on a per use basis.
			if (!colorContext) {
				var canvas = CanvasProvider.getCanvas(Size.create(1, 1));
				colorContext = canvas.getContext('2d');
				colorContext.globalCompositeOperation = 'copy';
			}
			// Set the fillStyle of the context to the passed name
			// and fill the canvas with it. Then retrieve the first pixel:
			colorContext.fillStyle = name;
			colorContext.fillRect(0, 0, 1, 1);
			var data = colorContext.getImageData(0, 0, 1, 1).data,
				rgb = [];
			for (var i = 0; i < 3; i++)
				rgb[i] = data[i] / 255;
			// If the name wasn't found, rgb is [0, 0, 0]
			if (rgb.join('') == '000' && name != 'black') {
				throw new Error('The named color "' + name
						+ '" does not exist.');
			} else {
				color = colorCache[name] = RGBColor.read(rgb);
			}
		}
		return color;
	}

	function hexToRGBColor(string) {
		var hex = string.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
		if (hex.length >= 4) {
			var rgb = new Array(3);
			for (var i = 0; i < 3; i++) {
				var channel = hex[i + 1];
				rgb[i] = parseInt(channel.length == 1
						? channel + channel : channel, 16) / 255;
			}
			return RGBColor.read(rgb);
		}
	};

	return {
		beans: true,

		initialize: function(arg) {
			var isArray = Array.isArray(arg);
			if (typeof arg == 'object' && !isArray) {
				if (!this._colorType) {
					// Called on the abstract Color class. Guess color type
					// from arg
					if (arg.red !== undefined) {
						return new RGBColor(arg.red, arg.green, arg.blue,
								arg.alpha);
					} else if (arg.gray !== undefined) {
						return new GrayColor(arg.gray, arg.alpha);
					} else if (arg.hue !== undefined) {
						return new HSBColor(arg.hue, arg.saturation,
								arg.brightness, arg.alpha);
					}
				} else {
					// Called on a subclass instance. Return the converted
					// color.
					var color = Color.read(arguments, 0, 1);
					return this._colorType
							? color.convert(this._colorType)
							: color;
				}
			} else if (typeof arg == 'string') {
				var rgbColor = arg.match(/^#[0-9a-f]{3,6}$/i)
						? hexToRGBColor(arg)
						: nameToRGBColor(arg);
				return this._colorType
						? rgbColor.convert(this._colorType)
						: rgbColor;
			} else {
				var components = isArray ? arg : arguments;
				if (!this._colorType) {
					// Called on the abstract Color class. Guess color type
					// from arg
					//if (components.length >= 4)
					//	return new CMYKColor(components);
					if (components.length >= 3)
						return new RGBColor(components);
					return new GrayColor(components);
				} else {
					// Called on a subclass instance. Just copy over
					// components.
					Base.each(this._components,
						function(name, i) {
							var value = components[i];
							// TODO: should we call the setter?
							// this['set' + name.capitalize()]
							this[name] = value !== undefined
									? value
									// TODO: is this correct?
									// Shouldn't alpha be set to -1?
									: name == 'alpha' ? 1 : null;
						}, this);
				}
			}
		},

		getType: function() {
			return this._colorType;
		},

		getComponents: function() {
			var l = this._components.length;
			var components = new Array(l);
			for (var i = 0; i < l; i++) {
				components[i] = this['_' + this._components[i]];
			}
			return components;
		},

		/**
		 * A value between 0 and 1 that specifies the color's alpha value.
		 * All colors of the different subclasses support alpha values.
		 */
		getAlpha: function() {
			return this._alpha != null ? this._alpha : 1;
		},

		setAlpha: function(alpha) {
			this._alpha = Math.min(Math.max(alpha, 0), 1);
			this._cssString = null;
		},

		/**
		 * Checks if the color has an alpha value.
		 *
		 * @return {@true if the color has an alpha value}
		 */
		hasAlpha: function() {
			return this._alpha != null;
		},

		/**
		 * Checks if the component color values of the color are the
		 * same as those of the supplied one.
		 *
		 * @param obj the GrayColor to compare with
		 * @return {@true if the GrayColor is the same}
		 */
		equals: function(color) {
			if (color._colorType == this._colorType) {
				for (var i = 0, l = this._components; i < l; i++) {
					var component = '_' + this._components[i];
					if (this[component] !== color[component])
						return false;
				}
				return true;
			}
			return false;
		},

		toString: function() {
			var string = '';
			for (var i = 0, l = this._components.length; i < l; i++) {
				var component = this._components[i];
				var value = this['_' + component];
				if (component == 'alpha' && value == null)
					value = 1;
				string += (i > 0 ? ', ' : '') + component + ': ' + value;
			}
			return '{ ' + string + ' }';
		},

		toCssString: function() {
			if (!this._cssString) {
				var color = this._colorType == 'rgb'
						? this
						: this.convert('rgb');
				var alpha = color.getAlpha();
				var components = [
					Math.round(color.getRed() * 255),
					Math.round(color.getGreen() * 255),
					Math.round(color.getBlue() * 255),
					alpha != null ? this.alpha : 1
				];
				this._cssString = 'rgba(' + components.join(', ') + ')';
			}
			return this._cssString;
		},

		getCanvasStyle: function() {
			return this.toCssString();
		}
	};
}, new function() {
	var converters = {
		'rgb-hsb': function(color) {
			var r = color._red,
				g = color._green,
				b = color._blue,
				alpha = color._alpha,
				max = Math.max(r, g, b),
				min = Math.min(r, g, b),
				delta = max - min,
				hue,
				saturation = (max != 0) ? delta / max : 0,
				brightness = max;
			if (saturation == 0) {
				hue = 0;
			} else {
				var rr = (max - r) / delta,
					gr = (max - g) / delta,
					br = (max - b) / delta;
				hue = r == max
					? br - gr
					: g == max
						? 2 + rr - br
						: 4 + gr - rr;
				hue /= 6;
				if (hue < 0)
					hue++;
			}
			return new HSBColor(hue * 360, saturation, brightness, alpha);
		},

		'hsb-rgb': function(color) {
			var h = color._hue,
				s = color._saturation,
				b = color._brightness,
				a = color._alpha,
				f = h % 60,
				p = (b * (1 - s)) / 1,
				q = (b * (60 - s * f)) / 60,
				t = (b * (60 - s * (60 - f))) / 60;
			switch (Math.floor(h / 60)) {
				case 0: return new RGBColor(b, t, p, a);
				case 1: return new RGBColor(q, b, p, a);
				case 2: return new RGBColor(p, b, t, a);
				case 3: return new RGBColor(p, q, b, a);
				case 4: return new RGBColor(t, p, b, a);
				case 5: return new RGBColor(b, p, q, a);
			}
		},

		'rgb-gray': function(color) {
			// Using the standard NTSC conversion formula that is used for
			// calculating the effective luminance of an RGB color:
			// http://www.mathworks.com/support/solutions/en/data/1-1ASCU/index.html?solution=1-1ASCU
			return new GrayColor(1 -
					(color._red * 0.2989
					+ color._green * 0.587
					+ color._blue * 0.114),
					color._alpha
				);
		},

		'gray-rgb': function(color) {
			var component = 1 - color.getGray();
			return new RGBColor(component, component, component, color._alpha);
		},

		'hsb-gray': function(color) {
			var rgbColor = converters['hsb-rgb'](color);
			return converters['rgb-gray'](rgbColor);
		},

		'gray-hsb': function(color) {
			return new HSBColor(0, 0, 1 - color._gray, color._alpha);
		}
	};

	return {
		convert: function(type) {
			return this._colorType == type
				? this
				: converters[this._colorType + '-' + type](this);
		}
	};
}, new function() {
	return Base.each({
		rgb: ['red', 'green', 'blue'],
		hsb: ['hue', 'saturation', 'brightness'],
		gray: ['gray']
	}, function(components, colorType) {
		Base.each(components, function(component) {
			this['get' + Base.capitalize(component)] = function() {
				return this.convert(colorType)[component];
			};
			this['set' + Base.capitalize(component)] = function(value) {
				var color = this.convert(colorType);
				color[component] = value;
				color = color.convert(this._colorType);
				for (var i = 0, l = this._components.length; i < l; i++) {
					this[this._components[i]] = color[this._components[i]];
				}
			};
		}, this);
	}, { beans: true });
});
<commit_msg>One more nameToRGBColor() simplification, by reversing return logic.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Color = this.Color = Base.extend(new function() {

	var colorCache = {},
		colorContext;

	function nameToRGBColor(name) {
		var color = colorCache[name];
		if (color)
			return color;
		// Use a 1x1 Canvas to draw to with the given name and then
		// retrieve the rgb values from. Build a cache for all these
		// colors on a per use basis.
		if (!colorContext) {
			var canvas = CanvasProvider.getCanvas(Size.create(1, 1));
			colorContext = canvas.getContext('2d');
			colorContext.globalCompositeOperation = 'copy';
		}
		// Set the fillStyle of the context to the passed name
		// and fill the canvas with it. Then retrieve the first pixel:
		colorContext.fillStyle = name;
		colorContext.fillRect(0, 0, 1, 1);
		var data = colorContext.getImageData(0, 0, 1, 1).data,
			rgb = [];
		for (var i = 0; i < 3; i++)
			rgb[i] = data[i] / 255;
		// If the name wasn't found, rgb is [0, 0, 0]
		if (rgb.join('') == '000' && name != 'black') {
			throw new Error('The named color "' + name
					+ '" does not exist.');
		}
		return (colorCache[name] = RGBColor.read(rgb));
	}

	function hexToRGBColor(string) {
		var hex = string.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
		if (hex.length >= 4) {
			var rgb = new Array(3);
			for (var i = 0; i < 3; i++) {
				var channel = hex[i + 1];
				rgb[i] = parseInt(channel.length == 1
						? channel + channel : channel, 16) / 255;
			}
			return RGBColor.read(rgb);
		}
	};

	return {
		beans: true,

		initialize: function(arg) {
			var isArray = Array.isArray(arg);
			if (typeof arg == 'object' && !isArray) {
				if (!this._colorType) {
					// Called on the abstract Color class. Guess color type
					// from arg
					if (arg.red !== undefined) {
						return new RGBColor(arg.red, arg.green, arg.blue,
								arg.alpha);
					} else if (arg.gray !== undefined) {
						return new GrayColor(arg.gray, arg.alpha);
					} else if (arg.hue !== undefined) {
						return new HSBColor(arg.hue, arg.saturation,
								arg.brightness, arg.alpha);
					}
				} else {
					// Called on a subclass instance. Return the converted
					// color.
					var color = Color.read(arguments, 0, 1);
					return this._colorType
							? color.convert(this._colorType)
							: color;
				}
			} else if (typeof arg == 'string') {
				var rgbColor = arg.match(/^#[0-9a-f]{3,6}$/i)
						? hexToRGBColor(arg)
						: nameToRGBColor(arg);
				return this._colorType
						? rgbColor.convert(this._colorType)
						: rgbColor;
			} else {
				var components = isArray ? arg : arguments;
				if (!this._colorType) {
					// Called on the abstract Color class. Guess color type
					// from arg
					//if (components.length >= 4)
					//	return new CMYKColor(components);
					if (components.length >= 3)
						return new RGBColor(components);
					return new GrayColor(components);
				} else {
					// Called on a subclass instance. Just copy over
					// components.
					Base.each(this._components,
						function(name, i) {
							var value = components[i];
							// TODO: should we call the setter?
							// this['set' + name.capitalize()]
							this[name] = value !== undefined
									? value
									// TODO: is this correct?
									// Shouldn't alpha be set to -1?
									: name == 'alpha' ? 1 : null;
						}, this);
				}
			}
		},

		getType: function() {
			return this._colorType;
		},

		getComponents: function() {
			var l = this._components.length;
			var components = new Array(l);
			for (var i = 0; i < l; i++) {
				components[i] = this['_' + this._components[i]];
			}
			return components;
		},

		/**
		 * A value between 0 and 1 that specifies the color's alpha value.
		 * All colors of the different subclasses support alpha values.
		 */
		getAlpha: function() {
			return this._alpha != null ? this._alpha : 1;
		},

		setAlpha: function(alpha) {
			this._alpha = Math.min(Math.max(alpha, 0), 1);
			this._cssString = null;
		},

		/**
		 * Checks if the color has an alpha value.
		 *
		 * @return {@true if the color has an alpha value}
		 */
		hasAlpha: function() {
			return this._alpha != null;
		},

		/**
		 * Checks if the component color values of the color are the
		 * same as those of the supplied one.
		 *
		 * @param obj the GrayColor to compare with
		 * @return {@true if the GrayColor is the same}
		 */
		equals: function(color) {
			if (color._colorType == this._colorType) {
				for (var i = 0, l = this._components; i < l; i++) {
					var component = '_' + this._components[i];
					if (this[component] !== color[component])
						return false;
				}
				return true;
			}
			return false;
		},

		toString: function() {
			var string = '';
			for (var i = 0, l = this._components.length; i < l; i++) {
				var component = this._components[i];
				var value = this['_' + component];
				if (component == 'alpha' && value == null)
					value = 1;
				string += (i > 0 ? ', ' : '') + component + ': ' + value;
			}
			return '{ ' + string + ' }';
		},

		toCssString: function() {
			if (!this._cssString) {
				var color = this._colorType == 'rgb'
						? this
						: this.convert('rgb');
				var alpha = color.getAlpha();
				var components = [
					Math.round(color.getRed() * 255),
					Math.round(color.getGreen() * 255),
					Math.round(color.getBlue() * 255),
					alpha != null ? this.alpha : 1
				];
				this._cssString = 'rgba(' + components.join(', ') + ')';
			}
			return this._cssString;
		},

		getCanvasStyle: function() {
			return this.toCssString();
		}
	};
}, new function() {
	var converters = {
		'rgb-hsb': function(color) {
			var r = color._red,
				g = color._green,
				b = color._blue,
				alpha = color._alpha,
				max = Math.max(r, g, b),
				min = Math.min(r, g, b),
				delta = max - min,
				hue,
				saturation = (max != 0) ? delta / max : 0,
				brightness = max;
			if (saturation == 0) {
				hue = 0;
			} else {
				var rr = (max - r) / delta,
					gr = (max - g) / delta,
					br = (max - b) / delta;
				hue = r == max
					? br - gr
					: g == max
						? 2 + rr - br
						: 4 + gr - rr;
				hue /= 6;
				if (hue < 0)
					hue++;
			}
			return new HSBColor(hue * 360, saturation, brightness, alpha);
		},

		'hsb-rgb': function(color) {
			var h = color._hue,
				s = color._saturation,
				b = color._brightness,
				a = color._alpha,
				f = h % 60,
				p = (b * (1 - s)) / 1,
				q = (b * (60 - s * f)) / 60,
				t = (b * (60 - s * (60 - f))) / 60;
			switch (Math.floor(h / 60)) {
				case 0: return new RGBColor(b, t, p, a);
				case 1: return new RGBColor(q, b, p, a);
				case 2: return new RGBColor(p, b, t, a);
				case 3: return new RGBColor(p, q, b, a);
				case 4: return new RGBColor(t, p, b, a);
				case 5: return new RGBColor(b, p, q, a);
			}
		},

		'rgb-gray': function(color) {
			// Using the standard NTSC conversion formula that is used for
			// calculating the effective luminance of an RGB color:
			// http://www.mathworks.com/support/solutions/en/data/1-1ASCU/index.html?solution=1-1ASCU
			return new GrayColor(1 -
					(color._red * 0.2989
					+ color._green * 0.587
					+ color._blue * 0.114),
					color._alpha
				);
		},

		'gray-rgb': function(color) {
			var component = 1 - color.getGray();
			return new RGBColor(component, component, component, color._alpha);
		},

		'hsb-gray': function(color) {
			var rgbColor = converters['hsb-rgb'](color);
			return converters['rgb-gray'](rgbColor);
		},

		'gray-hsb': function(color) {
			return new HSBColor(0, 0, 1 - color._gray, color._alpha);
		}
	};

	return {
		convert: function(type) {
			return this._colorType == type
				? this
				: converters[this._colorType + '-' + type](this);
		}
	};
}, new function() {
	return Base.each({
		rgb: ['red', 'green', 'blue'],
		hsb: ['hue', 'saturation', 'brightness'],
		gray: ['gray']
	}, function(components, colorType) {
		Base.each(components, function(component) {
			this['get' + Base.capitalize(component)] = function() {
				return this.convert(colorType)[component];
			};
			this['set' + Base.capitalize(component)] = function(value) {
				var color = this.convert(colorType);
				color[component] = value;
				color = color.convert(this._colorType);
				for (var i = 0, l = this._components.length; i < l; i++) {
					this[this._components[i]] = color[this._components[i]];
				}
			};
		}, this);
	}, { beans: true });
});
<|endoftext|>


<commit_before>'use strict';

var leveldown = require('leveldown');
var levelup = require('levelup');
var Q = require('q');
var ttl = require('level-ttl');

function _noop() { /** noop */ }

/**
 * Create a hangar instance
 *
 * options:
 * - location - The location of the backing datastore
 * - [ttl] - The time-to-live for cache entries. default: 4 hours
 * - [checkFrequency] - The frequency to check TTL values. default: 5 minutes
 * - [keyEncoding] - The encoding to use for the keys. default: utf8 (string)
 * - [valueEncoding] - The encoding to use for the values. default: json
 *
 * @constructor
 * @param {Object} options - A set of options to use in the cache
 */
function Hangar(options) {
  this.options = options || {};
  if (typeof options.location !== 'string') {
    throw new Error('Must provide a valid location');
  }
  this.options.ttl = {
    // default ttl to 4 hours
    ttl: options.ttl || 1000 * 60 * 60 * 4
  };
  if (typeof options.checkFrequency !== 'number') {
    // default frequency check to 5 minutes
    this.options.checkFrequency = 1000 * 60 * 5;
  }
  // default keys to utf8 string encoding
  this.options.keyEncoding = options.keyEncoding || 'utf8';
  // default values to json encoding
  this.options.valueEncoding = options.valueEncoding || 'json';
}

/**
 * Start and open a connection to the cache
 *
 * @param {Function} [callback] - The callback will receive an error as the first parameter if the cache cannot be started
 */
Hangar.prototype.start = function (callback) {
  callback = callback || _noop;
  var self = this;
  try {
    self.db = ttl(levelup(self.options.location, self.options), {
      checkFrequency: self.options.checkFrequency
    });
  } catch (err) {
    return callback(err);
  }
  return callback();
};

/**
 * Stop and close the connection to the cache
 *
 * @param {Function} [callback] - The callback will receive an error as the first parameter if the cache cannot be started
 */
Hangar.prototype.stop = function (callback) {
  callback = callback || _noop;
  this.db.close(callback);
};

/**
 * Stop, close, and destroy the cache contents
 *
 * @param {Function} [callback] - The callback will receive an error as the first parameter if the cache cannot be started
 */
Hangar.prototype.drop = function (callback) {
  callback = callback || _noop;
  var self = this;
  self.stop(function (err) {
    if (err) {
      return callback(err);
    }
    leveldown.destroy(self.options.location, callback);
  });
};

/**
 * Retrieve an entry from the cache
 *
 * @param {String} key - The key to lookup
 * @param {Function} [callback] - The callback will receive an error as the first parameter if value cannot be found, or the value as the second paramater if it was found
 */
Hangar.prototype.get = function (key, callback) {
  callback = callback || _noop;
  this.db.get(key, callback);
};

/**
 * Eventually retrieve an entry from the cache
 *
 * Example:
 * _pget(this, 'k1').then(callback);
 *
 * @param {Object} thisArg - The object to use as this
 * @param {String} key - The key to lookup
 * @api private
 */
function _pget(thisArg) {
  var args = [].slice.call(arguments, 1);
  return Q.nbind(thisArg.get, thisArg).apply(thisArg, args);
}

/**
 * Retrieve entries from the cache. Order of key index will be maintained.
 *
 * @param {Array} keys - The keys to lookup
 * @param {Function} [callback] - The callback will receive an error as the first parameter if all of the values cannot be found, or all of the values as the second paramater if they were found
 */
Hangar.prototype.getMany = function (keys, callback) {
  callback = callback || _noop;
  var values = [];

  for (var i = 0, l = keys.length; i < l; i++) {
    values.push(_pget(this, keys[i]));
  }
  return Q.all(values).nodeify(callback);
};

/**
 * Set an entry in the cache
 *
 * @param {String} key - The key to set
 * @param {Object} value - The value to set
 * @param {Function} [callback] - The callback will receive an error as the first parameter if the key cannot be set
 */
Hangar.prototype.set = function (key, value, callback) {
  callback = callback || _noop;
  this.db.put(key, value, this.options.ttl, callback);
};

/**
 * Set multiple entries in the cache. Order of key and value index will be maintained.
 *
 * Example:
 * h.setMany(['k1', 'k2'], ['v1', 'v2'], function (err) {
 *   h.getMany(['k1', 'k2'], function (values) {
 *     console.log(values); //=> ['v1', 'v2']
 *   });
 * });
 *
 * @param {String} key - The keys to set
 * @param {Object} value - The values to set
 * @param {Function} [callback] - The callback will receive an error as the first parameter if all of the keys cannot be set
 */
Hangar.prototype.setMany = function (keys, values, callback) {
  callback = callback || _noop;
  var ops = [];

  for (var i = 0, l = keys.length; i < l; i++) {
    ops.push({
      type: 'put',
      key: keys[i],
      value: values[i]
    });
  }
  this.db.batch(ops, this.options.ttl, callback);
};

/**
 * Populate the cache with properties and values of an object literal
 *
 * Example:
 * var multi = { 'k1': 'v1', 'k2': 'v2' }
 *
 * h.setObject(multi, function (err) {
 *   h.getMany(['k1', 'k2'], function (values) {
 *     console.log(values); //=> ['v1', 'v2']
 *   });
 * });
 *
 * @param {Object} obj - The keys/values to set in object literal form
 * @param {Function} [callback] - The callback will receive an error as the first parameter if all of the keys cannot be set
 */
Hangar.prototype.setObject = function (obj, callback) {
  callback = callback || _noop;
  var ops = [];

  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      ops.push({
        type: 'put',
        key: key,
        value: obj[key]
      });
    }
  }

  this.db.batch(ops, this.options.ttl, callback);
};

/**
 * Remove an entry from the cache
 *
 * @param {String} key - The key of the entry to remove
 * @param {Function} [callback] - The callback will receive an error as the first parameter if all of the keys cannot be removed
 *
 */
Hangar.prototype.del = function (key, callback) {
  callback = callback || _noop;
  this.db.del(key, callback);
};

/**
 * Remove multiple entries from the cache
 *
 * @param {Array} keys - The keys of the entries to remove
 * @param {Function} [callback] - The callback will receive an error as the first parameter if all of the keys cannot be removed
 */
Hangar.prototype.delMany = function (keys, callback) {
  callback = callback || _noop;
  var ops = [];

  for (var i = 0, l = keys.length; i < l; i++) {
    ops.push({
      type: 'del',
      key: keys[i]
    });
  }
  this.db.batch(ops, callback);
};

module.exports = Hangar;
<commit_msg>Rework getMany logic<commit_after>'use strict';

var leveldown = require('leveldown');
var levelup = require('levelup');
var ttl = require('level-ttl');

var Promise = require('bluebird');

function _noop() { /* noop */ }

/**
 * Create a hangar instance
 *
 * options:
 * - location - The location of the backing datastore
 * - [ttl] - The time-to-live for cache entries. default: 4 hours
 * - [checkFrequency] - The frequency to check TTL values. default: 5 minutes
 * - [keyEncoding] - The encoding to use for the keys. default: utf8 (string)
 * - [valueEncoding] - The encoding to use for the values. default: json
 *
 * @constructor
 * @param {Object} options - A set of options to use in the cache
 */
function Hangar(options) {
  this.options = options || {};
  if (typeof options.location !== 'string') {
    throw new Error('Must provide a valid location');
  }
  this.options.ttl = {
    // default ttl to 4 hours
    ttl: options.ttl || 1000 * 60 * 60 * 4
  };
  if (typeof options.checkFrequency !== 'number') {
    // default frequency check to 5 minutes
    this.options.checkFrequency = 1000 * 60 * 5;
  }
  // default keys to utf8 string encoding
  this.options.keyEncoding = options.keyEncoding || 'utf8';
  // default values to json encoding
  this.options.valueEncoding = options.valueEncoding || 'json';
}

/**
 * Start and open a connection to the cache
 *
 * @param {Function} [callback]
 */
Hangar.prototype.start = function(callback) {
  callback = callback || _noop;
  var self = this;
  try {
    self.db = ttl(levelup(self.options.location, self.options), {
      checkFrequency: self.options.checkFrequency
    });
  } catch (err) {
    return callback(err);
  }
  return callback();
};

/**
 * Stop and close the connection to the cache
 *
 * @param {Function} [callback]
 */
Hangar.prototype.stop = function(callback) {
  callback = callback || _noop;
  this.db.close(callback);
};

/**
 * Stop, close, and destroy the cache contents
 *
 * @param {Function} [callback]
 */
Hangar.prototype.drop = function(callback) {
  callback = callback || _noop;
  var self = this;
  self.stop(function(err) {
    if (err) {
      return callback(err);
    }
    leveldown.destroy(self.options.location, callback);
  });
};

/**
 * Retrieve an entry from the cache
 *
 * @param {String} key - The key to lookup
 * @param {Function} [callback]
 */
Hangar.prototype.get = function(key, callback) {
  callback = callback || _noop;
  this.db.get(key, callback);
};

/**
 * Eventually retrieve an entry from the cache
 *
 * Example:
 * _pget(this, 'k1').then(callback);
 *
 * @param {Object} thisArg - The object to use as this
 * @param {String} key - The key to lookup
 * @api private
 */
function _pget(thisArg, key) {
  return new Promise(function(resolve, reject) {
    thisArg.get(key, function(err, value) {
      if (err) {
        return reject(err);
      }
      return resolve(value);
    });
  });
}

function _error(callback) {
  return function(err) {
    callback(err);
  };
}

function _success(callback) {
  return function(values) {
    callback(null, values);
  };
}

/**
 * Retrieve entries from the cache. Order of keys will be maintained.
 *
 * @param {Array} keys - The keys to lookup
 * @param {Function} [callback]
 */
Hangar.prototype.getMany = function(keys, callback) {
  callback = callback || _noop;
  var tasks = [];

  for (var i = 0, l = keys.length; i < l; i++) {
    tasks.push(_pget(this, keys[i]));
  }
  return Promise.all(tasks).then(_success(callback), _error(callback));
};

/**
 * Set an entry in the cache
 *
 * @param {String} key - The key to set
 * @param {Object} value - The value to set
 * @param {Function} [callback]
 */
Hangar.prototype.set = function(key, value, callback) {
  callback = callback || _noop;
  this.db.put(key, value, this.options.ttl, callback);
};

/**
 * Set multiple entries in the cache. Order of keys/values will be maintained.
 *
 * Example:
 * h.setMany(['k1', 'k2'], ['v1', 'v2'], function (err) {
 *   h.getMany(['k1', 'k2'], function (values) {
 *     console.log(values); //=> ['v1', 'v2']
 *   });
 * });
 *
 * @param {String} key - The keys to set
 * @param {Object} value - The values to set
 * @param {Function} [callback]
 */
Hangar.prototype.setMany = function(keys, values, callback) {
  callback = callback || _noop;
  var ops = [];

  for (var i = 0, l = keys.length; i < l; i++) {
    ops.push({
      type: 'put',
      key: keys[i],
      value: values[i]
    });
  }
  this.db.batch(ops, this.options.ttl, callback);
};

/**
 * Populate the cache with properties and values of an object literal
 *
 * Example:
 * var multi = { 'k1': 'v1', 'k2': 'v2' }
 *
 * h.setObject(multi, function (err) {
 *   h.getMany(['k1', 'k2'], function (values) {
 *     console.log(values); //=> ['v1', 'v2']
 *   });
 * });
 *
 * @param {Object} obj - The keys/values to set in object literal form
 * @param {Function} [callback]
 */
Hangar.prototype.setObject = function(obj, callback) {
  callback = callback || _noop;
  var ops = [];

  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      ops.push({
        type: 'put',
        key: key,
        value: obj[key]
      });
    }
  }

  this.db.batch(ops, this.options.ttl, callback);
};

/**
 * Remove an entry from the cache
 *
 * @param {String} key - The key of the entry to remove
 * @param {Function} [callback]
 *
 */
Hangar.prototype.del = function(key, callback) {
  callback = callback || _noop;
  this.db.del(key, callback);
};

/**
 * Remove multiple entries from the cache
 *
 * @param {Array} keys - The keys of the entries to remove
 * @param {Function} [callback]
 */
Hangar.prototype.delMany = function(keys, callback) {
  callback = callback || _noop;
  var ops = [];

  for (var i = 0, l = keys.length; i < l; i++) {
    ops.push({
      type: 'del',
      key: keys[i]
    });
  }
  this.db.batch(ops, callback);
};

module.exports = Hangar;
<|endoftext|>


<commit_before>var Model=function(options){
	this.options=options;
};
Model.prototype.generateCode=function(){
	return this.generateLines().join("\n");
};
Model.prototype.generateLines=function(){
	var e=this.options.encode;
	var code=this.options.code;
	var data=code.data;
	var i18n=this.options.i18n;
	return this.listLibraries().map(function(lib){
		return "library("+lib+")";
	}).concat([
		"",
		"# "+i18n('load data'),
		e(data)+"=read.csv('"+e(code.filename)+"')",
	],code.postprocess?[
		"# "+i18n('postprocess data'),
		e(code.postprocess),
	]:[],code.needSplit?[
		"# "+i18n('split data into training/test set'),
		"set.seed("+e(code.splitSeed)+")",
		"split=sample.split("+e(data)+"$"+e(code.y)+",SplitRatio="+e(code.splitRatio)+")",
		e(data.train)+"="+e(data)+"[split,]",
		e(data.test)+"="+e(data)+"[!split,]",
	]:[],this.generateModelProbLines(code.needSplit?data.train:data),[
		"# "+i18n('in-sample class prediction'),
		e(data['class'])+"=+("+e(data.prob)+">="+e(code.threshold)+")",
		"# "+i18n('in-sample accuracy'),
		e(data.acc)+"=mean("+e(data)+"$"+e(code.y)+"=="+e(data['class'])+")",
		"# "+i18n('in-sample AUC'),
		e(data.auc)+"=performance(",
		"\t"+"prediction("+e(data.prob)+","+e(data)+"$"+e(code.y)+"),'auc'",
		")@y.values[[1]]",
	]);
};
Model.prototype.listLibraries=function(){
	libs=[
		'ROCR', // for AUC computation
	];
	if (this.options.code.needSplit) {
		libs.push('caTools');
	}
	return libs;
	// TODO require libs from code
}

var BaselineModel=function(options){
	Model.call(this,options);
};
BaselineModel.prototype=Object.create(Model.prototype);
BaselineModel.prototype.constructor=BaselineModel;
BaselineModel.prototype.name='Baseline method';
BaselineModel.prototype.generateModelProbLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		// no model
		"# "+i18n('in-sample probability prediction'), // on complete dataset
		e(data.prob)+"=rep_len(mean("+e(data)+"$"+e(code.y)+"),nrow("+e(data)+"))",
	];
};

var LogregModel=function(options){
	Model.call(this,options);
};
LogregModel.prototype=Object.create(Model.prototype);
LogregModel.prototype.constructor=LogregModel;
LogregModel.prototype.name='Logistic regression';
LogregModel.prototype.generateModelProbLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		"# "+i18n('build model'),
		e(data.model)+"=glm("+e(code.formula)+",data="+e(data)+",family=binomial)",
		"# "+i18n('in-sample probability prediction'), // on complete dataset
		e(data.prob)+"=predict("+e(data.model)+",type='response')",
	];
};

var CartModel=function(options){
	Model.call(this,options);
};
CartModel.prototype=Object.create(Model.prototype);
CartModel.prototype.constructor=CartModel;
CartModel.prototype.name='Regression tree';
CartModel.prototype.generateModelProbLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		"# "+i18n('build model'),
		e(data.model)+"=rpart("+e(code.formula)+",data="+e(data)+")",
		"# "+i18n('in-sample probability prediction'), // on complete dataset
		e(data.prob)+"=predict("+e(data.model)+")",
	];
};
CartModel.prototype.listLibraries=function(){
	return Model.prototype.listLibraries.call(this).concat([
		'rpart',
	]);
};
<commit_msg>training/test split prediction code with wrong comments<commit_after>var Model=function(options){
	this.options=options;
};
Model.prototype.generateCode=function(){
	return this.generateLines().join("\n");
};
Model.prototype.generateLines=function(){
	var e=this.options.encode;
	var code=this.options.code;
	var data=code.data;
	var i18n=this.options.i18n;
	var lines=[];
	lines=lines.concat(this.listLibraries().map(function(lib){
		return "library("+lib+")";
	}),[
		"",
		"# "+i18n('load data'),
		e(data)+"=read.csv('"+e(code.filename)+"')",
	]);
	if (code.postprocess) {
		lines=lines.concat([
			"# "+i18n('postprocess data'),
			e(code.postprocess),
		]);
	}
	var modelLines;
	if (code.needSplit) {
		lines=lines.concat([
				"# "+i18n('split data into training/test set'),
				"set.seed("+e(code.splitSeed)+")",
				"split=sample.split("+e(data)+"$"+e(code.y)+",SplitRatio="+e(code.splitRatio)+")",
				e(data.train)+"="+e(data)+"[split,]",
				e(data.test)+"="+e(data)+"[!split,]",
		]);
		modelLines=this.generateModelLines(data.train);
		if (modelLines.length) {
			lines=lines.concat(["# "+i18n('build model')]);
		}
		lines=lines.concat(
			modelLines,
			["# "+i18n('training set probability prediction')],
			this.generateProbLines(data.train),
			this.generateClassLines(data.train),
			["# "+i18n('test set probability prediction')],
			this.generateProbLines(data.test),
			this.generateClassLines(data.test)
		);
	} else {
		modelLines=this.generateModelLines(data);
		if (modelLines.length) {
			lines=lines.concat(["# "+i18n('build model')]);
		}
		lines=lines.concat(
			modelLines,
			["# "+i18n('in-sample probability prediction')],
			this.generateProbLines(data),
			this.generateClassLines(data)
		);
	}
	return lines;
};
Model.prototype.generateClassLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		"# "+i18n('in-sample class prediction'),
		e(data['class'])+"=+("+e(data.prob)+">="+e(code.threshold)+")",
		"# "+i18n('in-sample accuracy'),
		e(data.acc)+"=mean("+e(data)+"$"+e(code.y)+"=="+e(data['class'])+")",
		"# "+i18n('in-sample AUC'),
		e(data.auc)+"=performance(",
		"\t"+"prediction("+e(data.prob)+","+e(data)+"$"+e(code.y)+"),'auc'",
		")@y.values[[1]]",
	];
};
Model.prototype.listLibraries=function(){
	libs=[
		'ROCR', // for AUC computation
	];
	if (this.options.code.needSplit) {
		libs.push('caTools');
	}
	return libs;
	// TODO require libs from code
}

var BaselineModel=function(options){
	Model.call(this,options);
};
BaselineModel.prototype=Object.create(Model.prototype);
BaselineModel.prototype.constructor=BaselineModel;
BaselineModel.prototype.name='Baseline method';
BaselineModel.prototype.generateModelLines=function(data){
	return []; // no model
}
BaselineModel.prototype.generateProbLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		e(data.prob)+"=rep_len(mean("+e(data)+"$"+e(code.y)+"),nrow("+e(data)+"))",
	];
};

var LogregModel=function(options){
	Model.call(this,options);
};
LogregModel.prototype=Object.create(Model.prototype);
LogregModel.prototype.constructor=LogregModel;
LogregModel.prototype.name='Logistic regression';
LogregModel.prototype.generateModelLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		e(data.model)+"=glm("+e(code.formula)+",data="+e(data)+",family=binomial)",
	];
};
LogregModel.prototype.generateProbLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		e(data.prob)+"=predict("+e(data.model)+",type='response')",
	];
};

var CartModel=function(options){
	Model.call(this,options);
};
CartModel.prototype=Object.create(Model.prototype);
CartModel.prototype.constructor=CartModel;
CartModel.prototype.name='Regression tree';
CartModel.prototype.generateModelLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		e(data.model)+"=rpart("+e(code.formula)+",data="+e(data)+")",
	];
};
CartModel.prototype.generateProbLines=function(data){
	var e=this.options.encode;
	var code=this.options.code;
	var i18n=this.options.i18n;
	return [
		e(data.prob)+"=predict("+e(data.model)+")",
	];
};
CartModel.prototype.listLibraries=function(){
	return Model.prototype.listLibraries.call(this).concat([
		'rpart',
	]);
};
<|endoftext|>


<commit_before>/*
 *
 * (c) Copyright Ascensio System Limited 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

/**
 *  Links.js
 *
 *  Created by Julia Radzhabova on 22.12.2017
 *  Copyright (c) 2017 Ascensio System SIA. All rights reserved.
 *
 */

define([
    'core',
    'documenteditor/main/app/view/Links',
    'documenteditor/main/app/view/NoteSettingsDialog',
    'documenteditor/main/app/view/HyperlinkSettingsDialog',
    'documenteditor/main/app/view/TableOfContentsSettings'
], function () {
    'use strict';

    DE.Controllers.Links = Backbone.Controller.extend(_.extend({
        models : [],
        collections : [
        ],
        views : [
            'Links'
        ],
        sdkViewName : '#id_main',

        initialize: function () {

            this.addListeners({
                'Links': {
                    'links:contents': this.onTableContents,
                    'links:update': this.onTableContentsUpdate,
                    'links:notes': this.onNotesClick,
                    'links:hyperlink': this.onHyperlinkClick
                }
            });
        },
        onLaunch: function () {
            this._state = {
                prcontrolsdisable:undefined
            };
        },

        setApi: function (api) {
            if (api) {
                this.api = api;
                this.api.asc_registerCallback('asc_onFocusObject', this.onApiFocusObject.bind(this));
                this.api.asc_registerCallback('asc_onCanAddHyperlink',      _.bind(this.onApiCanAddHyperlink, this));
                this.api.asc_registerCallback('asc_onCoAuthoringDisconnect',_.bind(this.onCoAuthoringDisconnect, this));
                Common.NotificationCenter.on('api:disconnect', _.bind(this.onCoAuthoringDisconnect, this));
            }
            return this;
        },

        setConfig: function(config) {
            this.toolbar = config.toolbar;
            this.view = this.createView('Links', {
                toolbar: this.toolbar.toolbar
            });
        },

        SetDisabled: function(state) {
            this.view && this.view.SetDisabled(state);
        },

        getView: function(name) {
            return !name && this.view ?
                this.view : Backbone.Controller.prototype.getView.call(this, name);
        },

        onCoAuthoringDisconnect: function() {
            this.SetDisabled(true);
        },

        onApiFocusObject: function(selectedObjects) {
            if (!this.toolbar.editMode) return;

            var pr, i = -1, type,
                paragraph_locked = false,
                header_locked = false,
                in_header = false,
                in_equation = false,
                in_image = false;

            while (++i < selectedObjects.length) {
                type = selectedObjects[i].get_ObjectType();
                pr   = selectedObjects[i].get_ObjectValue();

                if (type === Asc.c_oAscTypeSelectElement.Paragraph) {
                    paragraph_locked = pr.get_Locked();
                } else if (type === Asc.c_oAscTypeSelectElement.Header) {
                    header_locked = pr.get_Locked();
                    in_header = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Image) {
                    in_image = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Math) {
                    in_equation = true;
                }
            }

            this._state.prcontrolsdisable = paragraph_locked || header_locked;

            var need_disable = paragraph_locked || in_equation || in_image || in_header;
            _.each (this.view.btnsNotes, function(item){
                item.setDisabled(need_disable);
            }, this);
        },

        onApiCanAddHyperlink: function(value) {
            var need_disable = !value || this._state.prcontrolsdisable;

            if ( this.toolbar.editMode ) {
                _.each (this.view.btnsHyperlink, function(item){
                    item.setDisabled(need_disable);
                }, this);
            }
        },

        onHyperlinkClick: function(btn) {
            var me = this,
                win, props, text;

            if (me.api){

                var handlerDlg = function(dlg, result) {
                    if (result == 'ok') {
                        props = dlg.getSettings();
                        (text!==false)
                            ? me.api.add_Hyperlink(props)
                            : me.api.change_Hyperlink(props);
                    }

                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                };

                text = me.api.can_AddHyperlink();

                if (text !== false) {
                    win = new DE.Views.HyperlinkSettingsDialog({
                        api: me.api,
                        handler: handlerDlg
                    });

                    props = new Asc.CHyperlinkProperty();
                    props.put_Text(text);

                    win.show();
                    win.setSettings(props);
                } else {
                    var selectedElements = me.api.getSelectedElements();
                    if (selectedElements && _.isArray(selectedElements)){
                        _.each(selectedElements, function(el, i) {
                            if (selectedElements[i].get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink)
                                props = selectedElements[i].get_ObjectValue();
                        });
                    }
                    if (props) {
                        win = new DE.Views.HyperlinkSettingsDialog({
                            api: me.api,
                            handler: handlerDlg
                        });
                        win.show();
                        win.setSettings(props);
                    }
                }
            }

            Common.component.Analytics.trackEvent('ToolBar', 'Add Hyperlink');
        },

        onTableContents: function(type){
            switch (type) {
                case 0:
                    var props = this.api.asc_GetTableOfContentsPr(),
                        hasTable = !!props;
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(true);
                    props.put_RightAlignTab(true);
                    props.put_TabLeader( Asc.c_oAscTabLeader.Dot);
                    (hasTable) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(props);
                    break;
                case 1:
                    var props = this.api.asc_GetTableOfContentsPr(),
                        hasTable = !!props;
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(false);
                    props.put_TabLeader( Asc.c_oAscTabLeader.None);
                    (hasTable) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(props);
                    break;
                case 'settings':
                    var props = this.api.asc_GetTableOfContentsPr(),
                        me = this;
                    var win = new DE.Views.TableOfContentsSettings({
                        api: this.api,
                        props: props,
                        handler: function(result, value) {
                            if (result == 'ok') {
                                (props) ? me.api.asc_SetTableOfContentsPr(value) : me.api.asc_AddTableOfContents(value);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        }
                    });
                    win.show();
                    break;
                case 'remove':
                    this.api.asc_RemoveTableOfContents();
                    break;
            }
        },

        onTableContentsUpdate: function(type){
            if (type == 'pages')
                this.api.asc_updateTableOfContents();
            else
                this.api.asc_updateaddTableOfContents();
        },

        onNotesClick: function(type) {
            var me = this;
            switch (type) {
                case 'ins_footnote':
                    this.api.asc_AddFootnote();
                    break;
                case 'delele':
                    Common.UI.warning({
                        msg: this.view.confirmDeleteFootnotes,
                        buttons: ['yes', 'no'],
                        primary: 'yes',
                        callback: _.bind(function (btn) {
                            if (btn == 'yes') {
                                this.api.asc_RemoveAllFootnotes();
                            }
                            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
                        }, this)
                    });
                    break;
                case 'settings':
                    (new DE.Views.NoteSettingsDialog({
                        api: me.api,
                        handler: function (result, settings) {
                            if (settings) {
                                me.api.asc_SetFootnoteProps(settings.props, settings.applyToAll);
                                if (result == 'insert')
                                    me.api.asc_AddFootnote(settings.custom);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        },
                        props: me.api.asc_GetFootnoteProps()
                    })).show();
                    break;
                case 'prev':
                    this.api.asc_GotoFootnote(false);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
                case 'next':
                    this.api.asc_GotoFootnote(true);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
            }
        }

    }, DE.Controllers.Links || {}));
});<commit_msg>[DE] Update table of contents.<commit_after>/*
 *
 * (c) Copyright Ascensio System Limited 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

/**
 *  Links.js
 *
 *  Created by Julia Radzhabova on 22.12.2017
 *  Copyright (c) 2017 Ascensio System SIA. All rights reserved.
 *
 */

define([
    'core',
    'documenteditor/main/app/view/Links',
    'documenteditor/main/app/view/NoteSettingsDialog',
    'documenteditor/main/app/view/HyperlinkSettingsDialog',
    'documenteditor/main/app/view/TableOfContentsSettings'
], function () {
    'use strict';

    DE.Controllers.Links = Backbone.Controller.extend(_.extend({
        models : [],
        collections : [
        ],
        views : [
            'Links'
        ],
        sdkViewName : '#id_main',

        initialize: function () {

            this.addListeners({
                'Links': {
                    'links:contents': this.onTableContents,
                    'links:update': this.onTableContentsUpdate,
                    'links:notes': this.onNotesClick,
                    'links:hyperlink': this.onHyperlinkClick
                }
            });
        },
        onLaunch: function () {
            this._state = {
                prcontrolsdisable:undefined
            };
        },

        setApi: function (api) {
            if (api) {
                this.api = api;
                this.api.asc_registerCallback('asc_onFocusObject', this.onApiFocusObject.bind(this));
                this.api.asc_registerCallback('asc_onCanAddHyperlink',      _.bind(this.onApiCanAddHyperlink, this));
                this.api.asc_registerCallback('asc_onCoAuthoringDisconnect',_.bind(this.onCoAuthoringDisconnect, this));
                Common.NotificationCenter.on('api:disconnect', _.bind(this.onCoAuthoringDisconnect, this));
            }
            return this;
        },

        setConfig: function(config) {
            this.toolbar = config.toolbar;
            this.view = this.createView('Links', {
                toolbar: this.toolbar.toolbar
            });
        },

        SetDisabled: function(state) {
            this.view && this.view.SetDisabled(state);
        },

        getView: function(name) {
            return !name && this.view ?
                this.view : Backbone.Controller.prototype.getView.call(this, name);
        },

        onCoAuthoringDisconnect: function() {
            this.SetDisabled(true);
        },

        onApiFocusObject: function(selectedObjects) {
            if (!this.toolbar.editMode) return;

            var pr, i = -1, type,
                paragraph_locked = false,
                header_locked = false,
                in_header = false,
                in_equation = false,
                in_image = false;

            while (++i < selectedObjects.length) {
                type = selectedObjects[i].get_ObjectType();
                pr   = selectedObjects[i].get_ObjectValue();

                if (type === Asc.c_oAscTypeSelectElement.Paragraph) {
                    paragraph_locked = pr.get_Locked();
                } else if (type === Asc.c_oAscTypeSelectElement.Header) {
                    header_locked = pr.get_Locked();
                    in_header = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Image) {
                    in_image = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Math) {
                    in_equation = true;
                }
            }

            this._state.prcontrolsdisable = paragraph_locked || header_locked;

            var need_disable = paragraph_locked || in_equation || in_image || in_header;
            _.each (this.view.btnsNotes, function(item){
                item.setDisabled(need_disable);
            }, this);
        },

        onApiCanAddHyperlink: function(value) {
            var need_disable = !value || this._state.prcontrolsdisable;

            if ( this.toolbar.editMode ) {
                _.each (this.view.btnsHyperlink, function(item){
                    item.setDisabled(need_disable);
                }, this);
            }
        },

        onHyperlinkClick: function(btn) {
            var me = this,
                win, props, text;

            if (me.api){

                var handlerDlg = function(dlg, result) {
                    if (result == 'ok') {
                        props = dlg.getSettings();
                        (text!==false)
                            ? me.api.add_Hyperlink(props)
                            : me.api.change_Hyperlink(props);
                    }

                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                };

                text = me.api.can_AddHyperlink();

                if (text !== false) {
                    win = new DE.Views.HyperlinkSettingsDialog({
                        api: me.api,
                        handler: handlerDlg
                    });

                    props = new Asc.CHyperlinkProperty();
                    props.put_Text(text);

                    win.show();
                    win.setSettings(props);
                } else {
                    var selectedElements = me.api.getSelectedElements();
                    if (selectedElements && _.isArray(selectedElements)){
                        _.each(selectedElements, function(el, i) {
                            if (selectedElements[i].get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink)
                                props = selectedElements[i].get_ObjectValue();
                        });
                    }
                    if (props) {
                        win = new DE.Views.HyperlinkSettingsDialog({
                            api: me.api,
                            handler: handlerDlg
                        });
                        win.show();
                        win.setSettings(props);
                    }
                }
            }

            Common.component.Analytics.trackEvent('ToolBar', 'Add Hyperlink');
        },

        onTableContents: function(type){
            switch (type) {
                case 0:
                    var props = this.api.asc_GetTableOfContentsPr(),
                        hasTable = !!props;
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(true);
                    props.put_RightAlignTab(true);
                    props.put_TabLeader( Asc.c_oAscTabLeader.Dot);
                    (hasTable) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(props);
                    break;
                case 1:
                    var props = this.api.asc_GetTableOfContentsPr(),
                        hasTable = !!props;
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(false);
                    props.put_TabLeader( Asc.c_oAscTabLeader.None);
                    (hasTable) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(props);
                    break;
                case 'settings':
                    var props = this.api.asc_GetTableOfContentsPr(),
                        me = this;
                    var win = new DE.Views.TableOfContentsSettings({
                        api: this.api,
                        props: props,
                        handler: function(result, value) {
                            if (result == 'ok') {
                                (props) ? me.api.asc_SetTableOfContentsPr(value) : me.api.asc_AddTableOfContents(value);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        }
                    });
                    win.show();
                    break;
                case 'remove':
                    this.api.asc_RemoveTableOfContents();
                    break;
            }
        },

        onTableContentsUpdate: function(type){
            this.api.asc_UpdateTableOfContents(type == 'pages');
        },

        onNotesClick: function(type) {
            var me = this;
            switch (type) {
                case 'ins_footnote':
                    this.api.asc_AddFootnote();
                    break;
                case 'delele':
                    Common.UI.warning({
                        msg: this.view.confirmDeleteFootnotes,
                        buttons: ['yes', 'no'],
                        primary: 'yes',
                        callback: _.bind(function (btn) {
                            if (btn == 'yes') {
                                this.api.asc_RemoveAllFootnotes();
                            }
                            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
                        }, this)
                    });
                    break;
                case 'settings':
                    (new DE.Views.NoteSettingsDialog({
                        api: me.api,
                        handler: function (result, settings) {
                            if (settings) {
                                me.api.asc_SetFootnoteProps(settings.props, settings.applyToAll);
                                if (result == 'insert')
                                    me.api.asc_AddFootnote(settings.custom);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        },
                        props: me.api.asc_GetFootnoteProps()
                    })).show();
                    break;
                case 'prev':
                    this.api.asc_GotoFootnote(false);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
                case 'next':
                    this.api.asc_GotoFootnote(true);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
            }
        }

    }, DE.Controllers.Links || {}));
});<|endoftext|>


<commit_before>'use strict';

var assert = require('assert'),
    api = require('../api/api'),
    port = api.port + 1,
    mb = require('../mb').create(port),
    path = require('path'),
    isWindows = require('os').platform().indexOf('win') === 0,
    BaseHttpClient = require('../api/http/baseHttpClient'),
    promiseIt = require('../testHelpers').promiseIt,
    timeout = parseInt(process.env.MB_SLOW_TEST_TIMEOUT || 3000),
    smtp = require('../api/smtp/smtpClient'),
    http = BaseHttpClient.create('http'),
    https = BaseHttpClient.create('https');

describe('mb command line', function () {
    if (isWindows) {
        // slower process startup time because Windows
        this.timeout(timeout * 2);
    }
    else {
        this.timeout(timeout);
    }

    // I normally advocated separating the data needed for the assertions from the test setup,
    // but I wanted this to be a reasonably complex regression test
    promiseIt('should support complex configuration with --configfile in multiple files', function () {
        var args = ['--configfile', path.join(__dirname, 'imposters/imposters.ejs')];

        return mb.start(args).then(function () {
            return http.post('/orders', '', 4545);
        }).then(function (response) {
            assert.strictEqual(response.statusCode, 201);
            assert.strictEqual(response.headers.location, 'http://localhost:4545/orders/123');
            return http.post('/orders', '', 4545);
        }).then(function (response) {
            assert.strictEqual(response.statusCode, 201);
            assert.strictEqual(response.headers.location, 'http://localhost:4545/orders/234');
            return http.get('/orders/123', 4545);
        }).then(function (response) {
            assert.strictEqual(response.body, 'Order 123');
            return http.get('/orders/234', 4545);
        }).then(function (response) {
            assert.strictEqual(response.body, 'Order 234');
            return https.get('/accounts/123', 5555);
        }).then(function (response) {
            assert.strictEqual(response.statusCode, 401);
            return https.responseFor({
                method: 'GET',
                path: '/accounts/123',
                port: 5555,
                headers: { authorization: 'Basic blah===' }
            });
        }).then(function (response) {
            assert.ok(response.body.indexOf('<id>123</id>') > 0);
            return https.responseFor({
                method: 'GET',
                path: '/accounts/234',
                port: 5555,
                headers: { authorization: 'Basic blah===' }
            });
        }).then(function (response) {
            assert.strictEqual(response.statusCode, 404);
            return smtp.send({
                from: '"From 1" <from1@mb.org>',
                to: ['"To 1" <to1@mb.org>'],
                subject: 'subject 1',
                text: 'text 1'
            }, 6565);
        }).finally(function () {
            return mb.stop();
        });
    });

    // This is the response resolver injection example on /docs/api/injection
    promiseIt('should evaluate stringify function in templates when loading configuration files', function () {
        var args = ['--configfile', path.join(__dirname, 'templates/imposters.ejs'), '--allowInjection'];

        return mb.start(args).then(function () {
            return http.get('/first', 4546);
        }).then(function (response) {
            assert.deepEqual(response.body, { count: 1 });
            return http.get('/second', 4546);
        }).then(function (response) {
            assert.deepEqual(response.body, { count: 2 });
            return http.get('/first', 4546);
        }).then(function (response) {
            assert.deepEqual(response.body, { count: 1 });
            return http.get('/counter', 4546);
        }).then(function (response) {
            assert.strictEqual(response.body, 'There have been 2 proxied calls');
        }).finally(function () {
            return mb.stop();
        });
    });

    promiseIt('should not render through ejs when --no-parse option provided', function () {
        var args = ['--configfile', path.join(__dirname, 'noParse.json'), '--noParse'];

        return mb.start(args).then(function () {
            return http.get('/', 4545);
        }).then(function (response) {
            assert.strictEqual(response.body, '<% should not render through ejs');
        }).finally(function () {
            return mb.stop();
        });
    });
});
<commit_msg>Fixing test with case-sensitive filesystems<commit_after>'use strict';

var assert = require('assert'),
    api = require('../api/api'),
    port = api.port + 1,
    mb = require('../mb').create(port),
    path = require('path'),
    isWindows = require('os').platform().indexOf('win') === 0,
    BaseHttpClient = require('../api/http/baseHttpClient'),
    promiseIt = require('../testHelpers').promiseIt,
    timeout = parseInt(process.env.MB_SLOW_TEST_TIMEOUT || 3000),
    smtp = require('../api/smtp/smtpClient'),
    http = BaseHttpClient.create('http'),
    https = BaseHttpClient.create('https');

describe('mb command line', function () {
    if (isWindows) {
        // slower process startup time because Windows
        this.timeout(timeout * 2);
    }
    else {
        this.timeout(timeout);
    }

    // I normally advocated separating the data needed for the assertions from the test setup,
    // but I wanted this to be a reasonably complex regression test
    promiseIt('should support complex configuration with --configfile in multiple files', function () {
        var args = ['--configfile', path.join(__dirname, 'imposters/imposters.ejs')];

        return mb.start(args).then(function () {
            return http.post('/orders', '', 4545);
        }).then(function (response) {
            assert.strictEqual(response.statusCode, 201);
            assert.strictEqual(response.headers.location, 'http://localhost:4545/orders/123');
            return http.post('/orders', '', 4545);
        }).then(function (response) {
            assert.strictEqual(response.statusCode, 201);
            assert.strictEqual(response.headers.location, 'http://localhost:4545/orders/234');
            return http.get('/orders/123', 4545);
        }).then(function (response) {
            assert.strictEqual(response.body, 'Order 123');
            return http.get('/orders/234', 4545);
        }).then(function (response) {
            assert.strictEqual(response.body, 'Order 234');
            return https.get('/accounts/123', 5555);
        }).then(function (response) {
            assert.strictEqual(response.statusCode, 401);
            return https.responseFor({
                method: 'GET',
                path: '/accounts/123',
                port: 5555,
                headers: { authorization: 'Basic blah===' }
            });
        }).then(function (response) {
            assert.ok(response.body.indexOf('<id>123</id>') > 0);
            return https.responseFor({
                method: 'GET',
                path: '/accounts/234',
                port: 5555,
                headers: { authorization: 'Basic blah===' }
            });
        }).then(function (response) {
            assert.strictEqual(response.statusCode, 404);
            return smtp.send({
                from: '"From 1" <from1@mb.org>',
                to: ['"To 1" <to1@mb.org>'],
                subject: 'subject 1',
                text: 'text 1'
            }, 6565);
        }).finally(function () {
            return mb.stop();
        });
    });

    // This is the response resolver injection example on /docs/api/injection
    promiseIt('should evaluate stringify function in templates when loading configuration files', function () {
        var args = ['--configfile', path.join(__dirname, 'templates/imposters.ejs'), '--allowInjection'];

        return mb.start(args).then(function () {
            return http.get('/first', 4546);
        }).then(function (response) {
            assert.deepEqual(response.body, { count: 1 });
            return http.get('/second', 4546);
        }).then(function (response) {
            assert.deepEqual(response.body, { count: 2 });
            return http.get('/first', 4546);
        }).then(function (response) {
            assert.deepEqual(response.body, { count: 1 });
            return http.get('/counter', 4546);
        }).then(function (response) {
            assert.strictEqual(response.body, 'There have been 2 proxied calls');
        }).finally(function () {
            return mb.stop();
        });
    });

    promiseIt('should not render through ejs when --noParse option provided', function () {
        var args = ['--configfile', path.join(__dirname, 'noparse.json'), '--noParse'];

        return mb.start(args).then(function () {
            return http.get('/', 4545);
        }).then(function (response) {
            assert.strictEqual(response.body, '<% should not render through ejs');
        }).finally(function () {
            return mb.stop();
        });
    });
});
<|endoftext|>


<commit_before>var express = require('express');
var geocode = require('./geocode');
var mongoose = require('./mongo');
var otp = require('./otp');

/**
 * Expose `router`
 */

var router = module.exports = express.Router();

/**
 * Main check
 */

router.all('/', checkGeocoder, checkOTP, function(req, res) {
  var checks = {
    api: true,
    db: mongoose.connection.readyState === 1,
    geocoder: !req.geocoder,
    logger: 'not implemented',
    otp: !req.otp,
    worker: 'not implemented'
  };

  // TODO: implement checks
  res.status(200).send(checks);
});

/**
 * OTP
 */

router.all('/otp', checkOTP, function(req, res) {
  if (!req.otp) {
    res.status(204).end();
  } else {
    res.status(400).send(req.otp);
  }
});

/**
 * Check Geocoder
 */

function checkGeocoder(req, res, next) {
  geocode.suggest('1133 15th St NW, Washington, DC', function(err, suggestions) {
    req.geocoder = err;
    next();
  });
}

/**
 * Check OTP
 */

function checkOTP(req, res, next) {
  otp.get({
    url: '/index/routes'
  }, function(err, routes) {
    req.otp = err;
    next();
  });
}
<commit_msg>Use encode to check geocoder status<commit_after>var express = require('express');
var geocode = require('./geocode');
var mongoose = require('./mongo');
var otp = require('./otp');

/**
 * Expose `router`
 */

var router = module.exports = express.Router();

/**
 * Main check
 */

router.all('/', checkGeocoder, checkOTP, function(req, res) {
  var checks = {
    api: true,
    db: mongoose.connection.readyState === 1,
    geocoder: !req.geocoder,
    logger: 'not implemented',
    otp: !req.otp,
    worker: 'not implemented'
  };

  // TODO: implement checks
  res.status(200).send(checks);
});

/**
 * OTP
 */

router.all('/otp', checkOTP, function(req, res) {
  if (!req.otp) {
    res.status(204).end();
  } else {
    res.status(400).send(req.otp);
  }
});

/**
 * Check Geocoder
 */

function checkGeocoder(req, res, next) {
  geocode.encode('1133 15th St NW, Washington, DC', function(err, suggestions) {
    req.geocoder = err;
    next();
  });
}

/**
 * Check OTP
 */

function checkOTP(req, res, next) {
  otp.get({
    url: '/index/routes'
  }, function(err, routes) {
    req.otp = err;
    next();
  });
}
<|endoftext|>