Source: common/core/users/crosscuts.js

/* globals define */
/**
 * @author pmeijer / https://github.com/pmeijer
 */

define([
    'common/core/constants',
    'common/util/guid',
    'common/core/CoreIllegalArgumentError',
], function (CORE_CONSTANTS, guid, CoreIllegalArgumentError) {
    'use strict';
    var CROSSCUT_REGISTRY_NAME = 'CrossCuts';
    var CROSSCUT_ID_PREFIX = 'Crosscut_';

    var _core = null;

    /**
     * Module for handling of crosscuts using the core. <br>
     * To include in your module (e.g. Plugin) require via 'common/core/users/crosscut' and invoke initialize
     * by passing a reference to a core instance.
     * <br>
     * @example
     * crosscuts.initialize(core);
     * crosscuts.getIds();
     * @module crosscuts
     */
    var exports = {};

    /**
     * Initializes the module with a core instance. This must be called before any other method.
     * @param {Core} core - An instance of a Core module.
     */
    exports.initialize = function (core) {
        _core = core;
    };

    /**
     *
     * @param node
     * @param crosscutId
     * @param [memberPath]
     * @private
     */
    function _ensureCrosscutExists(node, crosscutId, memberPath) {
        var exists = false;

        if (typeof memberPath === 'string') {
            // This ensure the crosscut exists.
            exports.getMemberPaths(node, crosscutId).forEach(function (mPath) {
                if (memberPath === mPath) {
                    exists = true;
                }
            });

            if (!exists) {
                throw new CoreIllegalArgumentError('Member [' + memberPath + '] does not exist in crosscut [' +
                    crosscutId + ']');
            }
        } else {
            exports.getIds(node).forEach(function (cId) {
                if (crosscutId === cId) {
                    exists = true;
                }
            });

            if (!exists) {
                throw new CoreIllegalArgumentError('Crosscut does not exist [' + crosscutId + ']');
            }
        }
    }

    function _ensureInitialized() {
        if (_core === null) {
            throw new Error('Crosscut module has not been initialized!');
        }
    }

    function _getPath(nodeOrPath) {
        return typeof nodeOrPath === 'string' ? nodeOrPath : _core.getPath(nodeOrPath);
    }

    /**
     * Returns the raw stored crosscut info at the provided node.
     * @param {module:Core~Node} node
     * @returns {object[]}
     */
    exports.getInfo = function (node) {
        _ensureInitialized();

        return _core.getRegistry(node, CROSSCUT_REGISTRY_NAME) || [];
    };

    /**
     * Returns all titles of the crosscuts at the provided node.
     * @param {module:Core~Node} node - Owner of the crosscuts.
     * @returns {string[]}
     */
    exports.getTitles = function (node) {
        return exports.getInfo(node).map(function (cInfo) {
            return cInfo.title;
        });
    };

    /**
     * Returns all the cross-cut ideas at the provided node.
     * @param {module:Core~Node} node - Owner of the crosscuts.
     * @returns {string[]} The crosscut ids.
     */
    exports.getIds = function (node) {
        return exports.getInfo(node).map(function (cInfo) {
            return cInfo.SetID;
        });
    };

    /**
     * If title exists and is unique will return the id of the crosscut. If not
     * it will throw an exception.
     * @param {module:Core~Node} node - Owner of the crosscut.
     * @param {string} title
     * @returns {string} The crosscut id.
     */
    exports.getIdFromTitle = function (node, title) {
        var id;

        exports.getInfo(node).forEach(function (cInfo) {
            if (cInfo.title === title) {
                if (typeof id === 'string') {
                    throw new CoreIllegalArgumentError('Title [' + title + '] appears in more than one crosscut!');
                } else {
                    id = cInfo.SetID;
                }
            }
        });

        if (typeof id !== 'string') {
            throw new CoreIllegalArgumentError('Title [' + title + '] does not exist among crosscuts!');
        }

        return id;
    };

    /**
     * Returns the paths to all members in the specified crosscut.
     * @param {module:Core~Node} node - Owner of the crosscut.
     * @param {string} crosscutId
     * @returns {string[]} Paths of members
     */
    exports.getMemberPaths = function (node, crosscutId) {
        _ensureCrosscutExists(node, crosscutId);
        return _core.getMemberPaths(node, crosscutId);
    };

    /**
     * Return the position at the cross-cuts for the member in the given crosscut at the provided node.
     * @param {module:Core~Node} node - Owner of the crosscut.
     * @param {string} crosscutId - Crosscut id to add member to.
     * @param {module:Core~Node|string} memberNodeOrPath - Node, or path of, member to get position of.
     * @returns {object} The position of the member inside the crosscut.
     */
    exports.getMemberPosition = function (node, crosscutId, memberNodeOrPath) {
        _ensureInitialized();
        var memberPath = _getPath(memberNodeOrPath);
        _ensureCrosscutExists(node, crosscutId, memberPath);
        return _core.getMemberRegistry(node, crosscutId, memberPath, 'position') || {x: 100, y: 100};
    };

    /**
     * Adds a new crosscut to the node.
     * @param {module:Core~Node} node - Owner of the new crosscut.
     * @param {string} title - Visible title of crosscut.
     * @param {number} [order] - Tab order starting from 0, if not given will be placed at end.
     * @returns {string} The id of the newly created crosscut.
     */
    exports.createCrosscut = function (node, title, order) {
        var id = CROSSCUT_ID_PREFIX + guid();
        var cInfo = exports.getInfo(node);

        if (typeof order === 'number') {
            if (order < 0) {
                throw new CoreIllegalArgumentError('Provided order must be >= 0');
            }

            if (cInfo.length < order) {
                throw new CoreIllegalArgumentError('Provided order is greater than the largest possible index ' +
                    cInfo.length + '.');
            }

            cInfo.forEach(function (entry) {
                if (entry.order >= order) {
                    entry.order += 1;
                }
            });
        } else {
            order = cInfo.length;
        }

        cInfo.push({
            SetID: id,
            order: order,
            title: title,
        });

        _core.createSet(node, id);

        cInfo.sort(function (a, b) {
            if (a.order < b.order) {
                return -1;
            } else {
                return 1;
            }
        });

        _core.setRegistry(node, CROSSCUT_REGISTRY_NAME, cInfo);

        return id;
    };

    /**
     * Updates the position of the member inside the specified crosscut.
     * @param {module:Core~Node} node - Owner of the crosscut.
     * @param {string} crosscutId - Id of crosscut.
     * @param {module:Core~Node|string} memberNodeOrPath - Node, or path of, member to update position for.
     * @param {object} newPosition - Position of the member inside the crosscut.
     */
    exports.setMemberPosition = function (node, crosscutId, memberNodeOrPath, newPosition) {
        _ensureInitialized();
        var memberPath = _getPath(memberNodeOrPath);
        _ensureCrosscutExists(node, crosscutId, memberPath);
        _core.setMemberRegistry(node, crosscutId, memberPath, 'position', newPosition);
    };

    /**
     * Adds a member to the crosscut at an optionally provided position.
     * @param {module:Core~Node} node - Owner of the crosscut.
     * @param {string} crosscutId - Crosscut id to add member to.
     * @param {module:Core~Node} memberNode - Node that should be added to crosscut.
     * @param {object} [position={x:100, y:100}] - Position of the member inside crosscut.
     */
    exports.addMember = function (node, crosscutId, memberNode, position) {
        _ensureCrosscutExists(node, crosscutId);
        position = position || {x: 100, y: 100};
        _core.addMember(node, crosscutId, memberNode);
        exports.setMemberPosition(node, crosscutId, memberNode, position);
    };

    /**
     * Deletes the crosscut from the node.
     * @param {module:Core~Node} node - Owner of the crosscut.
     * @param {string} crosscutId - Id of crosscut to delete.
     */
    exports.deleteCrosscut = function (node, crosscutId) {
        var cInfo = exports.getInfo(node);
        var entryOrder;
        _ensureCrosscutExists(node, crosscutId);

        cInfo = cInfo.filter(function (entry) {
            if (entry.SetID === crosscutId) {
                entryOrder = entry.order;
                return false;
            }

            return true;
        });

        cInfo.forEach(function (entry) {
            if (entry.order > entryOrder) {
                entry.order -= 1;
            }
        });

        cInfo.sort(function (a, b) {
            if (a.order < b.order) {
                return -1;
            } else {
                return 1;
            }
        });

        _core.delSet(node, crosscutId);
        _core.setRegistry(node, CROSSCUT_REGISTRY_NAME, cInfo);
    };

    /**
     * Removes the member from the specified crosscut.
     * @param {module:Core~Node} node - Owner of the crosscut.
     * @param {string} crosscutId - Id of crosscut.
     * @param {module:Core~Node|string} memberNodeOrPath - Node, or path of, member to delete from crosscut.
     */
    exports.delMember = function (node, crosscutId, memberNodeOrPath) {
        _ensureInitialized();
        var memberPath =  _getPath(memberNodeOrPath);
        _ensureCrosscutExists(node, crosscutId, memberPath);
        _core.delMember(node, crosscutId, memberPath);
    };

    return exports;
});