/** * Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ RED.sidebar.versionControl = (function() { var sidebarContent; var sections; var allChanges = {}; var unstagedChangesList; var stageAllButton; var stagedChangesList; var unstageAllButton; var unstagedChanges; var stagedChanges; var bulkChangeSpinner; var unmergedContent; var unmergedChangesList; var commitButton; var localChanges; var localCommitList; var localCommitListShade; // var remoteCommitList; var isMerging; function viewFileDiff(entry,state) { var activeProject = RED.projects.getActiveProject(); var diffTarget = (state === 'staged')?"index":"tree"; utils.sendRequest({ url: "projects/"+activeProject.name+"/diff/"+diffTarget+"/"+encodeURIComponent(entry.file), type: "GET", responses: { 0: function(error) { console.log(error); // done(error,null); }, 200: function(data) { var title; if (state === 'unstaged') { title = RED._("sidebar.project.versionControl.unstagedChanges")+' : '+entry.file } else if (state === 'staged') { title = RED._("sidebar.project.versionControl.stagedChanges")+' : '+entry.file } else { title = RED._("sidebar.project.versionControl.resolveConflicts")+' : '+entry.file } var options = { diff: data.diff, title: title, unmerged: state === 'unmerged', project: activeProject } if (state == 'unstaged') { options.oldRevTitle = entry.indexStatus === " "?RED._("sidebar.project.versionControl.head"):RED._("sidebar.project.versionControl.staged"); options.newRevTitle = RED._("sidebar.project.versionControl.unstaged"); options.oldRev = entry.indexStatus === " "?"@":":0"; options.newRev = "_"; } else if (state === 'staged') { options.oldRevTitle = RED._("sidebar.project.versionControl.head"); options.newRevTitle = RED._("sidebar.project.versionControl.staged"); options.oldRev = "@"; options.newRev = ":0"; } else { options.oldRevTitle = RED._("sidebar.project.versionControl.local"); options.newRevTitle = RED._("sidebar.project.versionControl.remote"); options.commonRev = ":1"; options.oldRev = ":2"; options.newRev = ":3"; options.onresolve = function(resolution) { utils.sendRequest({ url: "projects/"+activeProject.name+"/resolve/"+encodeURIComponent(entry.file), type: "POST", responses: { 0: function(error) { console.log(error); // done(error,null); }, 200: function(data) { refresh(true); }, 400: { 'unexpected_error': function(error) { console.log(error); // done(error,null); } }, } },{resolutions:resolution.resolutions[entry.file]}); } } RED.diff.showUnifiedDiff(options); }, 400: { 'unexpected_error': function(error) { console.log(error); // done(error,null); } } } }) } function createChangeEntry(row, entry, status, state) { row.addClass("sidebar-version-control-change-entry"); var container = $('
').appendTo(row); if (entry.label) { row.addClass('node-info-none'); container.text(entry.label); if (entry.button) { container.css({ display: "inline-block", maxWidth: "300px", textAlign: "left" }) var toolbar = $('
').appendTo(container); $('').text(entry.button.label) .appendTo(toolbar) .click(entry.button.click); } return; } var icon = $('').appendTo(container); var entryLink = $('') .appendTo(container) .click(function(e) { e.preventDefault(); viewFileDiff(entry,state); }); var label = $('').appendTo(entryLink); var entryTools = $('
').appendTo(row); var bg; var revertButton; if (state === 'unstaged') { bg = $('').appendTo(entryTools); revertButton = $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); var spinner = utils.addSpinnerOverlay(container).addClass('projects-dialog-spinner-contain'); var notification = RED.notify(RED._("sidebar.project.versionControl.revert",{file:entry.file}), { type: "warning", modal: true, fixed: true, buttons: [ { text: RED._("common.label.cancel"), click: function() { spinner.remove(); notification.close(); } },{ text: RED._("sidebar.project.versionControl.revertChanges"), click: function() { notification.close(); var activeProject = RED.projects.getActiveProject(); var url = "projects/"+activeProject.name+"/files/_/"+entry.file; var options = { url: url, type: "DELETE", responses: { 200: function(data) { spinner.remove(); }, 400: { 'unexpected_error': function(error) { spinner.remove(); console.log(error); // done(error,null); } } } } RED.deploy.setDeployInflight(true); utils.sendRequest(options).always(function() { setTimeout(function() { RED.deploy.setDeployInflight(false); },500); }); } } ] }) }); } bg = $('').appendTo(entryTools); if (state !== 'unmerged') { $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); var activeProject = RED.projects.getActiveProject(); entry.spinner = utils.addSpinnerOverlay(row).addClass('projects-version-control-spinner-sidebar'); utils.sendRequest({ url: "projects/"+activeProject.name+"/stage/"+encodeURIComponent(entry.file), type: (state==='unstaged')?"POST":"DELETE", responses: { 0: function(error) { console.log(error); // done(error,null); }, 200: function(data) { refreshFiles(data); }, 400: { 'unexpected_error': function(error) { console.log(error); // done(error,null); } }, } },{}); }); } entry["update"+((state==='unstaged')?"Unstaged":"Staged")] = function(entry,status) { container.removeClass(); var iconClass = ""; if (status === 'A') { container.addClass("node-diff-added"); iconClass = "fa-plus-square"; } else if (status === '?') { container.addClass("node-diff-unchanged"); iconClass = "fa-question-circle-o"; } else if (status === 'D') { container.addClass("node-diff-deleted"); iconClass = "fa-minus-square"; } else if (status === 'M') { container.addClass("node-diff-changed"); iconClass = "fa-square"; } else if (status === 'R') { container.addClass("node-diff-changed"); iconClass = "fa-toggle-right"; } else if (status === 'U') { container.addClass("node-diff-conflicted"); iconClass = "fa-exclamation-triangle"; } else { iconClass = "fa-exclamation-triangle" } label.empty(); $('').text(entry.file.replace(/\\(.)/g,"$1")).appendTo(label); if (entry.oldName) { $('').prependTo(label); $('').text(entry.oldName.replace(/\\(.)/g,"$1")).prependTo(label); // label.text(entry.oldName+" -> "+entry.file); } // console.log(entry.file,status,iconClass); icon.removeClass(); icon.addClass("fa "+iconClass); if (entry.spinner) { entry.spinner.remove(); delete entry.spinner; } if (revertButton) { revertButton.toggle(status !== '?'); } entryLink.toggleClass("disabled",(status === 'D' || status === '?')); } entry["update"+((state==='unstaged')?"Unstaged":"Staged")](entry, status); } var utils; var emptyStagedItem; var emptyMergedItem; function init(_utils) { utils = _utils; RED.actions.add("core:show-version-control-tab",show); RED.events.on("deploy", function() { var activeProject = RED.projects.getActiveProject(); if (activeProject) { // TODO: this is a full refresh of the files - should be able to // just do an incremental refresh allChanges = {}; unstagedChangesList.editableList('empty'); stagedChangesList.editableList('empty'); unmergedChangesList.editableList('empty'); $.getJSON("projects/"+activeProject.name+"/status",function(result) { refreshFiles(result); }); } }); RED.events.on("login",function() { refresh(true); }); sidebarContent = $('
', {class:"sidebar-version-control"}); var stackContainer = $("
",{class:"sidebar-version-control-stack"}).appendTo(sidebarContent); sections = RED.stack.create({ container: stackContainer, fill: true, singleExpanded: true }); localChanges = sections.add({ title: RED._("sidebar.project.versionControl.localChanges"), collapsible: true }); localChanges.expand(); localChanges.content.css({height:"100%"}); var bg = $('
').appendTo(localChanges.header); $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); refresh(true); }) emptyStagedItem = { label: RED._("sidebar.project.versionControl.none") }; emptyMergedItem = { label: RED._("sidebar.project.versionControl.conflictResolve") }; var unstagedContent = $('').appendTo(localChanges.content); var header = $('').appendTo(unstagedContent); stageAllButton = $('') .appendTo(header) .click(function(evt) { evt.preventDefault(); evt.stopPropagation(); var toStage = Object.keys(allChanges).filter(function(fn) { return allChanges[fn].treeStatus !== ' '; }); updateBulk(toStage,true); }); unstagedChangesList = $("
    ",{style:"position: absolute; top: 30px; bottom: 0; right:0; left:0;"}).appendTo(unstagedContent); unstagedChangesList.editableList({ addButton: false, scrollOnAdd: false, addItem: function(row,index,entry) { createChangeEntry(row,entry,entry.treeStatus,'unstaged'); }, sort: function(A,B) { if (A.treeStatus === '?' && B.treeStatus !== '?') { return 1; } else if (A.treeStatus !== '?' && B.treeStatus === '?') { return -1; } return A.file.localeCompare(B.file); } }) unmergedContent = $('').appendTo(localChanges.content); header = $('').appendTo(unmergedContent); bg = $('
    ').appendTo(header); var abortMergeButton = $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); evt.stopPropagation(); var spinner = utils.addSpinnerOverlay(unmergedContent); var activeProject = RED.projects.getActiveProject(); RED.deploy.setDeployInflight(true); utils.sendRequest({ url: "projects/"+activeProject.name+"/merge", type: "DELETE", responses: { 0: function(error) { console.log(error); }, 200: function(data) { spinner.remove(); refresh(true); }, 400: { 'unexpected_error': function(error) { console.log(error); } }, } }).always(function() { setTimeout(function() { RED.deploy.setDeployInflight(false); },500); }); }); unmergedChangesList = $("
      ",{style:"position: absolute; top: 30px; bottom: 0; right:0; left:0;"}).appendTo(unmergedContent); unmergedChangesList.editableList({ addButton: false, scrollOnAdd: false, addItem: function(row,index,entry) { if (entry === emptyMergedItem) { entry.button = { label: RED._("sidebar.project.versionControl.commit"), click: function(evt) { evt.preventDefault(); evt.stopPropagation(); showCommitBox(); } } } createChangeEntry(row,entry,entry.treeStatus,'unmerged'); }, sort: function(A,B) { if (A.treeStatus === '?' && B.treeStatus !== '?') { return 1; } else if (A.treeStatus !== '?' && B.treeStatus === '?') { return -1; } return A.file.localeCompare(B.file); } }) var stagedContent = $('').appendTo(localChanges.content); header = $('').appendTo(stagedContent); bg = $('
      ').appendTo(header); var showCommitBox = function() { commitMessage.val(""); submitCommitButton.attr("disabled",true); unstagedContent.css("height","30px"); if (unmergedContent.is(":visible")) { unmergedContent.css("height","30px"); stagedContent.css("height","calc(100% - 60px - 175px)"); } else { stagedContent.css("height","calc(100% - 30px - 175px)"); } commitBox.show(); setTimeout(function() { commitBox.css("height","175px"); },10); stageAllButton.attr("disabled",true); unstageAllButton.attr("disabled",true); commitButton.attr("disabled",true); abortMergeButton.attr("disabled",true); commitMessage.focus(); } commitButton = $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); evt.stopPropagation(); showCommitBox(); }); unstageAllButton = $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); evt.stopPropagation(); var toUnstage = Object.keys(allChanges).filter(function(fn) { return allChanges[fn].indexStatus !== ' ' && allChanges[fn].indexStatus !== '?'; }); updateBulk(toUnstage,false); }); stagedChangesList = $("
        ",{style:"position: absolute; top: 30px; bottom: 0; right:0; left:0;"}).appendTo(stagedContent); stagedChangesList.editableList({ addButton: false, scrollOnAdd: false, addItem: function(row,index,entry) { createChangeEntry(row,entry,entry.indexStatus,'staged'); }, sort: function(A,B) { return A.file.localeCompare(B.file); } }) commitBox = $('').hide().appendTo(localChanges.content); var commitMessage = $('') .appendTo(commitBox) .on("change keyup paste",function() { submitCommitButton.attr('disabled',$(this).val().trim()===""); }); var commitToolbar = $('