<commit_before>/**
 * @file EditableSelect component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {findDOMNode} from 'react-dom';
import classNames from 'classnames';

import TextField from '../TextField';
import Popup from '../Popup';
import List from '../List';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import DropdownCalculation from '../_vendors/DropdownCalculation';

class EditableSelect extends Component {

    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value,
            listValue: props.value,
            filter: '',
            popupVisible: false,
            isAbove: false
        };

        this.showPopup = ::this.showPopup;
        this.closePopup = ::this.closePopup;
        this.filterData = ::this.filterData;
        this.popupRenderHandle = ::this.popupRenderHandle;
        this.changeHandle = ::this.changeHandle;
        this.onChangeValue = ::this.onChangeValue;

    }

    onChangeValue(value) {
        const {useFilter} = this.props;
        if (useFilter) {
            this.setState({
                value,
                filter: value,
                listValue: ''
            }, () => {
                const {onChange} = this.props;
                onChange && onChange(value);
            });
        } else {
            this.setState({
                value,
                listValue: ''
            }, () => {
                const {onChange} = this.props;
                onChange && onChange(value);
            });
        }

    }

    showPopup() {
        this.setState({
            popupVisible: true
        }, () => {
            this.props.onTriggerTouchTap && this.props.onTriggerTouchTap(this.state.popupVisible);
            this.props.onFocus && this.props.onFocus();
        });
    }

    closePopup() {
        this.setState({
            popupVisible: false
        }, () => {
            this.props.onTriggerTouchTap && this.props.onTriggerTouchTap(this.state.popupVisible);
            this.props.onPopupClosed && this.props.onPopupClosed();
        });
    }

    triggerHandler(el, triggerEl, popupEl, currentVisible, isAutoClose) {

        if (!triggerEl) {
            return true;
        }

        while (el) {
            if (el == popupEl) {
                return currentVisible;
            } else if (el == triggerEl) {
                return true;
            }
            el = el.parentNode;
        }

        return isAutoClose ? false : currentVisible;

    }

    filterData(filter = this.state.filter, data = this.props.data) {

        if (!filter) {
            return data;
        }

        const {displayField, isGrouped} = this.props,
            filterFunc = (originData) => {
                return originData.filter(item => typeof item === 'object' && !!item[displayField] ?
                    item[displayField].toString().toUpperCase().includes(filter.toUpperCase())
                    :
                    item.toString().toUpperCase().includes(filter.toUpperCase()));
            };

        if (isGrouped) {

            let result = Object.assign(data);

            for (let i = 0, len = result.length; i < len; i++) {
                let group = result[i];
                group.children = filterFunc(group.children);
                if (group.children.length < 1) {
                    result.splice(i, 1);
                    i--;
                }
            }

        }

        return filterFunc(data);

    }

    popupRenderHandle(popupEl) {

        const isAbove = DropdownCalculation.isAbove(this.editabledSelectEl, this.triggerEl, findDOMNode(popupEl));

        if (isAbove !== this.state.isAbove) {
            this.setState({
                isAbove
            });
        }

    }

    changeHandle(value) {
        const {valueField, renderer} = this.props;
        let itemValue = renderer ? renderer(value) : (typeof value == 'object' ? value[valueField] : value);
        const {autoClose} = this.props,
            state = {
                value: itemValue,
                listValue: value
            };

        if (autoClose) {
            state.popupVisible = false;
        }

        this.setState(state, () => {
            const {onChange} = this.props;
            onChange && onChange(itemValue);
        });

    }

    componentDidMount() {
        this.editabledSelectEl = this.refs.editabledSelect;
        this.triggerEl = findDOMNode(this.refs.trigger);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.props.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    render() {

        const {
                className, popupClassName, style, popupStyle, name, placeholder,
                disabled, useFilter, valueField, descriptionField, noMatchedMsg,
                triggerTheme, isGrouped, onItemTouchTap, renderer,
                onTriggerMouseOver, onTriggerMouseOut
            } = this.props,
            {value, listValue, filter, popupVisible, isAbove} = this.state,

            triggerClassName = classNames('editable-select-trigger', isAbove ? 'above' : 'blow', {
                activated: popupVisible,
                empty: !value
            }),

            editableSelectPopupClassName = classNames('editable-select-popup', isAbove ? 'above' : 'blow', {
                [popupClassName]: popupClassName
            }),

            editablePopupStyle = Object.assign({
                width: this.triggerEl && getComputedStyle(this.triggerEl).width
            }, popupStyle),

            listData = this.filterData();

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={value}/>
                        :
                        null
                }

                <TextField ref="trigger"
                           className={triggerClassName}
                           value={value}
                           rightIconCls={`fas fa-angle-${isAbove ? 'up' : 'down'} editable-select-trigger-icon`}
                           placeholder={placeholder}
                           disabled={disabled}
                           theme={triggerTheme}
                           onMouseOver={onTriggerMouseOver}
                           onMouseOut={onTriggerMouseOut}
                           onChange={this.onChangeValue}
                           onFocus={this.showPopup}/>

                <Popup ref="popup"
                       className={editableSelectPopupClassName}
                       style={editablePopupStyle}
                       visible={popupVisible}
                       triggerEl={this.triggerEl}
                       triggerHandler={this.triggerHandler}
                       hasTriangle={false}
                       position={isAbove ? Popup.Position.TOP_LEFT : Popup.Position.BOTTOM_LEFT}
                       onRender={this.popupRenderHandle}
                       onRequestClose={this.closePopup}>

                    <List className="editable-select-list"
                          isGrouped={isGrouped}
                          data={listData.length < 1 ? [] : listData}
                          valueField={valueField}
                          value={listValue}
                          displayField={valueField}
                          descriptionField={descriptionField}
                          renderer={renderer}
                          onItemTouchTap={onItemTouchTap}
                          onChange={this.changeHandle}/>

                </Popup>

        );

    }
}

EditableSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The name of the editableSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the editableSelect.
     */
    value: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The placeholder of the editableSelect.
     */
    placeholder: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            renderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onTouchTap: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * The invalid message of editableSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the editableSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the editableSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * If true,the drop-down box will have search input.
     */
    useFilter: PropTypes.bool,

    /**
     * The message of no matching option.
     */
    noMatchedMsg: PropTypes.string,

    /**
     * The theme of editableSelect.Can be primary,highlight,success,warning,error.
     */
    triggerTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * If true,the drop-down box will be have group selection.
     */
    isGrouped: PropTypes.bool,

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func

};

EditableSelect.defaultProps = {

    className: '',
    popupClassName: '',
    style: null,
    popupStyle: null,

    name: '',
    value: '',
    placeholder: 'Please select ...',
    data: [],
    invalidMsg: '',
    disabled: false,

    displayField: 'text',
    descriptionField: 'desc',

    infoMsg: '',
    autoClose: true,
    useFilter: false,
    noMatchedMsg: '',
    triggerTheme: Theme.DEFAULT,
    isGrouped: false

};

export default EditableSelect;<commit_msg>update EditableSelect<commit_after>/**
 * @file EditableSelect component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {findDOMNode} from 'react-dom';
import classNames from 'classnames';

import TextField from '../TextField';
import Popup from '../Popup';
import List from '../List';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import DropdownCalculation from '../_vendors/DropdownCalculation';

class EditableSelect extends Component {

    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value,
            listValue: props.value,
            filter: '',
            popupVisible: false,
            isAbove: false
        };

        this.showPopup = ::this.showPopup;
        this.closePopup = ::this.closePopup;
        this.filterData = ::this.filterData;
        this.popupRenderHandle = ::this.popupRenderHandle;
        this.changeHandle = ::this.changeHandle;
        this.onChangeValue = ::this.onChangeValue;

    }

    onChangeValue(value) {
        const {useFilter} = this.props;
        if (useFilter) {
            this.setState({
                value,
                filter: value,
                listValue: ''
            }, () => {
                const {onChange} = this.props;
                onChange && onChange(value);
            });
        } else {
            this.setState({
                value,
                listValue: ''
            }, () => {
                const {onChange} = this.props;
                onChange && onChange(value);
            });
        }

    }

    showPopup() {
        this.setState({
            popupVisible: true
        }, () => {
            this.props.onTriggerTouchTap && this.props.onTriggerTouchTap(this.state.popupVisible);
            this.props.onFocus && this.props.onFocus();
        });
    }

    closePopup() {
        this.setState({
            popupVisible: false
        }, () => {
            this.props.onTriggerTouchTap && this.props.onTriggerTouchTap(this.state.popupVisible);
            this.props.onPopupClosed && this.props.onPopupClosed();
        });
    }

    triggerHandler(el, triggerEl, popupEl, currentVisible, isAutoClose) {

        if (!triggerEl) {
            return true;
        }

        while (el) {
            if (el == popupEl) {
                return currentVisible;
            } else if (el == triggerEl) {
                return true;
            }
            el = el.parentNode;
        }

        return isAutoClose ? false : currentVisible;

    }

    filterData(filter = this.state.filter, data = this.props.data) {

        if (!filter) {
            return data;
        }

        const {displayField, isGrouped} = this.props,
            filterFunc = (originData) => {
                return originData.filter(item => typeof item === 'object' && !!item[displayField] ?
                    item[displayField].toString().toUpperCase().includes(filter.toUpperCase())
                    :
                    item.toString().toUpperCase().includes(filter.toUpperCase()));
            };

        if (isGrouped) {

            let result = Object.assign(data);

            for (let i = 0, len = result.length; i < len; i++) {
                let group = result[i];
                group.children = filterFunc(group.children);
                if (group.children.length < 1) {
                    result.splice(i, 1);
                    i--;
                }
            }

        }

        return filterFunc(data);

    }

    popupRenderHandle(popupEl) {

        const isAbove = DropdownCalculation.isAbove(this.editabledSelectEl, this.triggerEl, findDOMNode(popupEl));

        if (isAbove !== this.state.isAbove) {
            this.setState({
                isAbove
            });
        }

    }

    changeHandle(value) {
        const {valueField, renderer} = this.props;
        let itemValue = renderer ? renderer(value) : (typeof value == 'object' ? value[valueField] : value);
        const {autoClose} = this.props,
            state = {
                value: itemValue,
                listValue: value
            };

        if (autoClose) {
            state.popupVisible = false;
        }

        this.setState(state, () => {
            const {onChange} = this.props;
            onChange && onChange(itemValue);
        });

    }

    componentDidMount() {
        this.editabledSelectEl = this.refs.editabledSelect;
        this.triggerEl = findDOMNode(this.refs.trigger);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.props.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    render() {

        const {
                className, popupClassName, style, popupStyle, name, placeholder,
                disabled, useFilter, valueField, descriptionField, noMatchedMsg,
                triggerTheme, isGrouped, onItemTouchTap, renderer,
                onMouseOver, onMouseOut
            } = this.props,
            {value, listValue, filter, popupVisible, isAbove} = this.state,

            triggerClassName = classNames('editable-select-trigger', isAbove ? 'above' : 'blow', {
                activated: popupVisible,
                empty: !value
            }),

            editableSelectPopupClassName = classNames('editable-select-popup', isAbove ? 'above' : 'blow', {
                [popupClassName]: popupClassName
            }),

            editablePopupStyle = Object.assign({
                width: this.triggerEl && getComputedStyle(this.triggerEl).width
            }, popupStyle),

            listData = this.filterData();

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={value}/>
                        :
                        null
                }

                <TextField ref="trigger"
                           className={triggerClassName}
                           value={value}
                           rightIconCls={`fas fa-angle-${isAbove ? 'up' : 'down'} editable-select-trigger-icon`}
                           placeholder={placeholder}
                           disabled={disabled}
                           theme={triggerTheme}
                           onMouseOver={onMouseOver}
                           onMouseOut={onMouseOut}
                           onChange={this.onChangeValue}
                           onFocus={this.showPopup}/>

                <Popup ref="popup"
                       className={editableSelectPopupClassName}
                       style={editablePopupStyle}
                       visible={popupVisible}
                       triggerEl={this.triggerEl}
                       triggerHandler={this.triggerHandler}
                       hasTriangle={false}
                       position={isAbove ? Popup.Position.TOP_LEFT : Popup.Position.BOTTOM_LEFT}
                       onRender={this.popupRenderHandle}
                       onRequestClose={this.closePopup}>

                    <List className="editable-select-list"
                          isGrouped={isGrouped}
                          data={listData.length < 1 ? [] : listData}
                          valueField={valueField}
                          value={listValue}
                          displayField={valueField}
                          descriptionField={descriptionField}
                          renderer={renderer}
                          onItemTouchTap={onItemTouchTap}
                          onChange={this.changeHandle}/>

                </Popup>

        );

    }
}

EditableSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The name of the editableSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the editableSelect.
     */
    value: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The placeholder of the editableSelect.
     */
    placeholder: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            renderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onTouchTap: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * The invalid message of editableSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the editableSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the editableSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * If true,the drop-down box will have search input.
     */
    useFilter: PropTypes.bool,

    /**
     * The message of no matching option.
     */
    noMatchedMsg: PropTypes.string,

    /**
     * The theme of editableSelect.Can be primary,highlight,success,warning,error.
     */
    triggerTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * If true,the drop-down box will be have group selection.
     */
    isGrouped: PropTypes.bool,

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

EditableSelect.defaultProps = {

    className: '',
    popupClassName: '',
    style: null,
    popupStyle: null,

    name: '',
    value: '',
    placeholder: 'Please select ...',
    data: [],
    invalidMsg: '',
    disabled: false,

    displayField: 'text',
    descriptionField: 'desc',

    infoMsg: '',
    autoClose: true,
    useFilter: false,
    noMatchedMsg: '',
    triggerTheme: Theme.DEFAULT,
    isGrouped: false

};

export default EditableSelect;<|endoftext|>


<commit_before>import debounce from 'lodash/debounce'
import requestAnimationFrame from 'raf';

import pluginManagerAvailable from './templates/plugin-manager/available.hbs'
import pluginManager from './api/pluginManager';

function applyFilter(searchQuery) {
    // debounce reduces number of server side calls while typing
    pluginManager.availablePluginsSearch(searchQuery.toLowerCase().trim(), 50, function (plugins) {
        var pluginsTable = document.getElementById('plugins');
        var tbody = pluginsTable.querySelector('tbody');
        var selectedPlugins = []
        var admin = pluginsTable.dataset.hasadmin === 'true';

        function clearOldResults() {
            if (!admin) {
                tbody.innerHTML = '';
            } else {
                var rows = tbody.querySelectorAll('tr');
                if (rows) {
                    selectedPlugins = []
                    rows.forEach(function (row) {
                        var input = row.querySelector('input');
                        if (input.checked === true) {
                            var pluginName = input.name.split('.')[1]
                            selectedPlugins.push(pluginName)
                        } else {
                            row.remove();
                        }
                    })
                }
            }
        }

        clearOldResults()
        var rows = pluginManagerAvailable({
            plugins: plugins.filter(plugin => !selectedPlugins.includes(plugin.name)),
            admin
        });

        tbody.insertAdjacentHTML('beforeend', rows);

        // @see JENKINS-64504 - Update the sticky buttons position after each search.
        requestAnimationFrame(() => {
            layoutUpdateCallback.call()
        })
    })
}

var handleFilter = function (e) {
    applyFilter(e.target.value)
};

var debouncedFilter = debounce(handleFilter, 150);

document.addEventListener("DOMContentLoaded", function () {
    var filterInput = document.getElementById('filter-box');

    filterInput.addEventListener('input', debouncedFilter);

    applyFilter(filterInput.value);

    setTimeout(function () {
        layoutUpdateCallback.call();
    }, 350)

});
<commit_msg>[JENKINS-64805] Fixes IE11 error on the available tab (#5263)<commit_after>import debounce from 'lodash/debounce'
import requestAnimationFrame from 'raf';

import pluginManagerAvailable from './templates/plugin-manager/available.hbs'
import pluginManager from './api/pluginManager';

function applyFilter(searchQuery) {
    // debounce reduces number of server side calls while typing
    pluginManager.availablePluginsSearch(searchQuery.toLowerCase().trim(), 50, function (plugins) {
        var pluginsTable = document.getElementById('plugins');
        var tbody = pluginsTable.querySelector('tbody');
        var selectedPlugins = []
        var admin = pluginsTable.dataset.hasadmin === 'true';

        function clearOldResults() {
            if (!admin) {
                tbody.innerHTML = '';
            } else {
                var rows = tbody.querySelectorAll('tr');
                if (rows) {
                    selectedPlugins = []
                    rows.forEach(function (row) {
                        var input = row.querySelector('input');
                        if (input.checked === true) {
                            var pluginName = input.name.split('.')[1]
                            selectedPlugins.push(pluginName)
                        } else {
                            row.remove();
                        }
                    })
                }
            }
        }

        clearOldResults()
        var rows = pluginManagerAvailable({
            plugins: plugins.filter(plugin => selectedPlugins.indexOf(plugin.name) === -1),
            admin
        });

        tbody.insertAdjacentHTML('beforeend', rows);

        // @see JENKINS-64504 - Update the sticky buttons position after each search.
        requestAnimationFrame(() => {
            layoutUpdateCallback.call()
        })
    })
}

var handleFilter = function (e) {
    applyFilter(e.target.value)
};

var debouncedFilter = debounce(handleFilter, 150);

document.addEventListener("DOMContentLoaded", function () {
    var filterInput = document.getElementById('filter-box');

    filterInput.addEventListener('input', debouncedFilter);

    applyFilter(filterInput.value);

    setTimeout(function () {
        layoutUpdateCallback.call();
    }, 350)

});
<|endoftext|>


<commit_before>// ***********************************************//
//                 Router
// ***********************************************//
require('akvo-flow/core-common');

FLOW.Router = Ember.Router.extend({
  enableLogging: true,
  loggedIn: false,
  location: 'none',
  //'hash'or 'none' for URLs

  resetState: function () {
    FLOW.selectedControl.set('selectedQuestionGroup', null);
    FLOW.selectedControl.set('selectedSurveyGroup', null);
    FLOW.selectedControl.set('selectedSurvey', null);
    FLOW.selectedControl.set('selectedQuestion', null);
    FLOW.surveyControl.set('content', null);
    FLOW.questionControl.set('OPTIONcontent', null);
  },

  root: Ember.Route.extend({
    doNavSurveys: function (router, context) {
      router.transitionTo('navSurveys.index');
    },
    doNavDevices: function (router, context) {
      router.transitionTo('navDevices.index');
    },
    doNavData: function (router, context) {
      router.transitionTo('navData.index');
    },
    doNavReports: function (router, context) {
      router.transitionTo('navReports.index');
    },
    doNavMaps: function (router, context) {
      router.transitionTo('navMaps');
    },
    doNavUsers: function (router, context) {
      router.transitionTo('navUsers');
    },
    doNavMessages: function (router, context) {
      router.transitionTo('navMessages');
    },
    // not used at the moment
    doNavAdmin: function (router, context) {
      router.transitionTo('navAdmin');
    },

    // non-working code for transitioning to navHome at first entry of the app
    //    setup: function(router){
    //      router.send("goHome");
    //    },
    //    goHome:function(router){
    //      router.transitionTo('navHome');
    //    },
    index: Ember.Route.extend({
      route: '/',
      redirectsTo: 'navSurveys.index'
    }),

    // ******************* SURVEYS ROUTER ********************
    navSurveys: Ember.Route.extend({
      route: '/surveys',
      connectOutlets: function (router, event) {
        router.get('applicationController').connectOutlet('navSurveys');
        router.set('navigationController.selected', 'navSurveys');
      },

      doNewSurvey: function (router, event) {
        router.transitionTo('navSurveys.navSurveysNew');
      },

      doEditSurvey: function (router, event) {
        FLOW.selectedControl.set('selectedSurvey', event.context);
        router.transitionTo('navSurveys.navSurveysEdit.index');
      },

      doSurveysMain: function (router, event) {
        FLOW.selectedControl.set('selectedQuestion', null);
        router.transitionTo('navSurveys.navSurveysMain');
      },

      index: Ember.Route.extend({
        route: '/',
        redirectsTo: 'navSurveysMain'
      }),

      navSurveysMain: Ember.Route.extend({
        route: '/main',
        connectOutlets: function (router, event) {
          router.get('navSurveysController').connectOutlet({
            name: 'navSurveysMain'
          });
          FLOW.surveyGroupControl.populate();
          FLOW.selectedControl.set('selectedQuestionGroup', null);
          FLOW.selectedControl.set('selectedSurvey', null);
        }
      }),

      navSurveysNew: Ember.Route.extend({
        route: '/new',
        connectOutlets: function (router, event) {
          var newSurvey;

          newSurvey = FLOW.store.createRecord(FLOW.Survey, {
            "name": "",
            "defaultLanguageCode": "en",
            "requireApproval": false,
            "status": "NOT_PUBLISHED",
            "surveyGroupId": FLOW.selectedControl.selectedSurveyGroup.get('keyId')
          });

          FLOW.selectedControl.set('selectedSurvey', newSurvey);
          router.transitionTo('navSurveys.navSurveysEdit.index');
        }
      }),

      navSurveysEdit: Ember.Route.extend({
        route: '/edit',
        connectOutlets: function (router, event) {
          router.get('navSurveysController').connectOutlet({
            name: 'navSurveysEdit'
          });
          // all questions should be closed when we enter
          FLOW.selectedControl.set('selectedQuestion', null);
          FLOW.attributeControl.populate();
        },

        doEditQuestions: function (router, event) {
          router.transitionTo('navSurveys.navSurveysEdit.editQuestions');
        },

        index: Ember.Route.extend({
          route: '/',
          redirectsTo: 'editQuestions'
        }),

        manageNotifications: Ember.Route.extend({
          route: '/notifications',
          connectOutlets: function (router, event) {
            router.get('navSurveysEditController').connectOutlet({
              name: 'manageNotifications'
            });
            FLOW.notificationControl.populate();
          }
        }),

        manageTranslations: Ember.Route.extend({
          route: '/translations',
          connectOutlets: function (router, event) {
            router.get('navSurveysEditController').connectOutlet({
              name: 'manageTranslations'
            });
            FLOW.translationControl.populate();
          }
        }),

        editQuestions: Ember.Route.extend({
          route: '/questions',
          connectOutlets: function (router, event) {
            router.get('navSurveysEditController').connectOutlet({
              name: 'editQuestions'
            });

          }
        })
      })
    }),

    //********************** DEVICES ROUTER *******************
    navDevices: Ember.Route.extend({
      route: '/devices',
      connectOutlets: function (router, event) {
        router.get('applicationController').connectOutlet('navDevices');
        router.set('navigationController.selected', 'navDevices');
      },

      doCurrentDevices: function (router, event) {
        router.transitionTo('navDevices.currentDevices');
      },

      doAssignSurveysOverview: function (router, event) {
        router.transitionTo('navDevices.assignSurveysOverview');
      },

      doEditSurveysAssignment: function (router, event) {
        router.transitionTo('navDevices.editSurveysAssignment');
      },

      doSurveyBootstrap: function (router, event) {
        router.transitionTo('navDevices.surveyBootstrap');
      },

      index: Ember.Route.extend({
        route: '/',
        redirectsTo: 'currentDevices'
      }),

      currentDevices: Ember.Route.extend({
        route: '/current-devices',
        connectOutlets: function (router, context) {
          router.get('navDevicesController').connectOutlet('currentDevices');
          FLOW.deviceGroupControl.populate();
          FLOW.deviceControl.populate();
          FLOW.surveyAssignmentControl.populate();
          router.resetState();
          router.set('devicesSubnavController.selected', 'currentDevices');
        }
      }),

      assignSurveysOverview: Ember.Route.extend({
        route: '/assign-surveys',
        connectOutlets: function (router, context) {
          router.get('navDevicesController').connectOutlet('assignSurveysOverview');
          router.set('devicesSubnavController.selected', 'assignSurveys');
        }
      }),

      editSurveysAssignment: Ember.Route.extend({
        route: '/assign-surveys',
        connectOutlets: function (router, context) {
          router.get('navDevicesController').connectOutlet('editSurveyAssignment');
          router.set('devicesSubnavController.selected', 'assignSurveys');
        }
      }),

      surveyBootstrap: Ember.Route.extend({
        route: '/manual-transfer',
        connectOutlets: function (router, context) {
          router.get('navDevicesController').connectOutlet('surveyBootstrap');
          router.set('devicesSubnavController.selected', 'surveyBootstrap');
        }
      })
    }),

    // ******************* DATA ROUTER ***********************
    navData: Ember.Route.extend({
      route: '/data',
      connectOutlets: function (router, event) {
        router.get('applicationController').connectOutlet('navData');
        router.set('navigationController.selected', 'navData');
      },

      doInspectData: function (router, event) {
        router.transitionTo('navData.inspectData');
      },
      doManageAttributes: function (router, event) {
        router.transitionTo('navData.manageAttributes');
      },
      doBulkUpload: function (router, event) {
        router.transitionTo('navData.bulkUpload');
      },
      doDataCleaning: function (router, event) {
        router.transitionTo('navData.dataCleaning');
      },
      doMonitoringData: function (router, event) {
        router.transitionTo('navData.monitoringData');
      },

      index: Ember.Route.extend({
        route: '/',
        redirectsTo: 'inspectData'
      }),

      inspectData: Ember.Route.extend({
        route: '/inspectdata',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('inspectData');
          router.set('datasubnavController.selected', 'inspectData');
          router.resetState();
          FLOW.surveyGroupControl.populate();
          FLOW.surveyInstanceControl.populate();
        }
      }),

      manageAttributes: Ember.Route.extend({
        route: '/manageattributes',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('manageAttributes');
          router.set('datasubnavController.selected', 'manageAttributes');
          FLOW.attributeControl.populate();
        }
      }),

      bulkUpload: Ember.Route.extend({
        route: '/bulkupload',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('bulkUpload');
          router.set('datasubnavController.selected', 'bulkUpload');
        }
      }),

      dataCleaning: Ember.Route.extend({
        route: '/datacleaning',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('dataCleaning');
          router.set('datasubnavController.selected', 'dataCleaning');
        }
      }),

      monitoringData: Ember.Route.extend({
        route: '/monitoringdata',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('monitoringData');
          router.set('datasubnavController.selected', 'monitoringData');
          FLOW.surveyGroupControl.populate(function (item) {
                 return item.get('monitoringGroup');
          });
        }
      }),
    }),

    // ************************** REPORTS ROUTER **********************************
    navReports: Ember.Route.extend({
      route: '/reports',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navReports');
        FLOW.surveyGroupControl.populate();
        router.resetState();
        router.set('navigationController.selected', 'navReports');
      },

      doExportReports: function (router, event) {
        router.transitionTo('navReports.exportReports');
      },

      doChartReports: function (router, event) {
        router.transitionTo('navReports.chartReports');
      },

      doStatistics: function(router, event) {
        router.transitionTo('navReports.statistics');
      },

      index: Ember.Route.extend({
        route: '/',
        redirectsTo: 'chartReports'
      }),

      exportReports: Ember.Route.extend({
        route: '/exportreports',
        connectOutlets: function (router, context) {
          router.get('navReportsController').connectOutlet('exportReports');
          router.set('reportsSubnavController.selected', 'exportReports');
          router.resetState();
        }
      }),

      chartReports: Ember.Route.extend({
        route: '/chartreports',
        connectOutlets: function (router, context) {
          router.get('navReportsController').connectOutlet('chartReports');
          router.set('reportsSubnavController.selected', 'chartReports');
          FLOW.surveyGroupControl.populate();
        }
      }),
      statistics: Ember.Route.extend({
        route: '/statistics',
        connectOutlets: function(router, context) {
          router.resetState();
          router.get('navReportsController').connectOutlet('statistics');
          router.set('reportsSubnavController.selected', 'statistics');
          FLOW.surveyGroupControl.populate();
        }
      })
    }),

    // ************************** MAPS ROUTER **********************************
    navMaps: Ember.Route.extend({
      route: '/maps',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navMaps');
        router.set('navigationController.selected', 'navMaps');
      }
    }),

    // ************************** USERS ROUTER **********************************
    navUsers: Ember.Route.extend({
      route: '/users',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navUsers');
        router.set('navigationController.selected', 'navUsers');
        FLOW.userControl.populate();
      }
    }),

    // ************************** MESSAGES ROUTER **********************************
    navMessages: Ember.Route.extend({
      route: '/users',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navMessages');
        router.set('navigationController.selected', 'navMessages');
        FLOW.messageControl.populate();
        router.resetState();
      }
    }),

    // ************************** ADMIN ROUTER **********************************
    // not used at the moment
    navAdmin: Ember.Route.extend({
      route: '/admin',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navAdmin');
        router.set('navigationController.selected', 'navAdmin');
      }
    })
  })
});
<commit_msg>[#625] Reset extra controls on "Go Back To Survey Overview"<commit_after>// ***********************************************//
//                 Router
// ***********************************************//
require('akvo-flow/core-common');

FLOW.Router = Ember.Router.extend({
  enableLogging: true,
  loggedIn: false,
  location: 'none',
  //'hash'or 'none' for URLs

  resetState: function () {
    FLOW.selectedControl.set('selectedQuestionGroup', null);
    FLOW.selectedControl.set('selectedSurveyGroup', null);
    FLOW.selectedControl.set('selectedSurvey', null);
    FLOW.selectedControl.set('selectedQuestion', null);
    FLOW.surveyControl.set('content', null);
    FLOW.questionControl.set('OPTIONcontent', null);
  },

  root: Ember.Route.extend({
    doNavSurveys: function (router, context) {
      router.transitionTo('navSurveys.index');
    },
    doNavDevices: function (router, context) {
      router.transitionTo('navDevices.index');
    },
    doNavData: function (router, context) {
      router.transitionTo('navData.index');
    },
    doNavReports: function (router, context) {
      router.transitionTo('navReports.index');
    },
    doNavMaps: function (router, context) {
      router.transitionTo('navMaps');
    },
    doNavUsers: function (router, context) {
      router.transitionTo('navUsers');
    },
    doNavMessages: function (router, context) {
      router.transitionTo('navMessages');
    },
    // not used at the moment
    doNavAdmin: function (router, context) {
      router.transitionTo('navAdmin');
    },

    // non-working code for transitioning to navHome at first entry of the app
    //    setup: function(router){
    //      router.send("goHome");
    //    },
    //    goHome:function(router){
    //      router.transitionTo('navHome');
    //    },
    index: Ember.Route.extend({
      route: '/',
      redirectsTo: 'navSurveys.index'
    }),

    // ******************* SURVEYS ROUTER ********************
    navSurveys: Ember.Route.extend({
      route: '/surveys',
      connectOutlets: function (router, event) {
        router.get('applicationController').connectOutlet('navSurveys');
        router.set('navigationController.selected', 'navSurveys');
      },

      doNewSurvey: function (router, event) {
        router.transitionTo('navSurveys.navSurveysNew');
      },

      doEditSurvey: function (router, event) {
        FLOW.selectedControl.set('selectedSurvey', event.context);
        router.transitionTo('navSurveys.navSurveysEdit.index');
      },

      doSurveysMain: function (router, event) {
        FLOW.selectedControl.set('selectedQuestion', null);
        router.transitionTo('navSurveys.navSurveysMain');
      },

      index: Ember.Route.extend({
        route: '/',
        redirectsTo: 'navSurveysMain'
      }),

      navSurveysMain: Ember.Route.extend({
        route: '/main',
        connectOutlets: function (router, event) {
          router.get('navSurveysController').connectOutlet({
            name: 'navSurveysMain'
          });
          FLOW.surveyGroupControl.populate();
          FLOW.selectedControl.set('selectedQuestionGroup', null);
          FLOW.selectedControl.set('selectedSurvey', null);
          FLOW.selectedControl.set('selectedQuestion', null);
          FLOW.questionControl.set('OPTIONcontent', null);

        }
      }),

      navSurveysNew: Ember.Route.extend({
        route: '/new',
        connectOutlets: function (router, event) {
          var newSurvey;

          newSurvey = FLOW.store.createRecord(FLOW.Survey, {
            "name": "",
            "defaultLanguageCode": "en",
            "requireApproval": false,
            "status": "NOT_PUBLISHED",
            "surveyGroupId": FLOW.selectedControl.selectedSurveyGroup.get('keyId')
          });

          FLOW.selectedControl.set('selectedSurvey', newSurvey);
          router.transitionTo('navSurveys.navSurveysEdit.index');
        }
      }),

      navSurveysEdit: Ember.Route.extend({
        route: '/edit',
        connectOutlets: function (router, event) {
          router.get('navSurveysController').connectOutlet({
            name: 'navSurveysEdit'
          });
          // all questions should be closed when we enter
          FLOW.selectedControl.set('selectedQuestion', null);
          FLOW.attributeControl.populate();
        },

        doEditQuestions: function (router, event) {
          router.transitionTo('navSurveys.navSurveysEdit.editQuestions');
        },

        index: Ember.Route.extend({
          route: '/',
          redirectsTo: 'editQuestions'
        }),

        manageNotifications: Ember.Route.extend({
          route: '/notifications',
          connectOutlets: function (router, event) {
            router.get('navSurveysEditController').connectOutlet({
              name: 'manageNotifications'
            });
            FLOW.notificationControl.populate();
          }
        }),

        manageTranslations: Ember.Route.extend({
          route: '/translations',
          connectOutlets: function (router, event) {
            router.get('navSurveysEditController').connectOutlet({
              name: 'manageTranslations'
            });
            FLOW.translationControl.populate();
          }
        }),

        editQuestions: Ember.Route.extend({
          route: '/questions',
          connectOutlets: function (router, event) {
            router.get('navSurveysEditController').connectOutlet({
              name: 'editQuestions'
            });

          }
        })
      })
    }),

    //********************** DEVICES ROUTER *******************
    navDevices: Ember.Route.extend({
      route: '/devices',
      connectOutlets: function (router, event) {
        router.get('applicationController').connectOutlet('navDevices');
        router.set('navigationController.selected', 'navDevices');
      },

      doCurrentDevices: function (router, event) {
        router.transitionTo('navDevices.currentDevices');
      },

      doAssignSurveysOverview: function (router, event) {
        router.transitionTo('navDevices.assignSurveysOverview');
      },

      doEditSurveysAssignment: function (router, event) {
        router.transitionTo('navDevices.editSurveysAssignment');
      },

      doSurveyBootstrap: function (router, event) {
        router.transitionTo('navDevices.surveyBootstrap');
      },

      index: Ember.Route.extend({
        route: '/',
        redirectsTo: 'currentDevices'
      }),

      currentDevices: Ember.Route.extend({
        route: '/current-devices',
        connectOutlets: function (router, context) {
          router.get('navDevicesController').connectOutlet('currentDevices');
          FLOW.deviceGroupControl.populate();
          FLOW.deviceControl.populate();
          FLOW.surveyAssignmentControl.populate();
          router.resetState();
          router.set('devicesSubnavController.selected', 'currentDevices');
        }
      }),

      assignSurveysOverview: Ember.Route.extend({
        route: '/assign-surveys',
        connectOutlets: function (router, context) {
          router.get('navDevicesController').connectOutlet('assignSurveysOverview');
          router.set('devicesSubnavController.selected', 'assignSurveys');
        }
      }),

      editSurveysAssignment: Ember.Route.extend({
        route: '/assign-surveys',
        connectOutlets: function (router, context) {
          router.get('navDevicesController').connectOutlet('editSurveyAssignment');
          router.set('devicesSubnavController.selected', 'assignSurveys');
        }
      }),

      surveyBootstrap: Ember.Route.extend({
        route: '/manual-transfer',
        connectOutlets: function (router, context) {
          router.get('navDevicesController').connectOutlet('surveyBootstrap');
          router.set('devicesSubnavController.selected', 'surveyBootstrap');
        }
      })
    }),

    // ******************* DATA ROUTER ***********************
    navData: Ember.Route.extend({
      route: '/data',
      connectOutlets: function (router, event) {
        router.get('applicationController').connectOutlet('navData');
        router.set('navigationController.selected', 'navData');
      },

      doInspectData: function (router, event) {
        router.transitionTo('navData.inspectData');
      },
      doManageAttributes: function (router, event) {
        router.transitionTo('navData.manageAttributes');
      },
      doBulkUpload: function (router, event) {
        router.transitionTo('navData.bulkUpload');
      },
      doDataCleaning: function (router, event) {
        router.transitionTo('navData.dataCleaning');
      },
      doMonitoringData: function (router, event) {
        router.transitionTo('navData.monitoringData');
      },

      index: Ember.Route.extend({
        route: '/',
        redirectsTo: 'inspectData'
      }),

      inspectData: Ember.Route.extend({
        route: '/inspectdata',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('inspectData');
          router.set('datasubnavController.selected', 'inspectData');
          router.resetState();
          FLOW.surveyGroupControl.populate();
          FLOW.surveyInstanceControl.populate();
        }
      }),

      manageAttributes: Ember.Route.extend({
        route: '/manageattributes',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('manageAttributes');
          router.set('datasubnavController.selected', 'manageAttributes');
          FLOW.attributeControl.populate();
        }
      }),

      bulkUpload: Ember.Route.extend({
        route: '/bulkupload',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('bulkUpload');
          router.set('datasubnavController.selected', 'bulkUpload');
        }
      }),

      dataCleaning: Ember.Route.extend({
        route: '/datacleaning',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('dataCleaning');
          router.set('datasubnavController.selected', 'dataCleaning');
        }
      }),

      monitoringData: Ember.Route.extend({
        route: '/monitoringdata',
        connectOutlets: function (router, context) {
          router.get('navDataController').connectOutlet('monitoringData');
          router.set('datasubnavController.selected', 'monitoringData');
          FLOW.surveyGroupControl.populate(function (item) {
                 return item.get('monitoringGroup');
          });
        }
      }),
    }),

    // ************************** REPORTS ROUTER **********************************
    navReports: Ember.Route.extend({
      route: '/reports',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navReports');
        FLOW.surveyGroupControl.populate();
        router.resetState();
        router.set('navigationController.selected', 'navReports');
      },

      doExportReports: function (router, event) {
        router.transitionTo('navReports.exportReports');
      },

      doChartReports: function (router, event) {
        router.transitionTo('navReports.chartReports');
      },

      doStatistics: function(router, event) {
        router.transitionTo('navReports.statistics');
      },

      index: Ember.Route.extend({
        route: '/',
        redirectsTo: 'chartReports'
      }),

      exportReports: Ember.Route.extend({
        route: '/exportreports',
        connectOutlets: function (router, context) {
          router.get('navReportsController').connectOutlet('exportReports');
          router.set('reportsSubnavController.selected', 'exportReports');
          router.resetState();
        }
      }),

      chartReports: Ember.Route.extend({
        route: '/chartreports',
        connectOutlets: function (router, context) {
          router.get('navReportsController').connectOutlet('chartReports');
          router.set('reportsSubnavController.selected', 'chartReports');
          FLOW.surveyGroupControl.populate();
        }
      }),
      statistics: Ember.Route.extend({
        route: '/statistics',
        connectOutlets: function(router, context) {
          router.resetState();
          router.get('navReportsController').connectOutlet('statistics');
          router.set('reportsSubnavController.selected', 'statistics');
          FLOW.surveyGroupControl.populate();
        }
      })
    }),

    // ************************** MAPS ROUTER **********************************
    navMaps: Ember.Route.extend({
      route: '/maps',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navMaps');
        router.set('navigationController.selected', 'navMaps');
      }
    }),

    // ************************** USERS ROUTER **********************************
    navUsers: Ember.Route.extend({
      route: '/users',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navUsers');
        router.set('navigationController.selected', 'navUsers');
        FLOW.userControl.populate();
      }
    }),

    // ************************** MESSAGES ROUTER **********************************
    navMessages: Ember.Route.extend({
      route: '/users',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navMessages');
        router.set('navigationController.selected', 'navMessages');
        FLOW.messageControl.populate();
        router.resetState();
      }
    }),

    // ************************** ADMIN ROUTER **********************************
    // not used at the moment
    navAdmin: Ember.Route.extend({
      route: '/admin',
      connectOutlets: function (router, context) {
        router.get('applicationController').connectOutlet('navAdmin');
        router.set('navigationController.selected', 'navAdmin');
      }
    })
  })
});
<|endoftext|>


<commit_before>// TODO: update to VIE 2.0
// var VIE = new VIE();

jQuery(document).ready(function($) {

    VIE.EntityManager.initializeCollection();

    VIE.EntityManager.entities.bind('add', function(model) {
        model.url = vie_phpcr_path + model.id;
        model.toJSON = model.toJSONLD;
    });

    // Load all RDFa entities into VIE
    VIE.RDFaEntities.getInstances();

    // Make all RDFa entities editable

    jQuery('[typeof][about]').each(function() {
        jQuery(this).vieSemanticHallo({
            plugins: {
                'dcterms:title': {
                    'halloformat': {},
                    'hallooverlay': {},
                    'hallotoolbarlinebreak': {}
                },
                'default': {
                    'halloimage': { 'searchUrl': 'liip/vie/assets/search/' },
                    'hallolinkimg': {},
                    'halloheadings': {},
                    'halloformat': {},
                    'hallolists': {},
                    'hallojustify': {},
                    'hallotoolbarlinebreak': { 'breakAfter': ['hallolinkimg'] },
                    'hallooverlay': {}
                }
            },
            floating: false,
            offset: {
                'x': 0,
                'y': "top"
            },
            showAlways: true
        });
    });

    $(this).bind('hallodeactivated', vieSaveContent);

    $(window).resize(function(){
        if(!$('.inEditMode')[0] == undefined ){
            $('.halloToolbar').css('left', $('.inEditMode')[0].offsetLeft);
        }
    });

    $('[contenteditable]').hover(function(){
        if(!$(this).hasClass('inEditMode')){
            var editButton = $('
edit
').addClass('editButton');
            $(this).append(editButton);
        }
    }, function(){
        $('.editButton').remove();
    });

});

function vieSaveContent() {
    // Go through all Backbone model instances loaded for the page
    VIE.EntityManager.entities.each(function(objectInstance) {

        if (!VIE.HalloEditable.refreshFromEditables(objectInstance)) {
            // No changes to this object, skip
            return true;
        }
        $(document).trigger("vieSaveStart");

        // Set the modified properties to the model instance
        objectInstance.save(null, {
            success: function(savedModel, response) {
                $(document).trigger("vieSaveSuccess");
                console.log(savedModel.id + " was saved");
            },
            error: function(response) {
                console.log("Save failed");
                console.log(response);
            }
        });
    });
}
<commit_msg>Add code to handle prevent saves by plugins<commit_after>// TODO: update to VIE 2.0
// var VIE = new VIE();

jQuery(document).ready(function($) {

    VIE.EntityManager.initializeCollection();

    VIE.EntityManager.entities.bind('add', function(model) {
        model.url = vie_phpcr_path + model.id;
        model.toJSON = model.toJSONLD;
    });

    // Load all RDFa entities into VIE
    VIE.RDFaEntities.getInstances();

    // Make all RDFa entities editable

    jQuery('[typeof][about]').each(function() {
        jQuery(this).vieSemanticHallo({
            plugins: {
                'dcterms:title': {
                    'halloformat': {},
                    'hallooverlay': {},
                    'hallotoolbarlinebreak': {}
                },
                'default': {
                    'halloimage': { 'searchUrl': 'liip/vie/assets/search/' },
                    'hallolinkimg': {},
                    'halloheadings': {},
                    'halloformat': {},
                    'hallolists': {},
                    'hallojustify': {},
                    'hallotoolbarlinebreak': { 'breakAfter': ['hallolinkimg'] },
                    'hallooverlay': {}
                }
            },
            floating: false,
            offset: {
                'x': 0,
                'y': "top"
            },
            showAlways: true
        });
    });

    $(this).bind('hallodeactivated', vieSaveContent);
    $(this).bind('startPreventSave', function() {
        preventSave = true;
    });
    $(this).bind('stopPreventSave', function() {
        preventSave = false;
    });

    $(window).resize(function(){
        if(!$('.inEditMode')[0] == undefined ){
            $('.halloToolbar').css('left', $('.inEditMode')[0].offsetLeft);
        }
    });

    $('[contenteditable]').hover(function(){
        if(!$(this).hasClass('inEditMode')){
            var editButton = $('
edit
').addClass('editButton');
            $(this).append(editButton);
        }
    }, function(){
        $('.editButton').remove();
    });

});

var preventSave = false;
function vieSaveContent() {
    if (preventSave) {
        setTimeout(vieSaveContent, 5000);
        return;
    }

    // Go through all Backbone model instances loaded for the page
    VIE.EntityManager.entities.each(function(objectInstance) {

        if (!VIE.HalloEditable.refreshFromEditables(objectInstance)) {
            // No changes to this object, skip
            return true;
        }
        $(document).trigger("vieSaveStart");

        // Set the modified properties to the model instance
        objectInstance.save(null, {
            success: function(savedModel, response) {
                $(document).trigger("vieSaveSuccess");
                console.log(savedModel.id + " was saved");
            },
            error: function(response) {
                console.log("Save failed");
                console.log(response);
            }
        });
    });
}
<|endoftext|>


<commit_before>(function(config, models, views, routers, utils, templates) {

views.Post = Backbone.View.extend({

  id: 'post',

  events: {
    'click .save': '_save',
    'click .cancel-save': '_toggleCommit',
    'click .save.confirm': 'updateFile',
    'click a.toggle.view': '_toggleView',
    'click a.toggle.meta': '_toggleMeta',
    'change input': '_makeDirty',
    'change #post_published': 'updateMetaData',
    'click .delete': '_delete',
    'click .toggle-options': '_toggleOptions'
  },

  _toggleOptions: function() {
    $('.options').toggle();
    return false;
  },

  _delete: function() {
    if (confirm("Are you sure you want to delete that file?")) {
      deletePost(app.state.user, app.state.repo, app.state.branch, this.model.path, this.model.file, _.bind(function(err) {
        if (err) return alert('Error during deletion. Please wait 30 seconds and try again.');
        router.navigate([app.state.user, app.state.repo, "tree", app.state.branch].join('/'), true);
      }, this));
    }
    return false;
  },

  updateURL: function() {
    var url = _.compact([app.state.user, app.state.repo, this.model.preview ? "blob" : "edit", app.state.branch, this.model.path, this.model.file]);
    router.navigate(url.join('/'), false);
  },

  _makeDirty: function(e) {
    this.dirty = true;
    if (this.editor) this.model.content = this.editor.getValue();
    if (this.metadataEditor) this.model.raw_metadata = this.metadataEditor.getValue();
    if (!this.$('.button.save').hasClass('saving')) {
      this.$('.button.save').html('COMMIT');
      this.$('.button.save').removeClass('inactive error');
    }
  },

  showDiff: function() {
    var text1 = this.prevContent;
    var text2 = this.serialize();
    var d = this.dmp.diff_main(text1, text2);
    this.dmp.diff_cleanupSemantic(d);
    var diff = this.dmp.diff_prettyHtml(d).replace(/¶/g, "");
    $('.diff-wrapper .diff').html(diff);
  },

  _toggleCommit: function() {
    this.hideMeta();
    this.$('.button.save').html(this.$('.document-menu').hasClass('commit') ? "SAVE" : "COMMIT");
    this.$('.button.save').toggleClass('confirm');
    this.$('.document-menu').toggleClass('commit');
    this.$('.button.cancel-save').toggle();
    this.$('.document-menu-content .options').hide();
    this.showDiff();
    this.$('.surface').toggle();
    this.$('.diff-wrapper').toggle();
    this.$('.commit-message').val("Updated "+$('input.filepath').val());
    return false;
  },

  _save: function(e) {
    if (!this.dirty) return false;
    this._toggleCommit();
    e.preventDefault();
    return false;
  },

  _toggleView: function(e) {
    var that = this;
    this.toggleView($(e.currentTarget).attr('data-view'));
    _.delay(function() { that.refreshCodeMirror(); }, 1);
    return false;
  },

  _toggleMeta: function(e) {
    var that = this;
    if (e) e.preventDefault();
    $('.toggle.meta').toggleClass('active');
    $('.metadata').toggle();
    _.delay(function() { that.refreshCodeMirror(); }, 1);
    return false;
  },

  refreshCodeMirror: function() {
    if ($('.toggle.meta').hasClass('active')) {
      $('.CodeMirror-scroll').height($('.document').height() / 2);
    } else {
      $('.CodeMirror-scroll').height($('.document').height());
    }
    this.editor.refresh();
    if (this.metadataEditor) this.metadataEditor.refresh();
  },

  toggleView: function(view) {
    this.view = view;
    if (view === 'preview') {
      this.model.preview = true;
      this.$('.post-content').html(marked(this.model.content));
    } else {
      this.model.preview = false;
    }
    this.hideMeta();
    this.updateURL();
    $('.toggle').removeClass('active');
    $('.toggle.'+view).addClass('active');

    $('.document .surface').removeClass('preview cheatsheet compose');
    $('.document .surface').addClass(view);
  },

  hideMeta: function() {
    $('.toggle.meta').removeClass('active');
    $('.metadata').hide();
  },

  right: function() {
    var view = $('.toggle.active').attr('data-view');
    if (view === 'preview') return;
    if (view === 'compose') return this.toggleView('preview');
    return this.toggleView('compose');
  },

  left: function() {
    var view = $('.toggle.active').attr('data-view');
    if (view === 'cheatsheet') return;
    if (view === 'compose') return this.toggleView('cheatsheet');
    return this.toggleView('compose');
  },

  initialize: function() {
    this.dmp = new diff_match_patch();
    this.mode = "edit";
    this.prevContent = this.serialize();
    if (!window.shortcutsRegistered) {
      key('⌘+s, ctrl+s', _.bind(function() { this.updateFile(); return false; }, this));
      key('ctrl+shift+right', _.bind(function() { this.right(); return false; }, this));
      key('ctrl+shift+left', _.bind(function() { this.left(); return false; }, this));
      key('esc', _.bind(function() { this.toggleView('compose'); return false; }, this));
      window.shortcutsRegistered = true;
    }
  },

  // TODO: We might not wanna use this
  parseMetadata: function(metadata) {
    var metadata = this.metadataEditor.getValue();
    if (!metadata) return {};
    try {
      return jsyaml.load(metadata);
    } catch(err) {
      return null;
    }
  },

  updateMetaData: function() {
    if (!this.model.jekyll) return true; // metadata -> skip

    // Update published
    function updatePublished(yamlStr, published) {
      var regex = /published: (false|true)/;
      if (yamlStr.match(regex)) {
        return yamlStr.replace(regex, "published: " + !!published);
      } else {
        return yamlStr + "\npublished: " + !!published;
      }
    }

    this.model.raw_metadata = this.metadataEditor.getValue();
    var published = this.$('#post_published').prop('checked');

    this.model.raw_metadata = updatePublished(this.model.raw_metadata, published);
    this.metadataEditor.setValue(this.model.raw_metadata);

    published ? $('#post').addClass('published') : $('#post').removeClass('published');

    return true;
  },

  updateFilename: function(filepath, cb) {
    var that = this;

    if (!_.validPathname(filepath)) return cb('error');
    app.state.path = this.model.path; // ?
    app.state.file = _.extractFilename(filepath)[1];
    app.state.path = _.extractFilename(filepath)[0];

    function finish() {
      that.model.path = app.state.path;
      that.model.file = app.state.file;
      // rerender header to reflect the filename change
      app.instance.header.render();
      that.updateURL();
    }

    if (this.model.persisted) {
      movePost(app.state.user, app.state.repo, app.state.branch, _.filepath(this.model.path, this.model.file), filepath, _.bind(function(err) {
        if (!err) finish()
        err ? cb('error') : cb(null)
      }, this));
    } else {
      finish();
      cb(null);
    }
  },

  serialize: function() {
    return serialize(this.model.content, this.model.jekyll ? this.model.raw_metadata : null)
  },

  updateFile: function() {
    var filepath = $('input.filepath').val();

    var file = _.extractFilename(filepath)[1];
    var that = this;
    var message = this.model.persisted ? "Updated " + filepath : "Created " + filepath;

    // Update content
    this.model.content = this.editor.getValue();

    // File contents
    var filecontent = this.serialize();;

    function updateState(label, classes) {
      $('.button.save').html(label)
                       .removeClass('inactive error saving')
                       .addClass(classes);
    }

    function save() {
      if (that.updateMetaData()) {
        saveFile(app.state.user, app.state.repo, app.state.branch, filepath, filecontent, message, function(err) {
          if (err) {
            _.delay(function() { that._makeDirty() }, 3000);
            updateState('! Try again in 30 seconds', 'error');
            return;
          }
          that.dirty = false;
          that.model.persisted = true;
          that.model.file = file;
          that.updateURL();
          that.prevContent = content;
          updateState('SAVED', 'inactive');
        });
      } else {
        updateState('! Metadata', 'error');
      }
    }

    updateState('SAVING ...', 'inactive saving');

    if (filepath === _.filepath(this.model.path, this.model.file)) return save();

    // Move or create file
    this.updateFilename(filepath, function(err) {
      err ? updateState('! Filename', 'error') : save();
    });
  },

  keyMap: function() {
    var that = this;
    return {
      "Shift-Ctrl-Left": function(codemirror) {
        that.left();
      },
      "Shift-Ctrl-Right": function(codemirror) {
        that.right();
      },
      "Shift-Ctrl-M": function(codemirror) {
        that._toggleMeta();
      },
      "Ctrl-S": function(codemirror) {
        that.updateFile();
      }
    };
  },

  initEditor: function() {
    var that = this;
    setTimeout(function() {
      if (that.model.jekyll) {
        that.metadataEditor = CodeMirror($('#raw_metadata')[0], {
          mode: 'yaml',
          value: that.model.raw_metadata,
          theme: 'prose-dark',
          lineWrapping: true,
          lineNumbers: true,
          extraKeys: that.keyMap(),
          onChange: _.bind(that._makeDirty, that)
        });
        $('#post .metadata').hide();
      }
      that.editor = CodeMirror($('#code')[0], {
        mode: that.model.lang,
        value: that.model.content,
        lineWrapping: true,
        lineNumbers: true,
        extraKeys: that.keyMap(),
        matchBrackets: true,
        theme: 'prose-bright',
        onChange: _.bind(that._makeDirty, that)
      });
      that.refreshCodeMirror();
    }, 100);
  },

  render: function() {
    var that = this;
    $(this.el).html(templates.post(_.extend(this.model, { mode: this.mode })));
    if (this.model.published) $(this.el).addClass('published');
    this.initEditor();
    return this;
  }
});

}).apply(this, window.args);
<commit_msg>Fixed regression.<commit_after>(function(config, models, views, routers, utils, templates) {

views.Post = Backbone.View.extend({

  id: 'post',

  events: {
    'click .save': '_save',
    'click .cancel-save': '_toggleCommit',
    'click .save.confirm': 'updateFile',
    'click a.toggle.view': '_toggleView',
    'click a.toggle.meta': '_toggleMeta',
    'change input': '_makeDirty',
    'change #post_published': 'updateMetaData',
    'click .delete': '_delete',
    'click .toggle-options': '_toggleOptions'
  },

  _toggleOptions: function() {
    $('.options').toggle();
    return false;
  },

  _delete: function() {
    if (confirm("Are you sure you want to delete that file?")) {
      deletePost(app.state.user, app.state.repo, app.state.branch, this.model.path, this.model.file, _.bind(function(err) {
        if (err) return alert('Error during deletion. Please wait 30 seconds and try again.');
        router.navigate([app.state.user, app.state.repo, "tree", app.state.branch].join('/'), true);
      }, this));
    }
    return false;
  },

  updateURL: function() {
    var url = _.compact([app.state.user, app.state.repo, this.model.preview ? "blob" : "edit", app.state.branch, this.model.path, this.model.file]);
    router.navigate(url.join('/'), false);
  },

  _makeDirty: function(e) {
    this.dirty = true;
    if (this.editor) this.model.content = this.editor.getValue();
    if (this.metadataEditor) this.model.raw_metadata = this.metadataEditor.getValue();
    if (!this.$('.button.save').hasClass('saving')) {
      this.$('.button.save').html('COMMIT');
      this.$('.button.save').removeClass('inactive error');
    }
  },

  showDiff: function() {
    var text1 = this.prevContent;
    var text2 = this.serialize();
    var d = this.dmp.diff_main(text1, text2);
    this.dmp.diff_cleanupSemantic(d);
    var diff = this.dmp.diff_prettyHtml(d).replace(/¶/g, "");
    $('.diff-wrapper .diff').html(diff);
  },

  _toggleCommit: function() {
    this.hideMeta();
    this.$('.button.save').html(this.$('.document-menu').hasClass('commit') ? "SAVE" : "COMMIT");
    this.$('.button.save').toggleClass('confirm');
    this.$('.document-menu').toggleClass('commit');
    this.$('.button.cancel-save').toggle();
    this.$('.document-menu-content .options').hide();
    this.showDiff();
    this.$('.surface').toggle();
    this.$('.diff-wrapper').toggle();
    this.$('.commit-message').val("Updated "+$('input.filepath').val());
    return false;
  },

  _save: function(e) {
    if (!this.dirty) return false;
    this._toggleCommit();
    e.preventDefault();
    return false;
  },

  _toggleView: function(e) {
    var that = this;
    this.toggleView($(e.currentTarget).attr('data-view'));
    _.delay(function() { that.refreshCodeMirror(); }, 1);
    return false;
  },

  _toggleMeta: function(e) {
    var that = this;
    if (e) e.preventDefault();
    $('.toggle.meta').toggleClass('active');
    $('.metadata').toggle();
    _.delay(function() { that.refreshCodeMirror(); }, 1);
    return false;
  },

  refreshCodeMirror: function() {
    if ($('.toggle.meta').hasClass('active')) {
      $('.CodeMirror-scroll').height($('.document').height() / 2);
    } else {
      $('.CodeMirror-scroll').height($('.document').height());
    }
    this.editor.refresh();
    if (this.metadataEditor) this.metadataEditor.refresh();
  },

  toggleView: function(view) {
    this.view = view;
    if (view === 'preview') {
      this.model.preview = true;
      this.$('.post-content').html(marked(this.model.content));
    } else {
      this.model.preview = false;
    }
    this.hideMeta();
    this.updateURL();
    $('.toggle').removeClass('active');
    $('.toggle.'+view).addClass('active');

    $('.document .surface').removeClass('preview cheatsheet compose');
    $('.document .surface').addClass(view);
  },

  hideMeta: function() {
    $('.toggle.meta').removeClass('active');
    $('.metadata').hide();
  },

  right: function() {
    var view = $('.toggle.active').attr('data-view');
    if (view === 'preview') return;
    if (view === 'compose') return this.toggleView('preview');
    return this.toggleView('compose');
  },

  left: function() {
    var view = $('.toggle.active').attr('data-view');
    if (view === 'cheatsheet') return;
    if (view === 'compose') return this.toggleView('cheatsheet');
    return this.toggleView('compose');
  },

  initialize: function() {
    this.dmp = new diff_match_patch();
    this.mode = "edit";
    this.prevContent = this.serialize();
    if (!window.shortcutsRegistered) {
      key('⌘+s, ctrl+s', _.bind(function() { this.updateFile(); return false; }, this));
      key('ctrl+shift+right', _.bind(function() { this.right(); return false; }, this));
      key('ctrl+shift+left', _.bind(function() { this.left(); return false; }, this));
      key('esc', _.bind(function() { this.toggleView('compose'); return false; }, this));
      window.shortcutsRegistered = true;
    }
  },

  // TODO: We might not wanna use this
  parseMetadata: function(metadata) {
    var metadata = this.metadataEditor.getValue();
    if (!metadata) return {};
    try {
      return jsyaml.load(metadata);
    } catch(err) {
      return null;
    }
  },

  updateMetaData: function() {
    if (!this.model.jekyll) return true; // metadata -> skip

    // Update published
    function updatePublished(yamlStr, published) {
      var regex = /published: (false|true)/;
      if (yamlStr.match(regex)) {
        return yamlStr.replace(regex, "published: " + !!published);
      } else {
        return yamlStr + "\npublished: " + !!published;
      }
    }

    this.model.raw_metadata = this.metadataEditor.getValue();
    var published = this.$('#post_published').prop('checked');

    this.model.raw_metadata = updatePublished(this.model.raw_metadata, published);
    this.metadataEditor.setValue(this.model.raw_metadata);

    published ? $('#post').addClass('published') : $('#post').removeClass('published');

    return true;
  },

  updateFilename: function(filepath, cb) {
    var that = this;

    if (!_.validPathname(filepath)) return cb('error');
    app.state.path = this.model.path; // ?
    app.state.file = _.extractFilename(filepath)[1];
    app.state.path = _.extractFilename(filepath)[0];

    function finish() {
      that.model.path = app.state.path;
      that.model.file = app.state.file;
      // rerender header to reflect the filename change
      app.instance.header.render();
      that.updateURL();
    }

    if (this.model.persisted) {
      movePost(app.state.user, app.state.repo, app.state.branch, _.filepath(this.model.path, this.model.file), filepath, _.bind(function(err) {
        if (!err) finish()
        err ? cb('error') : cb(null)
      }, this));
    } else {
      finish();
      cb(null);
    }
  },

  serialize: function() {
    return serialize(this.model.content, this.model.jekyll ? this.model.raw_metadata : null)
  },

  updateFile: function() {
    var filepath = $('input.filepath').val();

    var file = _.extractFilename(filepath)[1];
    var that = this;
    var message = this.model.persisted ? "Updated " + filepath : "Created " + filepath;

    // Update content
    this.model.content = this.editor.getValue();

    // File contents
    var filecontent = this.serialize();;

    function updateState(label, classes) {
      $('.button.save').html(label)
                       .removeClass('inactive error saving')
                       .addClass(classes);
    }

    function save() {
      if (that.updateMetaData()) {
        saveFile(app.state.user, app.state.repo, app.state.branch, filepath, filecontent, message, function(err) {
          if (err) {
            _.delay(function() { that._makeDirty() }, 3000);
            updateState('! Try again in 30 seconds', 'error');
            return;
          }
          that.dirty = false;
          that.model.persisted = true;
          that.model.file = file;
          that.updateURL();
          that.prevContent = filecontent;
          updateState('SAVED', 'inactive');
        });
      } else {
        updateState('! Metadata', 'error');
      }
    }

    updateState('SAVING ...', 'inactive saving');

    if (filepath === _.filepath(this.model.path, this.model.file)) return save();

    // Move or create file
    this.updateFilename(filepath, function(err) {
      err ? updateState('! Filename', 'error') : save();
    });
  },

  keyMap: function() {
    var that = this;
    return {
      "Shift-Ctrl-Left": function(codemirror) {
        that.left();
      },
      "Shift-Ctrl-Right": function(codemirror) {
        that.right();
      },
      "Shift-Ctrl-M": function(codemirror) {
        that._toggleMeta();
      },
      "Ctrl-S": function(codemirror) {
        that.updateFile();
      }
    };
  },

  initEditor: function() {
    var that = this;
    setTimeout(function() {
      if (that.model.jekyll) {
        that.metadataEditor = CodeMirror($('#raw_metadata')[0], {
          mode: 'yaml',
          value: that.model.raw_metadata,
          theme: 'prose-dark',
          lineWrapping: true,
          lineNumbers: true,
          extraKeys: that.keyMap(),
          onChange: _.bind(that._makeDirty, that)
        });
        $('#post .metadata').hide();
      }
      that.editor = CodeMirror($('#code')[0], {
        mode: that.model.lang,
        value: that.model.content,
        lineWrapping: true,
        lineNumbers: true,
        extraKeys: that.keyMap(),
        matchBrackets: true,
        theme: 'prose-bright',
        onChange: _.bind(that._makeDirty, that)
      });
      that.refreshCodeMirror();
    }, 100);
  },

  render: function() {
    var that = this;
    $(this.el).html(templates.post(_.extend(this.model, { mode: this.mode })));
    if (this.model.published) $(this.el).addClass('published');
    this.initEditor();
    return this;
  }
});

}).apply(this, window.args);
<|endoftext|>