Created
January 16, 2013 20:36
-
-
Save ferentchak/4550712 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<!-- Copyright (c) 2010 Rally Software Development Corp. All rights reserved --> | |
<html> | |
<head> | |
<title>Planning Board</title> | |
<meta name="Name" content="App: Planning Board"/> | |
<meta name="Version" content="2011.02.24"/> | |
<meta name="Vendor" content="Rally Software Labs"/> | |
<script type="text/javascript" src="/apps/1.23/sdk.js"></script> | |
<script type="text/javascript"> | |
/** | |
Copyright (c) 2011 Rally Software Development Corp. All rights reserved | |
*/ | |
var PlanningBoardCardRenderer = function(column, item) { | |
rally.sdk.ui.cardboard.BasicCardRenderer.call(this, column, item); | |
var that = this; | |
this.renderCard = function() { | |
var card = document.createElement("div"); | |
dojo.addClass(card, "card"); | |
dojo.addClass(card, rally.sdk.util.Ref.getTypeFromRef(item._ref)); | |
var header = document.createElement("div"); | |
dojo.addClass(header, "cardHeader"); | |
dojo.addClass(header, "dojoDndHandle"); | |
card.appendChild(header); | |
var idDiv = document.createElement("div"); | |
var link = new rally.sdk.ui.basic.Link({item: item}); | |
link.display(idDiv); | |
dojo.addClass(idDiv, "leftCardHeader"); | |
header.appendChild(idDiv); | |
if(item.PlanEstimate) { | |
idDiv.appendChild(document.createTextNode(" (" + item.PlanEstimate + ")")); | |
} | |
var ownerImg = document.createElement("img"); | |
dojo.addClass(ownerImg, "cardOwner"); | |
var ownerName = document.createElement("div"); | |
dojo.addClass(ownerName, "cardOwnerName"); | |
if (item.Owner !== null) { | |
ownerImg.setAttribute("src", rally.sdk.util.Ref.getUserImage(item.Owner._ref)); | |
ownerName.appendChild(document.createTextNode(item.Owner._refObjectName)); | |
} | |
else { | |
ownerImg.setAttribute("src", rally.sdk.loader.launcherPath + "/../images/profile-mark-18.png"); | |
ownerName.appendChild(document.createTextNode("No Owner")); | |
} | |
header.appendChild(ownerImg); | |
header.appendChild(ownerName); | |
var textDiv = document.createElement("div"); | |
dojo.addClass(textDiv, "cardContent"); | |
textDiv.innerHTML = item.Name; | |
card.appendChild(textDiv); | |
return card; | |
}; | |
this.renderDndCard = function() { | |
var avatarCard = that.renderCard(); | |
dojo.addClass(avatarCard, "avatar"); | |
return avatarCard; | |
}; | |
}; | |
/** | |
Copyright (c) 2011 Rally Software Development Corp. All rights reserved | |
*/ | |
var PlanningBoardColumnRenderer = function(board, value, options) { | |
rally.sdk.ui.cardboard.BasicColumnRenderer.call(this, board, value, options); | |
var that = this; | |
var dndContainer; | |
var points = 0; | |
var columnDiv; | |
var pointCapacity = Infinity; | |
var resourcesDisplay; | |
this.render = function() { | |
columnDiv = document.createElement("div"); | |
dojo.addClass(columnDiv, "column"); | |
var columnHeader = document.createElement("div"); | |
dojo.addClass(columnHeader, "columnHeader"); | |
var columnHeaderText = document.createElement("span"); | |
columnHeader.appendChild(columnHeaderText); | |
columnHeaderText.appendChild(document.createTextNode(options.displayValue)); | |
if (options && options.resources) { | |
pointCapacity = options.resources; | |
resourcesDisplay = document.createElement("div"); | |
dojo.addClass(resourcesDisplay, "resourcesDisplay"); | |
setCapacityText(); | |
columnHeader.appendChild(resourcesDisplay); | |
} | |
columnDiv.appendChild(columnHeader); | |
if (options.startDate) { | |
var tooltip = new rally.sdk.ui.basic.Tooltip({ | |
message: rally.sdk.util.DateTime.formatDate(options.startDate, "MM/dd/yyyy") + " - " + | |
rally.sdk.util.DateTime.formatDate(options.endDate, "MM/dd/yyyy"), | |
position: "above" | |
}); | |
tooltip.display(columnHeaderText); | |
} | |
dndContainer = document.createElement("div"); | |
dojo.addClass(dndContainer, "columnContent"); | |
columnDiv.appendChild(dndContainer); | |
return columnDiv; | |
}; | |
this.getDndContainer = function() { | |
return dndContainer; | |
}; | |
this.getColumnNode = function() { | |
return columnDiv; | |
}; | |
function setCapacityText() { | |
if (options && options.resources) { | |
resourcesDisplay.innerHTML = getCapacityText(points, pointCapacity); | |
} | |
if(options && options.startDate && | |
rally.sdk.util.DateTime.fromIsoFormatString(options.startDate) < new Date() && | |
options.endDate && rally.sdk.util.DateTime.fromIsoFormatString(options.endDate) > new Date()) { | |
resourcesDisplay.innerHTML += " *Current"; | |
} | |
} | |
function getCapacityText(points, pointCapacity) { | |
var pointDisplay = parseInt(points) === parseFloat(points) ? points : Number(points).toFixed(2); | |
return "(" + pointDisplay + "/" + (pointCapacity === Infinity ? | |
'<span class="infinitySymbol">∞</span>)' : (pointCapacity + ")")); | |
} | |
this.addNoDropClass = function(nodes) { | |
if (pointCapacity === Infinity) { | |
return false; | |
} | |
return pointCapacity <= points; | |
}; | |
this.cardRemoved = function(card) { | |
points = points - card.PlanEstimate; | |
setCapacityText(); | |
if (pointCapacity >= points) { | |
dojo.removeClass(columnDiv, "overCapacity"); | |
} | |
}; | |
this.cardAdded = function(card) { | |
points = points + card.PlanEstimate; | |
setCapacityText(); | |
if (pointCapacity < points) { | |
dojo.addClass(columnDiv, "overCapacity"); | |
} | |
}; | |
this.getDndContainer = function() { | |
return dndContainer; | |
}; | |
}; | |
function PlanningBoard() { | |
var checkBoxes; | |
var columnStates; | |
var cardboard; | |
var dropdown; | |
var rallyDataSource; | |
var projectIterationMap = {}; | |
var MAX_COLUMNS = 12; | |
var MAX_CARDS = Infinity; | |
var releaseMode; | |
var that = this; | |
this._createLayout = function(element) { | |
var headerDiv = document.createElement("div"); | |
element.appendChild(headerDiv); | |
var dropdownContainerDiv = document.createElement("div"); | |
dojo.addClass(dropdownContainerDiv, "dropdownContainer"); | |
headerDiv.appendChild(dropdownContainerDiv); | |
var dropdownDiv = document.createElement("span"); | |
dropdownDiv.id = "dropdown"; | |
dropdownContainerDiv.appendChild(dropdownDiv); | |
var checkBoxContainerDiv = document.createElement("div"); | |
dojo.addClass(checkBoxContainerDiv, "typeFilterContainer"); | |
headerDiv.appendChild(checkBoxContainerDiv); | |
var showSpan = document.createElement("span"); | |
showSpan.appendChild(document.createTextNode("Show:")); | |
checkBoxContainerDiv.appendChild(showSpan); | |
var userStoriesSpan = document.createElement("span"); | |
userStoriesSpan.id = "userStories"; | |
checkBoxContainerDiv.appendChild(userStoriesSpan); | |
var defectsSpan = document.createElement("span"); | |
defectsSpan.id = "defects"; | |
checkBoxContainerDiv.appendChild(defectsSpan); | |
var defectSuitesSpan = document.createElement("span"); | |
defectSuitesSpan.id = "defectSuites"; | |
checkBoxContainerDiv.appendChild(defectSuitesSpan); | |
//Create checkboxes | |
checkBoxes = []; | |
var userStoriesCheckBox = new rally.sdk.ui.basic.CheckBox({ | |
showLabel: true, | |
label: "User Stories", | |
labelPosition: "after", | |
value: "HierarchicalRequirement", | |
checked: true | |
}); | |
checkBoxes.push(userStoriesCheckBox); | |
userStoriesCheckBox.display("userStories"); | |
var defectsCheckBox = new rally.sdk.ui.basic.CheckBox({ | |
showLabel: true, | |
label: "Defects", | |
labelPosition: "after", | |
value: "Defect" | |
}); | |
checkBoxes.push(defectsCheckBox); | |
defectsCheckBox.display("defects"); | |
var defectSuitesCheckBox = new rally.sdk.ui.basic.CheckBox({ | |
showLabel: true, | |
label: "Defect Suites", | |
labelPosition: "after", | |
value: "DefectSuite" | |
}); | |
checkBoxes.push(defectSuitesCheckBox); | |
defectSuitesCheckBox.display("defectSuites"); | |
//Wire up events | |
dojo.forEach(checkBoxes, function(checkBox) { | |
checkBox.addEventListener("onChange", that._refreshBoard); | |
}); | |
var planningBoardDiv = document.createElement("div"); | |
planningBoardDiv.id = "planningBoard"; | |
dojo.addClass(planningBoardDiv, "planningBoard"); | |
element.appendChild(planningBoardDiv); | |
releaseMode = false; | |
var togglePlanningMode = { | |
key: "togglePlanningMode", | |
label: "Toggle Backlog Mode", | |
onClick: function() { | |
releaseMode = !releaseMode; | |
that._refreshBoard(); | |
} | |
}; | |
rally.sdk.ui.AppHeader.addPageTool(togglePlanningMode); | |
}; | |
this._getSelectedTypes = function() { | |
var types = []; | |
//Build types based on checkbox selections | |
dojo.forEach(checkBoxes, function(checkBox) { | |
if (checkBox.getChecked()) { | |
types.push(checkBox.getValue()); | |
} | |
}); | |
return types; | |
}; | |
this._getItems = function(callback) { | |
var queries = []; | |
var backlogQueries = []; | |
dojo.forEach(that._getSelectedTypes(), function(type) { | |
queries.push({ | |
key:type, | |
type: type, | |
fetch: "Name,FormattedID,Owner,ObjectID,Rank,PlanEstimate,Iteration", | |
query: rally.sdk.util.Query.and(['Iteration.StartDate >= "' + dropdown.getSelectedStart() + '"', | |
'Iteration.StartDate <= "' + dropdown.getSelectedEnd() + '"']), | |
order: "Rank" | |
}); | |
backlogQueries.push({ | |
key:type + "backlog", | |
type: type, | |
fetch: "Name,FormattedID,Owner,ObjectID,Rank,PlanEstimate,Iteration,Children", | |
query: new rally.sdk.util.Query('Iteration = "null"').and(releaseMode ? dropdown.getQueryFromSelected() : 'Release = "null"'), | |
order: "Rank" | |
}); | |
}); | |
var items = []; | |
var outstandingQueries = 2; | |
function concatResults(queryArray, results) { | |
rally.forEach(queryArray, function(query) { | |
if (results[query.key]) { | |
items = items.concat(results[query.key]); | |
} | |
}); | |
outstandingQueries--; | |
if(outstandingQueries === 0) { | |
rally.forEach(items, function(item) { | |
item._iterationName = item.Iteration ? item.Iteration.Name : null; | |
}); | |
//Filter out epics | |
items = dojo.filter(items, function(item) { | |
return !(item.Children && item.Children.length > 0); | |
}); | |
callback(items); | |
} | |
} | |
rallyDataSource.find(backlogQueries, function(results) { concatResults(backlogQueries, results); }); | |
rallyDataSource.findAll(queries, function(results) { concatResults(queries, results); }); | |
}; | |
this._getColumns = function(columnCallback) { | |
columnStates = {}; | |
projectIterationMap = {}; | |
var iterationQuery = { | |
key:"iterations", | |
type: "Iteration", | |
fetch: "Name,Project,StartDate,EndDate,Resources", | |
order: "EndDate", | |
query: rally.sdk.util.Query.and(['StartDate >= "' + dropdown.getSelectedStart() + '"', | |
'StartDate <= "' + dropdown.getSelectedEnd() + '"']) | |
}; | |
rallyDataSource.findAll(iterationQuery, function(results) { | |
columnStates = {"null":{ | |
displayValue: releaseMode ? "Release Backlog" : "Backlog", | |
resources:Infinity | |
}}; | |
dojo.forEach(results.iterations, function(iteration) { | |
// Create a map between project and the iterations its contains by iteration name. | |
var projectRef = rally.sdk.util.Ref.getRelativeRef(iteration.Project); | |
projectIterationMap[projectRef] = projectIterationMap[projectRef] || {}; | |
projectIterationMap[projectRef][iteration.Name] = rally.sdk.util.Ref.getRelativeRef(iteration); | |
// Builds list of unique iteration names and rolls up capacity | |
if (columnStates[iteration.Name]) { | |
columnStates[iteration.Name].resources += iteration.Resources; | |
} | |
else { | |
columnStates[iteration.Name] = {displayValue:iteration.Name, | |
resources:iteration.Resources || 0, | |
startDate: iteration.StartDate, | |
endDate: iteration.EndDate | |
}; | |
} | |
}); | |
columnCallback(columnStates); | |
}); | |
}; | |
this._postProcessColumnChange = function(obj, args) { | |
var projectRef = rally.sdk.util.Ref.getRelativeRef(args.originalItem.Project); | |
var iterationName = args.fieldsToUpdate[args.attribute]; | |
if (iterationName === "null") { | |
args.fieldsToUpdate.Iteration = "null"; | |
} | |
else if (projectIterationMap[projectRef][iterationName]) { | |
args.fieldsToUpdate.Iteration = projectIterationMap[projectRef][iterationName]; | |
} | |
else { | |
rally.forEach(projectIterationMap, function(value, key) { | |
if (value[iterationName]) { | |
args.fieldsToUpdate.Iteration = value[iterationName]; | |
} | |
}); | |
} | |
delete args.fieldsToUpdate[args.attribute]; | |
}; | |
this._refreshBoard = function() { | |
var cardboardConfig = { | |
types: [], | |
attribute: "_iterationName", | |
sortAscending: true, | |
order: "Rank", | |
columns:that._getColumns, | |
columnRenderer:PlanningBoardColumnRenderer, | |
cardRenderer:PlanningBoardCardRenderer, | |
maxColumnsPerBoard: MAX_COLUMNS, | |
maxCardsPerColumn: MAX_CARDS, | |
items:that._getItems | |
}; | |
cardboardConfig.types = that._getSelectedTypes(); | |
if (cardboard) { | |
cardboard.destroy(); | |
} | |
cardboard = new rally.sdk.ui.CardBoard(cardboardConfig, rallyDataSource); | |
cardboard.addEventListener(cardboard.getValidEvents().preUpdate, that._postProcessColumnChange); | |
cardboard.display("planningBoard"); | |
}; | |
this.display = function(element) { | |
rally.sdk.ui.AppHeader.showPageTools(true); | |
//Build app layout | |
this._createLayout(element); | |
rallyDataSource = new rally.sdk.data.RallyDataSource('__WORKSPACE_OID__', | |
'__PROJECT_OID__', | |
'__PROJECT_SCOPING_UP__', | |
'__PROJECT_SCOPING_DOWN__'); | |
rallyDataSource.setServer("preview.rallydev.com"); | |
var dropdownConfig = { | |
showLabel: true, query: '(State != "Accepted")' | |
}; | |
dropdown = new rally.sdk.ui.ReleaseDropdown(dropdownConfig, rallyDataSource); | |
dropdown.addEventListener("onChange", that._refreshBoard); | |
dropdown.addEventListener("onLoad", that._refreshBoard); | |
dropdown.display("dropdown"); | |
}; | |
} | |
</script> | |
<style type="text/css"> | |
.dropdownContainer { | |
float: left; | |
} | |
.typeFilterContainer { | |
float: right; | |
margin-right: 30px; | |
} | |
.planningEstimate{ | |
display:inline; | |
} | |
.planningBoard { | |
clear: both; | |
} | |
.overCapacity .columnHeader { | |
color: #FC5350; | |
} | |
.overCapacity .columnContent { | |
background-color: #F9E2E1; | |
} | |
.cardboard .columnHeader { | |
height: 35px; | |
} | |
.cardboard .columnHeader.current { | |
color: #000000; | |
font-weight:bold; | |
} | |
.cardboard .resourcesDisplay { | |
font-size: 12px; | |
line-height: 20px; | |
} | |
.cardboard .infinitySymbol { | |
font-size: 16px; | |
position: relative; | |
top: 2px; | |
} | |
</style> | |
<script type="text/javascript"> | |
function onLoad() { | |
var planningBoard = new PlanningBoard(); | |
planningBoard.display(dojo.body()); | |
} | |
rally.addOnLoad(onLoad); | |
</script> | |
</head> | |
<body> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment