milo

fragment_getState

function
 fragment_getState() 

Option name Type Description
range Range

DOM Range instance

renameChildren Boolean

optional parameter, true to rename fragment child components

wrapperClassName String

optional parameter to wrap in a custom component class

return Object

Creates an object with the state of wrapped range with components, including partially selected. The range will be cloned and wrapped in component with container facet before getting its state.
This function will log error and return undefined if range has no common ancestor that has component with container facet

function fragment_getState(range, renameChildren, wrapperClassName) {
    var rangeContainer = _getRangeContainer(range);
    if (! rangeContainer) {
        logger.error('fragment.getState: range has no common container');
        return;
    }

    var frag = range.cloneContents()
        , wrapper = _wrapFragmentInContainer(frag, wrapperClassName);

    _transferStates(rangeContainer, wrapper);
    if (renameChildren) _renameChildren(wrapper);
    var wrapperState = wrapper.getState();
    _.deferMethod(wrapper, 'destroy');
    return wrapperState;
}

fragment_getStateAsync

function
 fragment_getStateAsync() 

Option name Type Description
range Range

DOM Range instance

renameChildren Boolean

optional parameter, true to rename fragment child components

callback Function

always the last parameter, optional parameters can be dropped; result is passed via callback with any error as first parameter

Creates an object with the state of wrapped range with components, including partially selected. The range will be cloned and wrapped in component with container facet before getting its state.
This function will return result and any error via callback.

function fragment_getStateAsync(range, renameChildren, callback) {
    try {
        var rangeContainer = _getRangeContainer(range);
        if (! rangeContainer) {
            callback(new Error('fragment.getState: range has no common container'));
            return; // do NOT connect return to previous callback, getState should return undefined
        }

        if (typeof renameChildren == 'function') {
            callback = renameChildren;
            renameChildren = false;
        }

        var frag = range.cloneContents()
            , wrapper = _wrapFragmentInContainer(frag);

        _transferStates(rangeContainer, wrapper);
        _.defer(function() {
            wrapper.broadcast('stateready');
            _.defer(function() {
                if (renameChildren) _renameChildren(wrapper);
                var wrapperState = wrapper.getState();
                wrapper.destroy();
                callback(null, wrapperState);
            });
        });
    } catch (err) {
        callback(err);
    }
}


function _wrapFragmentInContainer(frag, wrapperClassName) {
    var wrapEl = document.createElement('div')
        , attr = new BindAttribute(wrapEl);

    _.extend(attr, {
        compClass: wrapperClassName || 'Component',
        compFacets: wrapperClassName ? [] : ['container'],
        compName: 'wrapper'
    });

    attr.decorate();

    wrapEl.appendChild(frag);
    var scope = binder(wrapEl);
    return scope.wrapper;
}


function _getRangeContainer(range) {
    var el = domUtils.containingElement(range.commonAncestorContainer);
    return Component.getContainingComponent(el, true, 'container');
}


function _transferStates(fromComp, toComp) {
    var fromScope = fromComp.container.scope;
    toComp.container.scope._each(function(toChildComp, name) {
        var fromChildComp = fromScope[name];
        if (! fromChildComp) return logger.error('fragment.getState: conponent', name, 'not found in range');
        var state = fromChildComp._getState(true);
        toChildComp.setState(state);
    });
}


function _renameChildren(comp) {
    comp.container.scope._each(function(child) {
        child.rename();
    });
}


function expandRangeToSiblings(range) {
    var siblings = getRangeSiblings(range);
    range = createRangeFromSiblings(siblings);
    return range;
}


function createRangeFromSiblings(nodes) {
    var range = document.createRange();
    if (nodes.siblings) {
        range.setStartBefore(nodes.start);
        range.setEndAfter(nodes.end);
    } else
        range.selectNode(nodes.start);
    return range;
}


function getRangeSiblings(range) {
    var containerNode = range.commonAncestorContainer
        , startNode = range.startContainer
        , endNode = range.endContainer;

    if (startNode == endNode) {
        if (startNode != containerNode) logger.error('deleteSelectionCommand logical error: start==end, but container is different');
        return { siblings: false, start: startNode };
    }

    if (startNode == containerNode || endNode == containerNode)
        return { siblings: false, start: containerNode };

    var startSibling = _findContainingChild(containerNode, startNode);
    var endSibling = _findContainingChild(containerNode, endNode);

    if (startSibling && endSibling) {
        if (startSibling == endSibling) {
            logger.error('deleteSelectionCommand logical error: same siblings');
            return { siblings: false, start: startSibling };
        } else
            return { siblings: true, start: startSibling, end: endSibling };
    }
}


function _findContainingChild(containerNode, selNode) {
    return _.find(containerNode.childNodes, function(node) {
        return node.contains(selNode);
    });
}


function _createNodesAndPathsFunc(func) {
    return function(rootEl, fromObj) {
        var toObj = {
            siblings: fromObj.siblings,
            start: func(rootEl, fromObj.start)
        };
        if (toObj.siblings)
            toObj.end = func(rootEl, fromObj.end);
        return toObj;
    };
}