%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /data/old/home/stash/atlassian-stash/static/feature/changeset/difftree/
Upload File :
Create Path :
Current File : //data/old/home/stash/atlassian-stash/static/feature/changeset/difftree/difftree.js

define('feature/changeset/difftree', [
    'aui',
    'jquery',
    'util/ajax',
    'util/events',
    'util/navbuilder',
    'model/content-tree-node-types',
    'exports'
], function(
    _aui,
    $,
    ajax,
    events,
    navbuilder,
    ContentNodeType,
    exports
) {
        var pathSeparator = '/';
        var DEFAULT_CHANGESET_LIMIT = 1000;
        var defaultMaximumOpen = ($.browser.msie && parseInt($.browser.version, 10) < 9) ? 20 : 200;

        function openTree(tree, maximumOpen) {
            maximumOpen = maximumOpen >= 0 ? maximumOpen : defaultMaximumOpen;
            var opened = 0;
            function openNodes(node) {
                if (node.metadata.isDirectory) {
                    node.state = 'open';
                    node.data.icon = 'aui-icon aui-icon-small aui-iconfont-devtools-folder-open';

                    for (var i = 0, l = node.children.length, child; i < l && opened < maximumOpen; i++) {
                        child = node.children[i];
                        openNodes(child);
                    }
                } else if (node.metadata.isFile) {
                    opened++;
                }
            }
            openNodes(tree);
        }

        function compareTreeNodes(a, b) {
            return a.children ?
                (b.children ? (a.data.title.toLowerCase() < b.data.title.toLowerCase() ? -1 : 1) : -1) :
                (!b.children ? (a.data.title.toLowerCase() < b.data.title.toLowerCase() ? -1 : 1) : 1);
        }

        function flattenTree(tree) {
            tree.childrenByTypeAndComponent = undefined;
            for (var i = 0, l = tree.children.length, child, components; i < l; i++) {
                child = tree.children[i];
                if (child.metadata.isDirectory) {
                    components = [child.data.title];
                    while (child.children.length === 1 && child.children[0].metadata.isDirectory) {
                        child = child.children[0];
                        components.push(child.data.title);
                    }
                    child.data.title = components.join(pathSeparator);
                    tree.children[i] = child;
                    flattenTree(child);
                }
            }
            tree.children.sort(compareTreeNodes);
        }

        function computeTree(changes, maximumOpen) {


            var tree = {
                data : {
                    icon : "aui-icon aui-icon-small aui-iconfont-devtools-folder-closed"
                },
                state : 'closed',
                metadata : {
                    isDirectory : true
                },
                children : [],
                childrenByTypeAndComponent : {}
            };
            for (var i = 0, l = changes.length, change, subTree; i < l; i++) {
                change = changes[i];
                subTree = tree;
                for (var j = 0, k = change.path.components.length, component, key, child; j < k; j++) {
                    component = change.path.components[j];
                    key = (j + 1 === k ? 'F' : 'D') + component;
                    if (Object.prototype.hasOwnProperty.call(subTree.childrenByTypeAndComponent, key)) {
                        subTree = subTree.childrenByTypeAndComponent[key];
                    } else {
                        var isLastPathComponent = j + 1 === k;
                        if (isLastPathComponent) {
                            var hasComments = !!(change.attributes &&
                                                 change.attributes.activeComments &&
                                                 parseInt(change.attributes.activeComments[0], 10));
                            child = {
                                data : {
                                    title : component,
                                    icon : "aui-icon aui-icon-small " + (change.nodeType === ContentNodeType.SUBMODULE ? 'aui-iconfont-devtools-submodule' : hasComments ? "aui-iconfont-devtools-file-commented" : "aui-iconfont-devtools-file"),
                                    attr : {
                                        id : 'change' + i,
                                        "class": "change-type-" + change.type + (change.conflict ? " conflict" : ""),
                                        href : "#" + change.path.toString,
                                        title: change.conflict ? AJS.I18n.getText('stash.web.pullrequest.tree.conflicted.file') :
                                                            hasComments ? AJS.I18n.getText('stash.web.pullrequest.tree.commented.file') : undefined
                                    }
                                },
                                metadata : {
                                    isFile : true,
                                    changeType : change.type,
                                    nodeType : change.nodeType,
                                    path : change.path,
                                    srcPath : change.srcPath,
                                    conflict: change.conflict,
                                    contentId: change.contentId,
                                    executable: change.executable,
                                    srcExecutable: change.srcExecutable
                                }
                            };
                        } else {
                            child = {
                                data : {
                                    title : component,
                                    icon : "aui-icon aui-icon-small aui-iconfont-devtools-folder-closed"
                                },
                                state : 'closed',
                                metadata : {
                                    isDirectory : true
                                },
                                children : [],
                                childrenByTypeAndComponent : {}
                            };
                        }
                        subTree.children.push(child);
                        subTree = subTree.childrenByTypeAndComponent[key] = child;
                    }
                }
            }
            flattenTree(tree);
            openTree(tree, maximumOpen);
            return tree;
        }

        function DiffTree(wrapperSelector, commitRange, options) {
            options = options || {};

            this._fileLimit = options.maxChanges || DEFAULT_CHANGESET_LIMIT;
            this._$wrapper = $(wrapperSelector);
            this._commitRange = commitRange;
            this._hasOtherParents = !!options.hasOtherParents;
        }

        DiffTree.prototype.init = function(selectedPathComponents) {

            this._initiallySelectedPathComponents = selectedPathComponents;
            this._firstCommentAddedHandler = _.bind(this._firstCommentAddedHandler, this);
            this._lastCommentDeletedHandler =_.bind(this._lastCommentDeletedHandler, this);

            events.on('stash.feature.comments.firstCommentAdded', this._firstCommentAddedHandler);
            events.on('stash.feature.comments.lastCommentDeleted', this._lastCommentDeletedHandler);

            if (!this.data) {
                return this.requestData();
            } else {
                return this.dataReceived();
            }
        };

        DiffTree.prototype._firstCommentAddedHandler = function() {
            var $icon = this.getSelectedFile().find('a > ins');
            $icon.hide()
                .removeClass('aui-iconfont-devtools-file').addClass('aui-iconfont-devtools-file-commented')
                .fadeIn('slow');
        };

        DiffTree.prototype._lastCommentDeletedHandler  = function() {
            var $icon = this.getSelectedFile().find('a > ins');
            $icon.hide()
                .removeClass('aui-iconfont-devtools-file-commented').addClass('aui-iconfont-devtools-file')
                .fadeIn('slow');
        };

        DiffTree.prototype.reset = function () {
            if (this._request) {
                this._request.abort();
                this._request = null;
                this._interrupted = true;
            }
            if (this._rendering) {
                this._rendering = false;
                this._interrupted = true;
            }
            events.off('stash.feature.comments.firstCommentAdded', this._firstCommentAddedHandler);
            events.off('stash.feature.comments.lastCommentDeleted', this._lastCommentDeletedHandler);
        };

        DiffTree.prototype.requestData = function() {
            var self = this;

            if (this._request) {
                this._request.abort();
                this._request = null;
            }
            this._request = ajax.rest({
                url : navbuilder.rest()
                    .currentRepo()
                    .changes(self._commitRange)
                    .withParams({ start : 0, limit : this._fileLimit })
                    .build()
            });
            return this._request.always(function() {
                self._request = null;
            }).then(function(data) {
                if (!data) {
                    var msg = _aui.escapeHtml(AJS.I18n.getText('stash.web.pullrequest.tree.nodata'));
                    self.prependMessage(msg, "error");
                    return $.Deferred().reject();
                } else {
                    self._rendering = true;
                    self._interrupted = false;

                    self.isTruncated = !data.isLastPage;
                    self.data = computeTree(data.values);
                    return self.dataReceived().done(function() {
                        self._rendering = false;
                    });
                }
            });
        };

        function startsWith(str, substring) {
            return str.substring(0, substring.length) === substring;
        }

        /**
         * This function will return the file node whose path matches your preferredPathComponents if it can.
         * If it can't, it'll instead return the first file node in the tree.
         * If there are no file nodes, it'll return null;
         *
         * It's used for selecting an initial node in the file tree when the tree is being initialized.
         *
         * @param data a flattened tree (usually the diffTree.data object)
         * @param preferredPathComponents the path components of the file to attempt to select - array of strings.
         */
        function getNodeToSelect(data, preferredPathComponents) {
            return getPreferredNode(data, preferredPathComponents) || getFirstNode(data);
        }

        /**
         * @returns the file node which matches the preferredPathComponents, or null if none match
         */
        function getPreferredNode(preferred, preferredComponents) {
            if (!preferredComponents) {
                return null;
            }
            preferredComponents = preferredComponents.slice(0);

            while (preferred && preferred.children) {
                var componentToMatch = preferredComponents.shift(),
                    isLastComponent = !preferredComponents.length;

                var i = preferred.children.length;
                while (i--) {
                    var childToCheck = preferred.children[i],
                        title = childToCheck.data.title;

                    if (componentToMatch === title && isLastComponent === Boolean(childToCheck.metadata.isFile)) { //matches exactly, go inside.
                        preferred = childToCheck;
                        break;
                    }

                    // this is a collapsed node that at least partially matches, keep pulling off components
                    if (!isLastComponent && startsWith(title, componentToMatch + pathSeparator)) {

                        while (preferredComponents.length > 1 &&
                            startsWith(title, componentToMatch + pathSeparator + preferredComponents[0])) {
                            componentToMatch += pathSeparator;
                            componentToMatch += preferredComponents.shift();
                        }

                        if (title !== componentToMatch) { // they passed in a bad path, we're not going to find it.
                            //this handles:
                            // - preferredPath too short
                            // - preferredPath partially matches a collapsed node
                            return null;
                        }
                        //else collapsed node was fully matched.
                        preferred = childToCheck;
                        break;
                    }

                    //this child doesn't match
                }

                if (i < 0) { // no child matched
                    return null;
                }
            }

            return preferred && preferred.metadata && preferred.metadata.isFile ?
                preferred : null;
        }

        function getFirstNode(first) {
            while (first && first.children) {
                first = first.children[0];
            }
            return first && first.metadata && first.metadata.isFile ? first : null;
        }

        function getPathFromRoot(tree, toNode) {

            if (tree === toNode) {
                return [ toNode ];
            }

            var i = tree.children ? tree.children.length : 0;
            while(i--) {
                var childResult = getPathFromRoot(tree.children[i], toNode);
                if (childResult) {
                    childResult.unshift(tree);
                    return childResult;
                }
            }

            return null;
        }

        DiffTree.prototype.prependMessage = function (contents, type) {
            this._$wrapper.find(".aui-message").remove();

            type = type || "warning";

            this._$wrapper.prepend(widget.aui.message[type]({
                extraClasses : 'diff-tree-scm-message',
                contents : contents
            }));
        };

        DiffTree.prototype.dataReceived = function() {
            var self = this;

            var deferred = $.Deferred();
            function resolveIfNotInterrupted() {
                if (!self._interrupted) {
                    deferred.resolve(self);
                } else {
                    deferred.reject(self);
                }
            }

            var initiallySelectedNode = getNodeToSelect(this.data, this._initiallySelectedPathComponents);
            var initiallySelectedIdArray;

            if (initiallySelectedNode) {
                initiallySelectedIdArray = [ initiallySelectedNode.data.attr.id ];

                // open the ancestors of the selected node.
                var toOpen = getPathFromRoot(this.data, initiallySelectedNode) || [];

                toOpen.pop(); // don't open the file, just the folders above it. Otherwise the file gets a twixie.

                while(toOpen.length) {
                    toOpen.pop().state = 'open';
                }

            } else {
                initiallySelectedIdArray = [ ];
            }

            var initializingTree = true;

            var $currentlySelectedNode;

            this._$wrapper.find(".aui-message").remove();
            if (this.isTruncated) {
                var contents = "";

                if (this._commitRange.getPullRequest()){
                    //TODO - Better message for pull request changesets that are too large to render.
                    contents = _aui.escapeHtml(AJS.I18n.getText('stash.web.pullrequest.tree.truncated', this._fileLimit));
                } else {
                    var gitCommand,
                        atRevision = this._commitRange.getUntilRevision(),
                        parentRevision = this._commitRange.getSinceRevision();

                    if (parentRevision) {
                        gitCommand = 'git diff-tree -C -r ' + parentRevision.getId() + ' ' + atRevision.getId();
                    } else {
                        gitCommand = 'git diff-tree -r --root ' + atRevision.getId();
                    }

                    contents = _aui.escapeHtml(AJS.I18n.getText('stash.web.changeset.tree.truncated', this._fileLimit)) +
                        '<p class="scm-command">' + _aui.escapeHtml(gitCommand) + '</p>';
                }

                this.prependMessage(contents, "warning");
            }
            if (this.data.children.length) {
                this.$tree = this._$wrapper.children(".file-tree");
                this.$tree.fadeOut('fast', function () {
                        self.$tree.empty()
                            .off('.jstree')
                            .jstree("destroy")
                            .on('loaded.jstree', function() {
                                //allow jstree plugins to finish loading (namely ui).
                                setTimeout(function() {
                                    initializingTree = false;
                                    resolveIfNotInterrupted();
                                }, 0);
                            }).jstree({
                                json_data : {
                                    data : self.data.children,
                                    progressive_render : true
                                },
                                core : {
                                    animation : 200
                                },
                                ui : {
                                    select_limit : 1,
                                    selected_parent_close: false,
                                    initially_select : initiallySelectedIdArray /* use this for deeplinking */
                                },
                                plugins : ["json_data", "ui"]
                            }).on('before.jstree', function(e, data) {
                                if (data.func === 'select_node') {
                                    var $node = $(data.args[0]).parent();

                                    if ($node.data('isFile') && (!$currentlySelectedNode || $currentlySelectedNode[0] !== $node[0])) {
                                        $currentlySelectedNode = $node;
                                        events.trigger('stash.feature.changeset.difftree.selectedNodeChanged', self, $node, initializingTree);
                                    } else if ($node.data('isDirectory')) {
                                        self.$tree.jstree("toggle_node", $node);
                                        return false; //e.preventDefault() doesn't work...
                                    } // else { ignore everything else }
                                }
                            }).on('open_node.jstree', function(e, data){
                                var $openedNode = data.args[0];
                                var $nodeIcon = $openedNode.children('a').children('ins');
                                $nodeIcon.removeClass('aui-iconfont-devtools-folder-closed');
                                $nodeIcon.addClass('aui-iconfont-devtools-folder-open');
                                events.trigger('stash.feature.changeset.difftree.nodeOpening', self, $openedNode);
                            }).on('after_open.jstree', function(e, data){
                                var $openedNode = data.args[0];
                                events.trigger('stash.feature.changeset.difftree.nodeOpened', self, $openedNode);
                            }).on('close_node.jstree', function(e, data){
                                var $closedNode = data.args[0];
                                var $nodeIcon = $closedNode.children('a').children('ins');
                                $nodeIcon.removeClass('aui-iconfont-devtools-folder-open');
                                $nodeIcon.addClass('aui-iconfont-devtools-folder-closed');
                                events.trigger('stash.feature.changeset.difftree.nodeClosing', self, $closedNode);
                            }).on('after_close.jstree', function(e, data){
                                var $closedNode = data.args[0];
                                events.trigger('stash.feature.changeset.difftree.nodeClosed', self, $closedNode);
                            }).on('loaded.jstree', function(e, data){
                                events.trigger('stash.feature.changeset.difftree.treeInitialised', self, self);
                            }).fadeIn('fast');
                    });
            } else {
                this.$tree = undefined;
                var $fileTree = this._$wrapper.children(".file-tree");
                $fileTree.fadeOut('fast', function () {
                    $fileTree.empty().off('.jstree').jstree("destroy");
                    var message = _aui.escapeHtml(
                        self._hasOtherParents ?
                            AJS.I18n.getText('stash.web.changeset.merge.tree.empty') :
                            AJS.I18n.getText('stash.web.changeset.tree.empty')
                    );
                    self.prependMessage(message, "info");
                    setTimeout(resolveIfNotInterrupted, 0);
                });
            }
            return deferred.promise();
        };

        DiffTree.prototype.getSelectedFile = function() {
            var $tree = this.$tree;
            return $tree ? $tree.jstree('get_selected') : null;
        };

        DiffTree.prototype.selectFile = function(pathComponents) {
            if (!this.$tree) {
                return;
            }
            var nodeToSelect = getNodeToSelect(this.data, pathComponents),
                currentlySelectedFile = this.getSelectedFile(),
                currentlySelectedPath = currentlySelectedFile && currentlySelectedFile.data('path'),
                currentlySelectedNode = currentlySelectedPath && getNodeToSelect(this.data, currentlySelectedPath.components);

            if (nodeToSelect && nodeToSelect !== currentlySelectedNode) {
                this.$tree.jstree('deselect_all').jstree('select_node', '#' + nodeToSelect.data.attr.id);
            }
        };

        DiffTree.prototype.openNextFile = function() {
            if (this.$tree) {
                var jstree = $.jstree._reference(this.$tree),
                    $currentNode = this.getSelectedFile(),
                    $nextFile = findFile(jstree, jstree._get_next, jstree._get_next($currentNode));

                if ($nextFile && $nextFile.length) {
                    $nextFile.find('a').focus().click();
                }
            }
        };

        DiffTree.prototype.openPrevFile = function() {
            if (this.$tree) {
                var jstree = $.jstree._reference(this.$tree),
                    $currentNode = this.getSelectedFile(),
                    $prevFile = findPrevFileOrClosedDir(jstree, jstree._get_prev($currentNode));

                if ($prevFile && $prevFile.length) {
                    $prevFile.find('a').focus().click();
                }
            }
        };

        /* Find leaf based on the getAdjacentNode function passed in */
        function findFile(jstree, getAdjacentNode, $node) {
            if ($node && $node.length && !$node.hasClass('jstree-leaf')) {
                jstree.open_node($node);
                $node = findFile(jstree, getAdjacentNode, getAdjacentNode.call(jstree, $node));
            }

            return $node;
        }

        /* Traverse up til you find a leaf OR closed directory then find its last leaf */
        function findPrevFileOrClosedDir(jstree, $node) {
            if ($node && !$node.hasClass('jstree-leaf')) {
                if ($node.hasClass('jstree-closed')) {
                    jstree.open_node($node);
                    $node = findFile(jstree, getLastChild, getLastChild.call(jstree, $node));
                } else if ($node.length) {
                    $node = findPrevFileOrClosedDir(jstree, jstree._get_prev($node));
                }
            }

            return $node;
        }

        function getLastChild($node) {
            return this._get_children($node).filter('.jstree-last');
        }

        exports.DiffTree = DiffTree;
        exports.computeTree = computeTree;
        exports.flattenTree = flattenTree;
        exports.compareTreeNodes = compareTreeNodes;
        exports.getNodeToSelect = getNodeToSelect;
        exports.getPathFromRoot = getPathFromRoot;
    });

Zerion Mini Shell 1.0