<commit_before>import snabbdom from 'snabbdom'
const patch = snabbdom.init([
    require('snabbdom/modules/props'), // for setting properties on DOM elements
    require('snabbdom/modules/style'), // handles styling on elements with support for animations
    require('snabbdom/modules/eventlisteners'), // attaches event listeners
]);

const styles = [
    'width',
    'height',
    'display',
    'padding',
    'margin',
    'position',
    'top',
    'bottom',
    'left',
    'right',
    'float',
    'border',
    'background',
    'boxShadow',
    'fontSize',
    'lineHeight',
    'fontWeight',
    'textAlign',
    'writingMode',
    'color',
    'alignItems',
    'cursor',
    'overflow',
    'transition',
]

export default function init(definitions, currentState, renderApp) {
    let node = document.createElement('div')
    document.body.appendChild(node)

    const state = {
        isOpen: true,
        currentState,
        selectedComponent: definitions.view,
        highlight: true,
    }

    function addChild(component, type){
        const empty = {
            box: {
                nodeType: 'box',
                style: {},
                children: {
                    _type: 'nodeArray',
                    value: [],
                }
            },
            text: {
                nodeType: 'text',
                style: {},
                value: ''
            },
            input: {
                nodeType: 'input',
                style: {},
                value: ''
            },
        }
        component.children.value.push(empty[type])
        render();
        renderApp();
    }

    function removeNode(component, parent){
        if(parent){
            parent.children.value = parent.children.value.filter((child)=> child !== component)
        } else {
            component.children.value = []
        }
        render();
        renderApp();
    }

    function changeHighlight() {
        state.highlight = !state.highlight
        render();
        renderApp();
    }

    function openMenu() {
        state.isOpen = !state.isOpen
        state.highlight = state.isOpen
        render();
        renderApp();
    }

    function selectComponent(compoenent, e) {
        e.stopImmediatePropagation()
        state.selectedComponent = compoenent
        render();
        renderApp();
    }

    function generateChildTree(component, parent) {
        return {
            sel: 'div',
            data: {
                style: {
                    display: 'flex',
                    border: '1px solid #FFA273',
                    padding: '2px',
                },
                on: {
                    click: [selectComponent, component]
                }
            },
            children: [
                {
                    sel: 'div',
                    data: {
                        style: {
                            display: 'flex',
                            flexDirection: 'column',
                            alignItems: 'center',
                            justifyContent: 'center',
                            position: 'relative',
                        },
                    },
                    children: [
                        {
                            sel: 'div',
                            data: {
                                style: {
                                    padding: '10px 0',
                                    textAlign: 'center',
                                    width: '106px',
                                    height: '34px',
                                    fontSize: '2em',
                                    backgroundColor: '#4d4d4d',
                                    margin: '5px',
                                    cursor: 'pointer',
                                    borderRadius: '5px',
                                    boxShadow: state.selectedComponent === component ? '0 0 10px 2px #ffffff' : undefined,
                                },
                            },
                            text: component.nodeType,
                        },
                        state.selectedComponent === component && {
                            sel: 'div',
                            data: {
                                style: {
                                    textAlign: 'center',
                                    width: '30px',
                                    height: '30px',
                                    fontSize: '22px',
                                    backgroundColor: '#4d4d4d',
                                    border: '1px solid white',
                                    cursor: 'pointer',
                                    borderRadius: '15px',
                                    color: 'white',
                                    position: 'absolute',
                                    right: '0',
                                    margin: '-22px 0 0 0',
                                    fontWeight: '800',
                                },
                                on: {
                                    click: [removeNode, component, parent]
                                }
                            },
                            text: 'x',
                        },
                        state.selectedComponent === component && component.nodeType === 'box' && {
                            sel: 'div',
                            data: {
                                style: {
                                    paddingLeft: '6px',
                                    width: '100px',
                                    height: '30px',
                                    fontSize: '22px',
                                    backgroundColor: '#4d4d4d',
                                    cursor: 'pointer',
                                    color: 'white',
                                    position: 'absolute',
                                    right: '5px',
                                    margin: '38px 0 0 0',
                                    fontWeight: '800',
                                    borderTop: '1px solid white',
                                },
                                on: {
                                    click: [addChild, component, 'box']
                                }
                            },
                            text: '+ box',
                        },
                        state.selectedComponent === component && component.nodeType === 'box' && {
                            sel: 'div',
                            data: {
                                style: {
                                    paddingLeft: '6px',
                                    width: '100px',
                                    height: '30px',
                                    fontSize: '22px',
                                    backgroundColor: '#4d4d4d',
                                    cursor: 'pointer',
                                    color: 'white',
                                    position: 'absolute',
                                    right: '5px',
                                    margin: '68px 0 0 0',
                                    fontWeight: '800',
                                    borderTop: '1px solid white',
                                },
                                on: {
                                    click: [addChild, component, 'text']
                                }
                            },
                            text: '+ text',
                        },
                        state.selectedComponent === component && component.nodeType === 'box' && {
                            sel: 'div',
                            data: {
                                style: {
                                    paddingLeft: '6px',
                                    width: '100px',
                                    height: '30px',
                                    fontSize: '22px',
                                    backgroundColor: '#4d4d4d',
                                    cursor: 'pointer',
                                    borderRadius: '0 0 5px 5px',
                                    color: 'white',
                                    position: 'absolute',
                                    right: '5px',
                                    margin: '98px 0 0 0',
                                    fontWeight: '800',
                                    borderTop: '1px solid white',
                                },
                                on: {
                                    click: [addChild, component, 'input']
                                }
                            },
                            text: '+ input',
                        },
                    ].filter((val)=> val)
                },
                {
                    sel: 'div',
                    data: {
                        style: {
                            display: 'flex',
                            flexDirection: 'column',
                            justifyContent: 'center',
                        },
                    },
                    children: component.children && component.children.value && component.children.value.map((child)=>generateChildTree(child, component))
                },
            ],
        }
    }

    function vdom() {
        return {
            sel: 'div',
            data: {
            },
            children: [
                {
                    sel: 'div',
                    data: {
                        style: {
                            position: 'absolute',
                            transform: 'rotate(180deg)',
                            writingMode: 'vertical-lr',
                            right: state.isOpen ? '700px' : '0px',
                            padding: '15px 5px 15px 5px',
                            borderRadius: '0px 5px 5px 0px',
                            top: '40px',
                            fontSize: '2em',
                            background: '#4d4d4d',
                            color: '#ffffff',
                            cursor: 'pointer',
                            transition: 'all 0.5s',
                            zIndex: '1000',
                        },
                        on: {
                            click: openMenu
                        }
                    },
                    text: state.isOpen ? 'Close devtools' : 'Open devtools',
                },
                {
                    sel: 'div',
                    data: {
                        style: {
                            position: 'absolute',
                            display: 'flex',
                            right: '50%',
                            padding: '15px',
                            borderRadius: '5px',
                            bottom: state.isOpen ? '15px' : '-50px',
                            background: '#4d4d4d',
                            color: '#ffffff',
                            cursor: 'pointer',
                            transition: 'all 0.5s',
                            zIndex: '2000',
                        },
                        on: {
                            click: changeHighlight
                        }
                    },
                    children: [
                        {
                            sel: 'input',
                            data: {
                                props: {
                                    type: 'checkbox',
                                    checked: state.highlight
                                },
                                style: {
                                    display: 'inline-block'
                                },
                            },
                        },
                        {
                            sel: 'span',
                            data: {
                                style: {
                                    userSelect: 'none',
                                    whiteSpace: 'nowrap',

                                },
                            },
                            text: 'Highlight live component'
                        },
                    ]
                },{
                    sel: 'div',
                    data: {
                        style: {
                            position: 'absolute',
                            left: '50px',
                            padding: '0 15px',
                            borderRadius: '5px',
                            bottom: '15px',
                            background: '#4d4d4d',
                            color: '#ffffff',
                            transition: 'all 0.5s',
                        },
                    },
                    children: [
                        {
                            sel: 'p',
                            data: {
                                style: {
                                },
                            },
                            children: [
                                {
                                    sel: 'span',
                                    data: {
                                    },
                                    text: 'This is an example'
                                },
                                {
                                    sel: 'a',
                                    data: {
                                        props: {
                                            href: 'http://todomvc.com/'
                                        },
                                        style: {
                                            color: 'inherit',
                                            marginLeft: '3px',
                                        },
                                    },
                                    text: 'TodoMVC application'
                                },
                            ]
                        },
                        {
                            sel: 'p',
                            data: {
                                style: {
                                },
                            },
                            children: [
                                {
                                    sel: 'span',
                                    data: {
                                        style: {
                                        },
                                    },
                                    text: 'It was build using the tool on the right - Ugnis v0.0.12-pre-alpha'
                                },
                            ],
                        },
                        {
                            sel: 'p',
                            data: {
                                style: {
                                },
                            },
                            children: [
                                {
                                    sel: 'span',
                                    data: {
                                    },
                                    text: 'Compare how this app would be implemented in code with React'
                                },
                                {
                                    sel: 'a',
                                    data: {
                                        props: {
                                            href: 'https://github.com/tastejs/todomvc/tree/gh-pages/examples/react/js'
                                        },
                                        style: {
                                            color: 'inherit',
                                            marginLeft: '3px',

                                        },
                                    },
                                    text: 'here'
                                },
                            ]
                        },
                    ]
                },
                {
                    sel: 'div',
                    data: {
                        style: {
                            boxShadow: '0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1)',
                            position: 'absolute',
                            right: state.isOpen ? '0px' : '-700px',
                            color: '#ffffff',
                            boxSizing: 'border-box',
                            borderLeft: '6px solid #4d4d4d',
                            width: '700px',
                            height: '100vh',
                            top: '0',
                            transition: 'all 0.5s',
                            overflow: 'hidden',
                        },
                    },
                    children: [
                        {
                            sel: 'div',
                            data: {
                                style: {
                                    height: '50%',
                                    overflow: 'scroll',
                                },
                            },
                            children: [
                                {
                                    sel: 'div',
                                    data: {
                                        style: {
                                            backgroundImage: 'url(/images/blueprint.png)',
                                            backgroundSize: '16px',
                                            padding: '10px',
                                            // display: 'inline-block',
                                            // minWidth: '100%',
                                            // minHeight: '100%',
                                        },
                                    },
                                    children: [generateChildTree(definitions.view)]
                                },
                            ]
                        },
                        // component and state
                        {
                            sel: 'div',
                            data: {
                                style: {
                                    display: 'flex',
                                    background: 'linear-gradient(to top, #ECE9E6 , #FFFFFF)',
                                    borderTop: '6px solid #4d4d4d',
                                    height: '50%',
                                    width: '100%',
                                    color: '#000000',
                                    position: 'absolute',
                                    bottom: '0',
                                    left: '0',
                                },
                            },
                            children: [
                                {
                                    sel: 'div',
                                    data: {
                                        style: {
                                            flex: '1',
                                            overflow: 'scroll',
                                            padding: '10px',
                                            borderRight: '6px solid #4d4d4d',
                                        },
                                    },
                                    children: [
                                        state.selectedComponent.nodeType === 'text' ? {
                                            sel: 'div',
                                            data: {
                                                style: {
                                                    width: '100%',
                                                    margin: '0 0 10px 0',
                                                },
                                            },
                                            children: [
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            display: 'inline-block',
                                                            width: '30%',
                                                            textAlign: 'right',
                                                        },
                                                    },
                                                    text: 'Text: '
                                                },
                                                {
                                                    sel: 'input',
                                                    data: {
                                                        props: {
                                                            value: state.selectedComponent.value || ''
                                                        },
                                                        style: {
                                                            display: 'inline-block',
                                                        },
                                                        on: {
                                                            input: (e)=> {
                                                                state.selectedComponent.value = e.target.value;
                                                                renderApp()
                                                            }
                                                        }
                                                    },
                                                }
                                            ]
                                        }:
                                        {
                                            sel: 'div',
                                            data: {
                                                style: {
                                                    width: '100%',
                                                    margin: '0 0 10px 0',
                                                },
                                            },
                                            children: [
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            display: 'inline-block',
                                                            width: '30%',
                                                            textAlign: 'right',
                                                        },
                                                    },
                                                    text: 'Children:'
                                                },
                                            ]
                                        }
                                    ].concat(Object.keys(state.selectedComponent.style).map((name)=>(
                                        {
                                            sel: 'div',
                                            data: {
                                                style: {
                                                    width: '100%',
                                                },
                                            },
                                            children: [
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            display: 'inline-block',
                                                            width: '30%',
                                                            textAlign: 'right',
                                                        },
                                                    },
                                                    text: name + ': '
                                                },
                                                {
                                                    sel: 'input',
                                                    data: {
                                                        props: {
                                                            value: state.selectedComponent.style[name] || ''
                                                        },
                                                        style: {
                                                            display: 'inline-block',
                                                        },
                                                        on: {
                                                            input: (e)=> {
                                                                state.selectedComponent.style = Object.assign({}, state.selectedComponent.style, {[name]: e.target.value});
                                                                renderApp()
                                                            }
                                                        }
                                                    },
                                                }
                                            ]
                                        }))
                                    ),
                                },
                                {
                                    sel: 'div',
                                    data: {
                                        style: {
                                            flex: '1',
                                            padding: '10px',
                                        },
                                    },
                                    children: Object.keys(definitions.state).map(stateName =>
                                        ({
                                            sel: 'div',
                                            data: {
                                                style: {
                                                    flex: '1',
                                                    padding: '10px',
                                                    fontSize: '1.5em',
                                                },
                                            },
                                            key: stateName,
                                            children: [
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            background: '#dddddd',
                                                        },
                                                    },
                                                    text: stateName + ': ' + (definitions.state[stateName].defaultValue === '' ? '\'\'': definitions.state[stateName].defaultValue)
                                                },
                                            ],
                                        })
                                    ),
                                }
                            ],
                        },
                    ]
                },
            ],
        }
    }

    function render() {
        const newDom = vdom()
        patch(node, newDom)
        node = newDom
    }

    render()

    return {
        state: state,
        emit: function (action, event, newState, mutations) {
            state.currentState = newState;
            render()
        }
    }
}<commit_msg>Joan of Arc had style. Jesus had style. This code now has style.<commit_after>import snabbdom from 'snabbdom'
const patch = snabbdom.init([
    require('snabbdom/modules/props'), // for setting properties on DOM elements
    require('snabbdom/modules/style'), // handles styling on elements with support for animations
    require('snabbdom/modules/eventlisteners'), // attaches event listeners
]);

const styles = [
    'width',
    'height',
    'display',
    'padding',
    'margin',
    'position',
    'top',
    'bottom',
    'left',
    'right',
    'float',
    'border',
    'background',
    'boxShadow',
    'fontSize',
    'lineHeight',
    'fontWeight',
    'textAlign',
    'writingMode',
    'color',
    'alignItems',
    'cursor',
    'overflow',
    'transition',
]

export default function init(definitions, currentState, renderApp) {
    let node = document.createElement('div')
    document.body.appendChild(node)

    const state = {
        isOpen: true,
        currentState,
        selectedComponent: definitions.view,
        highlight: true,
    }

    function addChild(component, type){
        const empty = {
            box: {
                nodeType: 'box',
                style: {},
                children: {
                    _type: 'nodeArray',
                    value: [],
                }
            },
            text: {
                nodeType: 'text',
                style: {},
                value: ''
            },
            input: {
                nodeType: 'input',
                style: {},
                value: ''
            },
        }
        component.children.value.push(empty[type])
        render();
        renderApp();
    }

    function addStyle(event){
        state.selectedComponent.style[event.target.value] = ''
        render();
        renderApp();
    }

    function deleteStyle(name){
        const tempObj = Object.assign({}, state.selectedComponent.style)
        delete tempObj[name]
        state.selectedComponent.style = tempObj
        render();
        renderApp();
    }

    function removeNode(component, parent){
        if(parent){
            parent.children.value = parent.children.value.filter((child)=> child !== component)
        } else {
            component.children.value = []
        }
        render();
        renderApp();
    }

    function changeHighlight() {
        state.highlight = !state.highlight
        render();
        renderApp();
    }

    function openMenu() {
        state.isOpen = !state.isOpen
        state.highlight = state.isOpen
        render();
        renderApp();
    }

    function selectComponent(compoenent, e) {
        e.stopImmediatePropagation()
        state.selectedComponent = compoenent
        render();
        renderApp();
    }

    function generateChildTree(component, parent) {
        return {
            sel: 'div',
            data: {
                style: {
                    display: 'flex',
                    border: '1px solid #FFA273',
                    padding: '2px',
                },
                on: {
                    click: [selectComponent, component]
                }
            },
            children: [
                {
                    sel: 'div',
                    data: {
                        style: {
                            display: 'flex',
                            flexDirection: 'column',
                            alignItems: 'center',
                            justifyContent: 'center',
                            position: 'relative',
                        },
                    },
                    children: [
                        {
                            sel: 'div',
                            data: {
                                style: {
                                    padding: '10px 0',
                                    textAlign: 'center',
                                    width: '106px',
                                    height: '34px',
                                    fontSize: '2em',
                                    backgroundColor: '#4d4d4d',
                                    margin: '5px',
                                    cursor: 'pointer',
                                    borderRadius: '5px',
                                    boxShadow: state.selectedComponent === component ? '0 0 10px 2px #ffffff' : undefined,
                                },
                            },
                            text: component.nodeType,
                        },
                        state.selectedComponent === component && {
                            sel: 'div',
                            data: {
                                style: {
                                    textAlign: 'center',
                                    width: '30px',
                                    height: '30px',
                                    fontSize: '22px',
                                    backgroundColor: '#4d4d4d',
                                    border: '1px solid white',
                                    cursor: 'pointer',
                                    borderRadius: '15px',
                                    color: 'white',
                                    position: 'absolute',
                                    right: '0',
                                    margin: '-22px 0 0 0',
                                    fontWeight: '800',
                                    zIndex: '100',
                                },
                                on: {
                                    click: [removeNode, component, parent]
                                }
                            },
                            text: 'x',
                        },
                        state.selectedComponent === component && component.nodeType === 'box' && {
                            sel: 'div',
                            data: {
                                style: {
                                    paddingLeft: '6px',
                                    width: '100px',
                                    height: '30px',
                                    fontSize: '22px',
                                    backgroundColor: '#4d4d4d',
                                    cursor: 'pointer',
                                    color: 'white',
                                    position: 'absolute',
                                    right: '5px',
                                    margin: '38px 0 0 0',
                                    fontWeight: '800',
                                    borderTop: '1px solid white',
                                    zIndex: '101',
                                },
                                on: {
                                    click: [addChild, component, 'box']
                                }
                            },
                            text: '+ box',
                        },
                        state.selectedComponent === component && component.nodeType === 'box' && {
                            sel: 'div',
                            data: {
                                style: {
                                    paddingLeft: '6px',
                                    width: '100px',
                                    height: '30px',
                                    fontSize: '22px',
                                    backgroundColor: '#4d4d4d',
                                    cursor: 'pointer',
                                    color: 'white',
                                    position: 'absolute',
                                    right: '5px',
                                    margin: '68px 0 0 0',
                                    fontWeight: '800',
                                    borderTop: '1px solid white',
                                    zIndex: '102',
                                },
                                on: {
                                    click: [addChild, component, 'text']
                                }
                            },
                            text: '+ text',
                        },
                        state.selectedComponent === component && component.nodeType === 'box' && {
                            sel: 'div',
                            data: {
                                style: {
                                    paddingLeft: '6px',
                                    width: '100px',
                                    height: '30px',
                                    fontSize: '22px',
                                    backgroundColor: '#4d4d4d',
                                    cursor: 'pointer',
                                    borderRadius: '0 0 5px 5px',
                                    color: 'white',
                                    position: 'absolute',
                                    right: '5px',
                                    margin: '98px 0 0 0',
                                    fontWeight: '800',
                                    borderTop: '1px solid white',
                                    zIndex: '103',
                                },
                                on: {
                                    click: [addChild, component, 'input']
                                }
                            },
                            text: '+ input',
                        },
                    ].filter((val)=> val)
                },
                {
                    sel: 'div',
                    data: {
                        style: {
                            display: 'flex',
                            flexDirection: 'column',
                            justifyContent: 'center',
                        },
                    },
                    children: component.children && component.children.value && component.children.value.map((child)=>generateChildTree(child, component))
                },
            ],
        }
    }

    function vdom() {
        return {
            sel: 'div',
            data: {
            },
            children: [
                {
                    sel: 'div',
                    data: {
                        style: {
                            position: 'absolute',
                            transform: 'rotate(180deg)',
                            writingMode: 'vertical-lr',
                            right: state.isOpen ? '700px' : '0px',
                            padding: '15px 5px 15px 5px',
                            borderRadius: '0px 5px 5px 0px',
                            top: '40px',
                            fontSize: '2em',
                            background: '#4d4d4d',
                            color: '#ffffff',
                            cursor: 'pointer',
                            transition: 'all 0.5s',
                            zIndex: '1000',
                        },
                        on: {
                            click: openMenu
                        }
                    },
                    text: state.isOpen ? 'Close devtools' : 'Open devtools',
                },
                {
                    sel: 'div',
                    data: {
                        style: {
                            position: 'absolute',
                            display: 'flex',
                            right: '50%',
                            padding: '15px',
                            borderRadius: '5px',
                            bottom: state.isOpen ? '15px' : '-50px',
                            background: '#4d4d4d',
                            color: '#ffffff',
                            cursor: 'pointer',
                            transition: 'all 0.5s',
                            zIndex: '2000',
                        },
                        on: {
                            click: changeHighlight
                        }
                    },
                    children: [
                        {
                            sel: 'input',
                            data: {
                                props: {
                                    type: 'checkbox',
                                    checked: state.highlight
                                },
                                style: {
                                    display: 'inline-block'
                                },
                            },
                        },
                        {
                            sel: 'span',
                            data: {
                                style: {
                                    userSelect: 'none',
                                    whiteSpace: 'nowrap',

                                },
                            },
                            text: 'Highlight live component'
                        },
                    ]
                },{
                    sel: 'div',
                    data: {
                        style: {
                            position: 'absolute',
                            left: '50px',
                            padding: '0 15px',
                            borderRadius: '5px',
                            bottom: '15px',
                            background: '#4d4d4d',
                            color: '#ffffff',
                            transition: 'all 0.5s',
                        },
                    },
                    children: [
                        {
                            sel: 'p',
                            data: {
                                style: {
                                },
                            },
                            children: [
                                {
                                    sel: 'span',
                                    data: {
                                    },
                                    text: 'This is an example'
                                },
                                {
                                    sel: 'a',
                                    data: {
                                        props: {
                                            href: 'http://todomvc.com/'
                                        },
                                        style: {
                                            color: 'inherit',
                                            marginLeft: '3px',
                                        },
                                    },
                                    text: 'TodoMVC application'
                                },
                            ]
                        },
                        {
                            sel: 'p',
                            data: {
                                style: {
                                },
                            },
                            children: [
                                {
                                    sel: 'span',
                                    data: {
                                        style: {
                                        },
                                    },
                                    text: 'It was build using the tool on the right - Ugnis v0.0.12-pre-alpha'
                                },
                            ],
                        },
                        {
                            sel: 'p',
                            data: {
                                style: {
                                },
                            },
                            children: [
                                {
                                    sel: 'span',
                                    data: {
                                    },
                                    text: 'Compare how this app would be implemented in code with React'
                                },
                                {
                                    sel: 'a',
                                    data: {
                                        props: {
                                            href: 'https://github.com/tastejs/todomvc/tree/gh-pages/examples/react/js'
                                        },
                                        style: {
                                            color: 'inherit',
                                            marginLeft: '3px',

                                        },
                                    },
                                    text: 'here'
                                },
                            ]
                        },
                    ]
                },
                {
                    sel: 'div',
                    data: {
                        style: {
                            boxShadow: '0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1)',
                            position: 'absolute',
                            right: state.isOpen ? '0px' : '-700px',
                            color: '#ffffff',
                            boxSizing: 'border-box',
                            borderLeft: '6px solid #4d4d4d',
                            width: '700px',
                            height: '100vh',
                            top: '0',
                            transition: 'all 0.5s',
                            overflow: 'hidden',
                        },
                    },
                    children: [
                        {
                            sel: 'div',
                            data: {
                                style: {
                                    height: '50%',
                                    overflow: 'scroll',
                                },
                            },
                            children: [
                                {
                                    sel: 'div',
                                    data: {
                                        style: {
                                            backgroundImage: 'url(/images/blueprint.png)',
                                            backgroundSize: '16px',
                                            padding: '10px',
                                            // display: 'inline-block',
                                            // minWidth: '100%',
                                            // minHeight: '100%',
                                        },
                                    },
                                    children: [generateChildTree(definitions.view)]
                                },
                            ]
                        },
                        // component and state
                        {
                            sel: 'div',
                            data: {
                                style: {
                                    display: 'flex',
                                    background: 'linear-gradient(to top, #ECE9E6 , #FFFFFF)',
                                    borderTop: '6px solid #4d4d4d',
                                    height: '50%',
                                    width: '100%',
                                    color: '#000000',
                                    position: 'absolute',
                                    bottom: '0',
                                    left: '0',
                                },
                            },
                            children: [
                                {
                                    sel: 'div',
                                    data: {
                                        style: {
                                            flex: '1',
                                            overflow: 'scroll',
                                            padding: '10px',
                                            borderRight: '6px solid #4d4d4d',
                                        },
                                    },
                                    children: [
                                        state.selectedComponent.nodeType === 'text' ? {
                                            sel: 'div',
                                            data: {
                                                style: {
                                                    width: '100%',
                                                    margin: '0 0 10px 0',
                                                },
                                            },
                                            children: [
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            display: 'inline-block',
                                                            width: '30%',
                                                            textAlign: 'right',
                                                        },
                                                    },
                                                    text: 'Text: '
                                                },
                                                {
                                                    sel: 'input',
                                                    data: {
                                                        props: {
                                                            value: state.selectedComponent.value || ''
                                                        },
                                                        style: {
                                                            display: 'inline-block',
                                                        },
                                                        on: {
                                                            input: (e)=> {
                                                                state.selectedComponent.value = e.target.value;
                                                                renderApp()
                                                            }
                                                        }
                                                    },
                                                }
                                            ]
                                        }:
                                        {
                                            sel: 'div',
                                            data: {
                                                style: {
                                                    width: '100%',
                                                    margin: '0 0 10px 0',
                                                },
                                            },
                                            children: [
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            display: 'inline-block',
                                                            width: '30%',
                                                            textAlign: 'right',
                                                        },
                                                    },
                                                    text: 'Children:'
                                                },
                                            ],
                                        },
                                        ...Object.keys(state.selectedComponent.style).map((name)=>(
                                        {
                                            sel: 'div',
                                            data: {
                                                style: {
                                                    width: '100%',
                                                },
                                            },
                                            children: [
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            display: 'inline-block',
                                                            width: '30%',
                                                            textAlign: 'right',
                                                        },
                                                    },
                                                    text: name + ': '
                                                },
                                                {
                                                    sel: 'input',
                                                    data: {
                                                        props: {
                                                            value: state.selectedComponent.style[name] || ''
                                                        },
                                                        style: {
                                                            display: 'inline-block',
                                                        },
                                                        on: {
                                                            input: (e)=> {
                                                                state.selectedComponent.style = Object.assign({}, state.selectedComponent.style, {[name]: e.target.value});
                                                                renderApp()
                                                            }
                                                        }
                                                    },
                                                },
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            display: 'inline-block'
                                                        },
                                                        on: {
                                                            click: [deleteStyle, name]
                                                        }
                                                    },
                                                    text: 'x'
                                                },
                                            ]
                                        })),
                                        {
                                            sel: 'select',
                                            data: {
                                                on: {
                                                    change: [addStyle]
                                                }
                                            },
                                            children: styles.map(style => ({
                                                sel: 'option',
                                                data: {
                                                    props: {
                                                        value: style,
                                                    },
                                                },
                                                text: style
                                            })),
                                        }
                                    ],
                                },
                                {
                                    sel: 'div',
                                    data: {
                                        style: {
                                            flex: '1',
                                            padding: '10px',
                                        },
                                    },
                                    children: Object.keys(definitions.state).map(stateName =>
                                        ({
                                            sel: 'div',
                                            data: {
                                                style: {
                                                    flex: '1',
                                                    padding: '10px',
                                                    fontSize: '1.5em',
                                                },
                                            },
                                            key: stateName,
                                            children: [
                                                {
                                                    sel: 'div',
                                                    data: {
                                                        style: {
                                                            background: '#dddddd',
                                                        },
                                                    },
                                                    text: stateName + ': ' + (definitions.state[stateName].defaultValue === '' ? '\'\'': definitions.state[stateName].defaultValue)
                                                },
                                            ],
                                        })
                                    ),
                                }
                            ],
                        },
                    ]
                },
            ],
        }
    }

    function render() {
        const newDom = vdom()
        patch(node, newDom)
        node = newDom
    }

    render()

    return {
        state: state,
        emit: function (action, event, newState, mutations) {
            state.currentState = newState;
            render()
        }
    }
}<|endoftext|>


<commit_before>"use strict";

require(['jquery', 'messages', 'extensions',
         'jquery.cookie', 'jquery.jeditable',
         'jquery.timeago', 'jquery.rating'], function($, messages) {
    if (!$.ajaxSettings.headers)
        $.ajaxSettings.headers = {};

    $.ajaxSettings.headers['X-CSRFToken'] = $.cookie('csrftoken');

    $.fn.csrfEditable = function(url, options) {
        return $(this).each(function() {
            var $elem = $(this);

            function error(xhr, status, error) {
                if (status == 403) {
                    $elem.css("background-color", "#fcc");
                }
            }

            $elem.editable(url, $.merge(options || {},
                                { select: true,
                                  ajaxoptions: { error: error, dataType: 'json' },
                                  callback: function(result, settings) {
                                      elem.text(result);
                                  },
                                  data: function(string, settings) {
                                      return $.trim(string);
                                  }}));
            $elem.addClass("editable");
        });
    };

    $(document).ready(function() {
        // Make the login link activatable.
        $("#login_link").click(function(event) {
            $(this).toggleClass('selected');
            $("#login_popup_form").slideToggle();
            return false;
        });

        $("abbr.timestamp").timeago();

        function closeUserSettings() {
            var needsClose = $('#global_domain_bar .user').hasClass('active');
            if (!needsClose)
                return false;

            $('#global_domain_bar .user').removeClass('active');
            $('#global_domain_bar .user_settings, #global_domain_bar .login_popup_form').animate({ top: '10px', opacity: 0 }, 200, function() {
                $(this).hide();
            });
            return true;
        }

        function openUserSettings() {
            $('#global_domain_bar .user').addClass('active');
            $('#global_domain_bar .user_settings, #global_domain_bar .login_popup_form').show().css({ top: '-10px', opacity: 0 }).animate({ top: '0', opacity: 1 }, 200);
        }

        $(document.body).click(function() {
            if (closeUserSettings())
                return false;
        });

        $('#global_domain_bar .user_settings, #global_domain_bar .login_popup_form').click(function(e) {
            e.stopPropagation();
        });

        $('#global_domain_bar .user').click(function() {
            if ($(this).hasClass('active')) {
                closeUserSettings();
            } else {
                openUserSettings();
            }
            return false;
        });

        $('#submit-ajax').click(function() {
            var $elem = $(this);

            var df = $.ajax({ url: $elem.data('url'),
                              type: 'POST' });
            df.done(function() {
                messages.addInfo("Your extension has been submitted.").hide().slideDown();
            });

            return false;
        });

        $('#local_extensions').addLocalExtensions();
        $('#error_report').fillInErrors();
        $('.extension.single-page').addExtensionSwitch();
        $('.comment .rating').each(function() {
            $(this).find('input').rating();
        });
        $('form .rating').rating();

        $('.expandy_header').click(function() {
            $(this).toggleClass('expanded').next().slideToggle();
        }).not('.expanded').next().hide();

        $('#extension_shell_versions_info').buildShellVersionsInfo();

        $('.extension_status_toggle a').click(function() {
            var $link = $(this);
            var $tr = $link.parents('tr');
            var href = $link.attr('href');
            var pk = $tr.data('pk');
            var $ext = $link.parents('.extension');

            var req = $.ajax({
                type: 'GET',
                dataType: 'json',
                data: { pk: pk },
                url: href
            });

            req.done(function(data) {
                $ext.data('svm', data.svm);
                $('#extension_shell_versions_info').buildShellVersionsInfo();
                $tr.find('.mvs').html(data.mvs);
                $tr.find('.extension_status_toggle').toggleClass('visible');
            });

            return false;
        });

        if (window._SW)
            try {
                window._SW();
            } catch(e) {
                if (console && console.error)
                    console.error(e);
            }
    });
});
<commit_msg>js: $.merge is an array merge, not an object merge<commit_after>"use strict";

require(['jquery', 'messages', 'extensions',
         'jquery.cookie', 'jquery.jeditable',
         'jquery.timeago', 'jquery.rating'], function($, messages) {
    if (!$.ajaxSettings.headers)
        $.ajaxSettings.headers = {};

    $.ajaxSettings.headers['X-CSRFToken'] = $.cookie('csrftoken');

    $.fn.csrfEditable = function(url, options) {
        return $(this).each(function() {
            var $elem = $(this);

            function error(xhr, status, error) {
                if (status == 403) {
                    $elem.css("background-color", "#fcc");
                }
            }

            $elem.editable(url, $.extend(options || {},
                                { select: true,
                                  ajaxoptions: { error: error, dataType: 'json' },
                                  callback: function(result, settings) {
                                      elem.text(result);
                                  },
                                  data: function(string, settings) {
                                      return $.trim(string);
                                  }}));
            $elem.addClass("editable");
        });
    };

    $(document).ready(function() {
        // Make the login link activatable.
        $("#login_link").click(function(event) {
            $(this).toggleClass('selected');
            $("#login_popup_form").slideToggle();
            return false;
        });

        $("abbr.timestamp").timeago();

        function closeUserSettings() {
            var needsClose = $('#global_domain_bar .user').hasClass('active');
            if (!needsClose)
                return false;

            $('#global_domain_bar .user').removeClass('active');
            $('#global_domain_bar .user_settings, #global_domain_bar .login_popup_form').animate({ top: '10px', opacity: 0 }, 200, function() {
                $(this).hide();
            });
            return true;
        }

        function openUserSettings() {
            $('#global_domain_bar .user').addClass('active');
            $('#global_domain_bar .user_settings, #global_domain_bar .login_popup_form').show().css({ top: '-10px', opacity: 0 }).animate({ top: '0', opacity: 1 }, 200);
        }

        $(document.body).click(function() {
            if (closeUserSettings())
                return false;
        });

        $('#global_domain_bar .user_settings, #global_domain_bar .login_popup_form').click(function(e) {
            e.stopPropagation();
        });

        $('#global_domain_bar .user').click(function() {
            if ($(this).hasClass('active')) {
                closeUserSettings();
            } else {
                openUserSettings();
            }
            return false;
        });

        $('#submit-ajax').click(function() {
            var $elem = $(this);

            var df = $.ajax({ url: $elem.data('url'),
                              type: 'POST' });
            df.done(function() {
                messages.addInfo("Your extension has been submitted.").hide().slideDown();
            });

            return false;
        });

        $('#local_extensions').addLocalExtensions();
        $('#error_report').fillInErrors();
        $('.extension.single-page').addExtensionSwitch();
        $('.comment .rating').each(function() {
            $(this).find('input').rating();
        });
        $('form .rating').rating();

        $('.expandy_header').click(function() {
            $(this).toggleClass('expanded').next().slideToggle();
        }).not('.expanded').next().hide();

        $('#extension_shell_versions_info').buildShellVersionsInfo();

        $('.extension_status_toggle a').click(function() {
            var $link = $(this);
            var $tr = $link.parents('tr');
            var href = $link.attr('href');
            var pk = $tr.data('pk');
            var $ext = $link.parents('.extension');

            var req = $.ajax({
                type: 'GET',
                dataType: 'json',
                data: { pk: pk },
                url: href
            });

            req.done(function(data) {
                $ext.data('svm', data.svm);
                $('#extension_shell_versions_info').buildShellVersionsInfo();
                $tr.find('.mvs').html(data.mvs);
                $tr.find('.extension_status_toggle').toggleClass('visible');
            });

            return false;
        });

        if (window._SW)
            try {
                window._SW();
            } catch(e) {
                if (console && console.error)
                    console.error(e);
            }
    });
});
<|endoftext|>


<commit_before>define(["three", "src/arcball", "src/mouse_to_world"], function(THREE, Arcball, MouseToWorld){
    return {new: function(model, scene, camera){
        var Controller = {
            radius: 1,
            fudgeFactor: 0.1,
            fudgeZ: 0.15, // circles don't look like they touch due to perspective,
                         // so we fudge the z a bit here
            rotating: false,

            init: function(model, scene, camera){
                this.model = model;
                this.camera = camera;
                this.arcball = Arcball.new(model, scene, camera);
                this.arcball.hideGuide(true);
                this.buildRotationGuides(scene);
                this.rotationFunction = null;
            },

            /**
             * Create the sphere around the model
             */
            buildRotationGuides: function(scene){
                    var segments = 64,
                    geometry = new THREE.CircleGeometry(this.radius, segments);
                geometry.vertices.shift(); // remove center

                this.rotationColour = {
                    x: 0xff0000,
                    y: 0x00ff00,
                    z: 0x0000ff
                };

                this.rotationGuides = {
                    y: new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: this.rotationColour.y})),
                    x: new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: this.rotationColour.x})),
                    z: new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: this.rotationColour.z}))
                };
                scene.add(this.rotationGuides.x);
                scene.add(this.rotationGuides.y);
                scene.add(this.rotationGuides.z);

                this.rotateByAxisAngle(this.rotationGuides.y, this.rotationVecs.x, Math.PI/2);
                this.rotateByAxisAngle(this.rotationGuides.x, this.rotationVecs.y, Math.PI/2);
                this.rotationGuides.z.position.z = this.fudgeZ;

            },

            iterateAlongAxis: function(callback){
                ["x", "y", "z"].forEach(callback);
            },

            updateRotation: function(mouseX, mouseY, dim){
                var that = this;
                if(this.rotating){
                    this.rotationFunction(mouseX, mouseY, dim);
                }
            },

            checkWithinRange: function(val, min, max){
                return val >= min && val <= max;
            },

            checkWithinFudge: function(val){
                return this.checkWithinRange(val, -this.fudgeFactor, this.fudgeFactor);
            },

            snapToAxis: function(pos, axis){
                if(axis == "y"){
                    pos.x = - pos.x
                    pos.y = 0;
                } else if(axis == "x") {
                    pos.x = 0;
                } else {
                    pos.normalize();
                }
                return pos;
            },

            rotationVecs: {
                x: new THREE.Vector3(1,0,0),
                y: new THREE.Vector3(0,1,0),
                z: new THREE.Vector3(0,0,1)
            },

            rotateByAxisAngle: function(model, axis, angle){
                var quaternion = new THREE.Quaternion();
                quaternion.setFromAxisAngle(axis, angle);
                this.arcball.rotateModelByQuaternion(model, quaternion);
            },

            getRotateAlongAxisFn: function(axis){
                var that = this;

                this.initialMouseReal = this.snapToAxis(this.initialMouseReal, axis);
                this.axisRotating = axis;
                this.rotationGuides[axis].material.color.setHex(0xffffff);
                return function(mouseX, mouseY, dim){
                    var point = that.snapToAxis(that.getRealPosition(mouseX, mouseY, dim), axis);
                    var angle = 0;
                    var rotationVec = that.rotationVecs[axis];
                    if(axis == "x" || axis == "y"){
                        var otherAxis = {
                            x: "y",
                            y: "x"
                        };
                        var delta = (that.initialMouseReal[otherAxis[axis]] - point[otherAxis[axis]])/(that.radius*2);
                        angle = that.positiveAngle(Math.PI * 2 * delta);

                    } else {
                        var startAngle = (Math.atan(that.initialMouseReal.y / that.initialMouseReal.x)),
                            endAngle = (Math.atan(point.y / point.x)),
                            delta = endAngle - startAngle,
                            startPoint = new THREE.Vector2(that.initialMouseReal.x, that.initialMouseReal.y),
                            endPoint = new THREE.Vector2(point.x, point.y);

                        angle = Math.acos(startPoint.dot(endPoint));
                        if(Math.sign(delta) == -1){
                            angle *= -1;
                        };
                    }
                    this.rotateByAxisAngle(that.model, rotationVec, angle);
                    that.initialMouseReal = point;
                };
            },

            positiveAngle: function(angle){
                if(Math.sign(angle) == -1){
                    angle += Math.PI * 2;
                }
                return angle;
            },

            getRealPosition: function(mouseX, mouseY, dim){
                var actualPos = this.arcball.actualPos(mouseX, mouseY, dim),
                    size = this.arcball.sizeFor(dim);
                return MouseToWorld(actualPos.x, actualPos.y, size.width, size.height, this.camera);
            },

            startRotation: function(initialMousePos, dim){
                this.initialMouseReal = this.getRealPosition(initialMousePos[0], initialMousePos[1], dim);
                // check that mouse is within an acceptable distance of the sliders
                if(this.initialMouseReal.length() > this.radius + this.fudgeFactor){
                    return;
                }

                this.rotating = true;

                // check whether they're clicking a slider
                if(this.checkWithinFudge(this.initialMouseReal.x)){
                    this.rotationFunction = this.getRotateAlongAxisFn("x");
                } else if (this.checkWithinFudge(this.initialMouseReal.y)){
                    this.rotationFunction = this.getRotateAlongAxisFn("y");
                } else if (this.checkWithinFudge(this.initialMouseReal.length() - this.radius)){
                    this.rotationFunction = this.getRotateAlongAxisFn("z");
                } else {
                    this.arcball.startRotation(initialMousePos, dim);
                    this.arcball.hideGuide(false);
                    this.axisRotating = null;
                    this.rotationFunction = function(mouseX, mouseY, dim){
                        return this.arcball.updateRotation(mouseX, mouseY, dim);
                    };
                }
            },

            endRotation: function(){
                this.rotationFunction = null;
                this.arcball.endRotation();
                this.rotating = false;
                if(this.axisRotating){
                    this.rotationGuides[this.axisRotating].material.color.setHex(
                        this.rotationColour[this.axisRotating]
                    );
                    this.axisRotating = null;
                }
                this.arcball.hideGuide(true);
            },
        };
        Controller.init(model, scene, camera);
        return Controller;
    }};
});
<commit_msg>Refactored discrete.js to make linter happy<commit_after>define(["three", "src/arcball", "src/mouse_to_world"], function(THREE, Arcball, MouseToWorld){
    return {new: function(model, scene, camera){
        var Controller = {
            radius: 1,
            fudgeFactor: 0.1,
            fudgeZ: 0.15, // circles don't look like they touch due to perspective,
                         // so we fudge the z a bit here
            rotating: false,

            init: function(model, scene, camera){
                this.model = model;
                this.camera = camera;
                this.arcball = Arcball.new(model, scene, camera);
                this.arcball.hideGuide(true);
                this.buildRotationGuides(scene);
                this.rotationFunction = null;
            },

            /**
             * Create the sphere around the model
             */
            buildRotationGuides: function(scene){
                    var segments = 64,
                    geometry = new THREE.CircleGeometry(this.radius, segments);
                geometry.vertices.shift(); // remove center

                this.rotationColour = {
                    x: 0xff0000,
                    y: 0x00ff00,
                    z: 0x0000ff
                };

                this.rotationGuides = {
                    y: new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: this.rotationColour.y})),
                    x: new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: this.rotationColour.x})),
                    z: new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: this.rotationColour.z}))
                };
                scene.add(this.rotationGuides.x);
                scene.add(this.rotationGuides.y);
                scene.add(this.rotationGuides.z);

                this.rotateByAxisAngle(this.rotationGuides.y, this.rotationVecs.x, Math.PI/2);
                this.rotateByAxisAngle(this.rotationGuides.x, this.rotationVecs.y, Math.PI/2);
                this.rotationGuides.z.position.z = this.fudgeZ;

            },

            iterateAlongAxis: function(callback){
                ["x", "y", "z"].forEach(callback);
            },

            updateRotation: function(mouseX, mouseY, dim){
                var that = this;
                if(this.rotating){
                    this.rotationFunction(mouseX, mouseY, dim);
                }
            },

            checkWithinRange: function(val, min, max){
                return val >= min && val <= max;
            },

            checkWithinFudge: function(val){
                return this.checkWithinRange(val, -this.fudgeFactor, this.fudgeFactor);
            },

            snapToAxis: function(pos, axis){
                if(axis == "y"){
                    pos.x = - pos.x;
                    pos.y = 0;
                } else if(axis == "x") {
                    pos.x = 0;
                } else {
                    pos.normalize();
                }
                return pos;
            },

            rotationVecs: {
                x: new THREE.Vector3(1,0,0),
                y: new THREE.Vector3(0,1,0),
                z: new THREE.Vector3(0,0,1)
            },

            rotateByAxisAngle: function(model, axis, angle){
                var quaternion = new THREE.Quaternion();
                quaternion.setFromAxisAngle(axis, angle);
                this.arcball.rotateModelByQuaternion(model, quaternion);
            },

            getRotateAlongAxisFn: function(axis){
                var that = this;

                this.initialMouseReal = this.snapToAxis(this.initialMouseReal, axis);
                this.axisRotating = axis;
                this.rotationGuides[axis].material.color.setHex(0xffffff);
                return function(mouseX, mouseY, dim){
                    var point = that.snapToAxis(that.getRealPosition(mouseX, mouseY, dim), axis);
                    var angle = 0;
                    var rotationVec = that.rotationVecs[axis];
                    if(axis == "x" || axis == "y"){
                        var otherAxis = {
                            x: "y",
                            y: "x"
                        };
                        var delta = (that.initialMouseReal[otherAxis[axis]] - point[otherAxis[axis]])/(that.radius*2);
                        angle = that.positiveAngle(Math.PI * 2 * delta);

                    } else {
                        var startAngle = (Math.atan(that.initialMouseReal.y / that.initialMouseReal.x)),
                            endAngle = (Math.atan(point.y / point.x)),
                            sign = Math.sign(endAngle - startAngle),
                            startPoint = new THREE.Vector2(that.initialMouseReal.x, that.initialMouseReal.y),
                            endPoint = new THREE.Vector2(point.x, point.y);

                        angle = Math.acos(startPoint.dot(endPoint));
                        if(sign == -1){ // startAngle > endAngle => negative rotation
                            angle *= -1;
                        }
                    }
                    this.rotateByAxisAngle(that.model, rotationVec, angle);
                    that.initialMouseReal = point;
                };
            },

            positiveAngle: function(angle){
                if(Math.sign(angle) == -1){
                    angle += Math.PI * 2;
                }
                return angle;
            },

            getRealPosition: function(mouseX, mouseY, dim){
                var actualPos = this.arcball.actualPos(mouseX, mouseY, dim),
                    size = this.arcball.sizeFor(dim);
                return MouseToWorld(actualPos.x, actualPos.y, size.width, size.height, this.camera);
            },

            startRotation: function(initialMousePos, dim){
                this.initialMouseReal = this.getRealPosition(initialMousePos[0], initialMousePos[1], dim);
                // check that mouse is within an acceptable distance of the sliders
                if(this.initialMouseReal.length() > this.radius + this.fudgeFactor){
                    return;
                }

                this.rotating = true;

                // check whether they're clicking a slider
                if(this.checkWithinFudge(this.initialMouseReal.x)){
                    this.rotationFunction = this.getRotateAlongAxisFn("x");
                } else if (this.checkWithinFudge(this.initialMouseReal.y)){
                    this.rotationFunction = this.getRotateAlongAxisFn("y");
                } else if (this.checkWithinFudge(this.initialMouseReal.length() - this.radius)){
                    this.rotationFunction = this.getRotateAlongAxisFn("z");
                } else {
                    this.arcball.startRotation(initialMousePos, dim);
                    this.arcball.hideGuide(false);
                    this.axisRotating = null;
                    this.rotationFunction = function(mouseX, mouseY, dim){
                        return this.arcball.updateRotation(mouseX, mouseY, dim);
                    };
                }
            },

            endRotation: function(){
                this.rotationFunction = null;
                this.arcball.endRotation();
                this.rotating = false;
                if(this.axisRotating){
                    this.rotationGuides[this.axisRotating].material.color.setHex(
                        this.rotationColour[this.axisRotating]
                    );
                    this.axisRotating = null;
                }
                this.arcball.hideGuide(true);
            },
        };
        Controller.init(model, scene, camera);
        return Controller;
    }};
});
<|endoftext|>


<commit_before>"use strict";

// Substance.Document 0.5.0
// (c) 2010-2013 Michael Aufreiter
// Substance.Document may be freely distributed under the MIT license.
// For all details and documentation:
// http://interior.substance.io/modules/document.html

// Import
// ========

var _ = require("underscore");
var util = require("substance-util");
var errors = util.errors;
var Data = require("substance-data");
var Operator = require("substance-operator");
var Chronicle = require("substance-chronicle");

// Module
// ========

var DocumentError = errors.define("DocumentError");

// Document
// --------
//
// A generic model for representing and transforming digital documents

var Document = function(options) {
  Data.Graph.call(this, options.schema, options);
};

// Default Document Schema
// --------

Document.schema = {
  // Static indexes
  "indexes": {
  },

  "types": {
    // Specific type for substance documents, holding all content elements
    "content": {
      "properties": {
      }
    },

    "view": {
      "properties": {
        "nodes": ["array", "content"]
      }
    }
  }
};

Document.Prototype = function() {

  var __super__ = util.prototype(this);

  // Get predecessor node for a given view and node id
  // --------
  //

  this.getPredecessor = function(view, id) {
    var pos = this.getPosition(view, id);
    if (pos === 0) return null;
    return this.getNodeFromPosition(view, pos-1);
  };

  // Get successor node for a given view and node id
  // --------
  //

  this.getSuccessor = function(view, id) {
    var pos = this.getPosition(view, id);
    if (pos === view.length - 1) return null;
    return this.getNodeFromPosition(view, pos+1);
  };

  // Returns true if given view and node pos has a successor
  // --------
  //

  this.hasSuccessor = function(view, nodePos) {
    var view = this.get(view).nodes;
    return nodePos < view.length - 1;
  };

  // Returns true if given view and node pos has a predecessor
  // --------
  //

  this.hasPredecessor = function(view, nodePos) {
    return nodePos > 0;
  };

  // Get successor node for a given view and node id
  // --------
  //

  this.getSuccessor = function(view, id) {
    var pos = this.getPosition(view, id);
    // if (pos === view.length - 1) return null;
    return this.getNodeFromPosition(view, pos+1);
  };

  // Get node position for a given view and node id
  // --------
  //

  this.getPosition = function(view, id) {
    return this.get(view).nodes.indexOf(id);
  };

  this.create = function(node) {
    __super__.create.call(this, node);
    return this.get(node.id);
  };

  // Delegates to Graph.get but wraps the result in the particular node constructor
  // --------
  //

  this.get = function(path) {
    var node = __super__.get.call(this, path);
    if (!node) return node;
    var NodeType = this.nodeTypes[node.type];
    return NodeType ? new NodeType(node) : node;
  };

  // Get node object from a given view and position
  // --------
  //

  this.getNodeFromPosition = function(view, pos) {
    var nodeId = this.get(view).nodes[pos];
    return nodeId ? this.get(nodeId) : null;
  };

  // Serialize to JSON
  // --------
  //
  // The command is converted into a sequence of graph commands

  this.toJSON = function() {
    var res = __super__.toJSON.call(this);
    res.id = this.id;
    return res;
  };

  // Hide elements from provided view
  // --------
  //

  this.hide = function(viewId, nodes) {
    var view = this.get(viewId).nodes;

    var indices = [];
    _.each(nodes, function(n) {
      var i = view.indexOf(n);
      if (i>=0) indices.push(i);
    }, this);

    indices = indices.sort().reverse();

    var ops = _.map(indices, function(index) {
      return Operator.ArrayOperation.Delete(index, view[index]);
    });

    var op = Operator.ObjectOperation.Update([viewId, "nodes"], Operator.ArrayOperation.Compound(ops));

    return this.apply(op);
  };

  // Position nodes in document
  // --------
  //

  this.position = function(viewId, nodes, target) {
    var view = this.get(viewId).nodes;
    var ops = [];
    var idx;

    // Create a sequence of atomic array operations that
    // are bundled into a Compound operation
    // 1. Remove elements (from right to left)
    // 2. Insert at the very position

    // the sequence contains selected nodes in the order they
    // occurr in the view
    var seq = _.intersection(view, nodes);
    var l = view.length;

    while(seq.length > 0) {
      var id = seq.pop();
      idx = view.indexOf(id);
      if (idx >= 0) {
        ops.push(Operator.ArrayOperation.Delete(idx, id));
        l--;
      }
    }

    // target index can be given as negative number (as known from python/ruby)
    target = Math.min(target, l);
    if (target<0) target = Math.max(0, l+target+1);

    for (idx = 0; idx < nodes.length; idx++) {
      ops.push(Operator.ArrayOperation.Insert(target + idx, nodes[idx]));
    }

    var update = Operator.ObjectOperation.Update([viewId, "nodes"], Operator.ArrayOperation.Compound(ops));
    return this.apply(update);
  };

  // Start simulation, which conforms to a transaction (think databases)
  // --------
  //

  this.startSimulation = function() {
    // TODO: this should be implemented in a more cleaner and efficient way.
    // Though, for now and sake of simplicity done by creating a copy
    var self = this;

    // experimental: if we have a chronicle then we can do our work on a temporary branch
    if (this.isVersioned) {

      // disconnect the listeners temporarily
      var listeners = this._events;
      this._events = [];

      // remember the original state
      var initialState = this.chronicle.getState();

      // create a temporary index
      var index = this.chronicle.index;
      this.chronicle.index = new Chronicle.TmpIndex(index);

      // inject a recording apply method
      var ops = [];
      var __apply__ = this.apply;
      this.apply = function(op) {
        op = __apply__.call(this, op);
        ops.push(op);
        return op;
      };

      this.save = function() {
        var _ops = [];
        for (var i = 0; i < ops.length; i++) {
          if (ops[i].type !== "compound") {
            _ops.push(ops[i]);
          } else {
            _ops = _ops.concat(ops[i].ops);
          }
        }
        var compound = Operator.ObjectOperation.Compound(_ops);

        this.chronicle.reset(initialState);

        // Restore the initial state
        this._events = listeners;
        this.apply = __apply__;
        this.chronicle.index = index;
        delete this.save;

        this.apply(compound);
      };

      return this;
    }

    // TODO: to discuss. Should Document always be chronicled?
    else {
      var simulation = this.fromSnapshot(this.toJSON());
      var ops = [];

      var __apply__ = simulation.apply;

      simulation.apply = function(op) {
        op = __apply__.call(simulation, op);
        ops.push(op);
        return op;
      };

      simulation.save = function() {
        var _ops = [];
        for (var i = 0; i < ops.length; i++) {
          if (ops[i].type !== "compound") {
            _ops.push(ops[i]);
          } else {
            _ops = _ops.concat(ops[i].ops);
          }
        }
        var compound = Operator.ObjectOperation.Compound(_ops);
        self.apply(compound);
        console.log("Saved simulated ops", self);
      };

      return simulation;
    }
  };

  this.fromSnapshot = function(data, options) {
    return Document.fromSnapshot(data, options);
  };

};

Document.Prototype.prototype = Data.Graph.prototype;
Document.prototype = new Document.Prototype();

// Add event support
_.extend(Document.prototype, util.Events);

Document.fromSnapshot = function(data, options) {
  options = options || {};
  options.seed = data;
  return new Document(options);
};

Document.DocumentError = DocumentError;

// Export
// ========

module.exports = Document;
<commit_msg>Improved simulation impl.<commit_after>"use strict";

// Substance.Document 0.5.0
// (c) 2010-2013 Michael Aufreiter
// Substance.Document may be freely distributed under the MIT license.
// For all details and documentation:
// http://interior.substance.io/modules/document.html

// Import
// ========

var _ = require("underscore");
var util = require("substance-util");
var errors = util.errors;
var Data = require("substance-data");
var Operator = require("substance-operator");
var Chronicle = require("substance-chronicle");

// Module
// ========

var DocumentError = errors.define("DocumentError");

// Document
// --------
//
// A generic model for representing and transforming digital documents

var Document = function(options) {
  Data.Graph.call(this, options.schema, options);
};

// Default Document Schema
// --------

Document.schema = {
  // Static indexes
  "indexes": {
  },

  "types": {
    // Specific type for substance documents, holding all content elements
    "content": {
      "properties": {
      }
    },

    "view": {
      "properties": {
        "nodes": ["array", "content"]
      }
    }
  }
};

Document.Prototype = function() {

  var __super__ = util.prototype(this);

  // Get predecessor node for a given view and node id
  // --------
  //

  this.getPredecessor = function(view, id) {
    var pos = this.getPosition(view, id);
    if (pos === 0) return null;
    return this.getNodeFromPosition(view, pos-1);
  };

  // Get successor node for a given view and node id
  // --------
  //

  this.getSuccessor = function(view, id) {
    var pos = this.getPosition(view, id);
    if (pos === view.length - 1) return null;
    return this.getNodeFromPosition(view, pos+1);
  };

  // Returns true if given view and node pos has a successor
  // --------
  //

  this.hasSuccessor = function(view, nodePos) {
    var view = this.get(view).nodes;
    return nodePos < view.length - 1;
  };

  // Returns true if given view and node pos has a predecessor
  // --------
  //

  this.hasPredecessor = function(view, nodePos) {
    return nodePos > 0;
  };

  // Get successor node for a given view and node id
  // --------
  //

  this.getSuccessor = function(view, id) {
    var pos = this.getPosition(view, id);
    // if (pos === view.length - 1) return null;
    return this.getNodeFromPosition(view, pos+1);
  };

  // Get node position for a given view and node id
  // --------
  //

  this.getPosition = function(view, id) {
    return this.get(view).nodes.indexOf(id);
  };

  this.create = function(node) {
    __super__.create.call(this, node);
    return this.get(node.id);
  };

  // Delegates to Graph.get but wraps the result in the particular node constructor
  // --------
  //

  this.get = function(path) {
    var node = __super__.get.call(this, path);
    if (!node) return node;
    var NodeType = this.nodeTypes[node.type];
    return NodeType ? new NodeType(node) : node;
  };

  // Get node object from a given view and position
  // --------
  //

  this.getNodeFromPosition = function(view, pos) {
    var nodeId = this.get(view).nodes[pos];
    return nodeId ? this.get(nodeId) : null;
  };

  // Serialize to JSON
  // --------
  //
  // The command is converted into a sequence of graph commands

  this.toJSON = function() {
    var res = __super__.toJSON.call(this);
    res.id = this.id;
    return res;
  };

  // Hide elements from provided view
  // --------
  //

  this.hide = function(viewId, nodes) {
    var view = this.get(viewId).nodes;

    var indices = [];
    _.each(nodes, function(n) {
      var i = view.indexOf(n);
      if (i>=0) indices.push(i);
    }, this);

    indices = indices.sort().reverse();

    var ops = _.map(indices, function(index) {
      return Operator.ArrayOperation.Delete(index, view[index]);
    });

    var op = Operator.ObjectOperation.Update([viewId, "nodes"], Operator.ArrayOperation.Compound(ops));

    return this.apply(op);
  };

  // Position nodes in document
  // --------
  //

  this.position = function(viewId, nodes, target) {
    var view = this.get(viewId).nodes;
    var ops = [];
    var idx;

    // Create a sequence of atomic array operations that
    // are bundled into a Compound operation
    // 1. Remove elements (from right to left)
    // 2. Insert at the very position

    // the sequence contains selected nodes in the order they
    // occurr in the view
    var seq = _.intersection(view, nodes);
    var l = view.length;

    while(seq.length > 0) {
      var id = seq.pop();
      idx = view.indexOf(id);
      if (idx >= 0) {
        ops.push(Operator.ArrayOperation.Delete(idx, id));
        l--;
      }
    }

    // target index can be given as negative number (as known from python/ruby)
    target = Math.min(target, l);
    if (target<0) target = Math.max(0, l+target+1);

    for (idx = 0; idx < nodes.length; idx++) {
      ops.push(Operator.ArrayOperation.Insert(target + idx, nodes[idx]));
    }

    var update = Operator.ObjectOperation.Update([viewId, "nodes"], Operator.ArrayOperation.Compound(ops));
    return this.apply(update);
  };

  // Start simulation, which conforms to a transaction (think databases)
  // --------
  //

  this.startSimulation = function() {

    var parent = this;

    var doc = _.extend({}, this);
    doc._events = [];

    // remember the original state
    var initialState = this.chronicle.getState();

    // create a temporary chronicle index
    doc.chronicle = _.extend({}, this.chronicle);
    doc.chronicle.index = new Chronicle.TmpIndex(this.chronicle.index);

    // inject a recording apply method
    var ops = [];
    var __apply__ = this.apply;
    doc.apply = function(op) {
      op = __apply__.call(this, op);
      ops.push(op);
      return op;
    };

    doc.save = function() {
      var _ops = [];
      for (var i = 0; i < ops.length; i++) {
        if (ops[i].type !== "compound") {
          _ops.push(ops[i]);
        } else {
          _ops = _ops.concat(ops[i].ops);
        }
      }
      var compound = Operator.ObjectOperation.Compound(_ops);
      this.chronicle.reset(initialState);

      parent.apply(compound);
    };

    return doc;
  };

};

Document.Prototype.prototype = Data.Graph.prototype;
Document.prototype = new Document.Prototype();

// Add event support
_.extend(Document.prototype, util.Events);

Document.fromSnapshot = function(data, options) {
  options = options || {};
  options.seed = data;
  return new Document(options);
};

Document.DocumentError = DocumentError;

// Export
// ========

module.exports = Document;
<|endoftext|>