'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

(function (global) {

    var MessagesConsumerMixin = {
        contextTypes: {
            messages: React.PropTypes.object,
            getMessage: React.PropTypes.func
        }
    };

    /******************************/
    /* REACT DND GENERIC COMPONENTS
    /******************************/
    var Types = {
        NODE_PROVIDER: 'node',
        SORTABLE_LIST_ITEM: 'sortable-list-item'
    };

    /**
     * Specifies which props to inject into your component.
     */
    function collect(connect, monitor) {
        return {
            connectDragSource: connect.dragSource(),
            isDragging: monitor.isDragging()
        };
    }

    function collectDrop(connect, monitor) {
        return {
            connectDropTarget: connect.dropTarget(),
            canDrop: monitor.canDrop(),
            isOver: monitor.isOver(),
            isOverCurrent: monitor.isOver({ shallow: true })
        };
    }

    /***********************/
    /* REACT DND SORTABLE LIST
     /***********************/
    /**
     * Specifies the drag source contract.
     * Only `beginDrag` function is required.
     */
    var sortableItemSource = {
        beginDrag: function beginDrag(props) {
            // Return the data describing the dragged item
            return { id: props.id };
        },
        endDrag: function endDrag(props) {
            props.endSwitching();
        }
    };

    var sortableItemTarget = {

        hover: function hover(props, monitor) {
            var draggedId = monitor.getItem().id;
            if (draggedId !== props.id) {
                props.switchItems(draggedId, props.id);
            }
        }

    };

    var sortableItem = React.createClass({
        displayName: 'sortableItem',

        propTypes: {
            connectDragSource: React.PropTypes.func.isRequired,
            connectDropTarget: React.PropTypes.func.isRequired,
            isDragging: React.PropTypes.bool.isRequired,
            id: React.PropTypes.any.isRequired,
            label: React.PropTypes.string.isRequired,
            switchItems: React.PropTypes.func.isRequired,
            removable: React.PropTypes.bool,
            onRemove: React.PropTypes.func
        },

        removeClicked: function removeClicked() {
            this.props.onRemove(this.props.id);
        },

        render: function render() {
            // Your component receives its own props as usual
            var id = this.props.id;

            // These two props are injected by React DnD,
            // as defined by your `collect` function above:
            var isDragging = this.props.isDragging;
            var connectDragSource = this.props.connectDragSource;
            var connectDropTarget = this.props.connectDropTarget;

            var remove;
            if (this.props.removable) {
                remove = React.createElement('span', { className: 'button mdi mdi-close', onClick: this.removeClicked });
            }
            return connectDragSource(connectDropTarget(React.createElement(
                ReactMUI.Paper,
                { zDepth: 1, style: { opacity: isDragging ? 0 : 1 } },
                React.createElement(
                    'div',
                    { className: this.props.className },
                    this.props.label,
                    remove
                )
            )));
        }
    });

    var NonDraggableListItem = React.createClass({
        displayName: 'NonDraggableListItem',

        render: function render() {
            var remove;
            if (this.props.removable) {
                remove = React.createElement('span', { className: 'button mdi mdi-close', onClick: this.removeClicked });
            }
            return React.createElement(
                ReactMUI.Paper,
                { zDepth: 1 },
                React.createElement(
                    'div',
                    { className: this.props.className },
                    this.props.label,
                    remove
                )
            );
        }
    });

    var DraggableListItem;
    if (global.ReactDND) {
        DraggableListItem = ReactDND.flow(ReactDND.DragSource(Types.SORTABLE_LIST_ITEM, sortableItemSource, collect), ReactDND.DropTarget(Types.SORTABLE_LIST_ITEM, sortableItemTarget, collectDrop))(sortableItem);
    } else {
        DraggableListItem = NonDraggableListItem;
    }

    var SortableList = React.createClass({
        displayName: 'SortableList',

        propTypes: {
            values: React.PropTypes.array.isRequired,
            onOrderUpdated: React.PropTypes.func,
            removable: React.PropTypes.bool,
            onRemove: React.PropTypes.func,
            className: React.PropTypes.string,
            itemClassName: React.PropTypes.string
        },

        getInitialState: function getInitialState() {
            return { values: this.props.values };
        },
        componentWillReceiveProps: function componentWillReceiveProps(props) {
            this.setState({ values: props.values, switchData: null });
        },

        findItemIndex: function findItemIndex(itemId, data) {
            for (var i = 0; i < data.length; i++) {
                if (data[i]['payload'] == itemId) {
                    return i;
                }
            }
        },

        switchItems: function switchItems(oldId, newId) {
            var oldIndex = this.findItemIndex(oldId, this.state.values);
            var oldItem = this.state.values[oldIndex];
            var newIndex = this.findItemIndex(newId, this.state.values);
            var newItem = this.state.values[newIndex];

            var currentValues = this.state.values.slice();
            currentValues[oldIndex] = newItem;
            currentValues[newIndex] = oldItem;

            // Check that it did not come back to original state
            var oldPrevious = this.findItemIndex(oldId, this.props.values);
            var newPrevious = this.findItemIndex(newId, this.props.values);
            if (oldPrevious == newIndex && newPrevious == oldIndex) {
                this.setState({ values: currentValues, switchData: null });
                //console.log("no more moves");
            } else {
                    this.setState({ values: currentValues, switchData: { oldId: oldId, newId: newId } });
                    //console.log({oldId:oldIndex, newId:newIndex});
                }
        },

        endSwitching: function endSwitching() {
            if (this.state.switchData) {
                // Check that it did not come back to original state
                if (this.props.onOrderUpdated) {
                    this.props.onOrderUpdated(this.state.switchData.oldId, this.state.switchData.newId, this.state.values);
                }
            }
            this.setState({ switchData: null });
        },

        render: function render() {
            var switchItems = this.switchItems;
            return React.createElement(
                'div',
                { className: this.props.className },
                this.state.values.map((function (item) {
                    return React.createElement(DraggableListItem, {
                        id: item.payload,
                        key: item.payload,
                        label: item.text,
                        switchItems: switchItems,
                        endSwitching: this.endSwitching,
                        removable: this.props.removable,
                        onRemove: this.props.onRemove,
                        className: this.props.itemClassName
                    });
                }).bind(this))
            );
        }
    });

    /****************************/
    /* REACT DND DRAG/DROP NODES
     /***************************/

    var nodeDragSource = {
        beginDrag: function beginDrag(props) {
            // Return the data describing the dragged item
            return { node: props.node };
        },

        endDrag: function endDrag(props, monitor, component) {
            if (!monitor.didDrop()) {
                return;
            }
            var item = monitor.getItem();
            var dropResult = monitor.getDropResult();
            var dnd = pydio.Controller.defaultActions.get("dragndrop");
            if (dnd) {
                var dndAction = pydio.Controller.getActionByName(dnd);
                // Make sure to enable
                dndAction.enable();
                dndAction.apply([item.node, dropResult.node]);
            }
        }
    };

    var nodeDropTarget = {

        hover: function hover(props, monitor) {},

        canDrop: function canDrop(props, monitor) {

            var source = monitor.getItem().node;
            var target = props.node;

            var dnd = pydio.Controller.defaultActions.get("dragndrop");
            if (dnd) {
                var dndAction = pydio.Controller.getActionByName(dnd);
                // Make sure to enable
                dndAction.enable();
                // Manually apply, do not use action.apply(), as it will
                // catch the exception we are trying to detect.
                global.actionArguments = [source, target, "canDrop"];
                try {
                    eval(dndAction.options.callbackCode);
                } catch (e) {
                    return false;
                }
                return true;
            }
            return false;
        },

        drop: function drop(props, monitor) {
            var hasDroppedOnChild = monitor.didDrop();
            if (hasDroppedOnChild) {
                return;
            }
            return { node: props.node };
        }

    };

    /*******************/
    /* MISC COMPONENTS */
    /*******************/

    /**
     * Tree Node
     */
    var SimpleTreeNode = React.createClass({
        displayName: 'SimpleTreeNode',

        propTypes: {
            collapse: React.PropTypes.bool,
            forceExpand: React.PropTypes.bool,
            childrenOnly: React.PropTypes.bool,
            depth: React.PropTypes.number,
            onNodeSelect: React.PropTypes.func,
            node: React.PropTypes.instanceOf(AjxpNode),
            dataModel: React.PropTypes.instanceOf(PydioDataModel),
            forceLabel: React.PropTypes.string,
            // Optional checkboxes
            checkboxes: React.PropTypes.array,
            checkboxesValues: React.PropTypes.object,
            checkboxesComputeStatus: React.PropTypes.func,
            onCheckboxCheck: React.PropTypes.func
        },

        getDefaultProps: function getDefaultProps() {
            return {
                collapse: false,
                childrenOnly: false,
                depth: 0,
                onNodeSelect: function onNodeSelect(node) {}
            };
        },

        componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
            var oldNode = this.props.node;
            var newNode = nextProps.node;
            if (newNode == oldNode && newNode.getMetadata().get("paginationData")) {
                var remapedChildren = this.state.children.map(function (c) {
                    c.setParent(newNode);return c;
                });
                var remapedPathes = this.state.children.map(function (c) {
                    return c.getPath();
                });
                var newChildren = this._nodeToChildren(newNode);
                newChildren.forEach(function (nc) {
                    if (remapedPathes.indexOf(nc.getPath()) === -1) {
                        remapedChildren.push(nc);
                    }
                });
                this.setState({ children: remapedChildren });
            } else {
                this.setState({ children: this._nodeToChildren(newNode) });
            }
        },

        getInitialState: function getInitialState() {
            return {
                showChildren: !this.props.collapse || this.props.forceExpand,
                children: this._nodeToChildren(this.props.node)
            };
        },

        _nodeToChildren: function _nodeToChildren() {
            var children = [];
            this.props.node.getChildren().forEach(function (c) {
                if (!c.isLeaf()) children.push(c);
            });
            return children;
        },

        onNodeSelect: function onNodeSelect(ev) {
            if (this.props.onNodeSelect) {
                this.props.onNodeSelect(this.props.node);
            }
            ev.preventDefault();
            ev.stopPropagation();
        },
        onChildDisplayToggle: function onChildDisplayToggle(ev) {
            if (this.props.node.getChildren().size) {
                this.setState({ showChildren: !this.state.showChildren });
            }
            ev.preventDefault();
            ev.stopPropagation();
        },
        render: function render() {
            var hasFolderChildrens = this.state.children.length ? true : false;
            var hasChildren = hasFolderChildrens ? React.createElement(
                'span',
                { onClick: this.onChildDisplayToggle },
                this.state.showChildren || this.props.forceExpand ? React.createElement('span', { className: 'tree-icon icon-angle-down' }) : React.createElement('span', { className: 'tree-icon icon-angle-right' })
            ) : React.createElement('span', { className: 'tree-icon icon-angle-right' });
            var isSelected = this.props.dataModel.getSelectedNodes().indexOf(this.props.node) !== -1 ? 'mui-menu-item mui-is-selected' : 'mui-menu-item';
            var selfLabel;
            if (!this.props.childrenOnly) {
                if (this.props.canDrop && this.props.isOverCurrent) {
                    isSelected += ' droppable-active';
                }
                var boxes;
                if (this.props.checkboxes) {
                    var values = {},
                        inherited = false,
                        disabled = {},
                        additionalClassName = '';
                    if (this.props.checkboxesComputeStatus) {
                        var status = this.props.checkboxesComputeStatus(this.props.node);
                        values = status.VALUES;
                        inherited = status.INHERITED;
                        disabled = status.DISABLED;
                        if (status.CLASSNAME) additionalClassName = ' ' + status.CLASSNAME;
                    } else if (this.props.checkboxesValues && this.props.checkboxesValues[this.props.node.getPath()]) {
                        values = this.props.checkboxesValues[this.props.node.getPath()];
                    }
                    var valueClasses = [];
                    boxes = this.props.checkboxes.map((function (c) {
                        var selected = values[c] !== undefined ? values[c] : false;
                        var click = (function (event, value) {
                            this.props.onCheckboxCheck(this.props.node, c, value);
                        }).bind(this);
                        if (selected) valueClasses.push(c);
                        return React.createElement(ReactMUI.Checkbox, {
                            name: c,
                            key: c + "-" + (selected ? "true" : "false"),
                            checked: selected,
                            onCheck: click,
                            disabled: disabled[c],
                            className: "cbox-" + c
                        });
                    }).bind(this));
                    isSelected += inherited ? " inherited " : "";
                    isSelected += valueClasses.length ? " checkbox-values-" + valueClasses.join('-') : " checkbox-values-empty";
                    boxes = React.createElement(
                        'div',
                        { className: "tree-checkboxes" + additionalClassName },
                        boxes
                    );
                }
                selfLabel = React.createElement(
                    'div',
                    { className: 'tree-item ' + isSelected + (boxes ? ' has-checkboxes' : ''), style: { paddingLeft: this.props.depth * 20 } },
                    React.createElement(
                        'div',
                        { className: 'tree-item-label', onClick: this.onNodeSelect, title: this.props.node.getLabel(),
                            'data-id': this.props.node.getPath() },
                        hasChildren,
                        ' ',
                        React.createElement('span', { className: 'tree-icon icon-folder-close' }),
                        ' ',
                        this.props.forceLabel ? this.props.forceLabel : this.props.node.getLabel()
                    ),
                    boxes
                );
            }

            var children = [];
            if (this.state.showChildren || this.props.forceExpand) {
                children = this.state.children.map((function (child) {
                    return React.createElement(DragDropTreeNode, {
                        childrenOnly: false,
                        forceExpand: this.props.forceExpand,
                        key: child.getPath(),
                        dataModel: this.props.dataModel,
                        node: child,
                        onNodeSelect: this.props.onNodeSelect,
                        collapse: this.props.collapse,
                        depth: this.props.depth + 1,
                        checkboxes: this.props.checkboxes,
                        checkboxesValues: this.props.checkboxesValues,
                        checkboxesComputeStatus: this.props.checkboxesComputeStatus,
                        onCheckboxCheck: this.props.onCheckboxCheck
                    });
                }).bind(this));
            }
            return React.createElement(
                'li',
                { ref: 'node', className: "treenode" + this.props.node.getPath().replace(/\//g, '_') },
                selfLabel,
                React.createElement(
                    'ul',
                    null,
                    children
                )
            );
        }
    });

    var WrappedTreeNode = React.createClass({
        displayName: 'WrappedTreeNode',

        propTypes: {
            connectDragSource: React.PropTypes.func.isRequired,
            connectDropTarget: React.PropTypes.func.isRequired,
            isDragging: React.PropTypes.bool.isRequired,
            isOver: React.PropTypes.bool.isRequired,
            canDrop: React.PropTypes.bool.isRequired
        },

        render: function render() {
            var connectDragSource = this.props.connectDragSource;
            var connectDropTarget = this.props.connectDropTarget;

            return connectDragSource(connectDropTarget(React.createElement(SimpleTreeNode, this.props)));
        }
    });

    var DragDropTreeNode;
    if (global.ReactDND) {
        DragDropTreeNode = ReactDND.flow(ReactDND.DragSource(Types.NODE_PROVIDER, nodeDragSource, collect), ReactDND.DropTarget(Types.NODE_PROVIDER, nodeDropTarget, collectDrop))(WrappedTreeNode);
    } else {
        DragDropTreeNode = SimpleTreeNode;
    }

    /**
     * Simple openable / loadable tree taking AjxpNode as inputs
     */
    var SimpleTree = React.createClass({
        displayName: 'SimpleTree',

        propTypes: {
            showRoot: React.PropTypes.bool,
            rootLabel: React.PropTypes.string,
            onNodeSelect: React.PropTypes.func,
            node: React.PropTypes.instanceOf(AjxpNode).isRequired,
            dataModel: React.PropTypes.instanceOf(PydioDataModel).isRequired,
            selectable: React.PropTypes.bool,
            selectableMultiple: React.PropTypes.bool,
            initialSelectionModel: React.PropTypes.array,
            onSelectionChange: React.PropTypes.func,
            forceExpand: React.PropTypes.bool,
            // Optional checkboxes
            checkboxes: React.PropTypes.array,
            checkboxesValues: React.PropTypes.object,
            checkboxesComputeStatus: React.PropTypes.func,
            onCheckboxCheck: React.PropTypes.func
        },

        getDefaultProps: function getDefaultProps() {
            return {
                showRoot: true,
                onNodeSelect: this.onNodeSelect
            };
        },

        onNodeSelect: function onNodeSelect(node) {
            this.props.dataModel.setSelectedNodes([node]);
        },

        render: function render() {
            return React.createElement(
                'ul',
                { className: this.props.className },
                React.createElement(DragDropTreeNode, {
                    childrenOnly: !this.props.showRoot,
                    forceExpand: this.props.forceExpand,
                    node: this.props.node ? this.props.node : this.props.dataModel.getRootNode(),
                    dataModel: this.props.dataModel,
                    onNodeSelect: this.onNodeSelect,
                    forceLabel: this.props.rootLabel,
                    checkboxes: this.props.checkboxes,
                    checkboxesValues: this.props.checkboxesValues,
                    checkboxesComputeStatus: this.props.checkboxesComputeStatus,
                    onCheckboxCheck: this.props.onCheckboxCheck
                })
            );
        }
    });

    /**
     * Simple MuiPaper with a figure and a legend
     */
    var SimpleFigureBadge = React.createClass({
        displayName: 'SimpleFigureBadge',

        propTypes: {
            colorIndicator: React.PropTypes.string,
            figure: React.PropTypes.number.isRequired,
            legend: React.PropTypes.string
        },

        getDefaultProps: function getDefaultProps() {
            return {
                colorIndicator: ''
            };
        },

        render: function render() {
            return React.createElement(
                ReactMUI.Paper,
                { style: { display: 'inline-block', marginLeft: 16 } },
                React.createElement(
                    'div',
                    { className: 'figure-badge', style: this.props.colorIndicator ? { borderLeftColor: this.props.colorIndicator } : {} },
                    React.createElement(
                        'div',
                        { className: 'figure' },
                        this.props.figure
                    ),
                    React.createElement(
                        'div',
                        { className: 'legend' },
                        this.props.legend
                    )
                )
            );
        }
    });

    /**
     * Search input building a set of query parameters and calling
     * the callbacks to display / hide results
     */
    var SearchBox = React.createClass({
        displayName: 'SearchBox',

        propTypes: {
            // Required
            parameters: React.PropTypes.object.isRequired,
            queryParameterName: React.PropTypes.string.isRequired,
            // Other
            textLabel: React.PropTypes.string,
            displayResults: React.PropTypes.func,
            hideResults: React.PropTypes.func,
            displayResultsState: React.PropTypes.bool,
            limit: React.PropTypes.number
        },

        getInitialState: function getInitialState() {
            return {
                displayResult: this.props.displayResultsState ? true : false
            };
        },

        getDefaultProps: function getDefaultProps() {
            var dm = new PydioDataModel();
            dm.setRootNode(new AjxpNode());
            return { dataModel: dm };
        },

        displayResultsState: function displayResultsState() {
            this.setState({
                displayResult: true
            });
        },

        hideResultsState: function hideResultsState() {
            this.setState({
                displayResult: false
            });
            this.props.hideResults();
        },

        onClickSearch: function onClickSearch() {
            var value = this.refs.query.getValue();
            var dm = this.props.dataModel;
            var params = this.props.parameters;
            params[this.props.queryParameterName] = value;
            params['limit'] = this.props.limit || 100;
            dm.getRootNode().setChildren([]);
            PydioApi.getClient().request(params, (function (transport) {
                var remoteNodeProvider = new RemoteNodeProvider({});
                remoteNodeProvider.parseNodes(dm.getRootNode(), transport);
                dm.getRootNode().setLoaded(true);
                this.displayResultsState();
                this.props.displayResults(value, dm);
            }).bind(this));
        },

        keyDown: function keyDown(event) {
            if (event.key == 'Enter') {
                this.onClickSearch();
            }
        },

        render: function render() {
            return React.createElement(
                'div',
                { className: this.props.className ? this.props.className : '' },
                React.createElement(
                    'div',
                    { style: { paddingTop: 22, float: 'right', opacity: 0.3 } },
                    React.createElement(ReactMUI.IconButton, {
                        ref: 'button',
                        onClick: this.onClickSearch,
                        iconClassName: 'icon-search',
                        tooltip: 'Search'
                    })
                ),
                React.createElement(
                    'div',
                    { className: 'searchbox-input-fill', style: { width: 220, float: 'right' } },
                    React.createElement(ReactMUI.TextField, { ref: 'query', onKeyDown: this.keyDown, floatingLabelText: this.props.textLabel })
                )
            );
        }

    });

    var LabelWithTip = React.createClass({
        displayName: 'LabelWithTip',

        propTypes: {
            label: React.PropTypes.string,
            tooltip: React.PropTypes.string,
            tooltipClassName: React.PropTypes.string,
            className: React.PropTypes.string,
            style: React.PropTypes.object
        },

        getInitialState: function getInitialState() {
            return { show: false };
        },

        show: function show() {
            this.setState({ show: true });
        },
        hide: function hide() {
            this.setState({ show: false });
        },

        render: function render() {
            if (this.props.tooltip) {
                var tooltipStyle = {};
                if (this.props.label) {
                    if (this.state.show) {
                        tooltipStyle = { bottom: -10, top: 'inherit' };
                    }
                } else {
                    tooltipStyle = { position: 'relative' };
                }
                var label;
                if (this.props.label) {
                    label = React.createElement(
                        'span',
                        { className: 'ellipsis-label' },
                        this.props.label
                    );
                }
                var style = this.props.style || { position: 'relative' };

                return React.createElement(
                    'span',
                    { onMouseEnter: this.show, onMouseLeave: this.hide, style: style, className: this.props.className },
                    label,
                    this.props.children,
                    React.createElement(ReactMUI.Tooltip, { label: this.props.tooltip, style: tooltipStyle, className: this.props.tooltipClassName, show: this.state.show })
                );
            } else {
                if (this.props.label) return React.createElement(
                    'span',
                    null,
                    this.props.label
                );else return React.createElement(
                    'span',
                    null,
                    'this.props.children'
                );
            }
        }

    });

    /**
     * Get info from Pydio controller an build an
     * action bar with active actions.
     * TBC
     */
    var SimpleReactActionBar = React.createClass({
        displayName: 'SimpleReactActionBar',

        propTypes: {
            dataModel: React.PropTypes.instanceOf(PydioDataModel).isRequired,
            node: React.PropTypes.instanceOf(AjxpNode).isRequired,
            actions: React.PropTypes.array
        },

        clickAction: function clickAction(event) {
            var actionName = event.currentTarget.getAttribute("data-action");
            this.props.dataModel.setSelectedNodes([this.props.node]);
            var a = global.pydio.Controller.getActionByName(actionName);
            a.fireContextChange(this.props.dataModel, true, global.pydio.user);
            //a.fireSelectionChange(this.props.dataModel);
            a.apply([this.props.dataModel]);
            event.stopPropagation();
            event.preventDefault();
        },

        render: function render() {
            var actions = this.props.actions.map((function (a) {
                return React.createElement('div', {
                    key: a.options.name,
                    className: a.options.icon_class + ' material-list-action-inline' || '',
                    title: a.options.title,
                    'data-action': a.options.name,
                    onClick: this.clickAction });
            }).bind(this));
            return React.createElement(
                'span',
                null,
                actions
            );
        }
    });

    /*******************/
    /* GENERIC EDITORS */
    /*******************/

    var LegacyUIWrapper = React.createClass({
        displayName: 'LegacyUIWrapper',

        propTypes: {
            componentName: React.PropTypes.string.isRequired,
            componentOptions: React.PropTypes.object,
            onLoadCallback: React.PropTypes.func
        },

        componentDidMount: function componentDidMount() {
            if (window[this.props.componentName]) {
                var element = this.refs.wrapper.getDOMNode();
                var options = this.props.componentOptions;
                this.legacyComponent = new window[this.props.componentName](element, options);
                if (this.props.onLoadCallback) {
                    this.props.onLoadCallback(this.legacyComponent);
                }
            }
        },

        componentWillUnmount: function componentWillUnmount() {
            if (this.legacyComponent) {
                this.legacyComponent.destroy();
            }
        },

        shouldComponentUpdate: function shouldComponentUpdate() {
            // Let's just never update this component again.
            return false;
        },

        render: function render() {
            return React.createElement('div', { id: this.props.id, className: this.props.className, style: this.props.style, ref: 'wrapper' });
        }
    });
    /**
     * Opens an oldschool Pydio editor in React context, based on node mime type.
     * @type {*|Function}
     */
    var ReactEditorOpener = React.createClass({
        displayName: 'ReactEditorOpener',

        propTypes: {
            node: React.PropTypes.instanceOf(AjxpNode),
            registry: React.PropTypes.instanceOf(Registry).isRequired,
            closeEditorContainer: React.PropTypes.func,
            registerCloseCallback: React.PropTypes.func
        },

        getInitialState: function getInitialState() {
            return { editorData: null };
        },

        _getEditorData: function _getEditorData(node) {
            var selectedMime = getAjxpMimeType(node);
            var editors = this.props.registry.findEditorsForMime(selectedMime, false);
            if (editors.length && editors[0].openable) {
                return editors[0];
            }
        },

        closeEditor: function closeEditor() {
            if (this.editor) {
                var el = this.editor.element;
                this.editor.destroy();
                el.remove();
                this.editor = null;
            }
            if (this.props.closeEditorContainer() !== false) {
                this.setState({ editorData: null, node: null });
            }
        },

        loadEditor: function loadEditor(node) {
            if (this.editor) {
                this.closeEditor();
            }
            var editorData = this._getEditorData(node);
            if (editorData) {
                this.props.registry.loadEditorResources(editorData.resourcesManager);
                this.setState({ editorData: editorData, node: node });
            } else {
                this.setState({ editorData: null, node: null });
            }
        },

        componentDidMount: function componentDidMount() {
            if (this.props.node) this.loadEditor(this.props.node);
        },

        componentWillReceiveProps: function componentWillReceiveProps(newProps) {
            if (newProps.node) this.loadEditor(newProps.node);
        },

        componentDidUpdate: function componentDidUpdate() {
            if (this.editor) {
                this.editor.destroy();
                this.editor = null;
            }
            if (this.state.editorData && this.state.editorData.formId && this.props.node) {
                var editorElement = $(this.refs.editor.getDOMNode()).down('#' + this.state.editorData.formId);
                if (editorElement) {
                    var editorOptions = {
                        closable: false,
                        context: this,
                        editorData: this.state.editorData
                    };
                    this.editor = new global[editorOptions.editorData['editorClass']](editorElement, editorOptions);
                    this.editor.open(this.props.node);
                    fitHeightToBottom(editorElement);
                }
            }
        },

        componentWillUnmount: function componentWillUnmount() {
            if (this.editor) {
                this.editor.destroy();
                this.editor = null;
            }
        },

        render: function render() {
            var editor;
            if (this.state.editorData) {
                if (this.state.editorData.formId) {
                    var content = (function () {
                        if (this.state && this.state.editorData && $(this.state.editorData.formId)) {
                            return { __html: $(this.state.editorData.formId).outerHTML };
                        } else {
                            return { __html: '' };
                        }
                    }).bind(this);
                    editor = React.createElement('div', { ref: 'editor', style: { height: "100vh" }, id: 'editor', key: this.state && this.props.node ? this.props.node.getPath() : null, dangerouslySetInnerHTML: content() });
                } else if (global[this.state.editorData.editorClass]) {
                    editor = React.createElement(global[this.state.editorData.editorClass], {
                        node: this.props.node,
                        closeEditor: this.closeEditor,
                        registerCloseCallback: this.props.registerCloseCallback
                    });
                }
            }
            return editor || null;
        }
    });

    /**
     * Two columns layout used for Workspaces and Plugins editors
     */
    var PaperEditorLayout = React.createClass({
        displayName: 'PaperEditorLayout',

        propTypes: {
            title: React.PropTypes.any,
            titleActionBar: React.PropTypes.any,
            leftNav: React.PropTypes.any,
            contentFill: React.PropTypes.bool,
            className: React.PropTypes.string
        },

        toggleMenu: function toggleMenu() {
            var crtLeftOpen = this.state && this.state.forceLeftOpen;
            this.setState({ forceLeftOpen: !crtLeftOpen });
        },

        render: function render() {
            return React.createElement(
                'div',
                { className: "paper-editor-content layout-fill vertical-layout" + (this.props.className ? ' ' + this.props.className : '') },
                React.createElement(
                    'div',
                    { className: 'paper-editor-title' },
                    React.createElement(
                        'h2',
                        null,
                        this.props.title,
                        ' ',
                        React.createElement(
                            'div',
                            { className: 'left-picker-toggle' },
                            React.createElement(ReactMUI.IconButton, { iconClassName: 'icon-caret-down', onClick: this.toggleMenu })
                        )
                    ),
                    React.createElement(
                        'div',
                        { className: 'title-bar' },
                        this.props.titleActionBar
                    )
                ),
                React.createElement(
                    'div',
                    { className: 'layout-fill main-layout-nav-to-stack' },
                    React.createElement(
                        'div',
                        { className: "paper-editor-left" + (this.state && this.state.forceLeftOpen ? ' picker-open' : ''), onClick: this.toggleMenu },
                        this.props.leftNav
                    ),
                    React.createElement(
                        'div',
                        { className: "layout-fill paper-editor-right" + (this.props.contentFill ? ' vertical-layout' : ''), style: this.props.contentFill ? {} : { overflowY: 'auto' } },
                        this.props.children
                    )
                )
            );
        }
    });
    /**
     * Navigation subheader used by PaperEditorLayout
     */
    var PaperEditorNavHeader = React.createClass({
        displayName: 'PaperEditorNavHeader',

        propTypes: {
            label: React.PropTypes.string
        },

        render: function render() {

            return React.createElement(
                'div',
                { className: 'mui-subheader' },
                this.props.children,
                this.props.label
            );
        }

    });
    /**
     * Navigation entry used by PaperEditorLayout.
     */
    var PaperEditorNavEntry = React.createClass({
        displayName: 'PaperEditorNavEntry',

        propTypes: {
            keyName: React.PropTypes.string.isRequired,
            onClick: React.PropTypes.func.isRequired,
            label: React.PropTypes.string,
            selectedKey: React.PropTypes.string,
            isLast: React.PropTypes.bool,
            // Drop Down Data
            dropDown: React.PropTypes.bool,
            dropDownData: React.PropTypes.object,
            dropDownChange: React.PropTypes.func,
            dropDownDefaultItems: React.PropTypes.array
        },

        onClick: function onClick() {
            this.props.onClick(this.props.keyName);
        },

        captureDropDownClick: function captureDropDownClick() {
            if (this.preventClick) {
                this.preventClick = false;
                return;
            }
            this.props.onClick(this.props.keyName);
        },

        dropDownChange: function dropDownChange(event, index, item) {
            this.preventClick = true;
            this.props.dropDownChange(item);
        },

        render: function render() {

            if (!this.props.dropDown || !this.props.dropDownData) {
                return React.createElement(
                    'div',
                    {
                        className: 'menu-entry' + (this.props.keyName == this.props.selectedKey ? ' menu-entry-selected' : '') + (this.props.isLast ? ' last' : ''),
                        onClick: this.onClick },
                    this.props.children,
                    this.props.label
                );
            }

            // dropDown & dropDownData are loaded
            var menuItemsTpl = [{ text: this.props.label, payload: '-1' }];
            if (this.props.dropDownDefaultItems) {
                menuItemsTpl = menuItemsTpl.concat(this.props.dropDownDefaultItems);
            }
            this.props.dropDownData.forEach(function (v, k) {
                menuItemsTpl.push({ text: v.label, payload: v });
            });
            return React.createElement(
                'div',
                { onClick: this.captureDropDownClick, className: 'menu-entry-dropdown' + (this.props.keyName == this.props.selectedKey ? ' menu-entry-selected' : '') + (this.props.isLast ? ' last' : '') },
                React.createElement(ReactMUI.DropDownMenu, {
                    menuItems: menuItemsTpl,
                    className: 'dropdown-full-width',
                    style: { width: 256 },
                    autoWidth: false,
                    onChange: this.dropDownChange
                })
            );
        }
    });

    /**************************/
    /* GENERIC LIST COMPONENT */
    /**************************/

    /**
     * Pagination component reading metadata "paginationData" from current node.
     */
    var ListPaginator = React.createClass({
        displayName: 'ListPaginator',

        mixins: [MessagesConsumerMixin],

        propTypes: {
            dataModel: React.PropTypes.instanceOf(PydioDataModel).isRequired,
            node: React.PropTypes.instanceOf(AjxpNode).isRequired
        },

        changePage: function changePage(event) {
            this.props.node.getMetadata().get("paginationData").set("new_page", event.currentTarget.getAttribute('data-page'));
            this.props.dataModel.requireContextChange(this.props.node);
        },

        onMenuChange: function onMenuChange(event, index, item) {
            this.props.node.getMetadata().get("paginationData").set("new_page", item.payload);
            this.props.dataModel.requireContextChange(this.props.node);
        },

        render: function render() {
            var pData = this.props.node.getMetadata().get("paginationData");
            var current = parseInt(pData.get("current"));
            var total = parseInt(pData.get("total"));
            var pages = [],
                next,
                last,
                previous,
                first;
            var pageWord = this.context.getMessage('331', '');
            for (var i = 1; i <= total; i++) {
                pages.push({ payload: i, text: pageWord + ' ' + i + (i == current ? ' / ' + total : '') });
            }
            if (!pages.length) {
                return null;
            }
            if (current > 1) previous = React.createElement(ReactMUI.FontIcon, { onClick: this.changePage, 'data-page': current - 1, className: 'icon-angle-left' });
            if (current < total) next = React.createElement(ReactMUI.FontIcon, { onClick: this.changePage, 'data-page': current + 1, className: 'icon-angle-right' });
            return React.createElement(
                'span',
                null,
                first,
                previous,
                React.createElement(ReactMUI.DropDownMenu, { onChange: this.onMenuChange, menuItems: pages, selectedIndex: current - 1 }),
                next,
                last,
                React.createElement(
                    'span',
                    { className: 'mui-toolbar-separator' },
                    ' '
                )
            );
        }

    });

    /**
     * Material List Entry
     */
    var ListEntry = React.createClass({
        displayName: 'ListEntry',

        propTypes: {
            showSelector: React.PropTypes.bool,
            selected: React.PropTypes.bool,
            selectorDisabled: React.PropTypes.bool,
            onSelect: React.PropTypes.func,
            onClick: React.PropTypes.func,
            iconCell: React.PropTypes.element,
            mainIcon: React.PropTypes.string,
            firstLine: React.PropTypes.node,
            secondLine: React.PropTypes.node,
            thirdLine: React.PropTypes.node,
            actions: React.PropTypes.element,
            activeDroppable: React.PropTypes.bool,
            className: React.PropTypes.string
        },

        onClick: function onClick(event) {
            if (this.props.showSelector) {
                if (this.props.selectorDisabled) return;
                this.props.onSelect(this.props.node);
                event.stopPropagation();
                event.preventDefault();
            } else if (this.props.onClick) {
                this.props.onClick(this.props.node);
            }
        },

        render: function render() {
            var selector;
            if (this.props.showSelector) {
                selector = React.createElement(
                    'div',
                    { className: 'material-list-selector' },
                    React.createElement(ReactMUI.Checkbox, { checked: this.props.selected, ref: 'selector', disabled: this.props.selectorDisabled })
                );
            }
            var iconCell;
            if (this.props.iconCell) {
                iconCell = this.props.iconCell;
            } else if (this.props.mainIcon) {
                iconCell = React.createElement(ReactMUI.FontIcon, { className: this.props.mainIcon });
            }
            var additionalClassName = this.props.className ? this.props.className + ' ' : '';
            if (this.props.canDrop && this.props.isOver) {
                additionalClassName += ' droppable-active ';
            }
            if (this.props.node) {
                additionalClassName += ' listentry' + this.props.node.getPath().replace(/\//g, '_') + ' ' + ' ajxp_node_' + (this.props.node.isLeaf() ? 'leaf' : 'collection') + ' ';
            }
            return React.createElement(
                'div',
                { onClick: this.onClick, className: additionalClassName + "material-list-entry material-list-entry-" + (this.props.thirdLine ? 3 : this.props.secondLine ? 2 : 1) + "-lines" + (this.props.selected ? " selected" : "") },
                selector,
                React.createElement(
                    'div',
                    { className: "material-list-icon" + (this.props.mainIcon || iconCell ? "" : " material-list-icon-none") },
                    iconCell
                ),
                React.createElement(
                    'div',
                    { className: 'material-list-text' },
                    React.createElement(
                        'div',
                        { className: 'material-list-line-1' },
                        this.props.firstLine
                    ),
                    React.createElement(
                        'div',
                        { className: 'material-list-line-2' },
                        this.props.secondLine
                    ),
                    React.createElement(
                        'div',
                        { className: 'material-list-line-3' },
                        this.props.thirdLine
                    )
                ),
                React.createElement(
                    'div',
                    { className: 'material-list-actions' },
                    this.props.actions
                )
            );
        }
    });

    var WrappedListEntry = React.createClass({
        displayName: 'WrappedListEntry',

        propTypes: {
            connectDragSource: React.PropTypes.func.isRequired,
            connectDropTarget: React.PropTypes.func.isRequired,
            isDragging: React.PropTypes.bool.isRequired,
            isOver: React.PropTypes.bool.isRequired,
            canDrop: React.PropTypes.bool.isRequired
        },

        render: function render() {
            // These two props are injected by React DnD,
            // as defined by your `collect` function above:
            var isDragging = this.props.isDragging;
            var connectDragSource = this.props.connectDragSource;
            var connectDropTarget = this.props.connectDropTarget;

            return connectDragSource(connectDropTarget(React.createElement(ListEntry, this.props)));
        }
    });

    var DragDropListEntry;
    if (global.ReactDND) {
        var DragDropListEntry = ReactDND.flow(ReactDND.DragSource(Types.NODE_PROVIDER, nodeDragSource, collect), ReactDND.DropTarget(Types.NODE_PROVIDER, nodeDropTarget, collectDrop))(WrappedListEntry);
    } else {
        DragDropListEntry = ListEntry;
    }

    /**
     * Specific header for Table layout, reading metadata from node and using keys
     */
    var TableListHeader = React.createClass({
        displayName: 'TableListHeader',

        mixins: [MessagesConsumerMixin],

        propTypes: {
            tableKeys: React.PropTypes.object.isRequired,
            loading: React.PropTypes.bool,
            reload: React.PropTypes.func
        },

        render: function render() {
            var cells = [];
            for (var key in this.props.tableKeys) {
                if (!this.props.tableKeys.hasOwnProperty(key)) continue;
                var data = this.props.tableKeys[key];
                var style = data['width'] ? { width: data['width'] } : null;
                cells.push(React.createElement(
                    'span',
                    { key: key, className: 'cell header_cell cell-' + key, style: style },
                    data['label']
                ));
            }
            return React.createElement(
                ReactMUI.Toolbar,
                { className: 'toolbarTableHeader' },
                React.createElement(
                    ReactMUI.ToolbarGroup,
                    { float: 'left' },
                    cells
                ),
                React.createElement(
                    ReactMUI.ToolbarGroup,
                    { float: 'right' },
                    React.createElement(ReactMUI.FontIcon, {
                        key: 1,
                        tooltip: this.context.getMessage('149', ''),
                        className: "icon-refresh" + (this.props.loading ? " rotating" : ""),
                        onClick: this.props.reload
                    })
                )
            );
        }
    });

    /**
     * Specific list entry rendered as a table row. Not a real table, CSS used.
     */
    var TableListEntry = React.createClass({
        displayName: 'TableListEntry',

        propTypes: {
            tableKeys: React.PropTypes.object.isRequired,
            renderActions: React.PropTypes.func
        },

        render: function render() {

            var actions = this.props.actions;
            if (this.props.renderActions) {
                actions = this.props.renderActions(this.props.node);
            }

            var cells = [];
            for (var key in this.props.tableKeys) {
                if (!this.props.tableKeys.hasOwnProperty(key)) continue;

                var data = this.props.tableKeys[key];
                var style = data['width'] ? { width: data['width'] } : null;
                var value = this.props.node.getMetadata().get(key);
                cells.push(React.createElement(
                    'span',
                    { key: key, className: 'cell cell-' + key, title: value, style: style, 'data-label': data['label'] },
                    value
                ));
            }

            return React.createElement(ListEntry, _extends({}, this.props, {
                iconCell: null,
                firstLine: cells,
                actions: actions,
                key: 'list-' + this.props.key
            }));
        }

    });

    /**
     * Callback based material list entry with custom icon render, firstLine, secondLine, etc.
     */
    var ConfigurableListEntry = React.createClass({
        displayName: 'ConfigurableListEntry',

        propTypes: {
            // SEE ALSO ListEntry PROPS
            renderIcon: React.PropTypes.func,
            renderFirstLine: React.PropTypes.func,
            renderSecondLine: React.PropTypes.func,
            renderThirdLine: React.PropTypes.func,
            renderActions: React.PropTypes.func
        },

        render: function render() {
            var icon, firstLine, secondLine, thirdLine;
            if (this.props.renderIcon) {
                icon = this.props.renderIcon(this.props.node);
            } else {
                var node = this.props.node;
                var iconClass = node.getMetadata().get("icon_class") ? node.getMetadata().get("icon_class") : node.isLeaf() ? "icon-file-alt" : "icon-folder-close";
                icon = React.createElement(ReactMUI.FontIcon, { className: iconClass });
            }

            if (this.props.renderFirstLine) {
                firstLine = this.props.renderFirstLine(this.props.node);
            } else {
                firstLine = this.props.node.getLabel();
            }

            if (this.props.renderSecondLine) {
                secondLine = this.props.renderSecondLine(this.props.node);
            }
            if (this.props.renderThirdLine) {
                thirdLine = this.props.renderThirdLine(this.props.node);
            }
            var actions = this.props.actions;
            if (this.props.renderActions) {
                actions = this.props.renderActions(this.props.node);
            }

            return React.createElement(DragDropListEntry, _extends({}, this.props, {
                iconCell: icon,
                firstLine: firstLine,
                secondLine: secondLine,
                thirdLine: thirdLine,
                actions: actions,
                key: 'list-' + this.props.key
            }));
        }

    });

    /**
     * Main List component
     */
    var SimpleFlatList = React.createClass({
        displayName: 'SimpleFlatList',

        mixins: [MessagesConsumerMixin],

        propTypes: {
            infiniteSliceCount: React.PropTypes.number,
            filterNodes: React.PropTypes.func,
            customToolbar: React.PropTypes.object,
            tableKeys: React.PropTypes.object,
            autoRefresh: React.PropTypes.number,
            reloadAtCursor: React.PropTypes.bool,
            heightAutoWithMax: React.PropTypes.number,
            observeNodeReload: React.PropTypes.bool,
            groupByFields: React.PropTypes.array,
            defaultGroupBy: React.PropTypes.string,

            entryEnableSelector: React.PropTypes.func,
            entryRenderIcon: React.PropTypes.func,
            entryRenderActions: React.PropTypes.func,
            entryRenderFirstLine: React.PropTypes.func,
            entryRenderSecondLine: React.PropTypes.func,
            entryRenderThirdLine: React.PropTypes.func,

            elementHeight: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.object]).isRequired

        },

        statics: {
            HEIGHT_ONE_LINE: 50,
            HEIGHT_TWO_LINES: 73
        },

        getDefaultProps: function getDefaultProps() {
            return { infiniteSliceCount: 30 };
        },

        clickRow: function clickRow(gridRow) {
            var node;
            if (gridRow.props) {
                node = gridRow.props.data.node;
            } else {
                node = gridRow;
            }
            if (node.isLeaf() && this.props.openEditor) {
                var res = this.props.openEditor(node);
                if (res === false) {
                    return;
                }
                var uniqueSelection = new Map();
                uniqueSelection.set(node, true);
                this.setState({ selection: uniqueSelection }, this.rebuildLoadedElements);
            } else if (!node.isLeaf()) {
                this.props.dataModel.setSelectedNodes([node]);
            }
        },

        getInitialState: function getInitialState() {
            this.actionsCache = { multiple: new Map() };
            this.dm = new PydioDataModel();
            this.dm.setContextNode(this.props.dataModel.getContextNode());
            var state = {
                loaded: this.props.node.isLoaded(),
                loading: !this.props.node.isLoaded(),
                showSelector: false,
                elements: this.props.node.isLoaded() ? this.buildElements(0, this.props.infiniteSliceCount) : [],
                containerHeight: this.props.heightAutoWithMax ? 0 : 500
            };
            if (this.props.defaultGroupBy) {
                state.groupBy = this.props.defaultGroupBy;
            }
            if (this.props.elementHeight instanceof Object) {
                state.elementHeight = this.computeElementHeightResponsive();
            }
            return state;
        },

        componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
            this.indexedElements = null;
            if (nextProps.filterNodes) this.props.filterNodes = nextProps.filterNodes;
            var currentLength = Math.max(this.state.elements.length, this.props.infiniteSliceCount);
            this.setState({
                loaded: nextProps.node.isLoaded(),
                loading: !nextProps.node.isLoaded(),
                showSelector: false,
                elements: nextProps.node.isLoaded() ? this.buildElements(0, currentLength, nextProps.node) : []
            });
            if (!nextProps.autoRefresh && this.refreshInterval) {
                global.clearInterval(this.refreshInterval);
                this.refreshInterval = null;
            } else if (nextProps.autoRefresh && !this.refreshInterval) {
                this.refreshInterval = global.setInterval(this.reload, nextProps.autoRefresh);
            }
            /*
            if(this.props.node === nextProps.node && this.indexedElements && nextProps.node.isLoaded()){
                nextProps.node.getChildren().forEach(function (child) {
                    var nodeActions = this.getActionsForNode(this.dm, child);
                    this.indexedElements.push({node: child, parent: false, actions: nodeActions});
                }.bind(this));
            }
            */
        },

        _loadNodeIfNotLoaded: function _loadNodeIfNotLoaded() {
            var node = this.props.node;
            if (!node.isLoaded()) {
                node.observeOnce("loaded", (function () {
                    if (!this.isMounted()) return;
                    if (this.props.node === node) {
                        this.setState({
                            loaded: true,
                            loading: false,
                            elements: this.buildElements(0, this.props.infiniteSliceCount)
                        });
                    }
                    if (this.props.heightAutoWithMax) {
                        this.updateInfiniteContainerHeight();
                    }
                }).bind(this));
                node.load();
            }
        },

        _loadingListener: function _loadingListener() {
            this.setState({ loaded: false, loading: true });
            this.indexedElements = null;
        },
        _loadedListener: function _loadedListener() {
            var currentLength = Math.max(this.state.elements.length, this.props.infiniteSliceCount);
            this.setState({
                loading: false,
                elements: this.buildElements(0, currentLength, this.props.node)
            });
            if (this.props.heightAutoWithMax) {
                this.updateInfiniteContainerHeight();
            }
        },

        reload: function reload() {
            if (this.props.reloadAtCursor && this._currentCursor) {
                this.loadStartingAtCursor();
                return;
            }
            this._loadingListener();
            this.props.node.observeOnce("loaded", this._loadedListener);
            this.props.node.reload();
        },

        loadStartingAtCursor: function loadStartingAtCursor() {
            this._loadingListener();
            var node = this.props.node;
            var cachedChildren = node.getChildren();
            var newChildren = [];
            node.observeOnce("loaded", (function () {
                var reorderedChildren = new Map();
                newChildren.map(function (c) {
                    reorderedChildren.set(c.getPath(), c);
                });
                cachedChildren.forEach(function (c) {
                    reorderedChildren.set(c.getPath(), c);
                });
                node._children = reorderedChildren;
                this._loadedListener();
            }).bind(this));
            node.setLoaded(false);
            node.observe("child_added", function (newChild) {
                newChildren.push(node._children.get(newChild));
            });
            this.props.node.load(null, { cursor: this._currentCursor });
        },

        wireReloadListeners: function wireReloadListeners() {
            this.wrappedLoading = this._loadingListener;
            this.wrappedLoaded = this._loadedListener;
            this.props.node.observe("loading", this.wrappedLoading);
            this.props.node.observe("loaded", this.wrappedLoaded);
        },
        stopReloadListeners: function stopReloadListeners() {
            this.props.node.stopObserving("loading", this.wrappedLoading);
            this.props.node.stopObserving("loaded", this.wrappedLoaded);
        },

        toggleSelector: function toggleSelector() {
            // Force rebuild elements
            this.setState({
                showSelector: !this.state.showSelector,
                selection: new Map()
            }, this.rebuildLoadedElements);
        },

        toggleSelection: function toggleSelection(node) {
            var selection = this.state.selection || new Map();
            if (selection.get(node)) selection['delete'](node);else selection.set(node, true);
            this.refs.all_selector.setChecked(false);
            this.setState({
                selection: selection
            }, this.rebuildLoadedElements);
        },

        selectAll: function selectAll() {
            if (!this.refs.all_selector.isChecked()) {
                this.setState({ selection: new Map() }, this.rebuildLoadedElements);
            } else {
                var selection = new Map();
                this.props.node.getChildren().forEach((function (child) {
                    if (this.props.filterNodes && !this.props.filterNodes(child)) {
                        return;
                    }
                    if (child.isLeaf()) {
                        selection.set(child, true);
                    }
                }).bind(this));
                this.refs.all_selector.setChecked(true);
                this.setState({ selection: selection }, this.rebuildLoadedElements);
            }
        },

        applyMultipleAction: function applyMultipleAction(ev) {
            if (!this.state.selection || !this.state.selection.size) {
                return;
            }
            var actionName = ev.currentTarget.getAttribute('data-action');
            var dm = this.dm || new PydioDataModel();
            dm.setContextNode(this.props.node);
            var selNodes = [];
            this.state.selection.forEach(function (v, node) {
                selNodes.push(node);
            });
            dm.setSelectedNodes(selNodes);
            var a = global.pydio.Controller.getActionByName(actionName);
            a.fireContextChange(dm, true, global.pydio.user);
            //a.fireSelectionChange(dm);
            a.apply([dm]);

            ev.stopPropagation();
            ev.preventDefault();
        },

        getActionsForNode: function getActionsForNode(dm, node) {
            var cacheKey = node.isLeaf() ? 'file' : 'dir';

            var nodeActions = [];
            if (this.actionsCache[cacheKey]) {
                nodeActions = this.actionsCache[cacheKey];
            } else {
                dm.setSelectedNodes([node]);
                global.pydio.Controller.actions.forEach((function (a) {
                    a.fireContextChange(dm, true, global.pydio.user);
                    //a.fireSelectionChange(dm);
                    if (a.context.selection && a.context.actionBar && a.selectionContext[cacheKey] && !a.deny && a.options.icon_class && (!this.props.actionBarGroups || this.props.actionBarGroups.indexOf(a.context.actionBarGroup) !== -1)) {
                        nodeActions.push(a);
                        if (node.isLeaf() && a.selectionContext.unique === false) {
                            this.actionsCache.multiple.set(a.options.name, a);
                        }
                    }
                }).bind(this));
                this.actionsCache[cacheKey] = nodeActions;
            }

            return nodeActions;
        },

        updateInfiniteContainerHeight: function updateInfiniteContainerHeight() {
            var containerHeight = this.refs.infiniteParent.getDOMNode().clientHeight;
            if (this.props.heightAutoWithMax) {
                var elementHeight = this.state.elementHeight ? this.state.elementHeight : this.props.elementHeight;
                containerHeight = Math.min(this.props.node.getChildren().size * elementHeight, this.props.heightAutoWithMax);
            }
            this.setState({ containerHeight: containerHeight });
        },

        computeElementHeightResponsive: function computeElementHeightResponsive() {
            var breaks = this.props.elementHeight;
            if (!(breaks instanceof Object)) {
                breaks = {
                    "min-width:480px": this.props.elementHeight,
                    "max-width:480px": Object.keys(this.props.tableKeys).length * 24 + 33
                };
            }
            if (global.matchMedia) {
                for (var k in breaks) {
                    if (breaks.hasOwnProperty(k) && global.matchMedia('(' + k + ')').matches) {
                        return breaks[k];
                    }
                }
            } else {
                var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
                if (width < 480) return breaks["max-width:480px"];else return breaks["max-width:480px"];
            }
            return 50;
        },

        updateElementHeightResponsive: function updateElementHeightResponsive() {
            var newH = this.computeElementHeightResponsive();
            if (!this.state || !this.state.elementHeight || this.state.elementHeight != newH) {
                this.setState({ elementHeight: newH }, (function () {
                    if (this.props.heightAutoWithMax) {
                        this.updateInfiniteContainerHeight();
                    }
                }).bind(this));
            }
        },

        componentDidMount: function componentDidMount() {
            this._loadNodeIfNotLoaded();
            if (this.refs.infiniteParent) {
                this.updateInfiniteContainerHeight();
                if (!this.props.heightAutoWithMax) {
                    if (global.addEventListener) {
                        global.addEventListener('resize', this.updateInfiniteContainerHeight);
                    } else {
                        global.attachEvent('onresize', this.updateInfiniteContainerHeight);
                    }
                }
            }
            if (this.props.autoRefresh) {
                this.refreshInterval = global.setInterval(this.reload, this.props.autoRefresh);
            }
            if (this.props.observeNodeReload) {
                this.wireReloadListeners();
            }
            if (this.props.elementHeight instanceof Object || this.props.tableKeys) {
                if (global.addEventListener) {
                    global.addEventListener('resize', this.updateElementHeightResponsive);
                } else {
                    global.attachEvent('onresize', this.updateElementHeightResponsive);
                }
                this.updateElementHeightResponsive();
            }
        },

        componentWillUnmount: function componentWillUnmount() {
            if (!this.props.heightAutoWithMax) {
                if (global.removeEventListener) {
                    global.removeEventListener('resize', this.updateInfiniteContainerHeight);
                } else {
                    global.detachEvent('onresize', this.updateInfiniteContainerHeight);
                }
            }
            if (this.props.elementHeight instanceof Object || this.props.tableKeys) {
                if (global.removeEventListener) {
                    global.removeEventListener('resize', this.updateElementHeightResponsive);
                } else {
                    global.detachEvent('resize', this.updateElementHeightResponsive);
                }
            }
            if (this.refreshInterval) {
                global.clearInterval(this.refreshInterval);
            }
            if (this.props.observeNodeReload) {
                this.stopReloadListeners();
            }
        },

        componentDidUpdate: function componentDidUpdate() {
            this._loadNodeIfNotLoaded();
        },

        onScroll: function onScroll(ev) {
            var scrollTop = ev.target.scrollTop;
            bufferCallback('infiniteScroller', 50, (function () {
                this.setState({ scrollerDelta: scrollTop });
            }).bind(this));
        },

        buildElementsFromNodeEntries: function buildElementsFromNodeEntries(nodeEntries, showSelector) {

            var components = [];
            nodeEntries.forEach((function (entry) {
                var data;
                if (entry.parent) {
                    data = {
                        node: entry.node,
                        key: entry.node.getPath(),
                        id: entry.node.getPath(),
                        mainIcon: "icon-level-up",
                        firstLine: "..",
                        secondLine: this.context.getMessage('react.1'),
                        onClick: this.clickRow,
                        showSelector: false,
                        selectorDisabled: true
                    };
                    components.push(React.createElement(ListEntry, data));
                } else if (entry.groupHeader) {
                    data = {
                        node: null,
                        key: entry.groupHeader,
                        id: entry.groupHeader,
                        mainIcon: null,
                        firstLine: entry.groupHeader,
                        className: 'list-group-header',
                        onClick: null,
                        showSelector: false,
                        selectorDisabled: true
                    };
                    components.push(React.createElement(ListEntry, data));
                } else {
                    data = {
                        node: entry.node,
                        onClick: this.clickRow,
                        onSelect: this.toggleSelection,
                        key: entry.node.getPath(),
                        id: entry.node.getPath(),
                        renderIcon: this.props.entryRenderIcon,
                        renderFirstLine: this.props.entryRenderFirstLine,
                        renderSecondLine: this.props.entryRenderSecondLine,
                        renderThirdLine: this.props.entryRenderThirdLine,
                        renderActions: this.props.entryRenderActions,
                        showSelector: showSelector,
                        selected: this.state && this.state.selection ? this.state.selection.get(entry.node) : false,
                        actions: React.createElement(SimpleReactActionBar, { node: entry.node, actions: entry.actions, dataModel: this.dm }),
                        selectorDisabled: !(this.props.entryEnableSelector ? this.props.entryEnableSelector(entry.node) : entry.node.isLeaf())
                    };
                    if (this.props.tableKeys) {
                        if (this.state && this.state.groupBy) {
                            data['tableKeys'] = LangUtils.deepCopy(this.props.tableKeys);
                            delete data['tableKeys'][this.state.groupBy];
                        } else {
                            data['tableKeys'] = this.props.tableKeys;
                        }
                        components.push(React.createElement(TableListEntry, data));
                    } else {
                        components.push(React.createElement(ConfigurableListEntry, data));
                    }
                }
            }).bind(this));
            return components;
        },

        buildElements: function buildElements(start, end, node, showSelector) {
            var theNode = this.props.node;
            if (node) theNode = node;
            var theShowSelector = this.state && this.state.showSelector;
            if (showSelector !== undefined) theShowSelector = showSelector;

            if (!this.indexedElements) {
                this.indexedElements = [];
                if (this.state && this.state.groupBy) {
                    var groupBy = this.state.groupBy;
                    var groups = {};
                    var groupKeys = [];
                }

                if (!this.props.skipParentNavigation && theNode.getParent() && this.props.dataModel.getContextNode() !== theNode) {
                    this.indexedElements.push({ node: theNode.getParent(), parent: true, actions: null });
                }

                theNode.getChildren().forEach((function (child) {
                    if (child.getMetadata().has('cursor')) {
                        var childCursor = parseInt(child.getMetadata().get('cursor'));
                        this._currentCursor = Math.max(this._currentCursor ? this._currentCursor : 0, childCursor);
                    }
                    if (this.props.filterNodes && !this.props.filterNodes(child)) {
                        return;
                    }
                    var nodeActions = this.getActionsForNode(this.dm, child);
                    if (groupBy) {
                        var groupValue = child.getMetadata().get(groupBy) || 'N/A';
                        if (!groups[groupValue]) {
                            groups[groupValue] = [];
                            groupKeys.push(groupValue);
                        }
                        groups[groupValue].push({ node: child, parent: false, actions: nodeActions });
                    } else {
                        this.indexedElements.push({ node: child, parent: false, actions: nodeActions });
                    }
                }).bind(this));

                if (groupBy) {
                    groupKeys = groupKeys.sort();
                    groupKeys.map((function (k) {
                        this.indexedElements.push({ node: null, groupHeader: k, parent: false, actions: null });
                        this.indexedElements = this.indexedElements.concat(groups[k]);
                    }).bind(this));
                }
            }

            var nodes = this.indexedElements.slice(start, end);
            if (!nodes.length && theNode.getMetadata().get('paginationData')) {
                /*
                //INFINITE SCROLLING ACCROSS PAGE. NOT SURE IT'S REALLY UX FRIENDLY FOR BIG LISTS OF USERS.
                //BUT COULD BE FOR E.G. LOGS
                var pData = theNode.getMetadata().get('paginationData');
                var total = parseInt(pData.get("total"));
                var current = parseInt(pData.get("current"));
                if(current < total){
                    pData.set("new_page", current+1);
                }
                this.dm.requireContextChange(theNode);
                */
                return [];
            } else {
                return nodes; //this.buildElementsFromNodeEntries(nodes, theShowSelector);
            }
        },

        rebuildLoadedElements: function rebuildLoadedElements() {
            var elemLength = this.state.elements.length;
            var newElements = this.buildElements(0, elemLength);
            this.setState({ elements: newElements });
            this.updateInfiniteContainerHeight();
        },

        handleInfiniteLoad: function handleInfiniteLoad() {
            var that = this;
            this.setState({
                isInfiniteLoading: true
            });
            var elemLength = that.state.elements.length;
            var newElements = that.buildElements(elemLength, elemLength + this.props.infiniteSliceCount);
            that.setState({
                isInfiniteLoading: false,
                elements: that.state.elements.concat(newElements)
            });
        },

        renderToolbar: function renderToolbar() {

            var rightButtons = React.createElement(ReactMUI.FontIcon, {
                key: 1,
                tooltip: 'Reload',
                className: "icon-refresh" + (this.state.loading ? " rotating" : ""),
                onClick: this.reload
            });
            var leftToolbar;
            var paginator;

            if (this.props.node.getMetadata().get("paginationData")) {
                paginator = React.createElement(ListPaginator, { dataModel: this.dm, node: this.props.node });
            }

            if (this.props.listTitle) {
                leftToolbar = React.createElement(
                    ReactMUI.ToolbarGroup,
                    { key: 0, float: 'left' },
                    React.createElement(
                        'div',
                        { className: 'list-title' },
                        this.props.listTitle
                    )
                );
            }

            if (this.props.searchResultData) {

                leftToolbar = React.createElement(
                    ReactMUI.ToolbarGroup,
                    { key: 0, float: 'left' },
                    React.createElement(
                        'h2',
                        { className: 'search-results-title' },
                        this.context.getMessage('react.3').replace('%s', this.props.searchResultData.term)
                    )
                );
                rightButtons = React.createElement(ReactMUI.RaisedButton, { key: 1, label: this.context.getMessage('react.4'), primary: true, onClick: this.props.searchResultData.toggleState });
            } else if (this.actionsCache.multiple.size) {
                var bulkLabel = this.context.getMessage('react.2');
                if (this.state.selection && this.state.showSelector) {
                    bulkLabel += " (" + this.state.selection.size + ")";
                }
                leftToolbar = React.createElement(
                    ReactMUI.ToolbarGroup,
                    { key: 0, float: 'left', className: 'hide-on-vertical-layout' },
                    React.createElement(ReactMUI.Checkbox, { ref: 'all_selector', onClick: this.selectAll }),
                    React.createElement(ReactMUI.FlatButton, { label: bulkLabel, onClick: this.toggleSelector })
                );

                if (this.state.showSelector) {
                    rightButtons = [];
                    var index = 0;
                    this.actionsCache.multiple.forEach((function (a) {
                        rightButtons.push(React.createElement(ReactMUI.RaisedButton, {
                            key: index,
                            label: a.options.text,
                            'data-action': a.options.name,
                            onClick: this.applyMultipleAction,
                            primary: true }));
                    }).bind(this));
                    rightButtons = React.createElement(
                        'span',
                        null,
                        rightButtons
                    );
                }
            }

            return React.createElement(
                ReactMUI.Toolbar,
                null,
                leftToolbar,
                React.createElement(
                    ReactMUI.ToolbarGroup,
                    { key: 1, float: 'right' },
                    paginator,
                    rightButtons
                )
            );
        },

        render: function render() {

            var containerClasses = "material-list vertical-layout layout-fill";
            if (this.props.className) {
                containerClasses += " " + this.props.className;
            }
            if (this.state.showSelector) {
                containerClasses += " list-show-selectors";
            }
            if (this.props.tableKeys) {
                containerClasses += " table-mode";
            }
            var toolbar;
            if (this.props.tableKeys) {
                var tableKeys;
                if (this.state && this.state.groupBy) {
                    tableKeys = LangUtils.deepCopy(this.props.tableKeys);
                    delete tableKeys[this.state.groupBy];
                } else {
                    tableKeys = this.props.tableKeys;
                }
                toolbar = React.createElement(TableListHeader, {
                    tableKeys: tableKeys,
                    loading: this.state.loading,
                    reload: this.reload,
                    ref: 'loading_indicator'
                });
            } else {
                toolbar = this.props.customToolbar ? this.props.customToolbar : this.renderToolbar();
            }

            var elements = this.buildElementsFromNodeEntries(this.state.elements, this.state.showSelector);
            return React.createElement(
                'div',
                { className: containerClasses },
                toolbar,
                React.createElement(
                    'div',
                    { className: this.props.heightAutoWithMax ? "infinite-parent-smooth-height" : "layout-fill", ref: 'infiniteParent' },
                    React.createElement(
                        Infinite,
                        {
                            elementHeight: this.state.elementHeight ? this.state.elementHeight : this.props.elementHeight,
                            containerHeight: this.state.containerHeight,
                            infiniteLoadBeginBottomOffset: 200,
                            onInfiniteLoad: this.handleInfiniteLoad
                        },
                        elements
                    )
                )
            );
        }

    });

    /**
     * Simple to use list component encapsulated with its own query mechanism
     * using a set of properties for the remote node provider.
     */
    var NodeListCustomProvider = React.createClass({
        displayName: 'NodeListCustomProvider',

        propTypes: {
            nodeProviderProperties: React.PropTypes.object,
            presetDataModel: React.PropTypes.instanceOf(PydioDataModel),
            autoRefresh: React.PropTypes.number,
            actionBarGroups: React.PropTypes.array,
            heightAutoWithMax: React.PropTypes.number,
            elementHeight: React.PropTypes.number.isRequired,
            nodeClicked: React.PropTypes.func,
            reloadOnServerMessage: React.PropTypes.string
        },

        reload: function reload() {
            if (this.refs.list && this.isMounted()) {
                this.refs.list.reload();
            }
        },

        componentWillUnmount: function componentWillUnmount() {
            if (this._smObs) {
                this.props.pydio.stopObserving("server_message", this._smObs);
                this.props.pydio.stopObserving("server_message:" + this.props.reloadOnServerMessage, this.reload);
            }
        },

        getInitialState: function getInitialState() {

            var dataModel, rootNode;
            if (this.props.presetDataModel) {
                dataModel = this.props.presetDataModel;
                rootNode = dataModel.getRootNode();
            } else {
                dataModel = new PydioDataModel(true);
                var rNodeProvider = new RemoteNodeProvider();
                dataModel.setAjxpNodeProvider(rNodeProvider);
                rNodeProvider.initProvider(this.props.nodeProviderProperties);
                rootNode = new AjxpNode("/", false, "loading", "folder.png", rNodeProvider);
                dataModel.setRootNode(rootNode);
            }
            if (this.props.nodeClicked) {
                // leaf
                this.openEditor = (function (node) {
                    this.props.nodeClicked(node);
                    return false;
                }).bind(this);
                // dir
                dataModel.observe("selection_changed", (function (event) {
                    var selectedNodes = event.memo.getSelectedNodes();
                    if (selectedNodes.length) {
                        this.props.nodeClicked(selectedNodes[0]);
                        event.memo.setSelectedNodes([]);
                    }
                }).bind(this));
            }
            if (this.props.reloadOnServerMessage && this.props.pydio) {
                this._smObs = (function (event) {
                    if (XMLUtils.XPathSelectSingleNode(event, this.props.reloadOnServerMessage)) this.reload();
                }).bind(this);
                this.props.pydio.observe("server_message", this._smObs);
                this.props.pydio.observe("server_message:" + this.props.reloadOnServerMessage, this.reload);
            }
            return { node: rootNode, dataModel: dataModel };
        },

        render: function render() {
            var legend;
            if (this.props.legend) {
                legend = React.createElement(
                    'div',
                    { className: 'subtitle' },
                    this.props.legend
                );
            }
            return React.createElement(
                'div',
                { className: this.props.heightAutoWithMax ? "" : "layout-fill vertical-layout" },
                React.createElement(ReactPydio.SimpleList, _extends({}, this.props, {
                    openEditor: this.openEditor,
                    ref: 'list',
                    style: { height: '100%' },
                    node: this.state.node,
                    dataModel: this.state.dataModel,
                    actionBarGroups: this.props.actionBarGroups,
                    skipParentNavigation: true
                }))
            );
        }

    });

    /********************/
    /* ASYNC COMPONENTS */
    /********************/
    /**
     * Load a component from server (if not already loaded) based on its namespace.
     */
    var AsyncComponent = React.createClass({
        displayName: 'AsyncComponent',

        propTypes: {
            namespace: React.PropTypes.string.isRequired,
            componentName: React.PropTypes.string.isRequired
        },

        _asyncLoad: function _asyncLoad() {
            ResourcesManager.loadClassesAndApply([this.props.namespace], (function () {
                this.setState({ loaded: true });
                if (this.refs['component'] && this.props.onLoad && !this.loadFired) {
                    this.props.onLoad(this.refs['component']);
                    this.loadFired = true;
                }
            }).bind(this));
        },

        componentDidMount: function componentDidMount() {
            this._asyncLoad();
        },

        componentWillReceiveProps: function componentWillReceiveProps(newProps) {
            if (this.props.namespace != newProps.namespace) {
                this.loadFired = false;
                this.setState({ loaded: false });
            }
        },

        componentDidUpdate: function componentDidUpdate() {
            if (!this.state.loaded) {
                this._asyncLoad();
            } else {
                if (this.refs['component'] && this.props.onLoad && !this.loadFired) {
                    this.props.onLoad(this.refs['component']);
                    this.loadFired = true;
                }
            }
        },

        getInitialState: function getInitialState() {
            return { loaded: false };
        },

        getComponent: function getComponent() {
            return this.refs.component ? this.refs.component : null;
        },

        render: function render() {
            if (this.state && this.state.loaded) {
                var nsObject = global[this.props.namespace];
                if (nsObject && nsObject[this.props.componentName]) {
                    var props = LangUtils.simpleCopy(this.props);
                    props['ref'] = 'component';
                    return React.createElement(nsObject[this.props.componentName], props, null);
                } else {
                    return React.createElement(
                        'div',
                        null,
                        'Component ',
                        this.props.namespace,
                        '.',
                        this.props.componentName,
                        ' not found!'
                    );
                }
            } else {
                return React.createElement(
                    'div',
                    null,
                    'Loading ...'
                );
            }
        }

    });
    /**
     * Specific AsyncComponent for Modal Dialog
     */
    var AsyncModal = React.createClass({
        displayName: 'AsyncModal',

        getInitialState: function getInitialState() {
            return {
                async: true,
                componentData: null,
                actions: [{ text: 'Cancel', ref: 'cancel' }, { text: 'Submit', ref: 'submit' }],
                title: null
            };
        },

        componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
            var componentData = nextProps.componentData;
            var state = { componentData: componentData, async: true };
            if (componentData && (!componentData instanceof Object || !componentData['namespace'])) {
                state['async'] = false;
                this.initModalFromComponent(componentData);
            }
            this.setState(state);
        },

        show: function show() {
            if (this.refs.dialog) this.refs.dialog.show();
        },

        hide: function hide() {
            this.refs.dialog.dismiss();
        },

        onActionsUpdate: function onActionsUpdate(component) {
            if (component.getButtons) {
                this.setState({ actions: component.getButtons() });
            }
        },

        onTitleUpdate: function onTitleUpdate(component) {
            if (component.getTitle) {
                this.setState({ title: component.getTitle() });
            }
        },

        onDialogClassNameUpdate: function onDialogClassNameUpdate(component) {
            if (component.getDialogClassName) {
                this.setState({ className: component.getDialogClassName() });
            }
        },

        initModalFromComponent: function initModalFromComponent(component) {
            if (component.getButtons) {
                this.setState({ actions: component.getButtons() });
            }
            if (component.getTitle) {
                this.setState({ title: component.getTitle() });
            }
            if (component.getDialogClassName) {
                this.setState({ className: component.getDialogClassName() });
            }
            if (component.setModal) {
                component.setModal(this);
            }
        },

        render: function render() {
            var modalContent;
            if (this.state.componentData) {
                if (this.state.async) {
                    modalContent = React.createElement(ReactPydio.AsyncComponent, _extends({}, this.props, {
                        namespace: this.state.componentData.namespace,
                        componentName: this.state.componentData.compName,
                        ref: 'modalAsync',
                        onLoad: this.initModalFromComponent,
                        dismiss: this.hide,
                        actionsUpdated: this.onActionsUpdate,
                        titleUpdated: this.onTitleUpdate,
                        classNameUpdated: this.onDialogClassNameUpdate,
                        modalData: { modal: this, payload: this.state.componentData['payload'] }
                    }));
                } else {
                    modalContent = this.state.componentData;
                }
            }
            return React.createElement(
                ReactMUI.Dialog,
                {
                    ref: 'dialog',
                    title: this.state.title,
                    actions: this.state.actions,
                    actionFocus: 'submit',
                    modal: false,
                    className: this.state.className,
                    dismissOnClickAway: true,
                    onShow: this.props.onShow,
                    onDismiss: this.props.onDismiss,
                    contentClassName: this.state.className
                },
                modalContent
            );
        }

    });

    var ReactPydio = global.ReactPydio || {};

    ReactPydio.MessagesConsumerMixin = MessagesConsumerMixin;

    ReactPydio.SortableList = SortableList;
    ReactPydio.SimpleList = SimpleFlatList;
    ReactPydio.NodeListCustomProvider = NodeListCustomProvider;
    ReactPydio.ListEntry = ListEntry;

    ReactPydio.SimpleFigureBadge = SimpleFigureBadge;
    ReactPydio.SimpleTree = SimpleTree;
    ReactPydio.SearchBox = SearchBox;

    ReactPydio.ReactEditorOpener = ReactEditorOpener;
    ReactPydio.LegacyUIWrapper = LegacyUIWrapper;
    ReactPydio.PaperEditorLayout = PaperEditorLayout;
    ReactPydio.PaperEditorNavEntry = PaperEditorNavEntry;
    ReactPydio.PaperEditorNavHeader = PaperEditorNavHeader;

    ReactPydio.AsyncComponent = AsyncComponent;
    ReactPydio.AsyncModal = AsyncModal;

    ReactPydio.LabelWithTip = LabelWithTip;

    global.ReactPydio = ReactPydio;
})(window);
