<commit_before>import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { c } from 'ttag';
import {
    Alert,
    usePlans,
    FormModal,
    Button,
    Price,
    usePayment,
    Payment,
    Paragraph,
    useStep,
    useApiWithoutResult,
    useApi,
    useEventManager,
    useNotifications,
    SubTitle,
    Label,
    Row,
    Field,
    Wizard
} from 'react-components';
import { DEFAULT_CURRENCY, DEFAULT_CYCLE } from 'proton-shared/lib/constants';
import { checkSubscription, subscribe } from 'proton-shared/lib/api/payments';

import CustomMailSection from './CustomMailSection';
import CustomVPNSection from './CustomVPNSection';
import OrderSummary from './OrderSummary';
import FeaturesList from './FeaturesList';
import { getCheckParams } from './helpers';

const SubscriptionModal = ({ onClose, cycle, currency, coupon, plansMap, ...rest }) => {
    const api = useApi();
    const [loading, setLoading] = useState(false);
    const { method, setMethod, parameters, setParameters, canPay, setCardValidity } = usePayment(handleSubmit);
    const { createNotification } = useNotifications();
    const [check, setCheck] = useState({});
    const [plans] = usePlans();
    const [model, setModel] = useState({ cycle, currency, coupon, plansMap });
    const { call } = useEventManager();
    const { step, next, previous } = useStep(0);
    const { request } = useApiWithoutResult(subscribe);

    const callCheck = async (m = model) => {
        try {
            setLoading(true);
            const result = await api(checkSubscription(getCheckParams({ ...m, plans })));
            const { Coupon, Gift } = result;
            const { Code } = Coupon || {}; // Coupon can equals null

            if (m.coupon && m.coupon !== Code) {
                const text = c('Error').t`Your coupon is invalid or cannot be applied to your plan`;
                createNotification({ text, type: 'error' });
                throw new Error(text);
            }

            if (m.gift && !Gift) {
                const text = c('Error').t`Invalid gift code`;
                createNotification({ text, type: 'error' });
                throw new Error(text);
            }

            setLoading(false);
            setCheck(result);
            return result;
        } catch (error) {
            setLoading(false);
            throw error;
        }
    };

    const handleChangeModel = async (newModel = {}, requireCheck = false) => {
        if (loading) {
            return;
        }

        if (requireCheck) {
            await callCheck(newModel);
        }

        setModel(newModel);
    };

    const handleSubmit = async () => {
        if (!canPay) {
            return;
        }

        try {
            setLoading(true);
            await request({ Amount: check.AmountDue, ...getCheckParams({ ...model, plans }), ...parameters });
            await call();
            setLoading(false);
            next();
        } catch (error) {
            setLoading(false);
            throw error;
        }
    };

    const STEPS = [
        {
            title: c('Title').t`Order summary`,
            section: <OrderSummary plans={plans} model={model} check={check} onChange={handleChangeModel} />,
            async onSubmit() {
                if (!check.AmountDue) {
                    try {
                        setLoading(true);
                        await request({ Amount: check.AmountDue, ...getCheckParams({ ...model, plans }) });
                        await call();
                        setLoading(false);
                    } catch (error) {
                        setLoading(false);
                        throw error;
                    }
                }
                next();
            }
        },
        {
            title: c('Title').t`Thank you!`,
            section: (
                <>
                    <SubTitle>{c('Info').t`Thank you for your subscription`}</SubTitle>
                    <Paragraph>{c('Info').t`Your new features are now available`}</Paragraph>
                    <FeaturesList />
                </>
            ),
            onSubmit: onClose
        }
    ];

    if (plansMap.vpnplus || plansMap.vpnbasic) {
        STEPS.unshift({
            title: c('Title').t`VPN protection`,
            section: <CustomVPNSection plans={plans} model={model} onChange={handleChangeModel} />,
            async onSubmit() {
                await callCheck();
                next();
            }
        });
    }

    if (plansMap.plus || plansMap.professional) {
        STEPS.unshift({
            title: c('Title').t`Customization`,
            section: <CustomMailSection plans={plans} model={model} onChange={handleChangeModel} />,
            async onSubmit() {
                await callCheck();
                next();
            }
        });
    }

    if (check.AmountDue > 0) {
        // Insert it before the last one
        STEPS.splice(STEPS.length - 1, 0, {
            title: c('Title').t`Payment details`,
            section: (
                <>
                    <Alert>{c('Info')
                        .t`Your payment details are protected with TLS encryption and Swiss privacy laws.`}</Alert>
                    <Row>
                        {c('Label').t`Amount due`}
                        <Field>
                            <Price currency={model.currency}>{check.AmountDue}</Price>
                        </Field>
                    </Row>
                    <Payment
                        type="subscription"
                        method={method}
                        amount={check.AmountDue}
                        currency={model.currency}
                        onParameters={setParameters}
                        onMethod={setMethod}
                        onValidCard={setCardValidity}
                    />
                    <Alert type="warning" learnMore="https://protonmail.com/terms-and-conditions">{c('Info')
                        .t`By clicking Next, you agree to abide by ProtonMail's terms and conditions.`}</Alert>
                </>
            ),
            onSubmit: handleSubmit
        });
    }

    const hasCancel = !step;
    const hasClose = step === STEPS.length - 1;
    const hasPrevious = !hasClose && step > 0;
    const hasNext = !hasClose;
    const steps = STEPS.map(({ title }) => title);

    const close = (() => {
        if (hasCancel) {
            return c('Action').t`Cancel`;
        }
        if (hasClose) {
            return c('Action').t`Close`;
        }
        if (hasPrevious) {
            return <Button onClick={previous}>{c('Action').t`Previous`}</Button>;
        }
    })();

    return (
        <FormModal
            onClose={onClose}
            onSubmit={STEPS[step].onSubmit}
            title={STEPS[step].title}
            loading={loading}
            close={close}
            submit={hasNext && c('Action').t`Next`}
            {...rest}
        >
            <Wizard step={step} steps={steps} hideText={true} />
            {STEPS[step].section}
        </FormModal>
    );
};

SubscriptionModal.propTypes = {
    onClose: PropTypes.func,
    cycle: PropTypes.number,
    coupon: PropTypes.string,
    currency: PropTypes.string,
    plansMap: PropTypes.object
};

SubscriptionModal.defaultProps = {
    coupon: '',
    currency: DEFAULT_CURRENCY,
    cycle: DEFAULT_CYCLE,
    plansMap: {}
};

export default SubscriptionModal;
<commit_msg>Hotfix - Close the subscription modal if the subscription has change during the process<commit_after>import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { c } from 'ttag';
import {
    Alert,
    usePlans,
    FormModal,
    Button,
    Price,
    usePayment,
    Payment,
    Paragraph,
    useStep,
    useApiWithoutResult,
    useApi,
    useEventManager,
    useNotifications,
    useSubscription,
    SubTitle,
    Label,
    Row,
    Field,
    Wizard
} from 'react-components';
import { DEFAULT_CURRENCY, DEFAULT_CYCLE } from 'proton-shared/lib/constants';
import { checkSubscription, subscribe } from 'proton-shared/lib/api/payments';

import CustomMailSection from './CustomMailSection';
import CustomVPNSection from './CustomVPNSection';
import OrderSummary from './OrderSummary';
import FeaturesList from './FeaturesList';
import { getCheckParams } from './helpers';

const SubscriptionModal = ({ onClose, cycle, currency, coupon, plansMap, ...rest }) => {
    const api = useApi();
    const [subscription] = useSubscription();
    const initialRun = useRef(true);
    const [loading, setLoading] = useState(false);
    const { method, setMethod, parameters, setParameters, canPay, setCardValidity } = usePayment(handleSubmit);
    const { createNotification } = useNotifications();
    const [check, setCheck] = useState({});
    const [plans] = usePlans();
    const [model, setModel] = useState({ cycle, currency, coupon, plansMap });
    const { call } = useEventManager();
    const { step, next, previous } = useStep(0);
    const { request } = useApiWithoutResult(subscribe);

    const callCheck = async (m = model) => {
        try {
            setLoading(true);
            const result = await api(checkSubscription(getCheckParams({ ...m, plans })));
            const { Coupon, Gift } = result;
            const { Code } = Coupon || {}; // Coupon can equals null

            if (m.coupon && m.coupon !== Code) {
                const text = c('Error').t`Your coupon is invalid or cannot be applied to your plan`;
                createNotification({ text, type: 'error' });
                throw new Error(text);
            }

            if (m.gift && !Gift) {
                const text = c('Error').t`Invalid gift code`;
                createNotification({ text, type: 'error' });
                throw new Error(text);
            }

            setLoading(false);
            setCheck(result);
            return result;
        } catch (error) {
            setLoading(false);
            throw error;
        }
    };

    const handleChangeModel = async (newModel = {}, requireCheck = false) => {
        if (loading) {
            return;
        }

        if (requireCheck) {
            await callCheck(newModel);
        }

        setModel(newModel);
    };

    const handleSubmit = async () => {
        if (!canPay) {
            return;
        }

        try {
            setLoading(true);
            await request({ Amount: check.AmountDue, ...getCheckParams({ ...model, plans }), ...parameters });
            await call();
            setLoading(false);
            next();
        } catch (error) {
            setLoading(false);
            throw error;
        }
    };

    const STEPS = [
        {
            title: c('Title').t`Order summary`,
            closeIfSubscriptionChange: true,
            section: <OrderSummary plans={plans} model={model} check={check} onChange={handleChangeModel} />,
            async onSubmit() {
                if (!check.AmountDue) {
                    try {
                        setLoading(true);
                        await request({ Amount: check.AmountDue, ...getCheckParams({ ...model, plans }) });
                        await call();
                        setLoading(false);
                    } catch (error) {
                        setLoading(false);
                        throw error;
                    }
                }
                next();
            }
        },
        {
            title: c('Title').t`Thank you!`,
            section: (
                <>
                    <SubTitle>{c('Info').t`Thank you for your subscription`}</SubTitle>
                    <Paragraph>{c('Info').t`Your new features are now available`}</Paragraph>
                    <FeaturesList />
                </>
            ),
            onSubmit: onClose
        }
    ];

    if (plansMap.vpnplus || plansMap.vpnbasic) {
        STEPS.unshift({
            title: c('Title').t`VPN protection`,
            closeIfSubscriptionChange: true,
            section: <CustomVPNSection plans={plans} model={model} onChange={handleChangeModel} />,
            async onSubmit() {
                await callCheck();
                next();
            }
        });
    }

    if (plansMap.plus || plansMap.professional) {
        STEPS.unshift({
            title: c('Title').t`Customization`,
            closeIfSubscriptionChange: true,
            section: <CustomMailSection plans={plans} model={model} onChange={handleChangeModel} />,
            async onSubmit() {
                await callCheck();
                next();
            }
        });
    }

    if (check.AmountDue > 0) {
        // Insert it before the last one
        STEPS.splice(STEPS.length - 1, 0, {
            title: c('Title').t`Payment details`,
            section: (
                <>
                    <Alert>{c('Info')
                        .t`Your payment details are protected with TLS encryption and Swiss privacy laws.`}</Alert>
                    <Row>
                        {c('Label').t`Amount due`}
                        <Field>
                            <Price currency={model.currency}>{check.AmountDue}</Price>
                        </Field>
                    </Row>
                    <Payment
                        type="subscription"
                        method={method}
                        amount={check.AmountDue}
                        currency={model.currency}
                        onParameters={setParameters}
                        onMethod={setMethod}
                        onValidCard={setCardValidity}
                    />
                    <Alert type="warning" learnMore="https://protonmail.com/terms-and-conditions">{c('Info')
                        .t`By clicking Next, you agree to abide by ProtonMail's terms and conditions.`}</Alert>
                </>
            ),
            onSubmit: handleSubmit
        });
    }

    const hasCancel = !step;
    const hasClose = step === STEPS.length - 1;
    const hasPrevious = !hasClose && step > 0;
    const hasNext = !hasClose;
    const steps = STEPS.map(({ title }) => title);

    const close = (() => {
        if (hasCancel) {
            return c('Action').t`Cancel`;
        }
        if (hasClose) {
            return c('Action').t`Close`;
        }
        if (hasPrevious) {
            return <Button onClick={previous}>{c('Action').t`Previous`}</Button>;
        }
    })();

    useEffect(() => {
        // If the subscription changes when we are editing the config, we close the modal
        if (!initialRun.current && STEPS[step].closeIfSubscriptionChange) {
            onClose();
            return createNotification({ text: c('Warning').t`You subscription has changed`, type: 'warning' });
        }
        initialRun.current = false;
    }, [subscription]);

    return (
        <FormModal
            onClose={onClose}
            onSubmit={STEPS[step].onSubmit}
            title={STEPS[step].title}
            loading={loading}
            close={close}
            submit={hasNext && c('Action').t`Next`}
            {...rest}
        >
            <Wizard step={step} steps={steps} hideText={true} />
            {STEPS[step].section}
        </FormModal>
    );
};

SubscriptionModal.propTypes = {
    onClose: PropTypes.func,
    cycle: PropTypes.number,
    coupon: PropTypes.string,
    currency: PropTypes.string,
    plansMap: PropTypes.object
};

SubscriptionModal.defaultProps = {
    coupon: '',
    currency: DEFAULT_CURRENCY,
    cycle: DEFAULT_CYCLE,
    plansMap: {}
};

export default SubscriptionModal;
<|endoftext|>


<commit_before>'use strict'

function LoginWorld() {
    this.__init__()
}

LoginWorld.prototype = {

    scene: null,

    player: null,

    username_label: null,
    username_field: null,
    password_label: null,
    password_field: null,

    set_player_look_at: null,

    __init__: function() {
        // Create the scene.
        this.scene = new THREE.Scene()

        // Going to try to create a plane here.
        var plane_geometry = new THREE.PlaneGeometry(2000, 2000, 100, 100)
        plane_geometry.applyMatrix(new THREE.Matrix4().makeRotationX(- Math.PI / 2))
        //var plane_material = new THREE.MeshBasicMaterial({color: 0x0000ff})
        var plane_material = new THREE.MeshLambertMaterial({color: 0xccffcc, side: THREE.FrontSide, wireframe: true})
        var plane_mesh     = new THREE.Mesh(plane_geometry, plane_material)
        this.add_to_scene(plane_mesh)

        // Add lights.
        /*
        var color1 = '#b9ffd2'
        var color2 = '#090920'
        var light = new THREE.HemisphereLight(color1, color2, .5)
        this.add_to_scene(light)
        */

        var light3 = new THREE.PointLight(0xccffcc, .8, 0)
        light3.position.set(5, 100, 5)
        this.add_to_scene(light3)

        // Add the text.

        this.username_label = new Floating2DText(50, 20, 'Username :')
        this.username_label.create(this.scene)
        this.username_field = new Floating2DText(100, 20, '')
        this.username_field.create(this.scene)

        this.password_label = new Floating2DText(50, 20, 'Password :')
        this.password_label.create(this.scene)
        this.password_field = new Floating2DText(100, 20, '')
        this.password_field.create(this.scene)

        this.login_button = new Floating2DText(50, 20, 'Login')
        this.login_button.create(this.scene)

        var username_position0 = new THREE.Vector3(0, 70, 45)
        var username_position1 = new THREE.Vector3(75, 70, 45)

        var password_position0 = new THREE.Vector3(0, 40, 45)
        var password_position1 = new THREE.Vector3(75, 40, 45)

        this.username_label.update_position_and_look_at(username_position0, new THREE.Vector3(0, 70, 55))
        this.username_field.update_position_and_look_at(username_position1, new THREE.Vector3(75, 70, 55))

        this.password_label.update_position_and_look_at(password_position0, new THREE.Vector3(0, 40, 55))
        this.password_field.update_position_and_look_at(password_position1, new THREE.Vector3(75, 40, 55))

        this.login_button.update_position_and_look_at(new THREE.Vector3(0, 10, 45), new THREE.Vector3(0, 10, 55))

        // Handle key press events.
        document.addEventListener('keypress', this.on_key_press.bind(this), false)

        this.set_player_look_at = false
    },

    add_to_scene: function(object) {
        this.scene.add(object)
    },

    update: function() {

        if (this.set_player_look_at) {
            this.player.look_at(new THREE.Vector3(0, 70, 45))
            this.set_player_look_at = false
        }

        var position = this.player.fps_controls.get_position()
        var direction = this.player.fps_controls.get_direction()

        var raycaster = new THREE.Raycaster(position, direction)

        /*
        var intersects = raycaster.intersectObjects(this.scene.children)
        if (intersects.length > 1) {
            console.log('There are ' + intersects.length + ' intersections!')
        }*/

        this.username_label.being_looked_at = false
        this.password_label.being_looked_at = false
        this.username_field.being_looked_at = false
        this.password_field.being_looked_at = false

        var aa = raycaster.intersectObject(this.username_label.object3d, true)
        var bb = raycaster.intersectObject(this.username_field.object3d, true)
        var cc = raycaster.intersectObject(this.password_label.object3d, true)
        var dd = raycaster.intersectObject(this.password_field.object3d, true)

        if (aa.length > 0) {
            this.username_label.being_looked_at = true
        } else if (bb.length > 0) {
            this.username_field.being_looked_at = true
        } else if (cc.length > 0) {
            this.password_label.being_looked_at = true
        } else if (dd.length > 0) {
            this.password_field.being_looked_at = true
        }
    },

    on_key_press: function(event) {
        switch(event.keyCode) {
        case 101: // e
            if (this.username_field.being_looked_at) {
                this.username_field.engage()
                this.player.engage()
            } else if (this.password_field.being_looked_at) {
                this.password_field.engage()
                this.player.engage()
            }
            break
        case 92: // backslash
            if (this.player.is_engaged()) {
                this.player.disengage()
                if (this.username_field.being_looked_at) {
                    this.username_field.disengage()
                } else if (this.password_field.being_looked_at) {
                    this.password_field.disengage()
                }
            }
            break
        }

        if (this.username_field.is_engaged()) {
            this.username_field.parse_keycode(event)
        } else if (this.password_field.is_engaged()) {
            this.password_field.parse_keycode(event)
        }
    },

    enter_world: function() {
        if (this.player == null) {
            this.set_player_look_at = true
        } else {
            this.player.look_at(new THREE.Vector3(0, 70, 45))
        }
    },

    exit_world: function() {

    }
}<commit_msg>General updating 10.<commit_after>'use strict'

function LoginWorld() {
    this.__init__()
}

LoginWorld.prototype = {

    scene: null,

    player: null,

    username_label: null,
    username_field: null,
    password_label: null,
    password_field: null,

    set_player_look_at: null,

    __init__: function() {
        // Create the scene.
        this.scene = new THREE.Scene()

        // Going to try to create a plane here.
        var plane_geometry = new THREE.PlaneGeometry(2000, 2000, 100, 100)
        plane_geometry.applyMatrix(new THREE.Matrix4().makeRotationX(- Math.PI / 2))
        //var plane_material = new THREE.MeshBasicMaterial({color: 0x0000ff})
        var plane_material = new THREE.MeshLambertMaterial({color: 0xccffcc, side: THREE.FrontSide, wireframe: true})
        var plane_mesh     = new THREE.Mesh(plane_geometry, plane_material)
        this.add_to_scene(plane_mesh)

        // Add lights.
        /*
        var color1 = '#b9ffd2'
        var color2 = '#090920'
        var light = new THREE.HemisphereLight(color1, color2, .5)
        this.add_to_scene(light)
        */

        var light3 = new THREE.PointLight(0xccffcc, .8, 0)
        light3.position.set(5, 100, 5)
        this.add_to_scene(light3)

        // Add the text.

        this.username_label = new Floating2DText(50, 20, 'Username :')
        this.username_label.create(this.scene)
        this.username_field = new Floating2DText(100, 20, '')
        this.username_field.create(this.scene)

        this.password_label = new Floating2DText(50, 20, 'Password :')
        this.password_label.create(this.scene)
        this.password_field = new Floating2DText(100, 20, '')
        this.password_field.create(this.scene)

        this.login_button = new Floating2DText(50, 20, 'Login')
        this.login_button.create(this.scene)

        var username_position0 = new THREE.Vector3(0, 70, 45)
        var username_position1 = new THREE.Vector3(75, 70, 45)

        var password_position0 = new THREE.Vector3(0, 40, 45)
        var password_position1 = new THREE.Vector3(75, 40, 45)

        this.username_label.update_position_and_look_at(username_position0, new THREE.Vector3(0, 70, 55))
        this.username_field.update_position_and_look_at(username_position1, new THREE.Vector3(75, 70, 55))

        this.password_label.update_position_and_look_at(password_position0, new THREE.Vector3(0, 40, 55))
        this.password_field.update_position_and_look_at(password_position1, new THREE.Vector3(75, 40, 55))

        this.login_button.update_position_and_look_at(new THREE.Vector3(0, 10, 45), new THREE.Vector3(0, 10, 55))

        // Handle key press events.
        document.addEventListener('keydown', this.on_key_press.bind(this), false)

        this.set_player_look_at = false
    },

    add_to_scene: function(object) {
        this.scene.add(object)
    },

    update: function() {

        if (this.set_player_look_at) {
            this.player.look_at(new THREE.Vector3(0, 70, 45))
            this.set_player_look_at = false
        }

        var position = this.player.fps_controls.get_position()
        var direction = this.player.fps_controls.get_direction()

        var raycaster = new THREE.Raycaster(position, direction)

        /*
        var intersects = raycaster.intersectObjects(this.scene.children)
        if (intersects.length > 1) {
            console.log('There are ' + intersects.length + ' intersections!')
        }*/

        this.username_label.being_looked_at = false
        this.password_label.being_looked_at = false
        this.username_field.being_looked_at = false
        this.password_field.being_looked_at = false

        var aa = raycaster.intersectObject(this.username_label.object3d, true)
        var bb = raycaster.intersectObject(this.username_field.object3d, true)
        var cc = raycaster.intersectObject(this.password_label.object3d, true)
        var dd = raycaster.intersectObject(this.password_field.object3d, true)

        if (aa.length > 0) {
            this.username_label.being_looked_at = true
        } else if (bb.length > 0) {
            this.username_field.being_looked_at = true
        } else if (cc.length > 0) {
            this.password_label.being_looked_at = true
        } else if (dd.length > 0) {
            this.password_field.being_looked_at = true
        }
    },

    on_key_press: function(event) {
        switch(event.keyCode) {
        case 101: // e
            if (this.username_field.being_looked_at) {
                this.username_field.engage()
                this.player.engage()
            } else if (this.password_field.being_looked_at) {
                this.password_field.engage()
                this.player.engage()
            }
            break
        case 92: // backslash
            if (this.player.is_engaged()) {
                this.player.disengage()
                if (this.username_field.being_looked_at) {
                    this.username_field.disengage()
                } else if (this.password_field.being_looked_at) {
                    this.password_field.disengage()
                }
            }
            break
        }

        if (this.username_field.is_engaged()) {
            this.username_field.parse_keycode(event)
        } else if (this.password_field.is_engaged()) {
            this.password_field.parse_keycode(event)
        }
    },

    enter_world: function() {
        if (this.player == null) {
            this.set_player_look_at = true
        } else {
            this.player.look_at(new THREE.Vector3(0, 70, 45))
        }
    },

    exit_world: function() {

    }
}<|endoftext|>


<commit_before>var should = require('should'),
	Model = require('../src/models/Model.js'),
	uuid = require('uuid');

describe('Model', function() {

	it('should thow an error for an invalid autoReverse value', function() {
		(function() {
			var m = new Model(null);
		}).should.throw('autoReverse must be boolean');
	});

	it('should thow an error for an invalid comment', function() {
		(function() {
			var m = new Model(true, null);
		}).should.throw('comment must be a string');
	});

	it('should thow an error for an invalid f value', function() {
		(function() {
			var m = new Model(true, "comment", -1);
		}).should.throw('`f` must be a number between 0 and 1 (inclusive)');
	});

	it('should thow an error for an invalid gravity value', function() {
		(function() {
			var m = new Model(true, "comment", 0, -1);
		}).should.throw('`gravity` must be a number between 0 and 1 (inclusive)');
	});

	it('should thow an error for an invalid gravityDirection value', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 0);
		}).should.throw('`gravityDirection` must be either -1 or 1');
	});

	it('should thow an error for an invalid height value', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 1, 0);
		}).should.throw('`height` must be a number greater than 0');
	});

	it('should thow an error for an invalid name', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 1, 1, null);
		}).should.throw('name must be a string');
	});

	it('should thow an error for an invalid k value', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 1, 1, "name", -1);
		}).should.throw('`k` must be a number between 0 and 1 (inclusive)');
	});

});<commit_msg>Additional tests for ranges of values<commit_after>var should = require('should'),
	Model = require('../src/models/Model.js'),
	uuid = require('uuid');

describe('Model', function() {

	it('should thow an error for an invalid autoReverse value', function() {
		(function() {
			var m = new Model(null);
		}).should.throw('autoReverse must be boolean');
	});

	it('should thow an error for an invalid comment', function() {
		(function() {
			var m = new Model(true, null);
		}).should.throw('comment must be a string');
	});

	it('should thow an error for an f value less than zero', function() {
		(function() {
			var m = new Model(true, "comment", -1);
		}).should.throw('`f` must be a number between 0 and 1 (inclusive)');
	});

	it('should thow an error for an f value greater than 1', function() {
		(function() {
			var m = new Model(true, "comment", 1.5);
		}).should.throw('`f` must be a number between 0 and 1 (inclusive)');
	});

	it('should thow an error for a gravity value less than zero', function() {
		(function() {
			var m = new Model(true, "comment", 0, -1);
		}).should.throw('`gravity` must be a number between 0 and 1 (inclusive)');
	});

	it('should thow an error for a gravity value greater than 1', function() {
		(function() {
			var m = new Model(true, "comment", 0, 1.5);
		}).should.throw('`gravity` must be a number between 0 and 1 (inclusive)');
	});

	it('should thow an error for an invalid gravityDirection value', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 0);
		}).should.throw('`gravityDirection` must be either -1 or 1');
	});

	it('should thow an error for an invalid height value', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 1, "string");
		}).should.throw('`height` must be a number greater than 0');
	});

	it('should thow an error for a height value less than zero', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 1, -1);
		}).should.throw('`height` must be a number greater than 0');
	});

	it('should thow an error for an invalid name', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 1, 1, null);
		}).should.throw('name must be a string');
	});

	it('should thow an error for a k value less than zero', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 1, 1, "name", -1);
		}).should.throw('`k` must be a number between 0 and 1 (inclusive)');
	});

	it('should thow an error for a k value greater than 1', function() {
		(function() {
			var m = new Model(true, "comment", 0, 0, 1, 1, "name", 1.5);
		}).should.throw('`k` must be a number between 0 and 1 (inclusive)');
	});

});<|endoftext|>


<commit_before>// --------------------------------------------------------------------------------------------------------------------
// Module for passing user control events to the server.
//
// @module controls.js
// --------------------------------------------------------------------------------------------------------------------

.import Precursors.Networking 1.0 as Networking
.import "../js/logging.js" as Logging

Qt.include("./vendor/lodash.js");

var logger = new Logging.Logger("controls");

// --------------------------------------------------------------------------------------------------------------------
// Initialization
// --------------------------------------------------------------------------------------------------------------------

connectSignals();
logger.debug("Initialized.")

// --------------------------------------------------------------------------------------------------------------------
// Public API
// --------------------------------------------------------------------------------------------------------------------

function sendCommand(commandName /*, ... */)
{
    networking.sendEvent('input', {
        type: 'command',
        name: commandName,
        args: _.rest(arguments)
    }, Networking.PChannels.CM_RELIABLE);
} // end sendCommand

function defineContextSlots(context, slots)
{
    _.forIn(slots.digital, function(handler, slotName)
    {
        var slot = context.digitalSlot(slotName);
        slot.stateChanged.connect(wrapDigital(slot, handler));
    });

    function wrapDigital(slot, handler)
    {
        return function()
        {
            handler(slot.state);
        } // end function
    } // end wrapDigital

    _.forIn(slots.analog, function(handler, slotName)
    {
        var slot = context.analogSlot(slotName);
        slot.valueChanged.connect(wrapAnalog(slot, handler));
    });

    function wrapAnalog(slot, handler)
    {
        return function()
        {
            handler(slot.value);
        } // end function
    } // end wrapAnalog
} // end defineContextSlots

function momentary(handler)
{
    return function(state)
    {
        if(state)
        {
            handler(state);
        } // end if
    }; // end function
} // end momentary

function connectSignals()
{
    defineFlightsim();
    defineCapital();
} // end connectSignals

function cameraZoom(value)
{
    mainWindow.camera.setPos(0, 0, value);
} // end cameraZoom

function defineFlightsim()
{
    var context = controls.context('flightsim');

    context.isActiveChanged.connect(function()
    {
        if(context.isActive)
        {
            // This context was just selected.
            mainWindow.camDolly.orientation = identityQuat(); // Reset orientation to the default.
            mainWindow.camDolly.rotatePitch(-11); // Rotate to match default camera orientation for flightsim.
            mainWindow.camDolly.parent = horde3d.avatar;
        } // end if
    }); // end isActiveChanged

    defineContextSlots(context, {
        digital: {
            quit: momentary(function()
            {
                logger.info("Exiting...");
                gameWindow.close();
            }),

            grabMouse: function(state)
            {
                gameWindow.grabMouse = state;
            },

            switchToCapitalMode: momentary(function(state)
            {
                controls.setCurrentContext('capital');
            }),

            fire: function(state)
            {
                console.log("Got 'fire'.", state);
            },

            coast: function(state)
            {
                console.log("Got 'coast'.", state);
            },

            headlights: function(state)
            {
                console.log("Got 'headlights'.", state);
            }
        },
        analog: {
            sideslip: _.partial(sendCommand, 'sideslip'),
            lift: _.partial(sendCommand, 'lift'),
            throttle: _.partial(sendCommand, 'throttle'),

            heading: _.partial(sendCommand, 'heading'),
            pitch: _.partial(sendCommand, 'pitch'),
            roll: _.partial(sendCommand, 'roll'),

            cameraHeading: function(value)
            {
                mainWindow.camDolly.rotateHeading(value * Math.PI);
            },
            cameraPitch: function(value)
            {
                mainWindow.camDolly.rotatePitch(value * Math.PI);
            },
            cameraZoom: cameraZoom
        }
    });
} // end defineFlightsim

function identityQuat()
{
    return Qt.quaternion(1, 0, 0, 0);
} // end identityQuat

function eulerToQuat(heading, pitch, roll)
{
    var c1 = Math.cos(heading / 2);
    var c2 = Math.cos(roll / 2);
    var c3 = Math.cos(pitch / 2);
    var s1 = Math.sin(heading / 2);
    var s2 = Math.sin(roll / 2);
    var s3 = Math.sin(pitch / 2);

    var w = c1 * c2 * c3 - s1 * s2 * s3;
    var x = s1 * s2 * c3 + c1 * c2 * s3;
    var y = s1 * c2 * c3 + c1 * s2 * s3;
    var z = c1 * s2 * c3 - s1 * c2 * s3;

    return Qt.quaternion(w, x, y, z);
} // end eulerToQuat

function rotationFromTo(from, to)
{
    return math3d.quatNormalized(
            math3d.quatMult(
                math3d.quatReciprocal(from),
                to
                )
            );
} // end rotationFromTo

function scaleQuat(quat, scale)
{
    var scalar = scale * math3d.quatScalar(quat);
    return math3d.quatNormalized(math3d.quatFromScalarAndVector(scalar, math3d.quatVector(quat)));
} // end scaleQuat

function setPosNegBars(posBar, negBar, value, limit)
{
    if(value > 0)
    {
        posBar.item.progress = Math.min(limit, value);
        negBar.item.progress = 0;
    }
    else
    {
        posBar.item.progress = 0;
        negBar.item.progress = Math.min(limit, -value);
    } // end if
} // end setPosNegBars

function scaleQuat(quat, factor)
{
    return math3d.slerp(identityQuat(), quat, factor);
} // end scaleQuat

function quatAngle(quat)
{
    //return 2 * Math.acos(math3d.quatScalar(quat));
    return 2 * Math.atan2(math3d.vectorLength(math3d.quatVector(quat)), math3d.quatScalar(quat));
} // end quatAngle

function defineCapital()
{
    var context = controls.context('capital');
    var slots = {
        reorient: context.digitalSlot('reorient')
    };
	var responsiveness = 1;
	var camTurntable;
    var shipRotateTimer;
    var targetHeadingQuat = identityQuat();
    var targetPitchQuat = identityQuat();
    var headingVelQuat = identityQuat();
    var pitchVelQuat = identityQuat();

    function updateShipRotation()
    {
        var avatar = horde3d.avatar;
        var avatarQuat = avatar.orientation;

        if(slots.reorient.state)
        {
            // Set the target orientation to match the camera.
            targetHeadingQuat = camTurntable.orientation;
            targetPitchQuat = mainWindow.camDolly.orientation;
        }
        else
        {
            // Update the target orientation based on the current heading/pitch velocities from input.
            //FIXME: Use the frame time here instead of 0.1!
            targetHeadingQuat = math3d.quatMult(targetHeadingQuat, scaleQuat(headingVelQuat, 0.1));
            targetPitchQuat = constrainPitch(math3d.quatMult(targetPitchQuat, scaleQuat(pitchVelQuat, 0.1)));
        } // end if

        var targetOrientation = math3d.quatMult(targetHeadingQuat, targetPitchQuat);
        var desiredRotation = rotationFromTo(
                avatarQuat,
                targetOrientation
                );

        var rotAngle = 2 * quatAngle(desiredRotation) / Math.PI;
        desiredRotation = scaleQuat(desiredRotation, Math.sqrt(Math.abs(rotAngle)) * responsiveness);

        var HEADING = 0;
        var PITCH = 2;
        var ROLL = 1;

        var tgtVelHPR = math3d.quatToHPR(desiredRotation);
        var tgtVelLimit = debugWindow.angularTgtVelMax;
        setPosNegBars(debugWindow.posTgtVelHeading, debugWindow.negTgtVelHeading, tgtVelHPR[HEADING], tgtVelLimit);
        setPosNegBars(debugWindow.posTgtVelPitch, debugWindow.negTgtVelPitch, tgtVelHPR[PITCH], tgtVelLimit);
        setPosNegBars(debugWindow.posTgtVelRoll, debugWindow.negTgtVelRoll, tgtVelHPR[ROLL], tgtVelLimit);

        var camHPR = math3d.quatToHPR(targetOrientation);
        setPosNegBars(debugWindow.posCamHeading, debugWindow.negCamHeading, camHPR[HEADING], Math.PI);
        setPosNegBars(debugWindow.posCamPitch, debugWindow.negCamPitch, camHPR[PITCH], Math.PI);
        setPosNegBars(debugWindow.posCamRoll, debugWindow.negCamRoll, camHPR[ROLL], Math.PI);

        var shipHPR = math3d.quatToHPR(avatarQuat);
        setPosNegBars(debugWindow.posShipHeading, debugWindow.negShipHeading, shipHPR[HEADING], Math.PI);
        setPosNegBars(debugWindow.posShipPitch, debugWindow.negShipPitch, shipHPR[PITCH], Math.PI);
        setPosNegBars(debugWindow.posShipRoll, debugWindow.negShipRoll, shipHPR[ROLL], Math.PI);

        sendCommand('heading', tgtVelHPR[HEADING]);
        sendCommand('pitch', tgtVelHPR[PITCH]);
        sendCommand('roll', tgtVelHPR[ROLL]);
    } // end updateShipRotation

    context.isActiveChanged.connect(function()
    {
        if(context.isActive)
        {
            // This context was just selected.

            if(!camTurntable)
            {
                camTurntable = horde3d.root.newGroup('camera turntable');

                // Bind 'camTurntable.pos' to 'avatar.pos'
                camTurntable.pos = Qt.binding(function() { return horde3d.avatar.pos });
            } // end if

            if(!shipRotateTimer)
            {
                shipRotateTimer = Qt.createQmlObject(
                        'import QtQuick 2.0; Timer {interval: 50; repeat: true; triggeredOnStart: true}',
                        mainWindow,
                        "shipRotateTimerDef"
                        );
                shipRotateTimer.triggered.connect(updateShipRotation);
            } // end if
            shipRotateTimer.start();

            // Reset orientation to the default, then only apply the heading from the ship.
            camTurntable.orientation = identityQuat();
            camTurntable.rotateHeading(horde3d.avatar.heading * 180 / Math.PI);

            mainWindow.camDolly.parent = camTurntable;

            // Set maximum values for rotation debugging controls.
            debugWindow.angularTgtVelMax = Math.PI * responsiveness;
        }
        else
        {
            shipRotateTimer.stop();
        } // end if
    }); // end isActiveChanged

    defineContextSlots(context, {
        digital: {
            quit: momentary(function()
            {
                logger.info("Exiting...");
                gameWindow.close();
            }),

            grabMouse: function(state)
            {
                gameWindow.grabMouse = state;
            },

            switchToFlightsimMode: momentary(function(state)
            {
                controls.setCurrentContext('flightsim');
            }),

            fire: function(state)
            {
                console.log("Got 'fire'.", state);
            },

            reorient: function(state)
            {
                console.log("Got 'reorient'.", state);
            },

            headlights: function(state)
            {
                console.log("Got 'headlights'.", state);
            }
        },
        analog: {
            throttle: _.partial(sendCommand, 'throttle'),

            heading: function(value)
            {
                headingVelQuat = eulerToQuat(value, 0, 0);
            },
            pitch: function(value)
            {
                pitchVelQuat = eulerToQuat(0, value, 0);
            },

            cameraHeading: function(value)
            {
                camTurntable.rotateHeading(value * Math.PI);
            },
            cameraPitch: function(value)
            {
                mainWindow.camDolly.rotatePitch(value * Math.PI);
                mainWindow.camDolly.orientation = constrainPitch(mainWindow.camDolly.orientation);
            },
            cameraZoom: cameraZoom
        }
    });

    function constrainPitch(quat)
    {
        if(quatAngle(quat) > Math.PI / 2)
        {
            return eulerToQuat(0, Math.PI / (math3d.quatX(quat) > 0 ? 2 : -2), 0);
        }
        else
        {
            return quat;
        } // end if
    } // end constrainPitch
} // end defineCapital
<commit_msg>Fixed initial camera zoom.<commit_after>// --------------------------------------------------------------------------------------------------------------------
// Module for passing user control events to the server.
//
// @module controls.js
// --------------------------------------------------------------------------------------------------------------------

.import Precursors.Networking 1.0 as Networking
.import "../js/logging.js" as Logging

Qt.include("./vendor/lodash.js");

var logger = new Logging.Logger("controls");

// --------------------------------------------------------------------------------------------------------------------
// Initialization
// --------------------------------------------------------------------------------------------------------------------

connectSignals();
logger.debug("Initialized.")

// --------------------------------------------------------------------------------------------------------------------
// Public API
// --------------------------------------------------------------------------------------------------------------------

function sendCommand(commandName /*, ... */)
{
    networking.sendEvent('input', {
        type: 'command',
        name: commandName,
        args: _.rest(arguments)
    }, Networking.PChannels.CM_RELIABLE);
} // end sendCommand

function defineContextSlots(context, slots)
{
    _.forIn(slots.digital, function(handler, slotName)
    {
        var slot = context.digitalSlot(slotName);
        slot.stateChanged.connect(wrapDigital(slot, handler));
    });

    function wrapDigital(slot, handler)
    {
        return function()
        {
            handler(slot.state);
        } // end function
    } // end wrapDigital

    _.forIn(slots.analog, function(handler, slotName)
    {
        var slot = context.analogSlot(slotName);
        slot.valueChanged.connect(wrapAnalog(slot, handler));
    });

    function wrapAnalog(slot, handler)
    {
        return function()
        {
            handler(slot.value);
        } // end function
    } // end wrapAnalog
} // end defineContextSlots

function momentary(handler)
{
    return function(state)
    {
        if(state)
        {
            handler(state);
        } // end if
    }; // end function
} // end momentary

function connectSignals()
{
    defineFlightsim();
    defineCapital();
} // end connectSignals

function setZoom(value)
{
    mainWindow.camera.setPos(0, 0, value);
} // end setZoom

function defineFlightsim()
{
    var context = controls.context('flightsim');

    context.analogSlot('cameraZoom').accumulatedValue = settings.get('flightsimCameraZoom', 200);

    context.isActiveChanged.connect(function()
    {
        if(context.isActive)
        {
            // This context was just selected.
            mainWindow.camDolly.orientation = identityQuat(); // Reset orientation to the default.
            mainWindow.camDolly.rotatePitch(-11); // Rotate to match default camera orientation for flightsim.
            mainWindow.camDolly.parent = horde3d.avatar;

            // Set zoom level
            setZoom(context.analogSlot('cameraZoom').value);
        } // end if
    }); // end isActiveChanged

    defineContextSlots(context, {
        digital: {
            quit: momentary(function()
            {
                logger.info("Exiting...");
                gameWindow.close();
            }),

            grabMouse: function(state)
            {
                gameWindow.grabMouse = state;
            },

            switchToCapitalMode: momentary(function(state)
            {
                controls.setCurrentContext('capital');
            }),

            fire: function(state)
            {
                console.log("Got 'fire'.", state);
            },

            coast: function(state)
            {
                console.log("Got 'coast'.", state);
            },

            headlights: function(state)
            {
                console.log("Got 'headlights'.", state);
            }
        },
        analog: {
            sideslip: _.partial(sendCommand, 'sideslip'),
            lift: _.partial(sendCommand, 'lift'),
            throttle: _.partial(sendCommand, 'throttle'),

            heading: _.partial(sendCommand, 'heading'),
            pitch: _.partial(sendCommand, 'pitch'),
            roll: _.partial(sendCommand, 'roll'),

            cameraHeading: function(value)
            {
                mainWindow.camDolly.rotateHeading(value * Math.PI);
            },
            cameraPitch: function(value)
            {
                mainWindow.camDolly.rotatePitch(value * Math.PI);
            },
            cameraZoom: function(value)
            {
                mainWindow.camera.setPos(0, 0, value);
                settings.set('flightsimCameraZoom', value);
                settings.save();
            }
        }
    });
} // end defineFlightsim

function identityQuat()
{
    return Qt.quaternion(1, 0, 0, 0);
} // end identityQuat

function eulerToQuat(heading, pitch, roll)
{
    var c1 = Math.cos(heading / 2);
    var c2 = Math.cos(roll / 2);
    var c3 = Math.cos(pitch / 2);
    var s1 = Math.sin(heading / 2);
    var s2 = Math.sin(roll / 2);
    var s3 = Math.sin(pitch / 2);

    var w = c1 * c2 * c3 - s1 * s2 * s3;
    var x = s1 * s2 * c3 + c1 * c2 * s3;
    var y = s1 * c2 * c3 + c1 * s2 * s3;
    var z = c1 * s2 * c3 - s1 * c2 * s3;

    return Qt.quaternion(w, x, y, z);
} // end eulerToQuat

function rotationFromTo(from, to)
{
    return math3d.quatNormalized(
            math3d.quatMult(
                math3d.quatReciprocal(from),
                to
                )
            );
} // end rotationFromTo

function scaleQuat(quat, scale)
{
    var scalar = scale * math3d.quatScalar(quat);
    return math3d.quatNormalized(math3d.quatFromScalarAndVector(scalar, math3d.quatVector(quat)));
} // end scaleQuat

function setPosNegBars(posBar, negBar, value, limit)
{
    if(value > 0)
    {
        posBar.item.progress = Math.min(limit, value);
        negBar.item.progress = 0;
    }
    else
    {
        posBar.item.progress = 0;
        negBar.item.progress = Math.min(limit, -value);
    } // end if
} // end setPosNegBars

function scaleQuat(quat, factor)
{
    return math3d.slerp(identityQuat(), quat, factor);
} // end scaleQuat

function quatAngle(quat)
{
    //return 2 * Math.acos(math3d.quatScalar(quat));
    return 2 * Math.atan2(math3d.vectorLength(math3d.quatVector(quat)), math3d.quatScalar(quat));
} // end quatAngle

function defineCapital()
{
    var context = controls.context('capital');
    var slots = {
        reorient: context.digitalSlot('reorient')
    };

    context.analogSlot('cameraZoom').accumulatedValue = settings.get('capitalCameraZoom', 500);

	var responsiveness = 1;
	var camTurntable;
    var shipRotateTimer;
    var targetHeadingQuat = identityQuat();
    var targetPitchQuat = identityQuat();
    var headingVelQuat = identityQuat();
    var pitchVelQuat = identityQuat();

    function updateShipRotation()
    {
        var avatar = horde3d.avatar;
        var avatarQuat = avatar.orientation;

        if(slots.reorient.state)
        {
            // Set the target orientation to match the camera.
            targetHeadingQuat = camTurntable.orientation;
            targetPitchQuat = mainWindow.camDolly.orientation;
        }
        else
        {
            // Update the target orientation based on the current heading/pitch velocities from input.
            //FIXME: Use the frame time here instead of 0.1!
            targetHeadingQuat = math3d.quatMult(targetHeadingQuat, scaleQuat(headingVelQuat, 0.1));
            targetPitchQuat = constrainPitch(math3d.quatMult(targetPitchQuat, scaleQuat(pitchVelQuat, 0.1)));
        } // end if

        var targetOrientation = math3d.quatMult(targetHeadingQuat, targetPitchQuat);
        var desiredRotation = rotationFromTo(
                avatarQuat,
                targetOrientation
                );

        var rotAngle = 2 * quatAngle(desiredRotation) / Math.PI;
        desiredRotation = scaleQuat(desiredRotation, Math.sqrt(Math.abs(rotAngle)) * responsiveness);

        var HEADING = 0;
        var PITCH = 2;
        var ROLL = 1;

        var tgtVelHPR = math3d.quatToHPR(desiredRotation);
        var tgtVelLimit = debugWindow.angularTgtVelMax;
        setPosNegBars(debugWindow.posTgtVelHeading, debugWindow.negTgtVelHeading, tgtVelHPR[HEADING], tgtVelLimit);
        setPosNegBars(debugWindow.posTgtVelPitch, debugWindow.negTgtVelPitch, tgtVelHPR[PITCH], tgtVelLimit);
        setPosNegBars(debugWindow.posTgtVelRoll, debugWindow.negTgtVelRoll, tgtVelHPR[ROLL], tgtVelLimit);

        var camHPR = math3d.quatToHPR(targetOrientation);
        setPosNegBars(debugWindow.posCamHeading, debugWindow.negCamHeading, camHPR[HEADING], Math.PI);
        setPosNegBars(debugWindow.posCamPitch, debugWindow.negCamPitch, camHPR[PITCH], Math.PI);
        setPosNegBars(debugWindow.posCamRoll, debugWindow.negCamRoll, camHPR[ROLL], Math.PI);

        var shipHPR = math3d.quatToHPR(avatarQuat);
        setPosNegBars(debugWindow.posShipHeading, debugWindow.negShipHeading, shipHPR[HEADING], Math.PI);
        setPosNegBars(debugWindow.posShipPitch, debugWindow.negShipPitch, shipHPR[PITCH], Math.PI);
        setPosNegBars(debugWindow.posShipRoll, debugWindow.negShipRoll, shipHPR[ROLL], Math.PI);

        sendCommand('heading', tgtVelHPR[HEADING]);
        sendCommand('pitch', tgtVelHPR[PITCH]);
        sendCommand('roll', tgtVelHPR[ROLL]);
    } // end updateShipRotation

    context.isActiveChanged.connect(function()
    {
        if(context.isActive)
        {
            // This context was just selected.

            if(!camTurntable)
            {
                camTurntable = horde3d.root.newGroup('camera turntable');

                // Bind 'camTurntable.pos' to 'avatar.pos'
                camTurntable.pos = Qt.binding(function() { return horde3d.avatar.pos });
            } // end if

            if(!shipRotateTimer)
            {
                shipRotateTimer = Qt.createQmlObject(
                        'import QtQuick 2.0; Timer {interval: 50; repeat: true; triggeredOnStart: true}',
                        mainWindow,
                        "shipRotateTimerDef"
                        );
                shipRotateTimer.triggered.connect(updateShipRotation);
            } // end if
            shipRotateTimer.start();

            // Reset orientation to the default, then only apply the heading from the ship.
            camTurntable.orientation = identityQuat();
            camTurntable.rotateHeading(horde3d.avatar.heading * 180 / Math.PI);

            mainWindow.camDolly.parent = camTurntable;

            // Set zoom level
            setZoom(context.analogSlot('cameraZoom').value);

            // Set maximum values for rotation debugging controls.
            debugWindow.angularTgtVelMax = Math.PI * responsiveness;
        }
        else
        {
            shipRotateTimer.stop();
        } // end if
    }); // end isActiveChanged

    defineContextSlots(context, {
        digital: {
            quit: momentary(function()
            {
                logger.info("Exiting...");
                gameWindow.close();
            }),

            grabMouse: function(state)
            {
                gameWindow.grabMouse = state;
            },

            switchToFlightsimMode: momentary(function(state)
            {
                controls.setCurrentContext('flightsim');
            }),

            fire: function(state)
            {
                console.log("Got 'fire'.", state);
            },

            reorient: function(state)
            {
                console.log("Got 'reorient'.", state);
            },

            headlights: function(state)
            {
                console.log("Got 'headlights'.", state);
            }
        },
        analog: {
            throttle: _.partial(sendCommand, 'throttle'),

            heading: function(value)
            {
                headingVelQuat = eulerToQuat(value, 0, 0);
            },
            pitch: function(value)
            {
                pitchVelQuat = eulerToQuat(0, value, 0);
            },

            cameraHeading: function(value)
            {
                camTurntable.rotateHeading(value * Math.PI);
            },
            cameraPitch: function(value)
            {
                mainWindow.camDolly.rotatePitch(value * Math.PI);
                mainWindow.camDolly.orientation = constrainPitch(mainWindow.camDolly.orientation);
            },
            cameraZoom: function(value)
            {
                setZoom(value);
                settings.set('capitalCameraZoom', value);
                settings.save();
            }
        }
    });

    function constrainPitch(quat)
    {
        if(quatAngle(quat) > Math.PI / 2)
        {
            return eulerToQuat(0, Math.PI / (math3d.quatX(quat) > 0 ? 2 : -2), 0);
        }
        else
        {
            return quat;
        } // end if
    } // end constrainPitch
} // end defineCapital
<|endoftext|>


<commit_before>/**
 * Backbone-Nested 1.1.2 - An extension of Backbone.js that keeps track of nested attributes
 *
 * http://afeld.github.com/backbone-nested/
 *
 * Copyright (c) 2011-2012 Aidan Feldman
 * MIT Licensed (LICENSE)
 */
/*global $, _, Backbone */
(function(){
  'use strict';

  var _delayedTriggers = [];

  Backbone.NestedModel = Backbone.Model.extend({

    get: function(attrStrOrPath){
      var attrPath = Backbone.NestedModel.attrPath(attrStrOrPath),
        result;

      Backbone.NestedModel.walkPath(this.attributes, attrPath, function(val, path){
        var attr = _.last(path);
        if (path.length === attrPath.length){
          // attribute found
          result = val[attr];
        }
      });

      return result;
    },

    has: function(attr){
      // for some reason this is not how Backbone.Model is implemented - it accesses the attributes object directly
      var result = this.get(attr);
      return !(result === null || _.isUndefined(result));
    },

    set: function(key, value, opts){
      var newAttrs = Backbone.NestedModel.deepClone(this.attributes),
        attrPath;

      if (_.isString(key)){
        // Backbone 0.9.0+ syntax: `model.set(key, val)` - convert the key to an attribute path
        attrPath = Backbone.NestedModel.attrPath(key);
      } else if (_.isArray(key)){
        // attribute path
        attrPath = key;
      }

      if (attrPath){
        opts = opts || {};
        this._setAttr(newAttrs, attrPath, value, opts);
      } else { // it's an Object
        opts = value || {};
        var attrs = key;
        for (var attrStr in attrs){
          if (attrs.hasOwnProperty(attrStr)){
            attrPath = Backbone.NestedModel.attrPath(attrStr);
            this._setAttr(newAttrs, attrPath, attrs[attrStr], opts);
          }
        }
      }

      if (!this._validate(newAttrs, opts)){
        // reset changed attributes
        this.changed = {};
        return false;
      }

      var setReturn;
      if (opts.unset && attrPath.length === 1){ // assume it is a singular attribute being unset
        // unsetting top-level attribute
        var unsetObj = {};
        unsetObj[key] = null;
        setReturn = Backbone.NestedModel.__super__.set.call(this, unsetObj, opts);
      } else {
        // normal set(), or an unset of nested attribute
        if (opts.unset){
          // make sure Backbone.Model won't unset the top-level attribute
          opts = _.extend({}, opts);
          delete opts.unset;
        }

        setReturn = Backbone.NestedModel.__super__.set.call(this, newAttrs, opts);
      }

      this._runDelayedTriggers();
      return setReturn;
    },

    unset: function(attrStr, opts){
      opts = _.extend({}, opts, {unset: true});
      this.set(attrStr, null, opts);

      return this;
    },

    add: function(attrStr, value, opts){
      var current = this.get(attrStr);
      if (!_.isArray(current)) throw new Error('current value is not an array');
      return this.set(attrStr + '[' + current.length + ']', value, opts);
    },

    remove: function(attrStr, opts){
      opts = opts || {};

      var attrPath = Backbone.NestedModel.attrPath(attrStr),
        aryPath = _.initial(attrPath),
        val = this.get(aryPath),
        i = _.last(attrPath);

      if (!_.isArray(val)){
        throw new Error("remove() must be called on a nested array");
      }

      // only trigger if an element is actually being removed
      var trigger = !opts.silent && (val.length >= i + 1),
        oldEl = val[i];

      // remove the element from the array
      val.splice(i, 1);
      opts.silent = true; // Triggers should only be fired in trigger section below
      this.set(aryPath, val, opts);

      if (trigger){
        attrStr = Backbone.NestedModel.createAttrStr(aryPath);
        this.trigger('remove:' + attrStr, this, oldEl);
        for (var aryCount = aryPath.length; aryCount >= 1; aryCount--) {
          attrStr = Backbone.NestedModel.createAttrStr(_.first(aryPath, aryCount));
          this.trigger('change:' + attrStr, this, oldEl);
        }
        this.trigger('change', this, oldEl);
      }

      return this;
    },

    toJSON: function(){
      return Backbone.NestedModel.deepClone(this.attributes);
    },

    // private
    _delayedTrigger: function(/* the trigger args */){
      _delayedTriggers.push(arguments);
    },

    _delayedChange: function(attrStr, newVal){
      this._delayedTrigger('change:' + attrStr, this, newVal);
      this.changed[attrStr] = newVal;
    },

    _runDelayedTriggers: function(){
      while (_delayedTriggers.length > 0){
        this.trigger.apply(this, _delayedTriggers.shift());
      }
    },

    // note: modifies `newAttrs`
    _setAttr: function(newAttrs, attrPath, newValue, opts){
      opts = opts || {};

      var fullPathLength = attrPath.length;
      var model = this;

      Backbone.NestedModel.walkPath(newAttrs, attrPath, function(val, path){
        var attr = _.last(path);
        var attrStr = Backbone.NestedModel.createAttrStr(path);

        // See if this is a new value being set
        var isNewValue = !_.isEqual(val[attr], newValue);

        if (path.length === fullPathLength){
          // reached the attribute to be set

          if (opts.unset){
            // unset the value
            delete val[attr];
            delete model._escapedAttributes[attrStr];
          } else {
            // Set the new value
            val[attr] = newValue;
          }

          // Trigger Change Event if new values are being set
          if (_.isObject(newValue) && isNewValue){
            // TODO need deep compare
            var nestedAttr, nestedVal;
            for (var a in newValue){
              if (newValue.hasOwnProperty(a)){
                nestedAttr = attrStr + '.' + a;
                nestedVal = newValue[a];
                if (!_.isEqual(model.get(nestedAttr), nestedVal)){
                  model._delayedChange(nestedAttr, nestedVal);
                }
              }
            }
          }

          // Trigger Remove Event if array being set to null
          if (newValue === null){
            var parentPath = Backbone.NestedModel.createAttrStr(_.initial(attrPath));
            model._delayedTrigger('remove:' + parentPath, model, val[attr]);
          }

        } else if (!val[attr]){
          if (_.isNumber(attr)){
            val[attr] = [];
          } else {
            val[attr] = {};
          }
        }

        if (!opts.silent){
          // let the superclass handle change events for top-level attributes
          if (path.length > 1 && isNewValue){
            model._delayedChange(attrStr, val[attr]);
          }

          if (_.isArray(val[attr])){
            model._delayedTrigger('add:' + attrStr, model, val[attr]);
          }
        }
      });
    }

  }, {
    // class methods

    attrPath: function(attrStrOrPath){
      var path;

      if (_.isString(attrStrOrPath)){
        // TODO this parsing can probably be more efficient
        path = (attrStrOrPath === '') ? [''] : attrStrOrPath.match(/[^\.\[\]]+/g);
        path = _.map(path, function(val){
          // convert array accessors to numbers
          return val.match(/^\d+$/) ? parseInt(val, 10) : val;
        });
      } else {
        path = attrStrOrPath;
      }

      return path;
    },

    createAttrStr: function(attrPath){
      var attrStr = attrPath[0];
      _.each(_.rest(attrPath), function(attr){
        attrStr += _.isNumber(attr) ? ('[' + attr + ']') : ('.' + attr);
      });

      return attrStr;
    },

    deepClone: function(obj){
      return $.extend(true, {}, obj);
    },

    walkPath: function(obj, attrPath, callback, scope){
      var val = obj,
        childAttr;

      // walk through the child attributes
      for (var i = 0; i < attrPath.length; i++){
        callback.call(scope || this, val, attrPath.slice(0, i + 1));

        childAttr = attrPath[i];
        val = val[childAttr];
        if (!val) break; // at the leaf
      }
    }

  });

})();
<commit_msg>Fix issue #42 and issue #35<commit_after>/**
 * Backbone-Nested 1.1.2 - An extension of Backbone.js that keeps track of nested attributes
 *
 * http://afeld.github.com/backbone-nested/
 *
 * Copyright (c) 2011-2012 Aidan Feldman
 * MIT Licensed (LICENSE)
 */
/*global $, _, Backbone */
(function(){
  'use strict';

  var _delayedTriggers = [];

  Backbone.NestedModel = Backbone.Model.extend({

    get: function(attrStrOrPath){
      var attrPath = Backbone.NestedModel.attrPath(attrStrOrPath),
        result;

      Backbone.NestedModel.walkPath(this.attributes, attrPath, function(val, path){
        var attr = _.last(path);
        if (path.length === attrPath.length){
          // attribute found
          result = val[attr];
        }
      });

      return result;
    },

    has: function(attr){
      // for some reason this is not how Backbone.Model is implemented - it accesses the attributes object directly
      var result = this.get(attr);
      return !(result === null || _.isUndefined(result));
    },

    set: function(key, value, opts){
      var newAttrs = Backbone.NestedModel.deepClone(this.attributes),
        attrPath;

      if (_.isString(key)){
        // Backbone 0.9.0+ syntax: `model.set(key, val)` - convert the key to an attribute path
        attrPath = Backbone.NestedModel.attrPath(key);
      } else if (_.isArray(key)){
        // attribute path
        attrPath = key;
      }

      if (attrPath){
        opts = opts || {};
        this._setAttr(newAttrs, attrPath, value, opts);
      } else { // it's an Object
        opts = value || {};
        var attrs = key;
        for (var _attrStr in attrs) {
          if (attrs.hasOwnProperty(_attrStr)) {
            this._setAttr(newAttrs,
                          Backbone.NestedModel.attrPath(_attrStr),
                          opts.unset ? null : attrs[_attrStr],
                          opts);
          }
        }
      }

      if (!this._validate(newAttrs, opts)){
        // reset changed attributes
        this.changed = {};
        return false;
      }

      if (opts.unset && attrPath && attrPath.length === 1){ // assume it is a singular attribute being unset
        // unsetting top-level attribute
        var unsetObj = {};
        unsetObj[key] = null;
        Backbone.NestedModel.__super__.set.call(this, unsetObj, opts);
      } else {
        // normal set(), or an unset of nested attribute
        if (opts.unset && attrPath){
          // make sure Backbone.Model won't unset the top-level attribute
          opts = _.extend({}, opts);
          delete opts.unset;
        }
        Backbone.NestedModel.__super__.set.call(this, newAttrs, opts);
      }

      this._runDelayedTriggers();
      return this;
    },

    unset: function(attrStr, opts){
      opts = _.extend({}, opts, {unset: true});
      this.set(attrStr, null, opts);
      return this;
    },

    clear: function(options) {
      options = options || {};
      var attrs = Backbone.NestedModel.deepClone(this.attributes);
      for (attr in attrs) attrs[attr] = void 0;
      if (!this._validate(attrs, options)) return false;

      var changes = options.changes = {};
      var now = this.attributes;
      var escaped = this._escapedAttributes;
      var prev = this._previousAttributes || {};
      for (var attr in attrs) {;
        if (_.has(now, attr)) {
          delete escaped[attr];
          (options.silent ? this._silent : changes)[attr] = true;
        }
        delete now[attr];
        delete this.changed[attr];
        delete this._pending[attr];
      }
      // Fire the `"change"` events.
      if (!options.silent) this.change(options);
    },

    add: function(attrStr, value, opts){
      var current = this.get(attrStr);
      if (!_.isArray(current)) throw new Error('current value is not an array');
      return this.set(attrStr + '[' + current.length + ']', value, opts);
    },

    remove: function(attrStr, opts){
      opts = opts || {};

      var attrPath = Backbone.NestedModel.attrPath(attrStr),
        aryPath = _.initial(attrPath),
        val = this.get(aryPath),
        i = _.last(attrPath);

      if (!_.isArray(val)){
        throw new Error("remove() must be called on a nested array");
      }

      // only trigger if an element is actually being removed
      var trigger = !opts.silent && (val.length >= i + 1),
        oldEl = val[i];

      // remove the element from the array
      val.splice(i, 1);
      opts.silent = true; // Triggers should only be fired in trigger section below
      this.set(aryPath, val, opts);

      if (trigger){
        attrStr = Backbone.NestedModel.createAttrStr(aryPath);
        this.trigger('remove:' + attrStr, this, oldEl);
        for (var aryCount = aryPath.length; aryCount >= 1; aryCount--) {
          attrStr = Backbone.NestedModel.createAttrStr(_.first(aryPath, aryCount));
          this.trigger('change:' + attrStr, this, oldEl);
        }
        this.trigger('change', this, oldEl);
      }

      return this;
    },

    toJSON: function(){
      return Backbone.NestedModel.deepClone(this.attributes);
    },

    // private
    _delayedTrigger: function(/* the trigger args */){
      _delayedTriggers.push(arguments);
    },

    _delayedChange: function(attrStr, newVal){
      this._delayedTrigger('change:' + attrStr, this, newVal);
      this.changed[attrStr] = newVal;
    },

    _runDelayedTriggers: function(){
      while (_delayedTriggers.length > 0){
        this.trigger.apply(this, _delayedTriggers.shift());
      }
    },

    // note: modifies `newAttrs`
    _setAttr: function(newAttrs, attrPath, newValue, opts){
      opts = opts || {};

      var fullPathLength = attrPath.length;
      var model = this;

      Backbone.NestedModel.walkPath(newAttrs, attrPath, function(val, path){
        var attr = _.last(path);
        var attrStr = Backbone.NestedModel.createAttrStr(path);

        // See if this is a new value being set
        var isNewValue = !_.isEqual(val[attr], newValue);

        if (path.length === fullPathLength){
          // reached the attribute to be set

          if (opts.unset){
            // unset the value
            delete val[attr];
            delete model._escapedAttributes[attrStr];
          } else {
            // Set the new value
            val[attr] = newValue;
          }

          // Trigger Change Event if new values are being set
          if (_.isObject(newValue) && isNewValue){
            var checkChanges = function(obj, prefix) {
              var nestedAttr, nestedVal;
              for (var a in obj){
                if (obj.hasOwnProperty(a)) {
                  nestedAttr = prefix + '.' + a;
                  nestedVal = obj[a];
                  if (!_.isEqual(model.get(nestedAttr), nestedVal)) {
                    model._delayedChange(nestedAttr, nestedVal);
                  }
                  if (_.isObject(nestedVal)) {
                    checkChanges(nestedVal, nestedAttr);
                  }
                }
              }
            };
            checkChanges(newValue, attrStr);

          }

          // Trigger Remove Event if array being set to null
          if (newValue === null){
            var parentPath = Backbone.NestedModel.createAttrStr(_.initial(attrPath));
            model._delayedTrigger('remove:' + parentPath, model, val[attr]);
          }

        } else if (!val[attr]){
          if (_.isNumber(attr)){
            val[attr] = [];
          } else {
            val[attr] = {};
          }
        }

        if (!opts.silent){
          // let the superclass handle change events for top-level attributes
          if (path.length > 1 && isNewValue){
            model._delayedChange(attrStr, val[attr]);
          }

          if (_.isArray(val[attr])){
            model._delayedTrigger('add:' + attrStr, model, val[attr]);
          }
        }
      });
    }

  }, {
    // class methods

    attrPath: function(attrStrOrPath){
      var path;

      if (_.isString(attrStrOrPath)){
        // TODO this parsing can probably be more efficient
        path = (attrStrOrPath === '') ? [''] : attrStrOrPath.match(/[^\.\[\]]+/g);
        path = _.map(path, function(val){
          // convert array accessors to numbers
          return val.match(/^\d+$/) ? parseInt(val, 10) : val;
        });
      } else {
        path = attrStrOrPath;
      }

      return path;
    },

    createAttrStr: function(attrPath){
      var attrStr = attrPath[0];
      _.each(_.rest(attrPath), function(attr){
        attrStr += _.isNumber(attr) ? ('[' + attr + ']') : ('.' + attr);
      });

      return attrStr;
    },

    deepClone: function(obj){
      return $.extend(true, {}, obj);
    },

    walkPath: function(obj, attrPath, callback, scope){
      var val = obj,
        childAttr;

      // walk through the child attributes
      for (var i = 0; i < attrPath.length; i++){
        callback.call(scope || this, val, attrPath.slice(0, i + 1));

        childAttr = attrPath[i];
        val = val[childAttr];
        if (!val) break; // at the leaf
      }
    }

  });

})();
<|endoftext|>


<commit_before>'use strict';

const Express = require('express');
const Router  = Express.Router();
const Auth    = require('../../helpers/authenticator');

Router.get('/', Auth.Admin.Protection, (req, res, next) => {
  res.render('admin/index', {
    title: 'Operations Hub',
    breadcrumb: true
  });
});

Router.use('/dns', require('./dns'));

Router.use('/instances', require('./instances'));

Router.use('/auth', require('./auth'));

Router.use('/log', require('./log'));

Router.use('/profile', require('./profile'));

Router.use('/assets', require('./assets'));

Router.use('/users', require('./user'));

Router.use('/tasks', require('./task'));

module.exports = Router;
<commit_msg>Fix module<commit_after>'use strict';

const Express = require('express');
const Router  = Express.Router();
const Auth    = require('../../helpers/authenticator');

Router.get('/', Auth.Admin.Protection, (req, res, next) => {
  res.render('admin/index', {
    title: 'Operations Hub',
    breadcrumb: true
  });
});

Router.use('/dns', require('./dns'));

Router.use('/instances', require('./instances'));

Router.use('/auth', require('./auth'));

Router.use('/log', require('./log'));

Router.use('/profile', require('./profile'));

Router.use('/assets', require('./assets'));

Router.use('/users', require('./user'));

Router.use('/tasks', require('./tasks'));

module.exports = Router;
<|endoftext|>


<commit_before>// Google BSD license http://code.google.com/google_bsd_license.html
// Copyright 2011 Google Inc. johnjbarton@google.com

/*globals define WebInspector  console */

// Implement WebInspector's SourceFrame using Orion Editor.
// This file will be injected into the WebInspector iframe

define(['OrionEditorEmbedded/editorInserter'],
function(                    editorInserter)  {

  /**
   * @extends {WebInspector.View}
   * @constructor
  */

  function SourceFrame(url) {
    // The View is a container element with hide/show operations.
    WebInspector.View.call(this);

    this._editor = editorInserter.createEditor(this.element);

    // element property defined and controlled by View
    this.element.addStyleClass("script-view");

    this._messages = []; // notifications from console
    this._searchResults = [];
  }

  SourceFrame.Events = {
    Loaded: "loaded"
  };

  SourceFrame.prototype = Object.create(WebInspector.View.prototype);

    SourceFrame.prototype.setContent = function(mimeType, content) {
      // push content into Orion editor

      // Notify system we are ready
      this._loaded = true;
      this.dispatchEventToListeners(SourceFrame.Events.Loaded);
    };
    //-------------------------------------------------------------------------
    // Panel switching events

    /*  See View.show.  willShow(), style visible, wasShown()
    */
    SourceFrame.prototype.willShow = function() {
      console.error("SourceFrame function not implemented");
    };

    /*
    ** Called at the end of View.show(), on all visible children.
    */
    SourceFrame.prototype.wasShown = function() {
      this._editor.getTextView().resize();
    };

    /* View method, called only by View._processWillHide
    ** Called during View.detach
    */
    SourceFrame.prototype.willHide = function() {
      console.error("SourceFrame function not implemented");
    };

    // Called during View.detach.
    SourceFrame.prototype.wasHidden = function() {
      console.error("SourceFrame function not implemented");
    };

    //-------------------------------------------------------------------------
    // Command to take focus
    SourceFrame.prototype.focus = function() {
      console.error("SourceFrame function not implemented");
    };

    // Called by Panel. Return StatusBarButton for the button bar across the bottom.
    Object.defineProperty(SourceFrame.prototype, 'statusBarItems', {
        get: function() {
        }
    });

    // Called by ScriptsPanel before calling SourceFrame methods
    Object.defineProperty(SourceFrame.prototype, 'loaded', {
        get: function() {
          return this._loaded;
        }
    });

    // Not called by any WebInsepctor code
    SourceFrame.prototype.hasContent = function() {
      return true;
    };

    //-------------------------------------------------------------------------
    // Diff

    // Called by ResourcesPanel to show diffs created in the editor
    SourceFrame.prototype.markDiff = function(diffData) {
      console.error("SourceFrame function not implemented");
    };

    //-------------------------------------------------------------------------
    // Message (on top of source in WebInspector)

    SourceFrame.prototype.addMessage = function(msg) {
      this._messages.push(msg);
      if (this.loaded) {
        this.addMessageToSource(msg.line - 1, msg);
      } // else we apply the array at load time.
    };

    SourceFrame.prototype.clearMessages = function() {
      this._clearMessageMarks();
      this._messages = [];
    };

    SourceFrame.prototype.addMessageToSource = function(line, msg) {
      console.error("SourceFrame function not implemented");
    };

    //-------------------------------------------------------------------------
    // Highlights
    SourceFrame.prototype.canHighlightLine = function(line) {
      return true;
    };

    SourceFrame.prototype.highlightLine = function(line) {
      console.error("SourceFrame function not implemented");
    };

    //-------------------------------------------------------------------------
    // Search
    SourceFrame.prototype.performSearch = function(query, callback) {
      console.error("SourceFrame function not implemented");
    };

    SourceFrame.prototype.searchCanceled = function() {
      console.error("SourceFrame function not implemented");
    };

    /* @return boolean
    */
    SourceFrame.prototype.hasSearchResults = function() {
    };

    // called by ScriptsPanel
    SourceFrame.prototype.jumpToFirstSearchResult = function() {
      this.jumpToSearchResult(0);
    };

    SourceFrame.prototype.jumpToLastSearchResult = function() {
      this.jumpToSearchResult(this._searchResults.length - 1);
    };

    SourceFrame.prototype.jumpToNextSearchResult = function() {
      this.jumpToSearchResult(this._currentSearchResultIndex + 1);
    };

    SourceFrame.prototype.jumpToPreviousSearchResult = function() {
      this.jumpToSearchResult(this._currentSearchResultIndex - 1);
    };

    SourceFrame.prototype.showingFirstSearchResult = function() {
      return this._searchResults.length &&  this._currentSearchResultIndex === 0;
    };

    SourceFrame.prototype.showingLastSearchResult = function() {
      return this._searchResults.length && this._currentSearchResultIndex === (this._searchResults.length - 1);
    };

    Object.defineProperty(SourceFrame.prototype, 'currentSearchResultIndex', {
        get: function() {
          return this._currentSearchResultIndex;
        }
    });

    SourceFrame.prototype.jumpToSearchResult = function(index) {
        if (!this.loaded || !this._searchResults.length) {
            return;
        }
      console.error("SourceFrame function not implemented");
    };

  return SourceFrame;
});
<commit_msg>Read source into editor panel<commit_after>// Google BSD license http://code.google.com/google_bsd_license.html
// Copyright 2011 Google Inc. johnjbarton@google.com

/*globals define WebInspector  console */

// Implement WebInspector's SourceFrame using Orion Editor.
// This file will be injected into the WebInspector iframe

define(['OrionEditorEmbedded/editorInserter', 'MetaObject/AJAX'],
function(                    editorInserter,              AJAX)  {

  /**
   * @extends {WebInspector.View}
   * @constructor
  */

  function SourceFrame(url) {
    // The View is a container element with hide/show operations.
    WebInspector.View.call(this);

    this._editor = editorInserter.createEditor(this.element);
    this._openURL(url);

    // element property defined and controlled by View
    this.element.addStyleClass("script-view");

    this._messages = []; // notifications from console
    this._searchResults = [];
  }

  SourceFrame.Events = {
    Loaded: "loaded"
  };

  SourceFrame.prototype = Object.create(WebInspector.View.prototype);

  SourceFrame.prototype._openURL = function(url) {
    AJAX.GET(
      url,
      function(event) {
        var src = event.currentTarget.responseText;
        this._editor.setInput(url, null, src);
        return this;
      }.bind(this),
      function(msg) {
        console.error('openURL('+url+') FAILS '+msg, msg);
      }
    );
  };

    SourceFrame.prototype.setContent = function(mimeType, content) {
      // push content into Orion editor
      this._editor.setText(content);
      // Notify system we are ready
      this._loaded = true;
      this.dispatchEventToListeners(SourceFrame.Events.Loaded);
    };
    //-------------------------------------------------------------------------
    // Panel switching events

    /*  See View.show.  willShow(), style visible, wasShown()
    */
    SourceFrame.prototype.willShow = function() {
      console.error("SourceFrame function not implemented");
    };

    /*
    ** Called at the end of View.show(), on all visible children.
    */
    SourceFrame.prototype.wasShown = function() {
      this._editor.getTextView().resize();
    };

    /* View method, called only by View._processWillHide
    ** Called during View.detach
    */
    SourceFrame.prototype.willHide = function() {
      console.error("SourceFrame function not implemented");
    };

    // Called during View.detach.
    SourceFrame.prototype.wasHidden = function() {
      console.error("SourceFrame function not implemented");
    };

    //-------------------------------------------------------------------------
    // Command to take focus
    SourceFrame.prototype.focus = function() {
      console.error("SourceFrame function not implemented");
    };

    // Called by Panel. Return StatusBarButton for the button bar across the bottom.
    Object.defineProperty(SourceFrame.prototype, 'statusBarItems', {
        get: function() {
        }
    });

    // Called by ScriptsPanel before calling SourceFrame methods
    Object.defineProperty(SourceFrame.prototype, 'loaded', {
        get: function() {
          return this._loaded;
        }
    });

    // Not called by any WebInsepctor code
    SourceFrame.prototype.hasContent = function() {
      return true;
    };

    //-------------------------------------------------------------------------
    // Diff

    // Called by ResourcesPanel to show diffs created in the editor
    SourceFrame.prototype.markDiff = function(diffData) {
      console.error("SourceFrame function not implemented");
    };

    //-------------------------------------------------------------------------
    // Message (on top of source in WebInspector)

    SourceFrame.prototype.addMessage = function(msg) {
      this._messages.push(msg);
      if (this.loaded) {
        this.addMessageToSource(msg.line - 1, msg);
      } // else we apply the array at load time.
    };

    SourceFrame.prototype.clearMessages = function() {
      this._clearMessageMarks();
      this._messages = [];
    };

    SourceFrame.prototype.addMessageToSource = function(line, msg) {
      console.error("SourceFrame function not implemented");
    };

    //-------------------------------------------------------------------------
    // Highlights
    SourceFrame.prototype.canHighlightLine = function(line) {
      return true;
    };

    SourceFrame.prototype.highlightLine = function(line) {
      console.error("SourceFrame function not implemented");
    };

    //-------------------------------------------------------------------------
    // Search
    SourceFrame.prototype.performSearch = function(query, callback) {
      console.error("SourceFrame function not implemented");
    };

    SourceFrame.prototype.searchCanceled = function() {
      console.error("SourceFrame function not implemented");
    };

    /* @return boolean
    */
    SourceFrame.prototype.hasSearchResults = function() {
    };

    // called by ScriptsPanel
    SourceFrame.prototype.jumpToFirstSearchResult = function() {
      this.jumpToSearchResult(0);
    };

    SourceFrame.prototype.jumpToLastSearchResult = function() {
      this.jumpToSearchResult(this._searchResults.length - 1);
    };

    SourceFrame.prototype.jumpToNextSearchResult = function() {
      this.jumpToSearchResult(this._currentSearchResultIndex + 1);
    };

    SourceFrame.prototype.jumpToPreviousSearchResult = function() {
      this.jumpToSearchResult(this._currentSearchResultIndex - 1);
    };

    SourceFrame.prototype.showingFirstSearchResult = function() {
      return this._searchResults.length &&  this._currentSearchResultIndex === 0;
    };

    SourceFrame.prototype.showingLastSearchResult = function() {
      return this._searchResults.length && this._currentSearchResultIndex === (this._searchResults.length - 1);
    };

    Object.defineProperty(SourceFrame.prototype, 'currentSearchResultIndex', {
        get: function() {
          return this._currentSearchResultIndex;
        }
    });

    SourceFrame.prototype.jumpToSearchResult = function(index) {
        if (!this.loaded || !this._searchResults.length) {
            return;
        }
      console.error("SourceFrame function not implemented");
    };

  return SourceFrame;
});
<|endoftext|>


<commit_before>var geom = require("geoscript/geom");

var drawGeometry = function(geometry, options) {

    options = options || {};
    var size = options.size || [500, 500];
    var buf = (buf in options) ? options.buf : 50;

    var awt = java.awt;
    var AffineTransform = java.awt.geom.AffineTransform;
    var swing = javax.swing;
    var LiteShape = Packages.org.geotools.geometry.jts.LiteShape;

    var e = geometry._geometry.getEnvelopeInternal();
    var scx = size[0] / e.width;
    var scy = size[1] / e.height;

    var tx = -1 * e.minX;
    var ty = -1 * e.minY;

    var at = new AffineTransform();

    // scale to size of canvas (inverting the y axis)
    at.scale(scx, -scy);

    // translate to the origin
    at.translate(tx, ty);

    // translate to account for invert
    at.translate(0, -size[1] / scy);

    // buffer
    at.translate(buf / scx, -buf / scy);

    var shp = LiteShape(geometry._geometry, at, false);

    var panel = new JavaAdapter(swing.JPanel, {
        paintComponent: function(g) {
            g.draw(shp);
        }
    });

    var s = new awt.Dimension(
        (size[0] + 2 * buf) | 0,
        (size[1] + 2 * buf) | 0
    );
    panel.setPreferredSize(s);
    var frame = new swing.JFrame();
    frame.contentPane = panel;
    frame.setSize(s);
    frame.visible = true;

};

var draw = function(obj, options) {
    if (obj instanceof geom.Geometry) {
        drawGeometry(obj, options);
    }
};

exports.draw = draw;

<commit_msg>Proper scaling of shapes in viewer.<commit_after>var geom = require("geoscript/geom");
var feature = require("geoscript/feature");
var util = require("geoscript/util");

var drawGeometry = function(geometry, options) {
    options = util.applyIf(options, {
        size: [500, 500],
        fill: true,
        strokeWidth: 1,
        buffer: 50,
        title: geometry.toString()
    });

    var width = options.size[0];
    var height = options.size[1];

    var envelope = geometry._geometry.getEnvelopeInternal();
    var shapeWidth = envelope.getWidth();
    var shapeHeight = envelope.getHeight();

    var transform = new java.awt.geom.AffineTransform();

    // scale to size of canvas (inverting the y axis)
    var scale = Math.min(width / shapeWidth, height / shapeHeight);
    transform.scale(scale, -scale);

    // center the shape using scaled offsets
    transform.translate(
        (options.buffer / scale) - envelope.getMinX() + (((width / scale) - shapeWidth) / 2),
        (-options.buffer / scale) - envelope.getMaxY() - (((height / scale) - shapeHeight) / 2)
    );

    var LiteShape = Packages.org.geotools.geometry.jts.LiteShape;
    var shp = new LiteShape(geometry._geometry, transform, false);

    var panel = new JavaAdapter(javax.swing.JPanel, {
        paintComponent: function(g) {
            if (options.fill) {
                g.setColor(java.awt.Color.WHITE);
                g.fill(shp);
            }
            g.setRenderingHint(
                java.awt.RenderingHints.KEY_ANTIALIASING,
                java.awt.RenderingHints.VALUE_ANTIALIAS_ON
            );
            g.setStroke(java.awt.BasicStroke(options.strokeWidth))
            g.setColor(java.awt.Color.BLACK);
            g.draw(shp);
        }
    });
    panel.setPreferredSize(new java.awt.Dimension(
        java.lang.Integer(width + 2 * options.buffer),
        java.lang.Integer(height + 2 * options.buffer)
    ));

    var frame = new javax.swing.JFrame(options.title);
    frame.setContentPane(panel);
    frame.pack();
    frame.setVisible(true);

};

var draw = function(obj, options) {
    if (obj instanceof geom.Geometry) {
        drawGeometry(obj, options);
    } else if (obj instanceof feature.Feature) {
        drawGeometry(
            obj.geometry,
            util.applyIf(options, {title: obj.toString()})
        );
    }
};

exports.draw = draw;
<|endoftext|>