<commit_before>import axios from 'axios';
import moment from 'moment';
import React, { Component } from 'react';
import Badge from 'react-bootstrap/Badge';
import Button from 'react-bootstrap/Button';
import ButtonGroup from 'react-bootstrap/ButtonGroup';
import ButtonToolbar from 'react-bootstrap/ButtonToolbar';
import Dropdown from 'react-bootstrap/Dropdown';
import DropdownButton from 'react-bootstrap/DropdownButton';
import FormControl from 'react-bootstrap/FormControl';
import InputGroup from 'react-bootstrap/InputGroup';
import Row from 'react-bootstrap/Row';
import Spinner from 'react-bootstrap/Spinner';
import { Link } from 'react-router-dom';
import { handleChange } from './forms';
import MyTable from './Table';
import { compare, ownerName, redirectIfNotConnected, sizeDisplayName, sizeWithFailures } from './uiutil';

const localSnapshots = "Local Snapshots"
const allSnapshots = "All Snapshots"

export class SourcesTable extends Component {
    constructor() {
        super();
        this.state = {
            sources: [],
            isLoading: false,
            error: null,

            localSourceName: "",
            selectedOwner: localSnapshots,
            selectedDirectory: "",
        };

        this.sync = this.sync.bind(this);
        this.fetchSourcesWithoutSpinner = this.fetchSourcesWithoutSpinner.bind(this);
        this.selectDirectory = this.selectDirectory.bind(this);
        this.handleChange = handleChange.bind(this);

        this.snapshotOnce = this.snapshotOnce.bind(this);
        this.snapshotEveryDay = this.snapshotEveryDay.bind(this);
        this.snapshotEveryHour = this.snapshotEveryHour.bind(this);
        this.createPolicy = this.createPolicy.bind(this);
        this.cancelSnapshot = this.cancelSnapshot.bind(this);
        this.startSnapshot = this.startSnapshot.bind(this);
    }

    componentDidMount() {
        this.setState({ isLoading: true });
        this.fetchSourcesWithoutSpinner();
        this.interval = window.setInterval(this.fetchSourcesWithoutSpinner, 3000);
    }

    componentWillUnmount() {
        window.clearInterval(this.interval);
    }

    fetchSourcesWithoutSpinner() {
        axios.get('/api/v1/sources').then(result => {
            this.setState({
                localSourceName: result.data.localUsername + "@" + result.data.localHost,
                sources: result.data.sources,
                isLoading: false,
            });
        }).catch(error => {
            redirectIfNotConnected(error);
            this.setState({
                error,
                isLoading: false
            });
        });
    }

    selectOwner(h) {
        this.setState({
            selectedOwner: h,
        });
    }

    sync() {
        this.setState({ isLoading: true });
        axios.post('/api/v1/repo/sync', {}).then(result => {
            this.fetchSourcesWithoutSpinner();
        }).catch(error => {
            alert('failed');
            this.setState({
                error,
                isLoading: false
            });
        });
    }

    selectDirectory() {
        // populated in 'preload.js' in Electron
        if (!window.require) {
            alert('Directory selection is not supported in a web browser.\n\nPlease enter path manually.');
            return;
        }

        const { dialog } = window.require('electron').remote;
        try {
            let dir = dialog.showOpenDialogSync({
                properties: ['openDirectory']
            });
            if (dir) {
                this.setState({
                    selectedDirectory: dir[0],
                });
            }
        } catch (e) {
            window.alert('Error: ' + e);
        }
    }

    createSource(request) {
        if (!this.state.selectedDirectory) {
            alert('Must specify directory to snapshot.');
            return
        }

        axios.post('/api/v1/sources', request).then(result => {
            this.fetchSourcesWithoutSpinner();
        }).catch(error => {
            if (error.response) {
                alert('Error: ' + error.response.data.error + " (" + error.response.data.code + ")");
                return
            }

            this.setState({
                error,
                isLoading: false
            });
        });
    }

    snapshotOnce() {
        this.createSource({
            path: this.state.selectedDirectory,
            createSnapshot: true,
            initialPolicy: {
            }
        });
    }

    snapshotEveryDay() {
        this.createSource({
            path: this.state.selectedDirectory,
            createSnapshot: true,
            initialPolicy: {
                scheduling: { intervalSeconds: 86400 },
            }
        });
    }

    snapshotEveryHour() {
        this.createSource({
            path: this.state.selectedDirectory,
            createSnapshot: true,
            initialPolicy: {
                scheduling: { intervalSeconds: 3600 },
            }
        });
    }

    createPolicy() {
        this.createSource({
            path: this.state.selectedDirectory,
            createSnapshot: false,
            initialPolicy: {
                scheduling: { intervalSeconds: 3600 },
            }
        });
    }

    statusCell(x, parent) {
        switch (x.cell.value) {
            case "IDLE":
                return <>
                    <Button variant="primary" size="sm" onClick={() => {
                        parent.startSnapshot(x.row.original.source);
                    }}>Snapshot now</Button>
                </>;

            case "PENDING":
                return <>
                    <Spinner animation="border" variant="secondary" size="sm" title="Snapshot will after the previous snapshot completes" />
                     Pending
                </>;

            case "UPLOADING":
                let u = x.row.original.upload;
                let title = "";
                let totals = "";
                if (u) {
                    title = " hashed " + u.hashedFiles + " files (" + sizeDisplayName(u.hashedBytes) + ")\n" +
                        " cached " + u.cachedFiles + " files (" + sizeDisplayName(u.cachedBytes) + ")\n" +
                        " dir " + u.directory;

                    const totalBytes = u.hashedBytes + u.cachedBytes;

                    totals = sizeDisplayName(totalBytes);
                    if (x.row.original.lastSnapshot) {
                        const percent = Math.round(totalBytes * 1000.0 / x.row.original.lastSnapshot.stats.totalSize) / 10.0;
                        totals += " " + percent + "%";
                    }
                }

                return <>
                    <Spinner animation="border" variant="primary" size="sm" title={title} /> Snapshotting {totals}

                    <Button variant="danger" size="sm" onClick={() => {
                        parent.cancelSnapshot(x.row.original.source);
                    }}>stop</Button>
                </>;

            default:
                return "";
        }
    }

    cancelSnapshot(source) {
        axios.post('/api/v1/sources/cancel?userName=' + source.userName + '&host=' + source.host + '&path=' + source.path, {}).then(result => {
            this.fetchSourcesWithoutSpinner();
        }).catch(error => {
            alert('failed');
        });
    }

    startSnapshot(source) {
        axios.post('/api/v1/sources/upload?userName=' + source.userName + '&host=' + source.host + '&path=' + source.path, {}).then(result => {
            this.fetchSourcesWithoutSpinner();
        }).catch(error => {
            alert('failed');
        });
    }

    render() {
        let { sources, isLoading, error } = this.state;
        if (error) {
            return
{error.message}
;
        }
        if (isLoading) {
            return <Spinner animation="border" variant="primary" />;
        }

        let uniqueOwners = sources.reduce((a, d) => {
            const owner = ownerName(d.source);

            if (!a.includes(owner)) { a.push(owner); }
            return a;
        }, []);

        uniqueOwners.sort();

        switch (this.state.selectedOwner) {
            case allSnapshots:
                // do nothing;
                break;

            case localSnapshots:
                sources = sources.filter(x => ownerName(x.source) === this.state.localSourceName);
                break;

            default:
                sources = sources.filter(x => ownerName(x.source) === this.state.selectedOwner);
                break;
        };

        const columns = [{
            id: 'path',
            Header: 'Path',
            accessor: x => x.source,
            sortType: (a, b) => {
                const v = compare(a.original.source.path, b.original.source.path);
                if (v !== 0) {
                    return v;
                }

                return compare(ownerName(a.original.source), ownerName(b.original.source));
            },
            width: "",
            Cell: x => <Link to={'/snapshots/single-source?userName=' + x.cell.value.userName + '&host=' + x.cell.value.host + '&path=' + x.cell.value.path}>{x.cell.value.path}</Link>,
        }, {
            id: 'owner',
            Header: 'Owner',
            accessor: x => x.source.userName + '@' + x.source.host,
            width: 250,
        }, {
            id: 'lastSnapshotSize',
            Header: 'Size',
            width: 120,
            accessor: x => x.lastSnapshot ? x.lastSnapshot.stats.totalSize : 0,
            Cell: x => sizeWithFailures(
                x.cell.value,
                x.row.original.lastSnapshot && x.row.original.lastSnapshot.rootEntry ? x.row.original.lastSnapshot.rootEntry.summ : null),
        }, {
            id: 'lastSnapshotTime',
            Header: 'Last Snapshot',
            width: 160,
            accessor: x => x.lastSnapshot ? x.lastSnapshot.startTime : null,
            Cell: x => x.cell.value ?
{moment(x.cell.value).fromNow()}
 : '',
        }, {
            id: 'nextSnapshotTime',
            Header: 'Next Snapshot',
            width: 160,
            accessor: x => x.nextSnapshotTime,
            Cell: x => (x.cell.value && x.row.original.status !== "UPLOADING") ? <>

{moment(x.cell.value).fromNow()}
                    {moment(x.cell.value).isBefore(moment()) && <>

                    <Badge variant="secondary">overdue</Badge>
                    </>}

            </> : '',
        }, {
            id: 'status',
            Header: 'Status',
            width: 300,
            accessor: x => x.status,
            Cell: x => this.statusCell(x, this),
        }]

        return

            <ButtonToolbar className="float-sm-right">

                <ButtonGroup>
                    <Dropdown>
                        <Dropdown.Toggle variant="outline-primary" id="dropdown-basic">
                            {this.state.selectedOwner}
                        </Dropdown.Toggle>

                        <Dropdown.Menu>
                            <Dropdown.Item onClick={() => this.selectOwner(localSnapshots)}>{localSnapshots}</Dropdown.Item>
                            <Dropdown.Item onClick={() => this.selectOwner(allSnapshots)}>{allSnapshots}</Dropdown.Item>
                            <Dropdown.Divider />
                            {uniqueOwners.map(v => <Dropdown.Item key={v} onClick={() => this.selectOwner(v)}>{v}</Dropdown.Item>)}
                        </Dropdown.Menu>
                    </Dropdown>
                </ButtonGroup>

                <ButtonGroup>
                    <Button variant="primary">Refresh</Button>
                </ButtonGroup>
            </ButtonToolbar>
            <ButtonToolbar>
                <InputGroup>
                    <FormControl
                        id="snapshot-path"
                        placeholder="Enter source path to create new snapshot"
                        name="selectedDirectory"
                        value={this.state.selectedDirectory}
                        onChange={this.handleChange}
                    />
                    <Button as={InputGroup.Prepend}
                        title="Snapshot"
                        variant="primary"
                        id="input-group-dropdown-2"
                        onClick={this.selectDirectory}>...</Button>
                </InputGroup>

                <DropdownButton
                    as={InputGroup.Append}
                    variant="success"
                    title="New Snapshot"
                    id="dropdown1">
                    <Dropdown.Item href="#" onClick={this.snapshotOnce}>Snapshot Once</Dropdown.Item>
                    <Dropdown.Item href="#" onClick={this.snapshotEveryHour}>Snapshot Every Hour</Dropdown.Item>
                    <Dropdown.Item href="#" onClick={this.snapshotEveryDay}>Snapshot Every Day</Dropdown.Item>
                    {/* <Dropdown.Item href="#" onClick={this.createPolicy}>Create Policy</Dropdown.Item> */}
                </DropdownButton>
            </ButtonToolbar>

            <Row>
                <MyTable data={sources} columns={columns} />
            </Row>

;
    }
}
<commit_msg>app: Disabled progress percentage display above 100% (#521)<commit_after>import axios from 'axios';
import moment from 'moment';
import React, { Component } from 'react';
import Badge from 'react-bootstrap/Badge';
import Button from 'react-bootstrap/Button';
import ButtonGroup from 'react-bootstrap/ButtonGroup';
import ButtonToolbar from 'react-bootstrap/ButtonToolbar';
import Dropdown from 'react-bootstrap/Dropdown';
import DropdownButton from 'react-bootstrap/DropdownButton';
import FormControl from 'react-bootstrap/FormControl';
import InputGroup from 'react-bootstrap/InputGroup';
import Row from 'react-bootstrap/Row';
import Spinner from 'react-bootstrap/Spinner';
import { Link } from 'react-router-dom';
import { handleChange } from './forms';
import MyTable from './Table';
import { compare, ownerName, redirectIfNotConnected, sizeDisplayName, sizeWithFailures } from './uiutil';

const localSnapshots = "Local Snapshots"
const allSnapshots = "All Snapshots"

export class SourcesTable extends Component {
    constructor() {
        super();
        this.state = {
            sources: [],
            isLoading: false,
            error: null,

            localSourceName: "",
            selectedOwner: localSnapshots,
            selectedDirectory: "",
        };

        this.sync = this.sync.bind(this);
        this.fetchSourcesWithoutSpinner = this.fetchSourcesWithoutSpinner.bind(this);
        this.selectDirectory = this.selectDirectory.bind(this);
        this.handleChange = handleChange.bind(this);

        this.snapshotOnce = this.snapshotOnce.bind(this);
        this.snapshotEveryDay = this.snapshotEveryDay.bind(this);
        this.snapshotEveryHour = this.snapshotEveryHour.bind(this);
        this.createPolicy = this.createPolicy.bind(this);
        this.cancelSnapshot = this.cancelSnapshot.bind(this);
        this.startSnapshot = this.startSnapshot.bind(this);
    }

    componentDidMount() {
        this.setState({ isLoading: true });
        this.fetchSourcesWithoutSpinner();
        this.interval = window.setInterval(this.fetchSourcesWithoutSpinner, 3000);
    }

    componentWillUnmount() {
        window.clearInterval(this.interval);
    }

    fetchSourcesWithoutSpinner() {
        axios.get('/api/v1/sources').then(result => {
            this.setState({
                localSourceName: result.data.localUsername + "@" + result.data.localHost,
                sources: result.data.sources,
                isLoading: false,
            });
        }).catch(error => {
            redirectIfNotConnected(error);
            this.setState({
                error,
                isLoading: false
            });
        });
    }

    selectOwner(h) {
        this.setState({
            selectedOwner: h,
        });
    }

    sync() {
        this.setState({ isLoading: true });
        axios.post('/api/v1/repo/sync', {}).then(result => {
            this.fetchSourcesWithoutSpinner();
        }).catch(error => {
            alert('failed');
            this.setState({
                error,
                isLoading: false
            });
        });
    }

    selectDirectory() {
        // populated in 'preload.js' in Electron
        if (!window.require) {
            alert('Directory selection is not supported in a web browser.\n\nPlease enter path manually.');
            return;
        }

        const { dialog } = window.require('electron').remote;
        try {
            let dir = dialog.showOpenDialogSync({
                properties: ['openDirectory']
            });
            if (dir) {
                this.setState({
                    selectedDirectory: dir[0],
                });
            }
        } catch (e) {
            window.alert('Error: ' + e);
        }
    }

    createSource(request) {
        if (!this.state.selectedDirectory) {
            alert('Must specify directory to snapshot.');
            return
        }

        axios.post('/api/v1/sources', request).then(result => {
            this.fetchSourcesWithoutSpinner();
        }).catch(error => {
            if (error.response) {
                alert('Error: ' + error.response.data.error + " (" + error.response.data.code + ")");
                return
            }

            this.setState({
                error,
                isLoading: false
            });
        });
    }

    snapshotOnce() {
        this.createSource({
            path: this.state.selectedDirectory,
            createSnapshot: true,
            initialPolicy: {
            }
        });
    }

    snapshotEveryDay() {
        this.createSource({
            path: this.state.selectedDirectory,
            createSnapshot: true,
            initialPolicy: {
                scheduling: { intervalSeconds: 86400 },
            }
        });
    }

    snapshotEveryHour() {
        this.createSource({
            path: this.state.selectedDirectory,
            createSnapshot: true,
            initialPolicy: {
                scheduling: { intervalSeconds: 3600 },
            }
        });
    }

    createPolicy() {
        this.createSource({
            path: this.state.selectedDirectory,
            createSnapshot: false,
            initialPolicy: {
                scheduling: { intervalSeconds: 3600 },
            }
        });
    }

    statusCell(x, parent) {
        switch (x.cell.value) {
            case "IDLE":
                return <>
                    <Button variant="primary" size="sm" onClick={() => {
                        parent.startSnapshot(x.row.original.source);
                    }}>Snapshot now</Button>
                </>;

            case "PENDING":
                return <>
                    <Spinner animation="border" variant="secondary" size="sm" title="Snapshot will after the previous snapshot completes" />
                     Pending
                </>;

            case "UPLOADING":
                let u = x.row.original.upload;
                let title = "";
                let totals = "";
                if (u) {
                    title = " hashed " + u.hashedFiles + " files (" + sizeDisplayName(u.hashedBytes) + ")\n" +
                        " cached " + u.cachedFiles + " files (" + sizeDisplayName(u.cachedBytes) + ")\n" +
                        " dir " + u.directory;

                    const totalBytes = u.hashedBytes + u.cachedBytes;

                    totals = sizeDisplayName(totalBytes);
                    if (x.row.original.lastSnapshot) {
                        const percent = Math.round(totalBytes * 1000.0 / x.row.original.lastSnapshot.stats.totalSize) / 10.0;
                        if (percent <= 100) {
                            totals += " " + percent + "%";
                        }
                    }
                }

                return <>
                    <Spinner animation="border" variant="primary" size="sm" title={title} /> Snapshotting {totals}

                    <Button variant="danger" size="sm" onClick={() => {
                        parent.cancelSnapshot(x.row.original.source);
                    }}>stop</Button>
                </>;

            default:
                return "";
        }
    }

    cancelSnapshot(source) {
        axios.post('/api/v1/sources/cancel?userName=' + source.userName + '&host=' + source.host + '&path=' + source.path, {}).then(result => {
            this.fetchSourcesWithoutSpinner();
        }).catch(error => {
            alert('failed');
        });
    }

    startSnapshot(source) {
        axios.post('/api/v1/sources/upload?userName=' + source.userName + '&host=' + source.host + '&path=' + source.path, {}).then(result => {
            this.fetchSourcesWithoutSpinner();
        }).catch(error => {
            alert('failed');
        });
    }

    render() {
        let { sources, isLoading, error } = this.state;
        if (error) {
            return
{error.message}
;
        }
        if (isLoading) {
            return <Spinner animation="border" variant="primary" />;
        }

        let uniqueOwners = sources.reduce((a, d) => {
            const owner = ownerName(d.source);

            if (!a.includes(owner)) { a.push(owner); }
            return a;
        }, []);

        uniqueOwners.sort();

        switch (this.state.selectedOwner) {
            case allSnapshots:
                // do nothing;
                break;

            case localSnapshots:
                sources = sources.filter(x => ownerName(x.source) === this.state.localSourceName);
                break;

            default:
                sources = sources.filter(x => ownerName(x.source) === this.state.selectedOwner);
                break;
        };

        const columns = [{
            id: 'path',
            Header: 'Path',
            accessor: x => x.source,
            sortType: (a, b) => {
                const v = compare(a.original.source.path, b.original.source.path);
                if (v !== 0) {
                    return v;
                }

                return compare(ownerName(a.original.source), ownerName(b.original.source));
            },
            width: "",
            Cell: x => <Link to={'/snapshots/single-source?userName=' + x.cell.value.userName + '&host=' + x.cell.value.host + '&path=' + x.cell.value.path}>{x.cell.value.path}</Link>,
        }, {
            id: 'owner',
            Header: 'Owner',
            accessor: x => x.source.userName + '@' + x.source.host,
            width: 250,
        }, {
            id: 'lastSnapshotSize',
            Header: 'Size',
            width: 120,
            accessor: x => x.lastSnapshot ? x.lastSnapshot.stats.totalSize : 0,
            Cell: x => sizeWithFailures(
                x.cell.value,
                x.row.original.lastSnapshot && x.row.original.lastSnapshot.rootEntry ? x.row.original.lastSnapshot.rootEntry.summ : null),
        }, {
            id: 'lastSnapshotTime',
            Header: 'Last Snapshot',
            width: 160,
            accessor: x => x.lastSnapshot ? x.lastSnapshot.startTime : null,
            Cell: x => x.cell.value ?
{moment(x.cell.value).fromNow()}
 : '',
        }, {
            id: 'nextSnapshotTime',
            Header: 'Next Snapshot',
            width: 160,
            accessor: x => x.nextSnapshotTime,
            Cell: x => (x.cell.value && x.row.original.status !== "UPLOADING") ? <>

{moment(x.cell.value).fromNow()}
                    {moment(x.cell.value).isBefore(moment()) && <>

                    <Badge variant="secondary">overdue</Badge>
                    </>}

            </> : '',
        }, {
            id: 'status',
            Header: 'Status',
            width: 300,
            accessor: x => x.status,
            Cell: x => this.statusCell(x, this),
        }]

        return

            <ButtonToolbar className="float-sm-right">

                <ButtonGroup>
                    <Dropdown>
                        <Dropdown.Toggle variant="outline-primary" id="dropdown-basic">
                            {this.state.selectedOwner}
                        </Dropdown.Toggle>

                        <Dropdown.Menu>
                            <Dropdown.Item onClick={() => this.selectOwner(localSnapshots)}>{localSnapshots}</Dropdown.Item>
                            <Dropdown.Item onClick={() => this.selectOwner(allSnapshots)}>{allSnapshots}</Dropdown.Item>
                            <Dropdown.Divider />
                            {uniqueOwners.map(v => <Dropdown.Item key={v} onClick={() => this.selectOwner(v)}>{v}</Dropdown.Item>)}
                        </Dropdown.Menu>
                    </Dropdown>
                </ButtonGroup>

                <ButtonGroup>
                    <Button variant="primary">Refresh</Button>
                </ButtonGroup>
            </ButtonToolbar>
            <ButtonToolbar>
                <InputGroup>
                    <FormControl
                        id="snapshot-path"
                        placeholder="Enter source path to create new snapshot"
                        name="selectedDirectory"
                        value={this.state.selectedDirectory}
                        onChange={this.handleChange}
                    />
                    <Button as={InputGroup.Prepend}
                        title="Snapshot"
                        variant="primary"
                        id="input-group-dropdown-2"
                        onClick={this.selectDirectory}>...</Button>
                </InputGroup>

                <DropdownButton
                    as={InputGroup.Append}
                    variant="success"
                    title="New Snapshot"
                    id="dropdown1">
                    <Dropdown.Item href="#" onClick={this.snapshotOnce}>Snapshot Once</Dropdown.Item>
                    <Dropdown.Item href="#" onClick={this.snapshotEveryHour}>Snapshot Every Hour</Dropdown.Item>
                    <Dropdown.Item href="#" onClick={this.snapshotEveryDay}>Snapshot Every Day</Dropdown.Item>
                    {/* <Dropdown.Item href="#" onClick={this.createPolicy}>Create Policy</Dropdown.Item> */}
                </DropdownButton>
            </ButtonToolbar>

            <Row>
                <MyTable data={sources} columns={columns} />
            </Row>

;
    }
}
<|endoftext|>


<commit_before>Ext.ns("SymPy");
SymPy.MobileShell = Ext.extend(
    SymPy.Shell, {
        constructor: function(config) {
            config = Ext.apply({}, config);
            SymPy.MobileShell.superclass.constructor.call(this, config);
        },
        renderToolbar: function(el) {
            SymPy.MobileShell.superclass.renderToolbar.call(this, el);
            Ext.DomHelper.insertAfter(
                this.promptEl,
                {
                    tag: 'div',
                    id: 'sympy-live-toolbar-history',
                    children: [{
                                   tag: 'button',
                                   id: 'button-history-prev',
                                   html: '\u2191'
                               }, {
                                   tag: 'button',
                                   id: 'button-history-next',
                                   html: '\u2193'
                               }]
                }, true);
            var insertEl = Ext.get(
                this.submitEl.query('option[value="enter"]')[0]);
            var submitEl = Ext.get(
                this.submitEl.query('option[value="shift-enter"]')[0]);
            insertEl.set({value: "enter-inserts-newline"}).update("inserts newline");
            submitEl.set({value: "enter-submits"}).update("submits");
            this.submitEl.next().remove();
            Ext.DomHelper.insertBefore(this.submitEl,{
                 tag: 'span',
                 html: 'Enter'
            });
            this.historyPrevEl = Ext.get("button-history-prev");
            this.historyNextEl = Ext.get("button-history-next");
        },
        render: function(el) {
            SymPy.MobileShell.superclass.render.call(this, el);
            this.renderSearches();
            this.promptEl.set({autocorrect: 'off', autocapitalize: 'off'});
            var shell = Ext.get("shell");
            Ext.each(
                this.toolbarEl.query('.sympy-live-separator'),
                function(n){
                    Ext.get(n).remove();
                }
            );
            Ext.get("output-format")
                .appendTo(shell)
                .insertBefore(this.outputEl);
            Ext.DomHelper.insertBefore(
                this.outputEl,
                {
                    'tag': 'span',
                    'cls': 'sympy-live-separator',
                    'html': '|'
                }
            );
            this.toolbarEl.down('span')
                .appendTo(shell)
                .insertBefore(this.outputEl);
            Ext.get("submit-behavior")
                .appendTo(shell)
                .insertBefore(this.outputEl);
            this.toolbarEl.down('span').remove();
            this.historyPrevEl.on("click", function(event){
                this.promptEl.focus(1000);
                this.prevInHistory();
            }, this);
            this.historyNextEl.on("click", function(event){
                this.promptEl.focus(1000);
                this.nextInHistory();
            }, this);
            Ext.get("menu").on("click", function(event){
                Ext.get("main-navigation").toggle(true);
                Ext.get("main-navigation").down("ul").toggle(true);
            });
        },
        handleKey: function(event) {
            if (event.getKey() == SymPy.Keys.ENTER) {
                var enterSubmits = (this.submitEl.getValue() ==
                                    "enter-submits");
                if (enterSubmits) {
                    event.stopEvent();
                    this.evaluate();
                    return true;
                }
                else if (this.supportsSelection){

                    var cursor = this.getCursor();

                    if (cursor !== null) {
                        var value = this.getValue();
                        var index = value.lastIndexOf('\n', cursor) + 1;
                        var spaces = "";

                        while (value[index++] == ' ') {
                            spaces += " ";
                        }

                        if (value[cursor-1] == ':') {
                            for (var i = 0; i < this.tabWidth; i++) {
                                spaces += " ";
                            }
                        }

                        var start = value.slice(0, cursor);
                        var end = value.slice(cursor);

                        this.setValue(start + '\n' + spaces + end);
                        this.setCursor(cursor + 1 + spaces.length);

                        event.stopEvent();
                        return true;
                    }
                }
            }
            SymPy.MobileShell.superclass.handleKey.call(this, event);
        },
        renderSearches: function(){
            this.savedSearches = Ext.get("saved-searches");
            this.recentSearches = Ext.get("recent-searches");
            var setupEval = (function(el){
                var nodes = el.query("button");
                var shell = this;  // closure
                Ext.each(nodes, function(node){
                    node = Ext.get(node);
                    node.on("click", function(event){
                        // We don't want the query to show up twice
                        var origPrivacy = shell.recordEl.getValue();
                        shell.recordEl.dom.value =  "on";
                        shell.setValue(this.first("pre").dom.innerHTML);
                        shell.evaluate();
                        Ext.get(document.body).scrollTo("top", 0);
                        shell.recordEl.dom.value = origPrivacy;
                    }, node);
                });
            });
            setupEval.call(this, this.recentSearches);
            setupEval.call(this, this.savedSearches);
            var toggle = function(){
                var height = null;
                return (function(event){
                    height = height || this.getHeight();
                    if (this.isVisible()){
                        this.animate({
                            height: {to: 0},
                            opacity: {to: 0}
                        }, 0.5, function(){this.toggle();});
                    }
                    else {
                        this.animate({
                            height: {to: height},
                            opacity: {to: 1}
                        }, 0.5, function(){this.toggle();});
                    }
                });
            }
            Ext.get("recent-searches-container").
                first("h3").
                on("click", toggle(), this.recentSearches);
            Ext.get("saved-searches-container").
                first("h3").
                on("click", toggle(), this.savedSearches);
        }
    });
<commit_msg>Scroll to top of output<commit_after>Ext.ns("SymPy");
SymPy.MobileShell = Ext.extend(
    SymPy.Shell, {
        constructor: function(config) {
            config = Ext.apply({}, config);
            SymPy.MobileShell.superclass.constructor.call(this, config);
        },
        renderToolbar: function(el) {
            SymPy.MobileShell.superclass.renderToolbar.call(this, el);
            Ext.DomHelper.insertAfter(
                this.promptEl,
                {
                    tag: 'div',
                    id: 'sympy-live-toolbar-history',
                    children: [{
                                   tag: 'button',
                                   id: 'button-history-prev',
                                   html: '\u2191'
                               }, {
                                   tag: 'button',
                                   id: 'button-history-next',
                                   html: '\u2193'
                               }]
                }, true);
            var insertEl = Ext.get(
                this.submitEl.query('option[value="enter"]')[0]);
            var submitEl = Ext.get(
                this.submitEl.query('option[value="shift-enter"]')[0]);
            insertEl.set({value: "enter-inserts-newline"}).update("inserts newline");
            submitEl.set({value: "enter-submits"}).update("submits");
            this.submitEl.next().remove();
            Ext.DomHelper.insertBefore(this.submitEl,{
                 tag: 'span',
                 html: 'Enter'
            });
            this.historyPrevEl = Ext.get("button-history-prev");
            this.historyNextEl = Ext.get("button-history-next");
        },
        render: function(el) {
            SymPy.MobileShell.superclass.render.call(this, el);
            this.renderSearches();
            this.promptEl.set({autocorrect: 'off', autocapitalize: 'off'});
            var shell = Ext.get("shell");
            Ext.each(
                this.toolbarEl.query('.sympy-live-separator'),
                function(n){
                    Ext.get(n).remove();
                }
            );
            Ext.get("output-format")
                .appendTo(shell)
                .insertBefore(this.outputEl);
            Ext.DomHelper.insertBefore(
                this.outputEl,
                {
                    'tag': 'span',
                    'cls': 'sympy-live-separator',
                    'html': '|'
                }
            );
            this.toolbarEl.down('span')
                .appendTo(shell)
                .insertBefore(this.outputEl);
            Ext.get("submit-behavior")
                .appendTo(shell)
                .insertBefore(this.outputEl);
            this.toolbarEl.down('span').remove();
            this.historyPrevEl.on("click", function(event){
                this.promptEl.focus(1000);
                this.prevInHistory();
            }, this);
            this.historyNextEl.on("click", function(event){
                this.promptEl.focus(1000);
                this.nextInHistory();
            }, this);
            Ext.get("menu").on("click", function(event){
                Ext.get("main-navigation").toggle(true);
                Ext.get("main-navigation").down("ul").toggle(true);
            });
        },
        handleKey: function(event) {
            if (event.getKey() == SymPy.Keys.ENTER) {
                var enterSubmits = (this.submitEl.getValue() ==
                                    "enter-submits");
                if (enterSubmits) {
                    event.stopEvent();
                    this.evaluate();
                    return true;
                }
                else if (this.supportsSelection){

                    var cursor = this.getCursor();

                    if (cursor !== null) {
                        var value = this.getValue();
                        var index = value.lastIndexOf('\n', cursor) + 1;
                        var spaces = "";

                        while (value[index++] == ' ') {
                            spaces += " ";
                        }

                        if (value[cursor-1] == ':') {
                            for (var i = 0; i < this.tabWidth; i++) {
                                spaces += " ";
                            }
                        }

                        var start = value.slice(0, cursor);
                        var end = value.slice(cursor);

                        this.setValue(start + '\n' + spaces + end);
                        this.setCursor(cursor + 1 + spaces.length);

                        event.stopEvent();
                        return true;
                    }
                }
            }
            SymPy.MobileShell.superclass.handleKey.call(this, event);
        },
        renderSearches: function(){
            this.savedSearches = Ext.get("saved-searches");
            this.recentSearches = Ext.get("recent-searches");
            var setupEval = (function(el){
                var nodes = el.query("button");
                var shell = this;  // closure
                Ext.each(nodes, function(node){
                    node = Ext.get(node);
                    node.on("click", function(event){
                        // We don't want the query to show up twice
                        var origPrivacy = shell.recordEl.getValue();
                        shell.recordEl.dom.value =  "on";
                        // And we're going to scroll to the output
                        var scrollY = shell.outputEl.getTop();

                        shell.setValue(this.first("pre").dom.innerHTML);
                        shell.evaluate();

                        Ext.get(document.body).scrollTo("top", scrollY);
                        shell.recordEl.dom.value = origPrivacy;
                    }, node);
                });
            });
            setupEval.call(this, this.recentSearches);
            setupEval.call(this, this.savedSearches);
            var toggle = function(){
                var height = null;
                return (function(event){
                    height = height || this.getHeight();
                    if (this.isVisible()){
                        this.animate({
                            height: {to: 0},
                            opacity: {to: 0}
                        }, 0.5, function(){this.toggle();});
                    }
                    else {
                        this.animate({
                            height: {to: height},
                            opacity: {to: 1}
                        }, 0.5, function(){this.toggle();});
                    }
                });
            }
            Ext.get("recent-searches-container").
                first("h3").
                on("click", toggle(), this.recentSearches);
            Ext.get("saved-searches-container").
                first("h3").
                on("click", toggle(), this.savedSearches);
        }
    });
<|endoftext|>


<commit_before>AUI.add('dialog', function(A) {

var L = A.Lang,
	isBoolean = L.isBoolean,
	isNumber = L.isNumber,
	isString = L.isString,
	isArray = L.isArray,
	isObject = L.isObject,

	ADD_CLASS = 'addClass',
	ANCHOR = 'a',
	BD = 'bd',
	BLANK = '',
	BODY_CONTENT = 'bodyContent',
	BOUNDING_BOX = 'boundingBox',
	BUTTON = 'button',
	BUTTONS = 'buttons',
	CLOSE = 'close',
	CONSTRAIN_TO_VIEWPORT = 'constrain2view',
	CONTAINER = 'container',
	CONTENT_BOX = 'contentBox',
	DD = 'dd',
	DEFAULT = 'default',
	DESTROY_ON_CLOSE = 'destroyOnClose',
	DIALOG = 'dialog',
	DOT = '.',
	DRAGGABLE = 'draggable',
	DRAG_INSTANCE = 'dragInstance',
	FOOTER_CONTENT = 'footerContent',
	FT = 'ft',
	HD = 'hd',
	HEADER = 'header',
	HEADER_CONTENT = 'headerContent',
	HOVER = 'hover',
	ICON = 'icon',
	INNER_HTML = 'innerHTML',
	IO = 'io',
	LOADING = 'loading',
	POST = 'POST',
	REMOVE_CLASS = 'removeClass',
	STACK = 'stack',
	STATE = 'state',
	TITLE = 'title',
	WIDGET = 'widget',

	getCN = A.ClassNameManager.getClassName,

	CSS_DIALOG_BUTTON = getCN(DIALOG, BUTTON),
	CSS_DIALOG_BUTTON_CONTAINER = getCN(DIALOG, BUTTON, CONTAINER),
	CSS_DIALOG_BUTTON_DEFAULT = getCN(DIALOG, BUTTON, DEFAULT),
	CSS_DIALOG_CLOSE = getCN(DIALOG, CLOSE),
	CSS_DIALOG_TITLE = getCN(DIALOG, TITLE),
	CSS_ICON = getCN(ICON),
	CSS_ICON_CLOSE = getCN(ICON, CLOSE),
	CSS_ICON_LOADING = getCN(ICON, LOADING),
	CSS_PREFIX = getCN(DD),
	CSS_STATE_DEFAULT = getCN(STATE, DEFAULT),
	CSS_STATE_HOVER = getCN(STATE, HOVER),
	CSS_WIDGET_HD = getCN(WIDGET, HD),
	CSS_DIALOG_HD = getCN(DIALOG, HD),
	CSS_DIALOG_BD = getCN(DIALOG, BD),
	CSS_DIALOG_FT = getCN(DIALOG, FT),

	TPL_LOADING = '

';

function Dialog(config) {
	Dialog.superclass.constructor.apply(this, arguments);
}

A.mix(Dialog, {
	NAME: DIALOG,

	ATTRS: {
		buttons: {
			value: [],
			validator: isArray
		},

		constrain2view: {
			value: true,
			validator: isBoolean
		},

		destroyOnClose: {
			value: false,
			validator: isBoolean
		},

		draggable: {
			lazyAdd: true,
			value: true,
			setter: function(v) {
				return this._setDraggable(v);
			}
		},

		dragInstance: {
			value: null
		},

		headerContent: {
			writeOnce: true,
			getter: function() {
				return this.titleContainter;
			}
		},

		io: {
			lazyAdd: true,
			value: null,
			setter: function(v) {
				return this._setIO(v);
			}
		},

		stack: {
			lazyAdd: true,
			value: true,
			setter: function(v) {
				return this._setStack(v);
			},
			validator: isBoolean
		},

		title: {
			value: BLANK,
			validator: function(v) {
				return isString(v) || isBoolean(v);
			}
		}
	}
});

A.extend(Dialog, A.Overlay, {
	/*
	* Lifecycle
	*/
	renderUI: function () {
		var instance = this;

		instance._renderElements();
	},

	bindUI: function () {
		var instance = this;

		instance._bindElements();
	},

	renderer: function() {
		var instance = this;

		Dialog.superclass.renderer.apply(instance, arguments);

		instance._afterRenderer();
	},

	destructor: function() {
		var instance = this;

		if (instance.OverlayManager) {
			instance.OverlayManager.destroy();
		}

		instance.get(BOUNDING_BOX).remove();
	},

	/*
	* Methods
	*/
	close: function() {
		var instance = this;

		if (instance.get(DESTROY_ON_CLOSE)) {
			instance.destroy();
		}
		else {
			instance.hide();
		}

		instance.fire('close');
	},

	_bindElements: function () {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);

		instance.closeIcon.on(
			'click',
			function(event) {
				instance.close();
			}
		);

		boundingBox.on('mousedown', function() {
			if (instance.OverlayManager) {
				instance.OverlayManager.bringToTop();
			}

			instance.fire('focus');
		});

		instance.after('titleChange', this._afterSetTitle);
	},

	_renderElements: function() {
		var instance = this;

		instance.titleContainter = A.Node.create('

');

		var closeIcon = new A.ToolItem(CLOSE);

		closeIcon.get('node').addClass(CSS_DIALOG_CLOSE);

		instance.closeIcon = closeIcon;

		var title = instance.get(TITLE);

		if (title !== false) {
			var titleContainter = instance.titleContainter;

			titleContainter.addClass(CSS_DIALOG_TITLE);

			titleContainter.html(title);
		}
	},

	_afterRenderer: function() {
		var instance = this;
		var bodyNode = instance.bodyNode;
		var footerNode = instance.footerNode;
		var headerNode = instance.headerNode;

		var stack = instance.get(STACK);
		var title = instance.get(TITLE);

		headerNode.addClass(CSS_DIALOG_HD);
		headerNode.addClass(CSS_STATE_DEFAULT);

		bodyNode.addClass(CSS_DIALOG_BD);

		if (footerNode) {
			footerNode.addClass(CSS_DIALOG_FT);
		}

		if (title === false) {
			headerNode.removeClass(CSS_STATE_DEFAULT);
			headerNode.removeClass(CSS_WIDGET_HD);
			headerNode.removeClass(CSS_DIALOG_HD);
		}

		headerNode.append(instance.titleContainter);

		instance.closeIcon.render(headerNode);

		instance._initButtons();

		// forcing lazyAdd:true attrs call the setter
		instance.get(DRAGGABLE);
		instance.get(IO);
	},

	_initButtons: function() {
		var instance = this;
		var buttons = instance.get(BUTTONS);
		var container = A.Node.create('

');
		var nodeModel = A.Node.create('<button></button>');

		container.addClass(CSS_DIALOG_BUTTON_CONTAINER);
		nodeModel.addClass(CSS_DIALOG_BUTTON);

		A.each(buttons, function(button, i) {
			var node = nodeModel.cloneNode();

			if (button.isDefault) {
				node.addClass(CSS_DIALOG_BUTTON_DEFAULT);
			}

			if (button.handler) {
				node.on('click', A.bind(button.handler, instance));
			}

			node.html(button.text || BLANK);

			container.append(node);
		});

		if (buttons.length) {
			instance.set(FOOTER_CONTENT, container);

			instance.footerNode.addClass(CSS_DIALOG_FT);
		}
	},

	_setDraggable: function(value) {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);

		var destroyDraggable = function() {
			var dragInstance = instance.get(DRAG_INSTANCE);

			if (dragInstance) {
				// TODO - YUI3 has a bug when destroy and recreates
				dragInstance.destroy();
				dragInstance.unplug(A.Plugin.DDConstrained);
			}
		};

		A.DD.DDM.CSS_PREFIX = CSS_PREFIX;

		if (value) {
			var defaults = {
				node: boundingBox,
				handles: [ DOT + CSS_DIALOG_HD ]
			};
			var dragOptions = A.merge(defaults, instance.get(DRAGGABLE) || {});

			// change the drag scope callback to execute using the dialog scope
			if (dragOptions.on) {
				A.each(dragOptions.on, function(fn, eventName) {
					dragOptions.on[eventName] = A.bind(fn, instance);
				});
			}

			destroyDraggable();

			var dragInstance = new A.DD.Drag(dragOptions);

			dragInstance.plug(A.Plugin.DDConstrained, {
				constrain2view: instance.get(CONSTRAIN_TO_VIEWPORT)
			});

			instance.set(DRAG_INSTANCE, dragInstance);
		}
		else {
			destroyDraggable();
		}

		return value;
	},

	_setIO: function(value) {
		var instance = this;

		if (value && !instance.get(BODY_CONTENT)) {
			instance.set(BODY_CONTENT, TPL_LOADING);
		}

		if (value) {
			instance.unplug(A.Plugin.StdModIOPlugin);

			value.uri = value.uri || value.url;
			value.cfg = value.cfg || {};

			var data = value.cfg.data;

			if (isObject(data)) {
				value.cfg.data = A.toQueryString(data);
			}

			instance.plug(A.Plugin.StdModIOPlugin, value);

			var autoRefresh = ('autoRefresh' in value) ? value.autoRefresh : true;

			if (instance.io && autoRefresh) {
				instance.io.refresh();
			}
		}
		else {
			instance.unplug(A.Plugin.StdModIOPlugin);
		}

		return value;
	},

	_setStack: function(value) {
		var instance = this;

		if (value) {
			A.DialogManager.register(instance);
		}
		else {
			A.DialogManager.remove(instance);
		}

		return value;
	},

	/*
	* Attribute Listeners
	*/
	_afterSetTitle: function(event) {
		var instance = this;

		if (!isBoolean(event.newVal)) {
			var headerNode = instance.headerNode;

			headerNode.addClass(CSS_WIDGET_HD);
			headerNode.addClass(CSS_DIALOG_HD);
			headerNode.addClass(CSS_STATE_DEFAULT);

			instance.titleContainter.html(event.newVal);
		}
	}
});

A.Dialog = Dialog;

A.DialogManager = new A.OverlayManager({
	zIndexBase: 1000
});

A.mix(A.DialogManager, {
	findByChild: function(child) {
		return A.Widget.getByNode(child);
	},

	closeByChild: function(child) {
		return A.DialogManager.findByChild(child).close();
	},

	refreshByChild: function(child, io) {
		var dialog = A.DialogManager.findByChild(child);

		if (dialog && dialog.io) {
			if (io) {
				dialog.set(IO, io);
			}
			else {
				dialog.io.refresh();
			}
		}
	}
});

}, '@VERSION', { requires: [ 'aui-base', 'overlay-manager', 'dd-constrain', 'io-stdmod', 'dialog-css' ] });<commit_msg>Source formatting<commit_after>AUI.add('dialog', function(A) {

var L = A.Lang,
	isBoolean = L.isBoolean,
	isNumber = L.isNumber,
	isString = L.isString,
	isArray = L.isArray,
	isObject = L.isObject,

	ADD_CLASS = 'addClass',
	ANCHOR = 'a',
	BD = 'bd',
	BLANK = '',
	BODY_CONTENT = 'bodyContent',
	BOUNDING_BOX = 'boundingBox',
	BUTTON = 'button',
	BUTTONS = 'buttons',
	CLOSE = 'close',
	CONSTRAIN_TO_VIEWPORT = 'constrain2view',
	CONTAINER = 'container',
	CONTENT_BOX = 'contentBox',
	DD = 'dd',
	DEFAULT = 'default',
	DESTROY_ON_CLOSE = 'destroyOnClose',
	DIALOG = 'dialog',
	DOT = '.',
	DRAGGABLE = 'draggable',
	DRAG_INSTANCE = 'dragInstance',
	FOOTER_CONTENT = 'footerContent',
	FT = 'ft',
	HD = 'hd',
	HEADER = 'header',
	HEADER_CONTENT = 'headerContent',
	HOVER = 'hover',
	ICON = 'icon',
	INNER_HTML = 'innerHTML',
	IO = 'io',
	LOADING = 'loading',
	POST = 'POST',
	REMOVE_CLASS = 'removeClass',
	SPACE = ' ',
	STACK = 'stack',
	STATE = 'state',
	TITLE = 'title',
	WIDGET = 'widget',

	getCN = A.ClassNameManager.getClassName,

	CSS_DIALOG_BUTTON = getCN(DIALOG, BUTTON),
	CSS_DIALOG_BUTTON_CONTAINER = getCN(DIALOG, BUTTON, CONTAINER),
	CSS_DIALOG_BUTTON_DEFAULT = getCN(DIALOG, BUTTON, DEFAULT),
	CSS_DIALOG_CLOSE = getCN(DIALOG, CLOSE),
	CSS_DIALOG_TITLE = getCN(DIALOG, TITLE),
	CSS_ICON = getCN(ICON),
	CSS_ICON_CLOSE = getCN(ICON, CLOSE),
	CSS_ICON_LOADING = getCN(ICON, LOADING),
	CSS_PREFIX = getCN(DD),
	CSS_STATE_DEFAULT = getCN(STATE, DEFAULT),
	CSS_STATE_HOVER = getCN(STATE, HOVER),
	CSS_WIDGET_HD = getCN(WIDGET, HD),
	CSS_DIALOG_HD = getCN(DIALOG, HD),
	CSS_DIALOG_BD = getCN(DIALOG, BD),
	CSS_DIALOG_FT = getCN(DIALOG, FT),

	TPL_LOADING = '

';

function Dialog(config) {
	Dialog.superclass.constructor.apply(this, arguments);
}

A.mix(Dialog, {
	NAME: DIALOG,

	ATTRS: {
		bodyContent: {
			value: SPACE
		},

		buttons: {
			value: [],
			validator: isArray
		},

		constrain2view: {
			value: true,
			validator: isBoolean
		},

		destroyOnClose: {
			value: false,
			validator: isBoolean
		},

		draggable: {
			lazyAdd: true,
			value: true,
			setter: function(v) {
				return this._setDraggable(v);
			}
		},

		dragInstance: {
			value: null
		},

		headerContent: {
			writeOnce: true,
			getter: function() {
				return this.titleContainter;
			}
		},

		io: {
			lazyAdd: true,
			value: null,
			setter: function(v) {
				return this._setIO(v);
			}
		},

		stack: {
			lazyAdd: true,
			value: true,
			setter: function(v) {
				return this._setStack(v);
			},
			validator: isBoolean
		},

		title: {
			value: BLANK,
			validator: function(v) {
				return isString(v) || isBoolean(v);
			}
		}
	}
});

A.extend(Dialog, A.Overlay, {
	/*
	* Lifecycle
	*/
	renderUI: function () {
		var instance = this;

		instance._renderElements();
	},

	bindUI: function () {
		var instance = this;

		instance._bindElements();
	},

	renderer: function() {
		var instance = this;

		Dialog.superclass.renderer.apply(instance, arguments);

		instance._afterRenderer();
	},

	destructor: function() {
		var instance = this;

		if (instance.OverlayManager) {
			instance.OverlayManager.destroy();
		}

		instance.get(BOUNDING_BOX).remove();
	},

	/*
	* Methods
	*/
	close: function() {
		var instance = this;

		if (instance.get(DESTROY_ON_CLOSE)) {
			instance.destroy();
		}
		else {
			instance.hide();
		}

		instance.fire('close');
	},

	_bindElements: function () {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);

		instance.closeIcon.on(
			'click',
			function(event) {
				instance.close();
			}
		);

		boundingBox.on('mousedown', function() {
			if (instance.OverlayManager) {
				instance.OverlayManager.bringToTop();
			}

			instance.fire('focus');
		});

		instance.after('titleChange', this._afterSetTitle);
	},

	_renderElements: function() {
		var instance = this;

		instance.titleContainter = A.Node.create('

');

		var closeIcon = new A.ToolItem(CLOSE);

		closeIcon.get('node').addClass(CSS_DIALOG_CLOSE);

		instance.closeIcon = closeIcon;

		var title = instance.get(TITLE);

		if (title !== false) {
			var titleContainter = instance.titleContainter;

			titleContainter.addClass(CSS_DIALOG_TITLE);

			titleContainter.html(title);
		}
	},

	_afterRenderer: function() {
		var instance = this;
		var bodyNode = instance.bodyNode;
		var footerNode = instance.footerNode;
		var headerNode = instance.headerNode;

		var stack = instance.get(STACK);
		var title = instance.get(TITLE);

		headerNode.addClass(CSS_DIALOG_HD);
		headerNode.addClass(CSS_STATE_DEFAULT);

		if (bodyNode) {
			bodyNode.addClass(CSS_DIALOG_BD);
		}

		if (footerNode) {
			footerNode.addClass(CSS_DIALOG_FT);
		}

		if (title === false) {
			headerNode.removeClass(CSS_STATE_DEFAULT);
			headerNode.removeClass(CSS_WIDGET_HD);
			headerNode.removeClass(CSS_DIALOG_HD);
		}

		headerNode.append(instance.titleContainter);

		instance.closeIcon.render(headerNode);

		instance._initButtons();

		// forcing lazyAdd:true attrs call the setter
		instance.get(DRAGGABLE);
		instance.get(IO);
	},

	_initButtons: function() {
		var instance = this;
		var buttons = instance.get(BUTTONS);
		var container = A.Node.create('

');
		var nodeModel = A.Node.create('<button></button>');

		container.addClass(CSS_DIALOG_BUTTON_CONTAINER);
		nodeModel.addClass(CSS_DIALOG_BUTTON);

		A.each(buttons, function(button, i) {
			var node = nodeModel.cloneNode();

			if (button.isDefault) {
				node.addClass(CSS_DIALOG_BUTTON_DEFAULT);
			}

			if (button.handler) {
				node.on('click', A.bind(button.handler, instance));
			}

			node.html(button.text || BLANK);

			container.append(node);
		});

		if (buttons.length) {
			instance.set(FOOTER_CONTENT, container);

			instance.footerNode.addClass(CSS_DIALOG_FT);
		}
	},

	_setDraggable: function(value) {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);

		var destroyDraggable = function() {
			var dragInstance = instance.get(DRAG_INSTANCE);

			if (dragInstance) {
				// TODO - YUI3 has a bug when destroy and recreates
				dragInstance.destroy();
				dragInstance.unplug(A.Plugin.DDConstrained);
			}
		};

		A.DD.DDM.CSS_PREFIX = CSS_PREFIX;

		if (value) {
			var defaults = {
				node: boundingBox,
				handles: [ DOT + CSS_DIALOG_HD ]
			};
			var dragOptions = A.merge(defaults, instance.get(DRAGGABLE) || {});

			// change the drag scope callback to execute using the dialog scope
			if (dragOptions.on) {
				A.each(dragOptions.on, function(fn, eventName) {
					dragOptions.on[eventName] = A.bind(fn, instance);
				});
			}

			destroyDraggable();

			var dragInstance = new A.DD.Drag(dragOptions);

			dragInstance.plug(A.Plugin.DDConstrained, {
				constrain2view: instance.get(CONSTRAIN_TO_VIEWPORT)
			});

			instance.set(DRAG_INSTANCE, dragInstance);
		}
		else {
			destroyDraggable();
		}

		return value;
	},

	_setIO: function(value) {
		var instance = this;

		if (value && !instance.get(BODY_CONTENT)) {
			instance.set(BODY_CONTENT, TPL_LOADING);
		}

		if (value) {
			instance.unplug(A.Plugin.StdModIOPlugin);

			value.uri = value.uri || value.url;
			value.cfg = value.cfg || {};

			var data = value.cfg.data;

			if (isObject(data)) {
				value.cfg.data = A.toQueryString(data);
			}

			instance.plug(A.Plugin.StdModIOPlugin, value);

			var autoRefresh = ('autoRefresh' in value) ? value.autoRefresh : true;

			if (instance.io && autoRefresh) {
				instance.io.refresh();
			}
		}
		else {
			instance.unplug(A.Plugin.StdModIOPlugin);
		}

		return value;
	},

	_setStack: function(value) {
		var instance = this;

		if (value) {
			A.DialogManager.register(instance);
		}
		else {
			A.DialogManager.remove(instance);
		}

		return value;
	},

	/*
	* Attribute Listeners
	*/
	_afterSetTitle: function(event) {
		var instance = this;

		if (!isBoolean(event.newVal)) {
			var headerNode = instance.headerNode;

			headerNode.addClass(CSS_WIDGET_HD);
			headerNode.addClass(CSS_DIALOG_HD);
			headerNode.addClass(CSS_STATE_DEFAULT);

			instance.titleContainter.html(event.newVal);
		}
	}
});

A.Dialog = Dialog;

A.DialogManager = new A.OverlayManager({
	zIndexBase: 1000
});

A.mix(A.DialogManager, {
	findByChild: function(child) {
		return A.Widget.getByNode(child);
	},

	closeByChild: function(child) {
		return A.DialogManager.findByChild(child).close();
	},

	refreshByChild: function(child, io) {
		var dialog = A.DialogManager.findByChild(child);

		if (dialog && dialog.io) {
			if (io) {
				dialog.set(IO, io);
			}
			else {
				dialog.io.refresh();
			}
		}
	}
});

}, '@VERSION', { requires: [ 'aui-base', 'overlay-manager', 'dd-constrain', 'io-stdmod', 'dialog-css' ] });<|endoftext|>


<commit_before>var qtg = require('com.googlecode.quicktigame2d');
var myDrawModel = require('js/drawModel');
var myAnimatedModel = require('js/animateModel');
var importAsset = require('js/importAsset');

/**
 * monster creation menu with part items
 */

//partsMenuView - the main view for all menu views
var partsMenuView = Ti.UI.createView({
	borderRadius:0,
	opacity: 1,
	left: 0
});

var makeMenuProps = function(typeOfMenu, res, zI) {
	var props = {
		zIndex: zI,
		borderRadius: 0,
		opacity: 1,
		left: 0,
		height: '100%',
		width: '100%'
	};
	props.image = 'images/menu/'+typeOfMenu+'/'+typeOfMenu+'_off_'+res+'.png';
	return props;
};

var makeScrollProps = function() {
	var props = {
		contentWidth: 'auto',
		contentHeight: 'auto',
		showVerticalScrollIndicator: false,
		showHorizontalScrollIndicator: false,
		height: '60%',
		width: '100%'
	};
	return props;
};

var fillItemList = function(o, typeOfMenu) {
	var leftMargin = 2;
	for (var i = 0; i < 8*10; i++){
		name = 'image'+i;
		var name = Ti.UI.createImageView({
			image:'/images/items/item'+(0+i%8)+'.png',
			top: headList.height/4,
			left: leftMargin
		});
		o.add(name);
		leftMargin+=32;
	};
};

exports.drawGameMenu = function(toWin, toGame){
	var scrW = toGame.screen.width;
	var scrH = toGame.screen.height*0.25;
	var scrT = toGame.screen.height*0.8;

	//partsMenuView.add(scrollView);
	partsMenuView.width = scrW;
	partsMenuView.height = scrH;
	partsMenuView.top = scrT;

	headMenu = Ti.UI.createImageView(makeMenuProps('head', Ti.App.res, 1));
	bodyMenu = Ti.UI.createImageView(makeMenuProps('body', Ti.App.res, 0));
	handsMenu = Ti.UI.createImageView(makeMenuProps('hands', Ti.App.res, 0));
	legsMenu = Ti.UI.createImageView(makeMenuProps('legs', Ti.App.res, 0));

	headList = Ti.UI.createScrollView(makeScrollProps());
	fillItemList(headList, 'head');

	bodyList = Ti.UI.createScrollView(makeScrollProps());
	fillItemList(bodyList, 'body');

	headMenu.add(headList);
	bodyMenu.add(bodyList);
	//handsMenu.add(handsList);
	//legsMenu.add(legsList);

	partsMenuView.add(headMenu);
	partsMenuView.add(bodyMenu);
	partsMenuView.add(handsMenu);
	partsMenuView.add(legsMenu);

	toWin.add(partsMenuView);
}

partsMenuView.addEventListener('click', function(e){
	var w = partsMenuView.width, h = partsMenuView.height;
	if ( (e.x>w-w) && (e.x<w/4) && (e.y<h/5) ) {
		partsMenuView.children[0].zIndex = 1; partsMenuView.children[0].setImage('images/menu/head/head_on_'+Ti.App.res+'.png');
		partsMenuView.children[1].zIndex = 0; partsMenuView.children[1].setImage('images/menu/body/body_off_'+Ti.App.res+'.png');
		partsMenuView.children[2].zIndex = 0; partsMenuView.children[2].setImage('images/menu/hands/hands_off_'+Ti.App.res+'.png');
		partsMenuView.children[3].zIndex = 0; partsMenuView.children[3].setImage('images/menu/legs/legs_off_'+Ti.App.res+'.png');
	};
	if ( (e.x>w/4) && (e.x<w/4*2) && (e.y<h/5) ) {
		partsMenuView.children[0].zIndex = 0; partsMenuView.children[0].setImage('images/menu/head/head_off_'+Ti.App.res+'.png');
		partsMenuView.children[1].zIndex = 1; partsMenuView.children[1].setImage('images/menu/body/body_on_'+Ti.App.res+'.png');
		partsMenuView.children[2].zIndex = 0; partsMenuView.children[2].setImage('images/menu/hands/hands_off_'+Ti.App.res+'.png');
		partsMenuView.children[3].zIndex = 0; partsMenuView.children[3].setImage('images/menu/legs/legs_off_'+Ti.App.res+'.png');
	};
	if ( (e.x>w/4*2) && (e.x<w/4*3) && (e.y<h/5) ) {
		partsMenuView.children[0].zIndex = 0; partsMenuView.children[0].setImage('images/menu/head/head_off_'+Ti.App.res+'.png');
		partsMenuView.children[1].zIndex = 0; partsMenuView.children[1].setImage('images/menu/body/body_off_'+Ti.App.res+'.png');
		partsMenuView.children[2].zIndex = 1; partsMenuView.children[2].setImage('images/menu/hands/hands_on_'+Ti.App.res+'.png');
		partsMenuView.children[3].zIndex = 0; partsMenuView.children[3].setImage('images/menu/legs/legs_off_'+Ti.App.res+'.png');
	};
	if ( (e.x>w/4*3) && (e.x<w) && (e.y<h/5) ) {
		partsMenuView.children[0].zIndex = 0; partsMenuView.children[0].setImage('images/menu/head/head_off_'+Ti.App.res+'.png');
		partsMenuView.children[1].zIndex = 0; partsMenuView.children[1].setImage('images/menu/body/body_off_'+Ti.App.res+'.png');
		partsMenuView.children[2].zIndex = 0; partsMenuView.children[2].setImage('images/menu/hands/hands_off_'+Ti.App.res+'.png');
		partsMenuView.children[3].zIndex = 1; partsMenuView.children[3].setImage('images/menu/legs/legs_on_'+Ti.App.res+'.png');
	};
});

exports.moveUp = function(){
	var menuMoveUp = Titanium.UI.createAnimation({
		duration: 500,
<<<<<<< HEAD
		top: game.size.height*0.6
=======
		top: game.size.height*0.75
>>>>>>> tmp_menu
	});
	partsMenuView.animate(menuMoveUp);
};

exports.moveDown = function(){
	var menuMoveDown = Titanium.UI.createAnimation({
		duration: 500,
		top: game.size.height*0.95
	});
	partsMenuView.animate(menuMoveDown);
};

var leftBodyArr;
var rightBodyArr;
var leftHeadArr;
var rightHeadArr;
var leftHandsArr;
var rightHandsArr;
var arrows;

exports.drawArrows = function(_win, _game){
	var img = 'images/arrow/arrow_'+Ti.App.res + '.png';
	var imgS = 'images/arrow/arrowS_'+Ti.App.res + '.png'

	leftBodyArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:350, left:5, transform:Ti.UI.create2DMatrix().scale(-1, 1), name:'leftBodyArr' });
	rightBodyArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:350, right:5, name:'rightBodyArr' });
	leftHeadArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:50, left:5, transform:Ti.UI.create2DMatrix().scale(-1, 1), name:'leftHeadArr' });
	rightHeadArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:50, right:5, name:'rightHeadArr' });
	leftHandsArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:200, left:5, transform:Ti.UI.create2DMatrix().scale(-1, 1), name:'leftHandsArr' });
	rightHandsArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:200, right:5, name:'rightHandsArr' });

	arrows = [leftBodyArr, rightBodyArr, leftHeadArr, rightHeadArr, leftHandsArr, rightHandsArr];
	for(var i= 0; i< arrows.length; i++){
		_win.add(arrows[i]);
		arrows[i].width = 63*(Ti.App.res/320);
		arrows[i].height = arrows[i].width;
		arrows[i].visible=false;
		arrows[i].addEventListener('click', onClick);
	}
}

function onClick(e){
	var part;
	var dir;
	for(var i=0; i< arrows.length; i++){
		if(e.source === arrows[i]){
			if( arrows[i].name.indexOf('right')!=-1) dir = 'right';
			else dir = 'left';

			if( arrows[i].name.indexOf('Body') != -1 ) part = 'body';
			else if( arrows[i].name.indexOf('Head') != -1 ) part = 'head';
			else part = 'hands';

			break;
		}
	}
	importAsset.changeCurrentAsset(part, dir);
	myAnimatedModel.changeAsset(part);
}

exports.setArrowsVisible=function(){
	var vis = arrows[0].visible;
	vis = !vis;
	for(var i= 0; i< arrows.length; i++){
		arrows[i].visible = vis;
	}
}

//leg slider
var slider = Titanium.UI.createSlider({
	top: 50,
    min: 0,
    max: 100,
    width: '100%',
    value: 50
});

var label = Ti.UI.createLabel({
    text: slider.value,
    width: '100%',
    height: 'auto',
    top: 30,
    left: 0,
    textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER
    });

slider.addEventListener('change', function(e) {
    label.text = String.format("%3.1f", e.value);
    Ti.App.Dmod = Math.sqrt(Math.pow(Ti.App.bodyGlobal.width*e.value/2, 2) + Math.pow(Ti.App.bodyGlobal.height/2, 2))*0.9;
    Ti.App.b = Math.asin( (Ti.App.bodyGlobal.width*e.value/100)/Ti.App.Dmod) * 180/Math.PI;
    Ti.API.info(e.value/100);
});

exports.drawLegSlider = function(toWin, toGame){
	toWin.add(label);
	toWin.add(slider);
	Ti.App.legDistance = slider.value/100;
};
<commit_msg>shitmenu<commit_after>var qtg = require('com.googlecode.quicktigame2d');
var myDrawModel = require('js/drawModel');
var myAnimatedModel = require('js/animateModel');
var importAsset = require('js/importAsset');

/**
 * monster creation menu with part items
 */

//partsMenuView - the main view for all menu views
var partsMenuView = Ti.UI.createView({
	borderRadius:0,
	opacity: 1,
	left: 0
});

var makeMenuProps = function(typeOfMenu, res, zI) {
	var props = {
		zIndex: zI,
		borderRadius: 0,
		opacity: 1,
		left: 0,
		height: '100%',
		width: '100%'
	};
	props.image = 'images/menu/'+typeOfMenu+'/'+typeOfMenu+'_off_'+res+'.png';
	return props;
};

var makeScrollProps = function() {
	var props = {
		contentWidth: 'auto',
		contentHeight: 'auto',
		showVerticalScrollIndicator: false,
		showHorizontalScrollIndicator: false,
		height: '60%',
		width: '100%'
	};
	return props;
};

var fillItemList = function(o, typeOfMenu) {
	var leftMargin = 2;
	for (var i = 0; i < 8*10; i++){
		name = 'image'+i;
		var name = Ti.UI.createImageView({
			image:'/images/items/item'+(0+i%8)+'.png',
			top: headList.height/4,
			left: leftMargin
		});
		o.add(name);
		leftMargin+=32;
	};
};

exports.drawGameMenu = function(toWin, toGame){
	var scrW = toGame.screen.width;
	var scrH = toGame.screen.height*0.25;
	var scrT = toGame.screen.height*0.95;

	//partsMenuView.add(scrollView);
	partsMenuView.width = scrW;
	partsMenuView.height = scrH;
	partsMenuView.top = scrT;

	headMenu = Ti.UI.createImageView(makeMenuProps('head', Ti.App.res, 1));
	bodyMenu = Ti.UI.createImageView(makeMenuProps('body', Ti.App.res, 0));
	handsMenu = Ti.UI.createImageView(makeMenuProps('hands', Ti.App.res, 0));
	legsMenu = Ti.UI.createImageView(makeMenuProps('legs', Ti.App.res, 0));

	headList = Ti.UI.createScrollView(makeScrollProps());
	fillItemList(headList, 'head');

	bodyList = Ti.UI.createScrollView(makeScrollProps());
	fillItemList(bodyList, 'body');

	headMenu.add(headList);
	bodyMenu.add(bodyList);
	//handsMenu.add(handsList);
	//legsMenu.add(legsList);

	partsMenuView.add(headMenu);
	partsMenuView.add(bodyMenu);
	partsMenuView.add(handsMenu);
	partsMenuView.add(legsMenu);

	toWin.add(partsMenuView);
}

partsMenuView.addEventListener('click', function(e){
	var w = partsMenuView.width, h = partsMenuView.height;
	if ( (e.x>w-w) && (e.x<w/4) && (e.y<h/5) ) {
		partsMenuView.children[0].zIndex = 1; partsMenuView.children[0].setImage('images/menu/head/head_on_'+Ti.App.res+'.png');
		partsMenuView.children[1].zIndex = 0; partsMenuView.children[1].setImage('images/menu/body/body_off_'+Ti.App.res+'.png');
		partsMenuView.children[2].zIndex = 0; partsMenuView.children[2].setImage('images/menu/hands/hands_off_'+Ti.App.res+'.png');
		partsMenuView.children[3].zIndex = 0; partsMenuView.children[3].setImage('images/menu/legs/legs_off_'+Ti.App.res+'.png');
	};
	if ( (e.x>w/4) && (e.x<w/4*2) && (e.y<h/5) ) {
		partsMenuView.children[0].zIndex = 0; partsMenuView.children[0].setImage('images/menu/head/head_off_'+Ti.App.res+'.png');
		partsMenuView.children[1].zIndex = 1; partsMenuView.children[1].setImage('images/menu/body/body_on_'+Ti.App.res+'.png');
		partsMenuView.children[2].zIndex = 0; partsMenuView.children[2].setImage('images/menu/hands/hands_off_'+Ti.App.res+'.png');
		partsMenuView.children[3].zIndex = 0; partsMenuView.children[3].setImage('images/menu/legs/legs_off_'+Ti.App.res+'.png');
	};
	if ( (e.x>w/4*2) && (e.x<w/4*3) && (e.y<h/5) ) {
		partsMenuView.children[0].zIndex = 0; partsMenuView.children[0].setImage('images/menu/head/head_off_'+Ti.App.res+'.png');
		partsMenuView.children[1].zIndex = 0; partsMenuView.children[1].setImage('images/menu/body/body_off_'+Ti.App.res+'.png');
		partsMenuView.children[2].zIndex = 1; partsMenuView.children[2].setImage('images/menu/hands/hands_on_'+Ti.App.res+'.png');
		partsMenuView.children[3].zIndex = 0; partsMenuView.children[3].setImage('images/menu/legs/legs_off_'+Ti.App.res+'.png');
	};
	if ( (e.x>w/4*3) && (e.x<w) && (e.y<h/5) ) {
		partsMenuView.children[0].zIndex = 0; partsMenuView.children[0].setImage('images/menu/head/head_off_'+Ti.App.res+'.png');
		partsMenuView.children[1].zIndex = 0; partsMenuView.children[1].setImage('images/menu/body/body_off_'+Ti.App.res+'.png');
		partsMenuView.children[2].zIndex = 0; partsMenuView.children[2].setImage('images/menu/hands/hands_off_'+Ti.App.res+'.png');
		partsMenuView.children[3].zIndex = 1; partsMenuView.children[3].setImage('images/menu/legs/legs_on_'+Ti.App.res+'.png');
	};
});

exports.moveUp = function(){
	var menuMoveUp = Titanium.UI.createAnimation({
		duration: 500,
		top: game.size.height*0.75

	});
	partsMenuView.animate(menuMoveUp);
};

exports.moveDown = function(){
	var menuMoveDown = Titanium.UI.createAnimation({
		duration: 500,
		top: game.size.height*0.95
	});
	partsMenuView.animate(menuMoveDown);
};

var leftBodyArr;
var rightBodyArr;
var leftHeadArr;
var rightHeadArr;
var leftHandsArr;
var rightHandsArr;
var arrows;

exports.drawArrows = function(_win, _game){
	var img = 'images/arrow/arrow_'+Ti.App.res + '.png';
	var imgS = 'images/arrow/arrowS_'+Ti.App.res + '.png'

	leftBodyArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:350, left:5, transform:Ti.UI.create2DMatrix().scale(-1, 1), name:'leftBodyArr' });
	rightBodyArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:350, right:5, name:'rightBodyArr' });
	leftHeadArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:50, left:5, transform:Ti.UI.create2DMatrix().scale(-1, 1), name:'leftHeadArr' });
	rightHeadArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:50, right:5, name:'rightHeadArr' });
	leftHandsArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:200, left:5, transform:Ti.UI.create2DMatrix().scale(-1, 1), name:'leftHandsArr' });
	rightHandsArr = Ti.UI.createButton({backgroundImage:img, backgroundSelectedImage:imgS, top:200, right:5, name:'rightHandsArr' });

	arrows = [leftBodyArr, rightBodyArr, leftHeadArr, rightHeadArr, leftHandsArr, rightHandsArr];
	for(var i= 0; i< arrows.length; i++){
		_win.add(arrows[i]);
		arrows[i].width = 63*(Ti.App.res/320);
		arrows[i].height = arrows[i].width;
		arrows[i].visible=false;
		arrows[i].addEventListener('click', onClick);
	}
}

function onClick(e){
	var part;
	var dir;
	for(var i=0; i< arrows.length; i++){
		if(e.source === arrows[i]){
			if( arrows[i].name.indexOf('right')!=-1) dir = 'right';
			else dir = 'left';

			if( arrows[i].name.indexOf('Body') != -1 ) part = 'body';
			else if( arrows[i].name.indexOf('Head') != -1 ) part = 'head';
			else part = 'hands';

			break;
		}
	}
	importAsset.changeCurrentAsset(part, dir);
	myAnimatedModel.changeAsset(part);
}

exports.setArrowsVisible=function(){
	var vis = arrows[0].visible;
	vis = !vis;
	for(var i= 0; i< arrows.length; i++){
		arrows[i].visible = vis;
	}
}

//leg slider
var slider = Titanium.UI.createSlider({
	top: 50,
    min: 0,
    max: 100,
    width: '100%',
    value: 50
});

var label = Ti.UI.createLabel({
    text: slider.value,
    width: '100%',
    height: 'auto',
    top: 30,
    left: 0,
    textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER
    });

slider.addEventListener('change', function(e) {
    label.text = String.format("%3.1f", e.value);
    Ti.App.Dmod = Math.sqrt(Math.pow(Ti.App.bodyGlobal.width*e.value/2, 2) + Math.pow(Ti.App.bodyGlobal.height/2, 2))*0.9;
    Ti.App.b = Math.asin( (Ti.App.bodyGlobal.width*e.value/100)/Ti.App.Dmod) * 180/Math.PI;
    Ti.API.info(e.value/100);
});

exports.drawLegSlider = function(toWin, toGame){
	toWin.add(label);
	toWin.add(slider);
	Ti.App.legDistance = slider.value/100;
};
<|endoftext|>


<commit_before>(function($) {
  var RangeRover = function () {
    this.options = {
      range: false,
      mode: 'plain',
      autocalculate: true,
      color: '#e8e8e8'
    };
    this.coordinates = {
      startSkate: {
        min: 0,
        max: 0
      },
      endSkate: {
        min: 0,
        max: 0
      }
    };
    this.selector = null;
    this.startSkate = null;
    this.endSkate = null;
    this.progressBar = null;
    // whether mouse/finger pressed or not
    this.enabledSkater = null;
    this.selected = {
      start: 0,
      end: 0
    };

    this.setEvendHandlers = function () {
      var self = this;

      self.startSkate.on('mousedown touchstart', function() {
        self.enabledSkater = 'startSkate';
      });

      if (self.options.range) {
        self.endSkate.on('mousedown touchstart', function() {
          self.enabledSkater = 'endSkate';
        });
      }

      $(document).on('mouseup touchend', function() {
        self.checkSelection(null);
        self.enabledSkater = null;
      });

      $(document).on('mousemove touchmove', function (e) {
        if (self.enabledSkater) {
          self.checkSelection(null);
          var positionToScroll = e.pageX - self.selector.offset().left;

          if (positionToScroll < self.coordinates[self.enabledSkater].min) {
            // use slider's left border as a min position
            positionToScroll = self.coordinates[self.enabledSkater].min;
          } else if (positionToScroll > self.coordinates[self.enabledSkater].max) {
            // use slider's right border as a max position
            positionToScroll = self.coordinates[self.enabledSkater].max;
          }
          // drag `skate` element if mouse/ finger pressed
          self[self.enabledSkater].css('left', positionToScroll);
        }
      });

      this.progressBar.on('click tap', function (e) {
        self.progressBarSelection(e.pageX);
      });
    };

    this.init = function () {
      var self = this;
      var progressBarContent = [];

      if (this.options.mode === 'plain') {
        self.options.data.size = 100;
        self.options.data = [this.options.data];
      }
      // add specific class to slider to use its width below
      self.selector.addClass('ds-container');
      if (self.options.autocalculate && this.options.mode === 'categorized') {
        RangeRover.autocalculateCategoriesSizes(self.options.data);
      }
      $.each(self.options.data, function(index, category) {
        // set category's percent size and background color to its div
        var exludedValuesPlain = category.exclude ? RangeRover.getExcludedValuesPlain(category) : [];
        var categoryContent = '
' + (category.name ? category.name : '' ) + '' + category.start + '';

        var valuesCount = category.end - category.start - exludedValuesPlain.length;
        // calculate category px width
        var categoryWidth = self.selector.width() / 100 * category.size;
        //  calculate value's px width
        var valueWidth = categoryWidth / valuesCount;

        for (var i = category.start; i < category.end; i++) {
          if (~exludedValuesPlain.indexOf(i)) {
              continue;
          }
          // set first element as a default
          if (!self.selected.start) {
            self.selected.start = i;
          }
          categoryContent += '';
        }
        if (index === self.options.data.length - 1) {
          categoryContent += '' + category.end + '';
          self.selected.end = category.end;
        }
        categoryContent += '
';
        progressBarContent.push(categoryContent);
      });

      // put progressBar's with ranges, skate elements to container
      var progressBarHtml = '
' + self.selected.start + '

' + progressBarContent.join('') + '
';
      if (this.options.range) {
        progressBarHtml += '
' + self.selected.end + '
'
      }

      this.selector.html(progressBarHtml);
      this.startSkate = this.selector.find('.ds-skate');
      if (this.options.range) {
        this.endSkate = this.selector.find('.ds-end-skate');
      }
      this.progressBar = this.selector.find('.ds-progress');
      if (this.options.color) {
        this.progressBar.css('background', this.options.color);
      }
      // prevent browser native drag
      this.selector.attr("ondragstart", 'return false');
      // calculate min left and max right coordinates to use as a border of slider
      this.calculateAndSetCoordinates();
      this.setEvendHandlers();
    };

    this.checkSelection = function (selectedPosition) {
      var selected;
      var self = this;
      var isTriggeredFromProgressBar = !!selectedPosition;
      var skateChanged = null;

      if (isTriggeredFromProgressBar) {
        if (!self.options.range) {
          skateChanged = 'start';
        }
        if (self.options.range && parseInt(self.endSkate.css('left'), 10) - selectedPosition < selectedPosition - parseInt(self.startSkate.css('left'), 10)) {
          self.endSkate.css('left', selectedPosition);
          skateChanged = 'end';
        } else {
          self.startSkate.css('left', selectedPosition);
          skateChanged = 'start';
        }
      } else {
        if (!self[self.enabledSkater]) {
          return;
        }
        selectedPosition = parseInt(self[self.enabledSkater].css('left'), 10);
      }
      // loop through all items to find selected one
      self.progressBar.find('.ds-item').each(function(index, item) {
        var itemLeftOffset = $(item).offset().left - self.selector.offset().left;
        if (itemLeftOffset <= selectedPosition && itemLeftOffset + $(item).width() > selectedPosition) {
          selected = +$(item).attr('data-year');
          return false;
        }
      });
      skateChanged = skateChanged || (self.enabledSkater ? self.enabledSkater.split('Skate')[0] : null);
      // update selectedYear and call onChange if selectedYear has been changed

      if (selected && selected !== this.selected[skateChanged]) {
        self.selected[skateChanged] = +selected;
        self.updateSelectedYear();
        if (self.options.onChange && typeof self.options.onChange === 'function') {
          self.onChange();
        }
      }
    };

    this.onChange = function () {
      if (this.options.range) {
        this.options.onChange(this.selected);
      } else {
        this.options.onChange(this.selected.start);
      }
    };

    this.progressBarSelection = function (pageX) {
      this.checkSelection(pageX - $('.ds-container').offset().left);
    };

    this.updateSelectedYear = function () {
      this.updateCoordinates();
      this.startSkate.children('.ds-skate-year-mark').html(this.selected.start);

      if (this.options.range) {
        this.endSkate.children('.ds-skate-year-mark').html(this.selected.end);
      }
    };

    this.updateCoordinates = function () {
      if (this.options.range) {
        this.coordinates.startSkate.max = parseInt(this.endSkate.css('left'), 10);
        this.coordinates.endSkate.min = parseInt(this.startSkate.css('left'), 10);
      }
    };

    this.calculateAndSetCoordinates = function () {
      this.coordinates.startSkate.max = this.selector.width() - this.startSkate.width() - parseInt(this.startSkate.css('border-width'), 10);

      if (this.options.range) {
        this.coordinates.endSkate.max = this.selector.width() - this.endSkate.width() - parseInt(this.endSkate.css('border-width'), 10);
      }
    };

    this.select = function (year) {
      if (year !== this.selectedYear) {
        var yearElement = $('.ds-category-item[data-year="' + year + '"]');
        if (!yearElement.length) {
          console.warn('RangeRover -> select: element `' + year + '` is not found.');
          return this;
        }
        var leftPosition = yearElement.offset().left;
        this.skate.css('left', leftPosition);
        this.selectedYear = year;
        this.updateSelectedYear();

        if (this.options.onChange && typeof this.options.onChange === 'function') {
          this.onChange();
        }
      }
      return this;
    }
  };

  RangeRover.autocalculateCategoriesSizes = function (categories) {
    var totalCount = categories.reduce(function(prev, next, index) {
      if (index === 1) {
        return (prev.end - prev.start) + (next.end - next.start);
      } else {
        return prev + (next.end - next.start);
      }
    });
    return categories.map(function(e) {
        e.size = 100 / totalCount * (e.end - e.start);
    });
  };

  RangeRover.getExcludedValuesPlain = function (category) {
    var plainYears = [];
    $.each(category.exclude, function (index, y) {
      if (typeof y === 'object' && y.start && y.end) {
        for (var i = y.start; i <= y.end; i++) {
          plainYears.push(i);
        }
      } else {
        plainYears.push(y);
      }
    });
    return plainYears;
  };

  RangeRover.isArray = function (obj) {
		if (Object.prototype.toString.call(obj) === '[object Array]') {
			return true;
		}
	};

  $.fn.extend({
    rangeRover: function (options) {
      var slider = new RangeRover();
      slider.options = $.extend(slider.options, options);

      if (!slider.options.data || RangeRover.isArray(slider.options.data) && !slider.options.data.length) {
        console.warn('RangeRover -> please provide data');
        return;
      }
      slider.selector = $(this);
      slider.init();

      if (options.onInit && typeof options.onInit === 'function') {
        options.onInit();
      }
      return slider;
    }
  });
})(jQuery);
<commit_msg>Make it possible to specify steps.<commit_after>(function($) {
  var RangeRover = function () {
    this.options = {
      range: false,
      mode: 'plain',
      autocalculate: true,
      color: '#e8e8e8',
      step: 1
    };
    this.coordinates = {
      startSkate: {
        min: 0,
        max: 0
      },
      endSkate: {
        min: 0,
        max: 0
      }
    };
    this.selector = null;
    this.startSkate = null;
    this.endSkate = null;
    this.progressBar = null;
    // whether mouse/finger pressed or not
    this.enabledSkater = null;
    this.selected = {
      start: 0,
      end: 0
    };
    this.setEvendHandlers = function () {
      var self = this;

      self.startSkate.on('mousedown touchstart', function() {
        self.enabledSkater = 'startSkate';
      });

      if (self.options.range) {
        self.endSkate.on('mousedown touchstart', function() {
          self.enabledSkater = 'endSkate';
        });
      }

      $(document).on('mouseup touchend', function() {
        self.checkSelection(null);
        self.enabledSkater = null;
      });

      $(document).on('mousemove touchmove', function (e) {
        if (self.enabledSkater) {
          self.checkSelection(null);
          var positionToScroll = e.pageX - self.selector.offset().left;

          if (positionToScroll < self.coordinates[self.enabledSkater].min) {
            // use slider's left border as a min position
            positionToScroll = self.coordinates[self.enabledSkater].min;
          } else if (positionToScroll > self.coordinates[self.enabledSkater].max) {
            // use slider's right border as a max position
            positionToScroll = self.coordinates[self.enabledSkater].max;
          }
          // drag `skate` element if mouse/ finger pressed
          self[self.enabledSkater].css('left', positionToScroll);
        }
      });

      this.progressBar.on('click tap', function (e) {
        self.progressBarSelection(e.pageX);
      });
    };

    this.init = function () {
      var self = this;
      var progressBarContent = [];

      if (this.options.mode === 'plain') {
        self.options.data.size = 100;
        self.options.data = [this.options.data];
      }
      // add specific class to slider to use its width below
      self.selector.addClass('ds-container');
      if (self.options.autocalculate && this.options.mode === 'categorized') {
        RangeRover.autocalculateCategoriesSizes(self.options.data);
      }

      $.each(self.options.data, function(index, category) {
        // set category's percent size and background color to its div
        var exludedValuesPlain = category.exclude ? RangeRover.getExcludedValuesPlain(category) : [];
        var categoryContent = '
' + (category.name ? category.name : '' ) + '' + category.start + '';

        var valuesCount = category.end - category.start - exludedValuesPlain.length;
        // calculate category px width
        var categoryWidth = self.selector.width() / 100 * category.size;
        //  calculate value's px width
        var valueWidth = categoryWidth / valuesCount;

        var j = 0;

        for (var i = category.start; i < category.end; i++) {
          j = i;
          if (i + self.options.step - 1 > category.end) {
              j = category.end;
          }
          if (~exludedValuesPlain.indexOf(i)) {
              continue;
          }
          // set first element as a default
          if (!self.selected.start) {
            self.selected.start = i;
          }
          categoryContent += '';
          i = i + self.options.step - 1;
        }
        if (index === self.options.data.length - 1) {
          categoryContent += '' + category.end + '';
          self.selected.end = category.end;
        }
        categoryContent += '
';
        progressBarContent.push(categoryContent);
      });

      // put progressBar's with ranges, skate elements to container
      var progressBarHtml = '
' + self.selected.start + '

' + progressBarContent.join('') + '
';
      if (this.options.range) {
        progressBarHtml += '
' + self.selected.end + '
'
      }

      this.selector.html(progressBarHtml);
      this.startSkate = this.selector.find('.ds-skate');
      if (this.options.range) {
        this.endSkate = this.selector.find('.ds-end-skate');
      }
      this.progressBar = this.selector.find('.ds-progress');
      if (this.options.color) {
        this.progressBar.css('background', this.options.color);
      }
      // prevent browser native drag
      this.selector.attr("ondragstart", 'return false');
      // calculate min left and max right coordinates to use as a border of slider
      this.calculateAndSetCoordinates();
      this.setEvendHandlers();
    };

    this.checkSelection = function (selectedPosition) {
      var selected;
      var self = this;
      var isTriggeredFromProgressBar = !!selectedPosition;
      var skateChanged = null;

      if (isTriggeredFromProgressBar) {
        if (!self.options.range) {
          skateChanged = 'start';
        }
        if (self.options.range && parseInt(self.endSkate.css('left'), 10) - selectedPosition < selectedPosition - parseInt(self.startSkate.css('left'), 10)) {
          self.endSkate.css('left', selectedPosition);
          skateChanged = 'end';
        } else {
          self.startSkate.css('left', selectedPosition);
          skateChanged = 'start';
        }
      } else {
        if (!self[self.enabledSkater]) {
          return;
        }
        selectedPosition = parseInt(self[self.enabledSkater].css('left'), 10);
      }
      // loop through all items to find selected one
      self.progressBar.find('.ds-item').each(function(index, item) {
        var itemLeftOffset = $(item).offset().left - self.selector.offset().left;
        if (itemLeftOffset <= selectedPosition && itemLeftOffset + $(item).width() > selectedPosition) {
          selected = +$(item).attr('data-year');
          return false;
        }
      });
      skateChanged = skateChanged || (self.enabledSkater ? self.enabledSkater.split('Skate')[0] : null);
      // update selectedYear and call onChange if selectedYear has been changed

      if (selected && selected !== this.selected[skateChanged]) {
        self.selected[skateChanged] = +selected;
        self.updateSelectedYear();
        if (self.options.onChange && typeof self.options.onChange === 'function') {
          self.onChange();
        }
      }
    };

    this.onChange = function () {
      if (this.options.range) {
        this.options.onChange(this.selected);
      } else {
        this.options.onChange(this.selected.start);
      }
    };

    this.progressBarSelection = function (pageX) {
      this.checkSelection(pageX - $('.ds-container').offset().left);
    };

    this.updateSelectedYear = function () {
      this.updateCoordinates();
      this.startSkate.children('.ds-skate-year-mark').html(this.selected.start);

      if (this.options.range) {
        this.endSkate.children('.ds-skate-year-mark').html(this.selected.end);
      }
    };

    this.updateCoordinates = function () {
      if (this.options.range) {
        this.coordinates.startSkate.max = parseInt(this.endSkate.css('left'), 10);
        this.coordinates.endSkate.min = parseInt(this.startSkate.css('left'), 10);
      }
    };

    this.calculateAndSetCoordinates = function () {
      this.coordinates.startSkate.max = this.selector.width() - this.startSkate.width() - parseInt(this.startSkate.css('border-width'), 10);

      if (this.options.range) {
        this.coordinates.endSkate.max = this.selector.width() - this.endSkate.width() - parseInt(this.endSkate.css('border-width'), 10);
      }
    };

    this.select = function (year) {
      if (year !== this.selectedYear) {
        var yearElement = $('.ds-category-item[data-year="' + year + '"]');
        if (!yearElement.length) {
          console.warn('RangeRover -> select: element `' + year + '` is not found.');
          return this;
        }
        var leftPosition = yearElement.offset().left;
        this.skate.css('left', leftPosition);
        this.selectedYear = year;
        this.updateSelectedYear();

        if (this.options.onChange && typeof this.options.onChange === 'function') {
          this.onChange();
        }
      }
      return this;
    }
  };

  RangeRover.autocalculateCategoriesSizes = function (categories) {
    var totalCount = categories.reduce(function(prev, next, index) {
      if (index === 1) {
        return (prev.end - prev.start) + (next.end - next.start);
      } else {
        return prev + (next.end - next.start);
      }
    });
    return categories.map(function(e) {
        e.size = 100 / totalCount * (e.end - e.start);
    });
  };

  RangeRover.getExcludedValuesPlain = function (category) {
    var plainYears = [];
    $.each(category.exclude, function (index, y) {
      if (typeof y === 'object' && y.start && y.end) {
        for (var i = y.start; i <= y.end; i++) {
          plainYears.push(i);
        }
      } else {
        plainYears.push(y);
      }
    });
    return plainYears;
  };

  RangeRover.isArray = function (obj) {
		if (Object.prototype.toString.call(obj) === '[object Array]') {
			return true;
		}
	};

  $.fn.extend({
    rangeRover: function (options) {
      var slider = new RangeRover();
      slider.options = $.extend(slider.options, options);

      if (!slider.options.data || RangeRover.isArray(slider.options.data) && !slider.options.data.length) {
        console.warn('RangeRover -> please provide data');
        return;
      }
      slider.selector = $(this);
      slider.init();

      if (options.onInit && typeof options.onInit === 'function') {
        options.onInit();
      }
      return slider;
    }
  });
})(jQuery);
<|endoftext|>