$(document).ready(function() { var view = new ckStreamView(); //view.Load(); $('#userSelect').hide(); $('#depotSelect').hide(); $('#viewPrompt').show(); $( "#trajectoryDialog" ).dialog({ autoOpen: false, show: "blind", hide: "blind", height: 400, width: 480, //modal: true, close: function() { view.hideChangeTrajectory(); view.closeDialog( "#changeSelector" ); view.closeDialog( "#changeDetails" ); }, open: function() { view.showChangeTrajectory(); } }); $( "#trajectoryDialog" ).dialog( "option", "position", [0,0] ); $( "#streamDialog" ).dialog({ autoOpen: false, show: "blind", hide: "blind" }); $( "#gitDetailsDialog" ).dialog({ autoOpen: false, show: "blind", hide: "blind" }); $( "#gitCreateDialog" ).dialog({ autoOpen: false, show: "blind", hide: "blind" }); $( "#p4ConnectionDialog" ).dialog({ autoOpen: false, show: "blind", hide: "blind", buttons: { "Connect": function() { view.connectToP4Server( $( "#p4Port" ).val(), $( "#p4User" ).val(), $( "#p4Workspace" ).val() ); localStorage.p4Port = $( "#p4Port" ).val(); localStorage.p4User = $( "#p4User" ).val(); localStorage.p4Workspace = $( "#p4Workspace" ).val(); $( this ).dialog( "close" ); }, Cancel: function() { $( this ).dialog( "close" ); } }, close: function() { }, open: function() { $( "#p4Port" ).val(localStorage.p4Port); $( "#p4User" ).val(localStorage.p4User); $( "#p4Workspace" ).val(localStorage.p4Workspace); } }); $( "#changeDetails" ).dialog({ autoOpen: false, show: "blind", hide: "blind" }); $( "#changeSelector" ).dialog({ autoOpen: false, show: "blind", hide: "blind" }); $("#userSelect").change( function () { var str = ""; $("select option:selected").each(function () { view.loadUserWorkspaces($(this).text()); }); }).change(); $("#depotSelect").change( function () { $("select option:selected").each(function () { view.selectEvent(); }); }); document.addEventListener('p4selection', view.selectEvent); $('#theDoc').click(view.backgroundClicked); $('#repoBrowse').click(view.setGitDirectory); $('#workspaceBrowse').click(view.setWorkspaceDirectory); //this initializes the GitP4Repo dialog...there's probably a better place to put this stuff. $(function() { var repoName = $( "#repoName" ), repoFolder = $( "#repoFolder" ), workspaceName = $( "#workspaceName" ), workspaceFolder = $( "#workspaceFolder" ), configFile = $( "#configFile" ), allFields = $( [] ).add( repoName ).add( repoFolder ).add( workspaceName ).add( workspaceFolder ).add( configFile ) , tips = $( ".validateTips" ); function updateTips( t ) { tips .text( t ) .addClass( "ui-state-highlight" ); setTimeout(function() { tips.removeClass( "ui-state-highlight", 1500 ); }, 500 ); } function checkLength( o, n, min, max ) { if ( o.val().length > max || o.val().length < min ) { o.addClass( "ui-state-error" ); updateTips( "Length of " + n + " must be between " + min + " and " + max + "." ); return false; } else { return true; } } function checkRegexp( o, regexp, n ) { if ( !( regexp.test( o.val() ) ) ) { o.addClass( "ui-state-error" ); updateTips( n ); return false; } else { return true; } } $( "#gitCreateDialog" ).dialog({ autoOpen: false, height: 480, width: 350, buttons: { "Create Git Repo": function() { var gitP4Info = new Object(); gitP4Info.gitRepoName = repoName.val(); gitP4Info.gitRepoFolder = repoFolder.val() gitP4Info.workspaceName = workspaceName.val(); gitP4Info.workspaceFolder = workspaceFolder.val(); localStorage.gitRepoDefaultFolder = repoFolder.val(); localStorage.workspaceDefaultFolder = workspaceFolder.val(); //gitP4Info.configFile = configFile.val(); //console.log('branch git repo from ' + view.getSelectedStream()); view.createGitP4Connection(gitP4Info); var bValid = true; allFields.removeClass( "ui-state-error" ); //TODO: real error testing /* bValid = bValid && checkLength( repoName, "repo name", 3, 16 ); bValid = bValid && checkLength( repoFolder, "repo location", 6, 80 ); bValid = bValid && checkLength( workspaceName, "workspace name", 5, 16 ); bValid = bValid && checkLength( workspaceFolder, "workspace location", 5, 16 ); bValid = bValid && checkRegexp( repoName, /^[a-z]([0-9a-z_])+$/i, "Username may consist of a-z, 0-9, underscores, begin with a letter." ); // From jquery.validate.js (by joern), contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/ //bValid = bValid && checkRegexp( repoDirectory, /^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w ]*))+\.(txt|TXT)$, "eg. C:/monkey" ); bValid = bValid && checkRegexp( password, /^([0-9a-zA-Z])+$/, "Password field only allow : a-z 0-9" ); */ if ( bValid ) { $( this ).dialog( "close" ); } }, Cancel: function() { $( this ).dialog( "close" ); } }, close: function() { }, open: function() { workspaceFolder.val(localStorage.workspaceDefaultFolder); repoFolder.val(localStorage.gitRepoDefaultFolder); //view.testGitInstallation(); //document.getElementById('configFile').value = view.getConfigFile(); } }); }); //temp //view.selectEvent(); view.connect(); }); function ckStreamView() { var thisView = this; var mStreamUtilities = new StreamUtilities(); var streamView = new StreamView(mStreamUtilities); //if the the user can't access the local hard drive (eg they're using P4V rather than P4ClientKit), //remove the stuff that won't work this.connect = function() { $( "#p4ConnectionDialog" ).dialog( "open" ); } var mDepots = []; var mCurrentUser;// = mStreamUtilities.getUser(); this.connectToP4Server = function(port, user, workspace ) { mStreamUtilities.setP4Server(port, user, workspace); mCurrentUser = mStreamUtilities.getUser(); this.Load(); this.selectEvent(); } this.closeDialog = function(dialog) { if( $( dialog ).dialog('isOpen')) $( dialog ).dialog('close'); } this.closeAllDialogs = function() { this.closeDialog( "#trajectoryDialog" ); this.closeDialog( "#streamDialog" ); this.closeDialog( "#gitDetailsDialog" ); this.closeDialog( "#changeDetails" ); this.closeDialog( "#gitCreateDialog" ); this.closeDialog( "#changeSelector" ); this.closeDialog( "#changeDetails" ); } this.backgroundClicked = function(evt) { if(evt.toElement.id != "theDoc") return; thisView.closeDialog( "#streamDialog" ); thisView.closeDialog( "#gitDetailsDialog" ); setSelectedStream(null, null); streamView.drawStreams(mRootNode, mDrawStatus); //console.log('click background'); } function openGitCreateDialog(x, y) { $( "#gitCreateDialog" ).dialog( "option", "position", [x,y] ); $( "#gitCreateDialog" ).dialog( "open" ); } this.setGitDirectory = function() { var dir = mStreamUtilities.selectDirectory(); $("#repoFolder").val(dir); } this.setWorkspaceDirectory = function() { var dir = mStreamUtilities.selectDirectory(); $("#workspaceFolder").val(dir); } function testGitInstallation() { return mStreamUtilities.gitP4Installed(); } var mSelectedDepot = null; var mSelectedStream = null; var mStreamNodeHash = new Object();//this is going to a Node[stream] hash var mDrawStatus = true; function setSelectedStream(stream, shiftKeyDown) { if( !stream ) var numSelected = 0; for(var i = 0; i < mStreams.length; i++) if( mStreamNodeHash[mStreams[i]] && mStreamNodeHash[mStreams[i]].selected ) numSelected++; if(!shiftKeyDown || numSelected > 1)//if multiple streams are already selected, de-select them all except the current { for(var i = 0; i < mStreams.length; i++) if(mStreamNodeHash[mStreams[i]]) mStreamNodeHash[mStreams[i]].selected = mStreams[i].mStream == stream; } else//if we've gotten here, there should be only one previously selected node in the graph { //ideally we want to set up a trail of selected streams between the two streams the user has chosen. var alreadySelectedNode; var currentNodeToBeSelected; for(var i = 0; i < mStreams.length; i++) { if(mStreamNodeHash[mStreams[i]]) { if( mStreamNodeHash[mStreams[i]].selected ) { alreadySelectedNode = mStreamNodeHash[mStreams[i]]; } else if( mStreams[i].mStream == stream ) { currentNodeToBeSelected = mStreamNodeHash[mStreams[i]]; currentNodeToBeSelected.selected = true; } } } //don't allow two streams in the same row to be selected (at least for now) if( currentNodeToBeSelected.row() == alreadySelectedNode.row() ) { alreadySelectedNode.selected = false; } else//cause all of the intermediate streams to be selected { var highNode; var lowNode; if( currentNodeToBeSelected.mRow > alreadySelectedNode.mRow ) { highNode = currentNodeToBeSelected; lowNode = alreadySelectedNode; } else { lowNode = currentNodeToBeSelected; highNode = alreadySelectedNode; } //if both nodes are mainline or above, get highest node and walk down parent until lowest node row is reached if( lowNode.mRow > -1 && highNode.mRow > -1 ) { var parentNode = mStreamNodeHash[ highNode.stream.mParent ]; while( parentNode.mRow > lowNode.mRow ) { parentNode.selected = true; parentNode = mStreamNodeHash[ parentNode.stream.mParent ]; } } //if both nodes are mainline or below, get lowest node and walk up parent until highest node is reached else if( lowNode.mRow < 1 && highNode.mRow < 1 ) { var parentNode = mStreamNodeHash[ lowNode.stream.mParent ]; while( parentNode.mRow < highNode.mRow ) { parentNode.selected = true; parentNode = mStreamNodeHash[ parentNode.stream.mParent ]; } } //if one node is above mainline and one node below, walk down parents from highest node, walk up parents from lowest node, until mainline else { var parentNode = mStreamNodeHash[ highNode.stream.mParent ]; while( parentNode.mRow > lowNode.mRow ) { parentNode.selected = true; parentNode = mStreamNodeHash[ parentNode.stream.mParent ]; if( !parentNode ) break;//we've reached the mainline } parentNode = mStreamNodeHash[ lowNode.stream.mParent ]; while( parentNode.mRow < highNode.mRow ) { parentNode.selected = true; parentNode = mStreamNodeHash[ parentNode.stream.mParent ]; if( !parentNode ) break;//we've reached the mainline } } } } } this.getSelectedStream = function() { return mSelectedStream; } this.createGitP4Connection = function(repoData) { mStreamUtilities.createGitRepo(mSelectedStream, repoData); loadStreams(mSelectedDepot); } function branchGitRepo() { var name = prompt("Enter a name for the new repo"); if( name ) { mStreamUtilities.branchGitRepo( mSelectedStream, name ); loadStreams(mSelectedDepot); } } this.selectEvent = function() { //console.log('select'); var selected = mStreamUtilities.getSelectedItem(); if( selected && ( selected.file || selected.folder )) { //console.log("selected i: " + key + " " + selected[key] + " mDepots.length: " + mDepots.length); var obj; if( selected.file ) obj= selected.file; else obj = selected.folder; var depotPath = obj.depotPath;//to do, trim this into an acceptable form, then pass to loadStreams var justDepot = mStreamUtilities.depotFromDepotPath(depotPath); if( mSelectedDepot != justDepot) { thisView.closeAllDialogs(); removeModalBackground(); $('#userSelect' ).show(); if( mStreamUtilities.canAccessLocalHarddrive() ) $('#depotSelect' ).show(); } for(var i = 0; i < mDepots.length; i++) { //console.log("depot: " + depot + " depotPath: " + depotPath); if( mDepots[i] == justDepot ) { mSelectedStream = mStreamUtilities.streamFromDepotPath(depotPath); //console.log("loading streams from depot: " + depot); if( mDepots[i] != mSelectedDepot )//don't reload depot if it's already loaded { mSelectedDepot = mDepots[i]; loadStreams(mDepots[i]); } else//the depot is already loaded, but a new stream might have been selected { setSelectedStream(mSelectedStream); streamView.drawStreams(mRootNode, mDrawStatus); } if(!modalBackground) { $('#userSelect').show(); if( mStreamUtilities.canAccessLocalHarddrive() ) $('#depotSelect' ).show(); } $('#viewPrompt').hide(); return; } } //if we've gotten here, the user selected a non-stream depot streamView.clearTree("streamGraph"); $('#userSelect').hide(); if( mStreamUtilities.canAccessLocalHarddrive() ) $('#depotSelect' ).hide(); $('#viewPrompt').show(); mSelectedDepot = null; mSelectedStream = null; } } function workspaceNodeListener() { this.nodeClicked = function(workspace, x, y) { console.log( 'Clicked on ' + workspace.Client + ' at ' + x + ' ,' + y); } } function nodeListener() { this.nodeClicked = function(stream, x, y, shiftKeyDown) { setSelectedStream(stream, shiftKeyDown); mSelectedStream = stream; streamView.drawStreams(mRootNode, mDrawStatus); } } function deleteGitRepo(gitRepo) { if( confirm('Are you sure you want to delete ' + gitRepo.mName + '?') ) { mStreamUtilities.deleteGitRepo(gitRepo); loadStreams(mSelectedDepot); } } function launchTerminal(gitRepo) { mStreamUtilities.openTerminal(gitRepo.mGitRepoFolder); } function gitP4Push(streamNode) { var bottomPoint = streamNode.GetTopLinkPoint(); var topPoint = streamNode.mParent.GetBottomLinkPoint(); mStreamUtilities.tractorBeam(topPoint, bottomPoint, true); } function gitP4Pull(streamNode) { var bottomPoint = streamNode.GetTopLinkPoint(); var topPoint = streamNode.mParent.GetBottomLinkPoint(); mStreamUtilities.tractorBeam(topPoint, bottomPoint, false); } function setStreamDialogData(stream) { document.getElementById("streamName").innerText = "Name: " + stream.mName; document.getElementById("streamPath").innerText = "Stream: " + stream.mStream; document.getElementById("streamOwner").innerText = "Owner: " + stream.mOwner; document.getElementById("ownerPic").src = "http://computer.perforce.com/depot/intranet/directory/pix/" + stream.mOwner + ".jpg"; document.getElementById("streamParent").innerText = "Parent: " + stream.mParent; document.getElementById("streamType").innerText = "Type: " + mStreamUtilities.typeToString(stream.mType); document.getElementById("streamDescription").innerText = "Description: " + stream.mDescription; document.getElementById("streamOptions").innerText = "Options: " + stream.mOptions; if( stream.Paths() ) { var paths = stream.Paths(); //console.log('paths length: ' + paths.length); var path = '<br>'; for( var i = 0; i < paths.length; i++ ) { var s = paths[i]; path += s + '<br>'; } document.getElementById("streamPaths").innerText = "Paths: " + paths; } streamView.drawStreams(mRootNode, mDrawStatus); } function setGitDetailsDialogData(repo) { document.getElementById("gitName").innerText = "Name: " + repo.mName; document.getElementById("gitParent").innerText = "Parent: " + repo.mParentName; document.getElementById("gitType").innerText = "Type: " + mStreamUtilities.typeToString(repo.mType); document.getElementById("gitDescription").innerText = "Description: " + repo.mDescription; document.getElementById("gitWorkspaceName").innerText = "Perforce workspace name: " + repo.mWorkspaceName; document.getElementById("gitWorkspacePath").innerText = "Perforce workspace path: " + repo.mP4WorkspaceFolder; document.getElementById("gitRepoPath").innerText = "Git repo path: " + repo.mGitRepoFolder; streamView.drawStreams(mRootNode, mDrawStatus); } var mSkin = 'default'; function changeSkin(skin) { mSkin = skin; regenerateTree(); } var mLayout = 'vertical'; function changeLayout(layout) { mLayout = layout; regenerateTree(); } function parseRepoData(data) { var buf = data.split(' '); var repoProps = {}; for( var i = 0; i < buf.length; i++ ) { var keyValuePair = buf[i].split('='); repoProps[keyValuePair[0]] = keyValuePair[1]; } return repoProps; } function addStreamNode(stream, parentNode) { var streamNode; //console.log( 'description: ' + stream.mDescription ); if( stream instanceof GitRepo ) streamNode = new GitRepoNode(stream, parentNode, mLayout, mStreamUtilities, mSkin); else if( mSkin == 'gumdrop') streamNode = new StreamNode(stream, parentNode, mLayout, mStreamUtilities); else streamNode = new DefaultStreamNode(stream, parentNode, mLayout, mStreamUtilities); streamNode.addClickListener(new nodeListener()); //set context menus if( stream instanceof GitRepo ) { streamNode.setMenu('gitRepoMenu', function(action, el, pos) { if( action == 'gitDetails' ) { setGitDetailsDialogData(stream); var x = pos.docX - document.body.scrollLeft; var y = pos.docY - document.body.scrollTop; $( "#gitDetailsDialog" ).dialog( "option", "position", [x,y] ); $( "#gitDetailsDialog" ).dialog( "open" ); } else if( action == 'branchGitRepo' ) { branchGitRepo(); } else if( action == 'deleteGitRepo' ) { deleteGitRepo(streamNode.gitRepo); } else if( action == 'gitCopyUp' ) { gitP4Push(streamNode); } else if( action == 'gitMergeDown' ) { gitP4Pull(streamNode); } else if( action == 'gitTerminal' ) { launchTerminal(stream); } else if( action == 'refresh' ) { loadStreams(mSelectedDepot); } else { alert( 'Stream: ' + stream + '\n\n' + 'Action: ' + action + '\n\n' + 'Element ID: ' + $(el).attr('id') + '\n\n' + 'X: ' + pos.x + ' Y: ' + pos.y + ' (relative to element)\n\n' + 'X: ' + pos.docX + ' Y: ' + pos.docY+ ' (relative to document)' ); } } ); } else { streamNode.setMenu('streamMenu', function(action, el, pos) { if( action == 'details' ) { setStreamDialogData(stream); var x = pos.docX - document.body.scrollLeft; var y = pos.docY - document.body.scrollTop; $( "#streamDialog" ).dialog( "option", "position", [x,y] ); $( "#streamDialog" ).dialog( "open" ); } else if( action == 'trajectory' ) { showTrajectory(); } else if( action == 'timeline' ) { showTimeline(); } else if( action == 'defaultSkin' ) { changeSkin('default'); } else if( action == 'gumdropSkin' ) { changeSkin('gumdrop'); } else if( action == 'verticalLayout' ) { changeLayout('vertical'); } else if( action == 'ancestorLayout' ) { changeLayout('ancestor'); } else if( action == 'gitRepoCreate' ) { if( !(mSelectedStream instanceof Stream) ) { alert('Before running this command, click on a stream to select it.'); return; } if( testGitInstallation() ) { openGitCreateDialog(x,y ); } else { var result = confirm('Unable to find the git installation. Click No to quit, or Yes to navigate to it\'s location.'); if( result ) { //browse to git location var dir = mStreamUtilities.selectDirectory('Select the folder where git is installed'); if( dir.length > 0 ) { localStorage.gitLocation = dir; openGitCreateDialog(x, y ); } } } } else { alert( 'Stream: ' + stream + '\n\n' + 'Action: ' + action + '\n\n' + 'Element ID: ' + $(el).attr('id') + '\n\n' + 'X: ' + pos.x + ' Y: ' + pos.y + ' (relative to element)\n\n' + 'X: ' + pos.docX + ' Y: ' + pos.docY+ ' (relative to document)' ); } } ); } return streamNode; } function timelineClosed() { removeModalBackground(); $('#userSelect' ).show(); if( mStreamUtilities.canAccessLocalHarddrive() ) $('#depotSelect' ).show(); } function timelineCloseListener() { this.Closed = function() { timelineClosed(); } } function showTimeline() { var streamNodes = new Array(); for(var i = 0; i < mStreams.length; i++) if(mStreamNodeHash[mStreams[i]] && mStreamNodeHash[mStreams[i]].selected) streamNodes.push(mStreamNodeHash[mStreams[i]]); if( streamNodes.length < 1 ) { alert('To view a timeline, first click on the stream or streams you would like to view and compare. To select multiple streams, hold down the shift key while clicking.'); return; } $('#userSelect' ).hide(); if( mStreamUtilities.canAccessLocalHarddrive() ) $('#depotSelect' ).hide(); setModalBackground(500); var timelineView = new TimelineDisplay('streamGraph', streamNodes, mStreamUtilities); timelineView.addCloseListener(new timelineCloseListener()); timelineView.Location(0, 0); timelineView.Show(500); var timelineModel = new TimelineModel( timelineView, mStreamUtilities ); timelineModel.Load(); } var modalBackground; function setModalBackground(animTime) { if( !modalBackground ) { modalBackground = document.createElement( "canvas" );//this will cover the entire document and prevent click on other elements modalBackground.style.position = "absolute"; var container = document.body; container.appendChild( modalBackground ); $(modalBackground).hide(); modalBackground.style.background = 'rgba(255, 255, 255, .8)'; //console.log( container); modalBackground.style.left = 0; modalBackground.style.top = 0; modalBackground.width = document.width; modalBackground.height = document.height; $(modalBackground).show('fade', animTime); } } function removeModalBackground() { if( modalBackground && modalBackground.parentElement) { $(modalBackground).hide(120, function() { modalBackground.parentElement.removeChild(modalBackground); modalBackground = null; }); } } function addWorkspaceNode(workspace) { var workspace = new Workspace(workspace); //console.log( 'currentUser: ' + mCurrentUser + ' Owner: ' + workspace.Owner ); var currentUserIsOwner = mCurrentUser == workspace.Owner; var workspaceNode = new WorkspaceNode( workspace, currentUserIsOwner, mStreamUtilities ); workspaceNode.addClickListener(new workspaceNodeListener()); workspaceNode.setMenu('workspaceMenu', function(action, el, pos) { if( action == 'details' ) { alert('clicked details for ' + workspace.Client); } else if( action == 'push' ) { alert('clicked push for ' + workspace.Client); } else if( action == 'pull' ) { alert('clicked pull for ' + workspace.Client); } else if( action == 'merge' ) { alert('clicked merge for ' + workspace.Client); } else if( action == 'promote' ) { alert('clicked promote for ' + workspace.Client); } else if( action == 'switch' ) { alert('clicked switch for ' + workspace.Client); } } ); return workspaceNode; } this.Load = function() { loadDepotList(); loadUsers(); } function showTrajectory() { var streamNodes = new Array(); for(var i = 0; i < mStreams.length; i++) streamNodes.push(mStreamNodeHash[mStreams[i]]); var trajectoryModel = new TrajectoryModel( mStreamUtilities ); var trajectoryView = new TrajectoryView('streamGraph', streamNodes, mLayout, mStreamUtilities, trajectoryModel); trajectoryView.Show(); trajectoryModel.addDepotChangesListener( trajectoryView.depotChangesListener ); trajectoryModel.addDepotIntegratedListener( trajectoryView.depotIntegratedListener ); trajectoryModel.addChangeHistoryListener( trajectoryView.changeHistoryListener ); trajectoryModel.Load(mSelectedDepot); } this.showChangeTrajectory = function() { this.changeMode(); } this.hideChangeTrajectory = function() { this.changeMode(); } this.changeMode = function() { mDrawStatus = !mDrawStatus; regenerateTree(); } function loadUsers() { mStreamUtilities.getUsers(usersCallback); } function usersCallback() { var listbox = document.getElementById('userSelect'); listbox.options.length = 0; mStreamUtilities.AddSelectOption(listbox, 'All workspaces hidden', 'All workspaces hidden', false) mStreamUtilities.AddSelectOption(listbox, 'Show all workspaces', 'Show all workspaces', false) var users = arguments[0]; for(var i = 0; i < users.size; i++) { var user = users.data[i]; mStreamUtilities.AddSelectOption(listbox, user.User, user.User, false) } } function doSearch(one, two) { alert('todo someday... ' + one + ' ' + two); } var selectDepotString = ' Select stream depot'; function loadDepotList() { mStreamUtilities.getDepots(depotListCallback); } function depotListCallback() { var depots = arguments[0]; mDepots = []; for(var i = 0; i < depots.size; i++) { var depot = depots.data[i]; if( depot.type ) depot.Type = depot.type; if( depot.name ) depot.Depot = depot.name; //console.log('depot type: ' + depot.Type); if( depot.Type == "stream" ) mDepots.push(depot.Depot); } mDepots.sort(function(x,y)//case insensitive sort depots { var a = String(x).toUpperCase(); var b = String(y).toUpperCase(); if (a > b) return 1; if (a < b) return -1; return 0; }); if(mStreamUtilities.canAccessLocalHarddrive()) { //load all depots into var listbox = document.getElementById('depotSelect'); listbox.options.length = 0; for(var i = 0; i < mDepots.length; i++) { mStreamUtilities.AddSelectOption(listbox, mDepots[i], mDepots[i], false) } } //thisView.selectEvent(); } var mRootNode = null; function regenerateTree() { //console.log('regenerate tree*****************************'); var root = []; root.Stream = ''; root.Owner = ''; root.Name = 'root'; root.Parent = null; root.Type = mStreamUtilities.typeCount; root.Description = 'invisible root'; root.Options = ''; root.Paths = null; root.changeFlowsFromParent = false; root.changeFlowsToParent = false; root.firmerThanParent = false; var mRoot = new Stream(root, mStreamUtilities/*"", "", "root", null, mStreamUtilities.StreamType.typeCount, "invisible root", "", ""*/); mRootNode = addStreamNode(mRoot, null); streamView.clearTree("streamGraph"); mStreamNodeHash = new Object(); //initialize the Node[stream] hash generateTree(mRootNode); streamView.refresh(mRootNode, mDrawStatus, mLayout); drawGraphBackround(); //draw streams first, then get merge/promote data (which can take a long time); if( mDrawStatus ) { for(var i = 0; i < mStreams.length; i++) loadMergePromoteData(mStreams[i]); } } function drawGraphBackround() { if( mLayout == 'ancestor' ) { var mainlineNodes = mRootNode.children(); for( var i = 0; i < mainlineNodes.length; i++ ) { var mainlineNode = mainlineNodes[i]; //create alternating stripes behind each stability/firmness level in the graph var maxRow = 0; var minRow = 0; var yOffset = -(mainlineNode.RowHeight() * .85); for( rowNum in mainlineNode.stabilityLevelHeights ) { var n = parseInt(rowNum);//for some reason, javascript gives back negative value keys as strings, not ints //console.log( 'rowNum: ' + n + ' max: ' + maxRow + ' min: ' + minRow); if( n > -1 ) yOffset += mainlineNode.RowHeight() * mainlineNode.stabilityLevelHeights[rowNum]; if( n > maxRow ) maxRow = n; if( n < minRow ) minRow = n; } var sRowX = mainlineNode.mX; var sRowY = mainlineNode.getY() - yOffset; var sRowWidth = mainlineNode.graphWidth; var blue = true; var startingY = sRowY; var endingY = startingY; for( var i = maxRow; i > minRow - 1; i-- ) { var rowsInStabilityLevel = mainlineNode.stabilityLevelHeights[i]; var sRowHeight = mainlineNode.RowHeight() * rowsInStabilityLevel; var rowBackground = document.createElement( "canvas" ); rowBackground.style.position = "absolute"; var container = document.getElementById("streamGraph"); rowBackground.style.zIndex = -30; container.appendChild( rowBackground ); if( blue ) rowBackground.style.background = 'aliceblue'; else rowBackground.style.background = 'rgb(250,250,250)'; //console.log( container); rowBackground.style.left = sRowX; rowBackground.style.top = sRowY; rowBackground.width = sRowWidth; rowBackground.height = sRowHeight; blue = !blue; sRowY += sRowHeight; endingY = sRowY; //console.log( 'rowNum: ' + i + ' rowsInStabilityLevel: ' + rowsInStabilityLevel + ' width: ' + sRowWidth + ' height: ' + sRowHeight + sRowHeight + ' y: ' + sRowY ); } //TODO: add axis labels-- Rows: 'Stability Level' and Columns: 'Distance To Main' var barWidth = 20; var yAxisBackground = document.createElement( "canvas" ); yAxisBackground.style.position = "absolute"; var container = document.getElementById("streamGraph"); yAxisBackground.style.zIndex = -29; container.appendChild( yAxisBackground ); yAxisBackground.style.background = 'red'; yAxisBackground.style.left = sRowX; yAxisBackground.style.top = startingY; yAxisBackground.width = barWidth; var yContext = yAxisBackground.getContext( '2d' ); var yTextWidth = mStreamUtilities.GetTextWidth('<---- less stable more stable ---->', yAxisBackground, "normal " + yAxisBackground.width * 0.5 + "px sans-serif"); if( yTextWidth > (endingY - startingY) ) yAxisBackground.height = yTextWidth + 10; else yAxisBackground.height = endingY - startingY; //yAxisBackground.height = endingY - startingY; //label the y axis var aliceBlue = new mStreamUtilities.StreamColor(240, 248, 255, 1); var yLingrad = mStreamUtilities.GetGradientFill('aliceblue', yContext, yAxisBackground, true); yContext.beginPath(); yContext.moveTo(0, 0); yContext.lineTo(yAxisBackground.width, 0); yContext.lineTo(yAxisBackground.width, yAxisBackground.height); yContext.lineTo(0, yAxisBackground.height); yContext.lineTo(0, 0); yContext.closePath(); yContext.save(); yContext.fillStyle = yLingrad; yContext.fill(); yContext.restore(); //to draw the label vertically, we have to rotate the context, draw, then rotate back yContext.save(); yContext.font = "normal " + yAxisBackground.width * 0.5 + "px sans-serif"; yContext.textBaseline = "middle"; yContext.translate( yAxisBackground.width/2, yTextWidth + 10 /*yAxisBackground.height/2*/); yContext.rotate(-Math.PI/2); var label = '<-- Less stable More stable -->'; yContext.fillStyle = 'gray'; yContext.fillText(label, 0, 0); yContext.restore(); yContext.strokeStyle = 'lightgray'; yContext.stroke(); var xAxisBackground = document.createElement( "canvas" ); xAxisBackground.style.position = "absolute"; var container = document.getElementById("streamGraph"); xAxisBackground.style.zIndex = -29; container.appendChild( xAxisBackground ); xAxisBackground.style.background = 'green'; xAxisBackground.style.left = sRowX + barWidth; xAxisBackground.style.top = startingY - barWidth; xAxisBackground.width = sRowWidth - barWidth; xAxisBackground.height = barWidth; //label the x axis var xContext = xAxisBackground.getContext( '2d' ); var aliceBlue = new mStreamUtilities.StreamColor(240, 248, 255, 1); var xLingrad = mStreamUtilities.GetGradientFill('aliceblue', xContext, xAxisBackground); xContext.beginPath(); xContext.moveTo(0, 0); xContext.lineTo(xAxisBackground.width, 0); xContext.lineTo(xAxisBackground.width, barWidth); xContext.lineTo(0, barWidth); xContext.lineTo(0, 0); xContext.closePath(); xContext.save(); xContext.fillStyle = xLingrad; xContext.fill(); xContext.restore(); //not sure why I have to re-set the font size here... xContext.font = "normal " + xAxisBackground.height * 0.5 + "px sans-serif"; xContext.textBaseline = "middle"; xContext.fillStyle = 'gray'; xContext.fillText('Distance from mainline -->', 5, xAxisBackground.height/2 - xAxisBackground.height * 0.02); xContext.strokeStyle = 'lightgray'; xContext.stroke(); } } } function refreshView() { setSelectedStream(mSelectedStream); streamView.refresh(mRootNode, mDrawStatus); } function loadStreams(depot) { mStreamUtilities.loadStreams(depot, StreamsCallback); } var mStreams = new Array(); function StreamsCallback() { mStreams.length = 0; //console.log('StreamsCallback'); var streams = arguments[0]; for (var i = 0; i < streams.size; i++) { var stream = new Stream(streams.data[i], mStreamUtilities); mStreams.push(stream); } var gitRepos = mStreamUtilities.getGitRepos(); if( gitRepos ) for( var i = 0; i < gitRepos.length; i++ ) mStreams.push( gitRepos[i] ); regenerateTree(mStreams); var userListbox = document.getElementById('userSelect'); userListbox.selectedIndex = 0; } var firstTimeLoadingWorkspaces = true; this.loadUserWorkspaces = function(user) { var userListbox = document.getElementById('userSelect'); var index = userListbox.selectedIndex; if( firstTimeLoadingWorkspaces ) { userListbox.options[0].text = 'All workspaces hidden'; firstTimeLoadingWorkspaces = false; } //clear the graph of workspaces if the user picks the first option if( index == 0 ) { for( var i = 0; i < mStreams.length; i++ ) { var stream = mStreams[i]; if(mStreamNodeHash[stream]) mStreamNodeHash[stream].workspaceNodes(null); workspacesCallback(stream,null); } return; } else if( index == 1 ) { for( var i = 0; i < mStreams.length; i++ ) { var stream = mStreams[i]; if(mStreamNodeHash[stream]) mStreamNodeHash[stream].workspaceNodes(null); var f = function(val) { return function(obj) { workspacesCallback(val,obj); } }; mStreamUtilities.getStreamWorkspaces(stream.mStream, f(stream)); } return; } var refreshTree = false; for( var i = 0; i < mStreams.length; i++ ) { var stream = mStreams[i]; if(mStreamNodeHash[stream]) mStreamNodeHash[stream].workspaceNodes(null); var f = function(val) { return function(obj) { workspacesCallback(val,obj); } }; mStreamUtilities.getUserStreamWorkspaces(user, stream.mStream, f(stream)); } } var workspaceCalls = 0; function workspacesCallback(stream, workspaces) { workspaceCalls++; var wSpaceNodes; if( workspaces && workspaces.size > 0 ) { //console.log( 'stream: ' + stream.mName ); wSpaceNodes = new Array(); for( var i = 0; i < workspaces.size; i++ ) { //console.log( 'stream: ' + stream.mName + ' ws: ' + workspaces.data[i].Client); wSpaceNodes.push(addWorkspaceNode(workspaces.data[i])); } } if( mStreamNodeHash[stream] ) mStreamNodeHash[stream].workspaceNodes( wSpaceNodes ); if( mStreams.length == workspaceCalls ) { workspaceCalls = 0; refreshView(); } } function generateTree(rootNode) { var mainlineStreams = new Array(); //console.log("mStreams.length: " + mStreams.length); for(var i = 0; i < mStreams.length; i++) { var st = mStreams[i]; //console.log('st.type(): ' + st.type()) if( st.type() == mStreamUtilities.StreamType.mainLine) mainlineStreams.push(st); } for(var i = 0; i < mainlineStreams.length; i++) { var mainline = mainlineStreams[i]; var node = addStreamNode(mainline, rootNode); mStreamNodeHash[mainline] = node; //console.log('Generating mainline: ' + node.stream.mName); addChildNodes(node); } } function addChildNodes(node) { for(var i = 0; i < mStreams.length; i++) { var stream = mStreams[i]; //console.log('stream.mParent: ' + stream.mParent + ' node.stream.mStream: ' + node.stream.mStream); if(stream.mParent == node.stream.mStream) { var childNode = addStreamNode(stream, node); mStreamNodeHash[stream] = childNode; //console.log('Generating node childNode: ' + childNode.stream.mName); addChildNodes(childNode); } } } function loadMergePromoteData(stream) { if( stream.mStream /*&& stream instanceof Stream*/ ) { //console.log('loadMergePromoteData for stream: ' + stream.mStream); mStreamUtilities.loadMergePromoteData( stream, mergePromoteDataCallback); } } function mergePromoteDataCallback() { var istat = arguments[0]; //console.log('mergePromoteDataCallback, istat.size: ' + istat.size); for(var i = 0; i < istat.size; i++) { var data = istat.data[i]; if( data.stream ) { var node; for(var j = 0; j < mStreams.length; j++) { if( data.stream == mStreams[j].mStream ) { node = mStreamNodeHash[mStreams[j]]; break; } } if(node) { node.integToParent = data.integToParent == 'true'; node.integFromParent = data.integFromParent == 'true'; node.DrawStatusIcons(); drawLinksToParent(node, true); //console.log('istat data for ' + node.stream.mName + ' integFromParent: ' + data.integFromParent + ' integToParent: ' + data.integToParent); } } } } function drawLinksToParent(node, showStatus) { for( var i = 0; i < mStreams.length; i++ ) { if( mStreams[i].mStream == node.stream.mParent ) { var link = new StreamLink(mStreamNodeHash[mStreams[i]], node, showStatus, mLayout, mStreamUtilities); link.draw(); return; } } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#7 | 8214 | David George |
Re-implemented gumdrop skin in stream graph. Made selection work in P4V. |
||
#6 | 8126 | David George |
Added test for stream selection before adding a git repo. Updated StreamUtilities to use refactored ClientKit.system commands. 'p4 set' now has a different output, updated getConfigFile to handle it. |
||
#5 | 8116 | David George | Interim checkin so Jaimen can test against ClientKit. | ||
#4 | 8110 | David George | This is a fairly unstable checkin that is a first attempt at getting the StreamGraph to work with the refactored ClientKit. | ||
#3 | 8090 | David George |
More optimization of the changelist trajectory dialog. Updated the third-party flexigrid. Updated jquery. Added timeTracker, a temporary object that'll assist me in tracking time for optimization purposes. |
||
#2 | 8084 | David George |
Began work on optimizing the changelist trajectory algorithm. By using lazy loading, it's now about 4x faster. Refactored TrajectoryView and TrajectoryModel so that the model doesn't know about the view (I know, should never have known about it in the first place. I figured out how to create a Listener pattern in JavaScript so that I could do this a little more elegantly). |
||
#1 | 8081 | David George |
Initial submit of JavaScript StreamGraph. Main functionality is: Change Trajectory (Change Flow), Timeline, and GitStreams. |