webgme
Version:
Web-based Generic Modeling Environment
1,171 lines (951 loc) • 42.2 kB
JavaScript
/*globals define, Raphael, WebGMEGlobal, $, requirejs*/
/*jshint browser: true*/
/**
* @author rkereskenyi / https://github.com/rkereskenyi
* @author nabana / https://github.com/nabana
*/
define(['js/logger',
'js/util',
'js/Loader/LoaderCircles',
'js/Utils/ComponentSettings',
'./ProjectRepositoryWidgetControl',
'common/regexp',
'moment',
'blockies',
'raphaeljs',
'css!./styles/ProjectRepositoryWidget.css'
], function (Logger,
util,
LoaderCircles,
ComponentSettings,
ProjectRepositoryWidgetControl,
REGEXP,
moment,
blockies) {
'use strict';
var ProjectRepositoryWidget,
MASTER_BRANCH_NAME = 'master',
REPOSITORY_LOG_VIEW_CLASS = 'project-repository-widget',
SHOW_MORE_BUTTON_TEXT = 'Show more...',
COMMIT_PACKAGE_COUNT = 100,
COMMIT_DATA = 'commitData',
COMMIT_CLASS = 'commit',
ACTUAL_COMMIT_CLASS = 'actual',
X_DELTA = 15,
Y_DELTA = 25, //Widgets/ProjectRepository/ProjectRepositoryWidget.scss - $table-row-height
CONTENT_WIDTH = 1,
CONTENT_HEIGHT = 1,
ITEM_WIDTH = 8, //Widgets/ProjectRepository/ProjectRepositoryWidget.scss - $commit-size
ITEM_HEIGHT = 8, //Widgets/ProjectRepository/ProjectRepositoryWidget.scss - $commit-size
LINE_CORNER_SIZE = 5,
LINE_FILL_COLOR = '#000000',
NON_EXISTING_PARENT_LINE_GRADIENT_NAME = 'grad1',
CREATE_BRANCH_EDIT_CONTROL_CLASS = 'create-branch-from-commit',
BRANCH_LABEL_CLASS = 'branch-label',
TAG_LABEL_CLASS = 'tag-label',
COMMON_LABEL_CLASS = 'common-label',
BTN_LOAD_COMMIT_CLASS = 'btnLoadCommit',
COMMIT_ID = 'commitId',
MESSAGE_DIV_CLASS = 'commit-message',
USER_TO_AVATAR_PATH = {};
ProjectRepositoryWidget = function (container, client, params) {
var commitCount = 'commit_count';
this._el = container;
this._historyType = 'commits';
this._start = null;
this._dialog = params.dialog;
this._projectAccess = client.getProjectAccess();
this._client = client;
this._config = ProjectRepositoryWidget.getDefaultConfig();
ComponentSettings.resolveWithWebGMEGlobal(this._config, ProjectRepositoryWidget.getComponentId());
this._commitBadges = Object.keys(this._config.commitBadges);
this.clear();
this._initializeUI();
if (params) {
COMMIT_PACKAGE_COUNT = params[commitCount] || COMMIT_PACKAGE_COUNT;
this._start = params.start;
}
//attach its controller
new ProjectRepositoryWidgetControl(client, this);
this._logger = Logger.create('gme:Widgets:ProjectRepository:ProjectRepositoryWidget',
WebGMEGlobal.gmeConfig.client.log);
this._logger.debug('Created');
};
ProjectRepositoryWidget.prototype.clear = function () {
var cCommit,
i;
this._commits = this._commits || [];
for (i = 0; i < this._commits.length; i += 1) {
cCommit = this._commits[i];
if (cCommit.ui) {
cCommit.ui.graphEl.remove();
cCommit.ui.commitBadges.forEach(function (commitBadge) {
if (typeof commitBadge.destroy === 'function') {
commitBadge.destroy();
}
});
cCommit.ui.tableEl.find('*').off();
cCommit.ui.tableEl.remove();
}
}
this._commits = [];
this._orderedCommitIds = [];
this._actualCommitId = undefined;
this._branches = [];
this._branchNames = [];
this._branchListUpdated = false;
this._tags = [];
this._tagNames = [];
this._tagListUpdated = false;
this._y = 0;
this._trackEnds = [];
this._renderIndex = -1;
this._width = CONTENT_WIDTH;
this._height = CONTENT_HEIGHT;
this._nonExistingParentPaths = [];
//clear UI content
this._el.empty();
//detach event handlers
this._el.off('click');
this._el.off('keyup');
this._el.parent().css({
width: '',
'margin-left': '',
'margin-top': '',
top: ''
});
};
ProjectRepositoryWidget.prototype.destroy = function () {
this.clear();
this._el.removeClass(REPOSITORY_LOG_VIEW_CLASS);
};
ProjectRepositoryWidget.prototype.addBranch = function (obj) {
this._addBranch(obj);
};
ProjectRepositoryWidget.prototype.clearBranches = function () {
this._clearBranches();
};
ProjectRepositoryWidget.prototype.addTag = function (obj) {
this._addTag(obj);
};
ProjectRepositoryWidget.prototype.clearTags = function () {
this._clearTags();
};
ProjectRepositoryWidget.prototype.addCommit = function (obj) {
var idx = this._orderedCommitIds.push(obj.id) - 1;
this._commits.push({
x: -1,
y: -1,
id: obj.id,
commitData: obj,
ui: undefined
});
this._calculatePositionForCommit(idx);
};
ProjectRepositoryWidget.prototype.showProgressbar = function () {
this._btnShowMore.hide();
this._loader.start();
};
ProjectRepositoryWidget.prototype.hideProgressbar = function () {
this._loader.stop();
this._btnShowMore.show();
};
ProjectRepositoryWidget.prototype.render = function () {
this._render();
};
ProjectRepositoryWidget.prototype.noMoreCommitsToDisplay = function () {
this._noMoreCommitsToDisplay();
};
ProjectRepositoryWidget.prototype.loadMoreCommits = function (start) {
if (start) {
this.onLoadMoreCommits(this._commits.length + COMMIT_PACKAGE_COUNT, start);
} else {
this.onLoadMoreCommits(COMMIT_PACKAGE_COUNT, start);
}
};
ProjectRepositoryWidget.prototype.branchesAndTagsUpdated = function () {
if (this._commits.length === 0) {
this._table.addClass('no-commits-in-table');
} else {
this._table.removeClass('no-commits-in-table');
}
};
/******************* PUBLIC API TO BE OVERRIDDEN IN THE CONTROLLER **********************/
ProjectRepositoryWidget.prototype.onLoadMoreCommits = function (num, start) {
this._logger.warn('onLoadMoreCommits not overridden in Controller num: "' + num + '", start: "' + start + '"');
};
ProjectRepositoryWidget.prototype.onLoadCommit = function (params) {
this._logger.warn('onLoadCommit is not overridden in Controller...params: \'' + JSON.stringify(params) + '\'');
};
ProjectRepositoryWidget.prototype.onDeleteBranchClick = function (branchName, branchHash) {
this._logger.warn('onDeleteBranchClick is not overridden in Controller...branch, branchHash:',
branchName, branchHash);
};
ProjectRepositoryWidget.prototype.onDeleteTagClick = function (branchName, branchHash) {
this._logger.warn('onDeleteTagClick is not overridden in Controller...branch, branchHash:',
branchName, branchHash);
};
ProjectRepositoryWidget.prototype.onCreateBranchFromCommit = function (params) {
this._logger.warn('onCreateBranchFromCommit is not overridden in Controller...params: \'' +
JSON.stringify(params) + '\'');
};
ProjectRepositoryWidget.prototype.onCreateTagFromCommit = function (params) {
this._logger.warn('onCreateTagFromCommit is not overridden in Controller...params: \'' +
JSON.stringify(params) + '\'');
};
ProjectRepositoryWidget.prototype.onSquashFromCommit = function (params) {
this._logger.warn('onSquashFromCommit is not overridden in Controller...params: \'' +
JSON.stringify(params) + '\'');
};
ProjectRepositoryWidget.prototype.onSelectBranch = function (params) {
this._logger.warn('onSelectBranch is not overridden in Controller...params: \'' +
JSON.stringify(params) + '\'');
};
/******************* PRIVATE API *****************************/
ProjectRepositoryWidget.cMessageStyleStr = 'div.' + MESSAGE_DIV_CLASS + ' { max-width: __MW__px; }';
ProjectRepositoryWidget.prototype._initializeUI = function () {
var self = this;
this._el.empty();
this._el.addClass(REPOSITORY_LOG_VIEW_CLASS);
//initialize all containers
this._cMessageStyle = $('<style/>', {type: 'text/css'});
this._cMessageStyle.html(ProjectRepositoryWidget.cMessageStyleStr.replace('__MW__', '400'));
this._el.append(this._cMessageStyle);
/*table layout*/
this._table = $('<table/>', {class: 'table table-hover commit-list'});
this._tHead = $('<thead/>');
this._tHead.append($('<tr><th>Graph</th><th>Actions</th><th>Commit</th>' +
'<th>Message</th><th>User</th><th>Time</th><th class="commit-badge-header">Badges</th></tr>'));
this._tBody = $('<tbody/>');
if (this._commitBadges.length === 0) {
this._tHead.find('.commit-badge-header').hide();
}
this._table.append(this._tHead).append(this._tBody);
this._tableCellActionsIndex = 1;
this._tableCellCommitIDIndex = 2;
this._tableCellMessageIndex = 3;
this._tableCellUserIndex = 4;
this._tableCellTimeStampIndex = 5;
this._tableCellCommitBadgesIndex = 6;
this._el.append(this._table);
//generate container for 'show more' button and progress bar
this._showMoreContainer = $('<div/>', {
class: 'show-more'
});
this._el.append(this._showMoreContainer);
this._loader = new LoaderCircles({containerElement: this._showMoreContainer});
this._loader.setSize(30);
//show more button
this._btnShowMore = $('<a/>', {
class: '',
href: '#'
});
this._btnShowMore.append(SHOW_MORE_BUTTON_TEXT);
this._showMoreContainer.append(this._btnShowMore);
//generate COMMITS container
this._commitsContainer = $('<div/>', {
class: 'commits',
/*"id": "commits",*/
tabindex: 0
});
this._el.append(this._commitsContainer);
this._svgPaper = Raphael(this._commitsContainer[0]); //jshint ignore: line
this._svgPaper.canvas.style.pointerEvents = 'visiblePainted';
this._svgPaper.setSize('100%', '1px');
$(this._svgPaper.canvas).css({top: '0'});
this._generateSVGGradientDefinition();
/*********** HOOK UP EVENT HANDLERS ***********/
this._el.on('click.' + BTN_LOAD_COMMIT_CLASS, '.' + BTN_LOAD_COMMIT_CLASS, function () {
var btn = $(this),
commitId = btn.data(COMMIT_ID);
self.onLoadCommit({id: commitId});
});
this._el.on('click.iconRemove', '.remove-branch-button', function (event) {
var btn = $(this),
branchName = btn.data('branch') + '', //force to be string
branchHash,
i;
for (i = 0; i < self._branches.length; i += 1) {
if (self._branches[i].name === branchName) {
branchHash = self._branches[i].commitId;
}
}
self.onDeleteBranchClick(branchName, branchHash);
event.stopPropagation();
event.preventDefault();
});
this._el.on('click.iconRemove', '.remove-tag-button', function (event) {
var btn = $(this),
tagName = btn.data('tag') + ''; //force to be string
self.onDeleteTagClick(tagName);
event.stopPropagation();
event.preventDefault();
});
this._el.on('click.select-branch-in-text', '.select-branch-in-text', function (event) {
var branchName = $(this).text();
self.onSelectBranch(branchName);
event.stopPropagation();
event.preventDefault();
});
this._btnShowMore.on('click', null, function (event) {
self.loadMoreCommits(self._start);
event.stopPropagation();
event.preventDefault();
});
this._el.off('click.btnCreateBranchFromCommit', '.btnCreateBranchFromCommit');
this._el.on('click.btnCreateBranchFromCommit', '.btnCreateBranchFromCommit', function () {
self._onCreateFromCommitButtonClick($(this));
});
this._el.off('click.btnCreateTagFromCommit', '.btnCreateTagFromCommit');
this._el.on('click.btnCreateTagFromCommit', '.btnCreateTagFromCommit', function () {
self._onCreateFromCommitButtonClick($(this));
});
this._el.off('click.btnSquashFromCommit', '.btnSquashFromCommit');
this._el.on('click.btnSquashFromCommit', '.btnSquashFromCommit', function () {
self._onCreateFromCommitButtonClick($(this));
});
};
ProjectRepositoryWidget.prototype._calculatePositionForCommit = function (cIndex) {
var trackLen = this._trackEnds.length,
cCommit = this._commits[cIndex],
trackEndCommit,
i,
foundTrack = false,
cIdx,
masterRemoteHeadCommit = false;
//check which trackBottom's parent is this guy
for (i = 0; i < trackLen; i += 1) {
cIdx = this._orderedCommitIds.indexOf(this._trackEnds[i]);
trackEndCommit = this._commits[cIdx];
if (trackEndCommit[COMMIT_DATA].parents.indexOf(cCommit.id) !== -1) {
foundTrack = true;
break;
}
}
//vertically it is sure to be next
cCommit.y = this._y;
this._y += Y_DELTA;
//horizontally it goes to the same 'column' as the found trackEnd
if (foundTrack === true) {
cCommit.x = trackEndCommit.x;
this._trackEnds[i] = cCommit.id;
} else {
//no fitting track-end found, start a new track for it
if (this._branches && this._branches.length > 0) {
if (this._branches[0].name === MASTER_BRANCH_NAME && this._branches[0].commitId === cCommit.id) {
masterRemoteHeadCommit = true;
}
}
if (masterRemoteHeadCommit === true) {
//insert this guy to be the first track from the left
this._trackEnds.splice(0, 0, cCommit.id);
cCommit.x = 0;
//shift all the already existing commits by one to the right
i = this._commits.length - 1;
while (i--) {
this._commits[i].x += X_DELTA;
}
this._renderIndex = -1;
} else {
this._trackEnds.push(cCommit.id);
cCommit.x = (this._trackEnds.length - 1) * X_DELTA;
}
}
//this._logger.debug("commitID: " + cCommit.id + ", X: " + cCommit.x + ", Y: " + cCommit.y);
};
ProjectRepositoryWidget.prototype._render = function () {
//render commits from this._renderIndex + 1 to lastItem
var len = this._commits.length,
cCommit,
idx = this._renderIndex === -1 ? 0 : this._renderIndex,
i,
pIdx,
j,
hasVisibleParent;
//render from the beginning
//clear commit container
if (this._renderIndex === -1) {
this._commitsContainer.find('.' + COMMIT_CLASS).remove();
this._svgPaper.clear();
this._tBody.empty();
}
//draw the commit points
for (i = idx; i < len; i += 1) {
cCommit = this._commits[i];
if (cCommit.ui) {
cCommit.ui.graphEl.remove();
cCommit.ui.commitBadges.forEach(function (commitBadge) {
if (typeof commitBadge.destroy === 'function') {
commitBadge.destroy();
}
});
cCommit.ui.tableEl.find('*').off();
cCommit.ui.tableEl.remove();
}
cCommit.ui = this._createItem({
x: cCommit.x,
y: cCommit.y,
id: cCommit.id,
index: i,
parents: cCommit[COMMIT_DATA].parents,
branch: cCommit[COMMIT_DATA].branch,
user: cCommit[COMMIT_DATA].user,
timestamp: cCommit[COMMIT_DATA].timestamp,
message: cCommit[COMMIT_DATA].message,
singleBranch: typeof this._start === 'string'
});
}
this._renderIndex = i;
this._resizeDialog(this._width, this._height);
//remove all nonexsiting parent connections
i = this._nonExistingParentPaths.length;
while (i--) {
this._nonExistingParentPaths[i].remove();
}
this._nonExistingParentPaths = [];
//draw the connections
for (i = 0; i < len; i += 1) {
cCommit = this._commits[i];
//draw lines to parents
if (cCommit[COMMIT_DATA].parents && cCommit[COMMIT_DATA].parents.length > 0) {
hasVisibleParent = false;
for (j = 0; j < cCommit[COMMIT_DATA].parents.length; j += 1) {
pIdx = this._orderedCommitIds.indexOf(cCommit[COMMIT_DATA].parents[j]);
if (pIdx >= idx) {
this._drawLine(this._commits[pIdx], cCommit);
}
hasVisibleParent = pIdx !== -1;
}
if (hasVisibleParent === false) {
//has no visible parent
//ie no line connecting to this guy, just floats in the air
//draw a line to the bottom of the page with a lighter color
this._drawLine(undefined, cCommit);
}
}
}
this._addBranchHeaderLabels();
this._addTagHeaderLabels();
};
ProjectRepositoryWidget.prototype._removeBranchHeaderLabels = function () {
this._tBody.find('.' + BRANCH_LABEL_CLASS).remove();
};
ProjectRepositoryWidget.prototype._removeTagHeaderLabels = function () {
this._tBody.find('.' + TAG_LABEL_CLASS).remove();
};
ProjectRepositoryWidget.prototype._addBranchHeaderLabels = function () {
var len = this._branches.length,
idx;
//update branch information only if any update happened
if (this._branchListUpdated === true) {
//remove existing labels
this._removeBranchHeaderLabels();
while (len--) {
idx = this._orderedCommitIds.indexOf(this._branches[len].commitId);
if (idx !== -1) {
this._applyBranchHeaderLabel(this._commits[idx], this._branches[len].name);
}
}
this._branchListUpdated = false;
}
};
ProjectRepositoryWidget.prototype._addTagHeaderLabels = function () {
var len = this._tags.length,
idx;
//update branch information only if any update happened
if (this._tagListUpdated === true) {
//remove existing labels
this._removeTagHeaderLabels();
while (len--) {
idx = this._orderedCommitIds.indexOf(this._tags[len].commitId);
if (idx !== -1) {
this._applyTagHeaderLabel(this._commits[idx], this._tags[len].name);
}
}
this._tagListUpdated = false;
}
};
ProjectRepositoryWidget.prototype._branchLabelDOMBase = $(
'<span class="label">' +
'<span class="label-name select-branch-in-text" title="Select branch"></span><i data-branch="" ' +
'class="glyphicon glyphicon-remove icon-white remove-branch-button" title="Delete branch"></i></span>'
);
ProjectRepositoryWidget.prototype._tagLabelDOMBase = $(
'<span class="label"><span class="label-name"><i data-tag="" ' +
'class="glyphicon glyphicon-tag tag-label-icon"></i></span>'
);
ProjectRepositoryWidget.prototype._applyBranchHeaderLabel = function (commit, branchName) {
var label = this._branchLabelDOMBase.clone(),
td,
div;
label.find('span.label-name').text(branchName);
label.find('i').attr('data-branch', branchName);
label.addClass(BRANCH_LABEL_CLASS);
label.addClass(COMMON_LABEL_CLASS);
label.addClass('label-success');
if (this._projectAccess.write === false) {
label.addClass('delete-prohibited');
}
td = this._tBody.children()[this._orderedCommitIds.indexOf(commit.id)].cells[this._tableCellMessageIndex];
div = $(td).find('div.' + MESSAGE_DIV_CLASS)[0];
div.insertBefore(label[0], div.childNodes[0]);
};
ProjectRepositoryWidget.prototype._applyTagHeaderLabel = function (commit, tagName) {
var label = this._tagLabelDOMBase.clone(),
td,
div;
label.find('span.label-name').text(tagName);
label.append($('<i data-tag="' + tagName + '" class="glyphicon glyphicon-remove icon-white remove-tag-button"' +
' title="Delete tag"/>'));
label.addClass(TAG_LABEL_CLASS);
label.addClass(COMMON_LABEL_CLASS);
label.addClass('label-primary');
if (this._projectAccess.delete === false) {
label.addClass('delete-prohibited');
}
td = this._tBody.children()[this._orderedCommitIds.indexOf(commit.id)].cells[this._tableCellMessageIndex];
div = $(td).find('div.' + MESSAGE_DIV_CLASS)[0];
div.insertBefore(label[0], div.childNodes[0]);
};
ProjectRepositoryWidget.prototype._addBranch = function (obj) {
var branchName = obj.name,
idx;
idx = this._branchNames.indexOf(branchName);
if (idx !== -1) {
//branch info already in the list
this._branches[idx] = obj;
} else {
if (branchName === MASTER_BRANCH_NAME) {
this._branches.splice(0, 0, obj);
this._branchNames.splice(0, 0, branchName);
} else {
this._branches.push(obj);
this._branchNames.push(branchName);
}
}
this._branchListUpdated = true;
this._addBranchHeaderLabels();
};
ProjectRepositoryWidget.prototype._addTag = function (obj) {
var tagName = obj.name,
idx;
idx = this._tagNames.indexOf(tagName);
if (idx !== -1) {
//branch info already in the list
this._tags[idx] = obj;
} else {
this._tags.push(obj);
this._tagNames.push(tagName);
}
this._tagListUpdated = true;
this._addTagHeaderLabels();
};
ProjectRepositoryWidget.prototype._clearBranches = function () {
this._branches = [];
this._branchNames = [];
this._removeBranchHeaderLabels();
this._branchListUpdated = true;
};
ProjectRepositoryWidget.prototype._clearTags = function () {
this._tags = [];
this._tagNames = [];
this._removeTagHeaderLabels();
this._tagListUpdated = true;
};
ProjectRepositoryWidget.prototype._trDOMBase = $(
'<tr><td></td><td class="actions"></td><td class="commit-hash"></td><td class="message-column">' +
'<div class="' + MESSAGE_DIV_CLASS + '"></div></td><td class="user-column"></td>' +
'<td></td><td class="commit-badge-column"></td></tr>'
);
ProjectRepositoryWidget.prototype._createBranchBtnDOMBase = $(
'<button class="btn btn-default btn-xs btnCreateBranchFromCommit" ' +
'href="#" title="Create new branch from here">' +
'<i class="glyphicon glyphicon-edit create-branch-icon"></i></button>'
);
ProjectRepositoryWidget.prototype._createTagBtnDOMBase = $(
'<button class="btn btn-default btn-xs btnCreateTagFromCommit" ' +
'href="#" title="Create a tag here">' +
'<i class="glyphicon glyphicon-tags create-tag-icon"></i></button>'
);
ProjectRepositoryWidget.prototype._squashBtnDOMBase = $(
'<button class="btn btn-default btn-xs btnSquashFromCommit" ' +
'href="#" title="Squash branch from this commit">' +
'<i class="glyphicon glyphicon-compressed squash-branch-icon"></i></button>'
);
//ProjectRepositoryWidget.prototype._loadCommitBtnDOMBase = $('' +
// '<button class="btn btn-default btn-xs ' + BTN_LOAD_COMMIT_CLASS + '" href="#" title="Load this commit">' +
// '<i class="glyphicon glyphicon-share"></i></button>'
//);
ProjectRepositoryWidget.prototype._createItem = function (params) {
var firstRow = this._tBody.children().length === 0,
userId = params.user || '',
result = {
graphEl: null,
tableEl: null,
commitBadges: []
},
itemObj,
tr,
btn,
commitBadgeContainer,
i,
when;
itemObj = $('<div/>', {
class: COMMIT_CLASS,
'data-id': params.id,
'data-b': params.branch
});
itemObj.css({
left: params.x,
top: params.y
});
if (params.actual) {
itemObj.addClass('actual');
}
this._commitsContainer.append(itemObj);
this._width = Math.max(this._width, params.x + ITEM_WIDTH);
this._height = Math.max(this._height, params.y + ITEM_HEIGHT);
//generate table row for this guy
tr = this._trDOMBase.clone();
//fill the data into the columns
if (firstRow === true) {
this._graphPlaceHolder = $('<div/>');
$(tr[0].cells[0]).append(this._graphPlaceHolder);
}
when = new Date(parseInt(params.timestamp, 10));
$(tr[0].cells[this._tableCellCommitIDIndex]).append(params.id.substr(0, 7));
$(tr[0].cells[this._tableCellCommitIDIndex]).attr('title', 'Select commit ' + params.id);
$(tr[0].cells[this._tableCellCommitIDIndex]).addClass(BTN_LOAD_COMMIT_CLASS);
$(tr[0].cells[this._tableCellCommitIDIndex]).data(COMMIT_ID, params.id);
$(tr[0].cells[this._tableCellMessageIndex]).find('div.' + MESSAGE_DIV_CLASS).text(params.message);
if (USER_TO_AVATAR_PATH.hasOwnProperty(userId) === false) {
USER_TO_AVATAR_PATH[userId] = blockies({
seed: userId,
size: 6,
scale: 8
}).toDataURL();
}
$(tr[0].cells[this._tableCellUserIndex]).addClass();
$(tr[0].cells[this._tableCellUserIndex]).append(
$('<span class="avatar-container">' + userId +
'<img class="avatar-image" src="' + USER_TO_AVATAR_PATH[userId] + '"/></span>'));
$(tr[0].cells[this._tableCellTimeStampIndex]).append(
//util.formattedDate(new Date(parseInt(params.timestamp, 10)), 'elapsed')
moment(when).fromNow()
);
$(tr[0].cells[this._tableCellTimeStampIndex]).attr(
'title', moment(when).local().format('dddd, MMMM Do YYYY, h:mm:ss a')
);
if (this._commitBadges.length === 0) {
$(tr[0].cells[this._tableCellCommitBadgesIndex]).hide();
} else {
commitBadgeContainer = $('<div>', {class: 'commit-badge-container'});
$(tr[0].cells[this._tableCellCommitBadgesIndex]).append(commitBadgeContainer);
for (i = 0; i < this._commitBadges.length; i += 1) {
this._loadCommitBadge(this._commitBadges[i], commitBadgeContainer, params, result);
}
}
//generate 'Create branch from here' button
btn = this._createBranchBtnDOMBase.clone();
btn.data(COMMIT_ID, params.id);
if (this._projectAccess.write === false) {
btn.disable(true);
}
tr[0].cells[this._tableCellActionsIndex].appendChild(btn[0]);
//generate 'Create tag here' button
btn = this._createTagBtnDOMBase.clone();
btn.data(COMMIT_ID, params.id);
if (this._projectAccess.write === false) {
btn.disable(true);
}
tr[0].cells[this._tableCellActionsIndex].appendChild(btn[0]);
//generate 'switch to this commit' button
//btn = this._generateLoadCommitBtnForCommit(params.id);
//tr[0].cells[this._tableCellActionsIndex].appendChild(btn[0]);
//generate 'Squash branch' button
if (params.singleBranch) {
btn = this._squashBtnDOMBase.clone();
btn.data(COMMIT_ID, params.id);
if (this._projectAccess.write === false) {
btn.disable(true);
}
tr[0].cells[this._tableCellActionsIndex].appendChild(btn[0]);
}
this._tBody.append(tr);
result.graphEl = itemObj;
result.tableEl = tr;
return result;
};
//ProjectRepositoryWidget.prototype._generateLoadCommitBtnForCommit = function (commitId) {
// var btn = this._loadCommitBtnDOMBase.clone();
// btn.data(COMMIT_ID, commitId);
//
// return btn;
//};
ProjectRepositoryWidget.prototype.setActualCommitId = function (commitId) {
var oldId = this._actualCommitId;
if (this._actualCommitId !== commitId) {
this._actualCommitId = commitId;
this._updateActualCommitUI(oldId, this._actualCommitId);
}
};
ProjectRepositoryWidget.prototype._updateActualCommitUI = function (oldId, newId) {
var idx = this._orderedCommitIds.indexOf(oldId);
//remove old actual marker
if (idx !== -1) {
this._commits[idx].ui.graphEl.removeClass(ACTUAL_COMMIT_CLASS);
//btn = this._generateLoadCommitBtnForCommit(oldId);
//add 'LoadCommit' button to that row
//this._tBody.children()[idx].cells[this._tableCellActionsIndex].appendChild(btn[0]);
}
//add 'actual' marker to new value
idx = this._orderedCommitIds.indexOf(newId);
if (idx !== -1) {
//add 'actual' class to commit DOM
this._commits[idx].ui.graphEl.addClass(ACTUAL_COMMIT_CLASS);
//remove 'LoadCommit' button from that row
$(this._tBody.children()[idx]
.cells[this._tableCellActionsIndex]).find('.' + BTN_LOAD_COMMIT_CLASS).remove();
}
};
ProjectRepositoryWidget.prototype._drawLine = function (srcDesc, dstDesc) {
var pathDef,
nonVisibleSource = srcDesc === undefined,
x = nonVisibleSource ? dstDesc.x + ITEM_WIDTH / 2 : srcDesc.x + ITEM_WIDTH / 2,
y = nonVisibleSource ? this._height : srcDesc.y + ITEM_HEIGHT / 2,
x2 = dstDesc.x + ITEM_WIDTH / 2,
y2 = dstDesc.y + ITEM_HEIGHT / 2,
dX = x2 - x,
path,
pathDefGradient;
if (dX === 0) {
//vertical line
y2 = dstDesc.y + ITEM_HEIGHT + 2;
if (nonVisibleSource === false) {
y = srcDesc.y - 2;
pathDef = ['M', x, y, 'L', x2, y2];
} else {
//is it the most bottom commit circle??
if (y !== dstDesc.y + ITEM_HEIGHT) {
//will be drawn as two separate path
//send one will be the gradient
pathDef = ['M', x, y - Y_DELTA, 'L', x2, y2];
//inject fake initial "move to" --> Gradient will be applied
pathDefGradient = ['M', x - 1, y, 'M', x, y, 'L', x2, y - Y_DELTA];
}
}
} else {
//multiple segment line
if (x2 < x) {
//from right to left (merge)
x2 = dstDesc.x + ITEM_WIDTH + 2;
y = srcDesc.y - 1;
y2 += 1;
pathDef = ['M', x, y, 'L', x, y2 + LINE_CORNER_SIZE, 'L', x - LINE_CORNER_SIZE, y2, 'L', x2, y2];
} else {
//from left to right (new branch)
x = srcDesc.x + ITEM_WIDTH + 2;
y2 = dstDesc.y + ITEM_HEIGHT + 3;
y += 1;
pathDef = ['M', x, y, 'L', x2 - LINE_CORNER_SIZE, y, 'L', x2, y - LINE_CORNER_SIZE, 'L', x2, y2];
}
}
if (nonVisibleSource === true) {
if (pathDef) {
path = this._svgPaper.path(pathDef.join(','));
path.attr({stroke: LINE_FILL_COLOR});
this._nonExistingParentPaths.push(path);
}
if (pathDefGradient) {
path = this._svgPaper.path(pathDefGradient.join(','));
path.node.setAttribute('stroke', 'url(#' + NON_EXISTING_PARENT_LINE_GRADIENT_NAME + ')');
this._nonExistingParentPaths.push(path);
}
} else {
path = this._svgPaper.path(pathDef.join(','));
path.attr({stroke: LINE_FILL_COLOR});
}
};
ProjectRepositoryWidget.prototype._resizeDialog = function (contentWidth, contentHeight) {
var WINDOW_PADDING = 30,
DIALOG_HEADER_HEIGHT = 70,
DIALOG_FOOTER_HEIGHT = 70,
wH = $(window).height(),
wW = $(window).width(),
tWidth;
this._svgPaper.setSize(contentWidth, contentHeight);
this._generateSVGGradientDefinition();
//set the correct with for the 'Graph' column in the table to fit the drawn graph
this._graphPlaceHolder.css('width', contentWidth);
//make it almost "full screen"
wW = wW - 2 * WINDOW_PADDING;
wH = wH - 2 * WINDOW_PADDING - DIALOG_HEADER_HEIGHT - DIALOG_FOOTER_HEIGHT;
this._cMessageStyle.html(ProjectRepositoryWidget.cMessageStyleStr.replace('__MW__', wW * 0.66));
tWidth = this._table.width();
this._showMoreContainer.css('width', tWidth);
};
ProjectRepositoryWidget.prototype._noMoreCommitsToDisplay = function () {
this._btnShowMore.hide();
this._btnShowMore.off('click');
this._loader.destroy();
//generate container for 'show more' button and progress bar
this._showMoreContainer.empty();
this._showMoreContainer.remove();
};
ProjectRepositoryWidget.prototype._generateSVGGradientDefinition = function () {
if (!this._svgPaper.canvas.getElementById(NON_EXISTING_PARENT_LINE_GRADIENT_NAME)) {
//generate gradient color dinamically into SVG
var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'),
linearGradient, stop0, stop1;
linearGradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient');
linearGradient.setAttribute('x1', '0%');
linearGradient.setAttribute('x2', '0%');
linearGradient.setAttribute('y1', '0%');
linearGradient.setAttribute('y2', '100%');
linearGradient.setAttribute('id', NON_EXISTING_PARENT_LINE_GRADIENT_NAME);
stop0 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
stop0.setAttribute('offset', '0%');
stop0.setAttribute('style', 'stop-color: ' + LINE_FILL_COLOR);
stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
stop1.setAttribute('offset', '100%');
stop1.setAttribute('style', 'stop-color: #FFFFFF');
linearGradient.appendChild(stop0);
linearGradient.appendChild(stop1);
defs.appendChild(linearGradient);
this._svgPaper.canvas.appendChild(defs);
}
};
ProjectRepositoryWidget.prototype._onCreateFromCommitButtonClick = function (btn) {
var td = btn.parent(),
currentBranchCreateCtrl,
isBranch = btn.hasClass('btnCreateBranchFromCommit'),
isSquash = btn.hasClass('btnSquashFromCommit'),
createBranchHTML = $('<div class="input-group form-group"></div>'),
txtInput = $('<input class="form-control span2 input-mini" type="text">'),
btnSave = $('<button class="btn btn-default btn-xs" type="button" title="Create">' +
'<i class="glyphicon glyphicon-ok"></i></button>'),
btnCancel = $('<button class="btn btn-default btn-xs" type="button" title="Cancel">' +
'<i class="glyphicon glyphicon-remove"></i></button>'),
self = this,
idx,
commitId;
//if squash, then show confirm dialog and be done with it
if (isSquash) {
commitId = btn.data(COMMIT_ID);
idx = this._orderedCommitIds.indexOf(commitId);
this._tBody.find('tr:lt(' + idx + ')').addClass('in-selection');
self.onSquashFromCommit({
branchName: this._start,
commitId: commitId
}, function () {
self._tBody.find('tr').removeClass('in-selection');
});
return;
}
//find already displayed branch create control and 'cancel' it
currentBranchCreateCtrl = this._tBody.find('.' + CREATE_BRANCH_EDIT_CONTROL_CLASS + ' > .btn');
if (currentBranchCreateCtrl.length !== 0) {
$(currentBranchCreateCtrl[1]).trigger('click');
}
//create new one for the clicked commit
createBranchHTML.addClass(CREATE_BRANCH_EDIT_CONTROL_CLASS);
createBranchHTML.append(txtInput).append(btnSave).append(btnCancel);
//save old content
td.children().css('display', 'none');
//add control to TD cell
td.append(createBranchHTML);
//on CANCEL don't do anything, revert DOM change
btnCancel.on('click', function (event) {
td.find('.' + CREATE_BRANCH_EDIT_CONTROL_CLASS).remove();
td.children().css('display', 'inline-block');
event.stopPropagation();
});
txtInput.on('keyup', function (event) {
var textVal = txtInput.val(),
hasError = false;
if (textVal === '') {
hasError = true;
} else if (isBranch) {
hasError = self._branchNames.indexOf(textVal) !== -1 || !REGEXP.BRANCH.test(textVal);
} else {
hasError = self._tagNames.indexOf(textVal) !== -1 || !REGEXP.TAG.test(textVal);
}
if (hasError) {
createBranchHTML.addClass('has-error');
btnSave.disable(true);
} else {
createBranchHTML.removeClass('has-error');
btnSave.disable(false);
}
switch (event.which) {
case 27: // [esc]
// discard changes on [esc]
btnCancel.trigger('click');
event.preventDefault();
event.stopPropagation();
break;
case 13: // [enter]
// save changes on [ENTER]
btnSave.trigger('click');
event.preventDefault();
event.stopPropagation();
break;
}
});
txtInput.focus().trigger('keyup');
//on SAVE save changes and revert DOM
btnSave.on('click', function (event) {
var newName = txtInput.val(),
exists = isBranch ? self._branchNames.indexOf(newName) > -1 : self._tagNames.indexOf(newName) > -1;
if (newName !== '' && exists === false) {
td.find('.' + CREATE_BRANCH_EDIT_CONTROL_CLASS).remove();
td.children().css('display', 'inline-block');
if (isBranch) {
self.onCreateBranchFromCommit({
commitId: btn.data(COMMIT_ID),
name: newName
});
} else {
self.onCreateTagFromCommit({
commitId: btn.data(COMMIT_ID),
name: newName
});
}
}
event.stopPropagation();
});
};
ProjectRepositoryWidget.prototype._loadCommitBadge = function (badgeId, container, params, result) {
var self = this,
placeHolder = $('<i>', {class: 'fa fa-spinner fa-spin place-holder'});
container.append(placeHolder);
requirejs([this._config.commitBadges[badgeId].path],
function (CommitBadge) {
// Ensure that the container is still part of the DOM
// https://api.jquery.com/jQuery.contains/
if ($.contains(self._el[0], container[0])) {
placeHolder.remove();
result.commitBadges.push(new CommitBadge(container, self._client, params));
} else {
self._logger.error('ProjectRepositoryWidget or table row removed before CommitBadge loaded');
}
},
function (err) {
self._logger.error(err);
}
);
};
ProjectRepositoryWidget.getDefaultConfig = function () {
return {
commitBadges: {
//'UIRecorderCommitBadge': {
// path: 'UIRecorder/UIRecorderCommitBadge'
//}
}
};
};
ProjectRepositoryWidget.getComponentId = function () {
return 'GenericUIProjectRepositoryWidget';
};
return ProjectRepositoryWidget;
});