// constants #set ($T_UPCOMING = "000000000001") #set ($T_STARTED = "000000000002") #set ($T_COMPLETED = "000000000003") #set ($T_CANCELLED = "000000000004") #set ($S_NOTSTARTED = "000000000101") #set ($S_STARTED = "000000000102") #set ($S_BUILT = "000000000103") #set ($S_ACCEPTED = "000000000104") #set ($S_DEPLOYED = "000000000105") #set ($S_CANCELLED = "000000000106") #set ($T_TESTNOTRUN = "000000000001") #set ($T_TESTPASSED = "000000000002") #set ($T_TESTFAILED = "000000000003") var EOSLabels = { REMAINING_DAYS : '$REMAINING_DAYS', BDCHART : '$BDCHART', BDCHARTBYTASK : '$BDCHARTBYTASK', BDCHARTBYSTORY : '$BDCHARTBYSTORY', INVALID_SUBCLASS : '$INVALID_SUBCLASS', INVALID_SUPERCLASS : '$INVALID_SUPERCLASS', TB_ADD_STORY : '$TB_ADD_STORY', TB_COPY_STORY : '$TB_COPY_STORY', TB_EDIT_STORY : '$TB_EDIT_STORY', TB_ADD_TASK : '$TB_ADD_TASK', TB_ADD_TASK_TOSTORY : '$TB_ADD_TASK_TOSTORY', TB_EDIT_TASK : '$TB_EDIT_TASK', TB_COPY_TASK : '$TB_COPY_TASK', TB_SORT_BYRANK : '$TB_SORT_BYRANK', TB_SORT_BYSTATUS : '$TB_SORT_BYSTATUS', TB_ADD_OBSTACLE : '$TB_ADD_OBSTACLE', TB_EDIT_OBSTACLE : '$TB_EDIT_OBSTACLE', TB_EDIT_OBSTACLES : '$TB_EDIT_OBSTACLES', TB_VIEW_OBSTACLES : '$TB_VIEW_OBSTACLES', TB_STORYID : '$TB_STORYID', TB_STORYTIME : '$TB_STORYTIME', TB_TASKID : '$TB_TASKID', TB_TASKTIME : '$TB_TASKTIME', TB_SPBLOCKED : '$TB_SPBLOCKED', TB_SPBLOCKED2 : '$TB_SPBLOCKED2', TB_OBSTACLESTTIP : '$TB_OBSTACLESTTIP', TB_OBSTACLESTTIP2 : '$TB_OBSTACLESTTIP2', TB_BDDLG_TASK : '$TB_BDDLG_TASK', TB_BDDLG_STORY : '$TB_BDDLG_STORY', TB_NOTSET : '$TB_NOTSET', TB_NOTSTARTED : '$TB_NOTSTARTED', TB_STARTED : '$TB_STARTED', TB_BUILT : '$TB_BUILT', TB_CUSTOMERACCEPTED : '$TB_CUSTOMERACCEPTED', TB_DEPLOYED : '$TB_DEPLOYED', TB_CANCELLED : '$TB_CANCELLED', TB_BD_DAY : '$TB_BD_DAY', TB_BD_DAYS : '$TB_BD_DAYS', TB_SYNCTASK : '$TB_SYNCTASK', TB_SYNCTASK_SYNCING : '$TB_SYNCTASK_SYNCING', TB_LOAD_ERROR : '$TB_LOAD_ERROR', TB_TASK_NOTSTARTED : '$TB_TASK_NOTSTARTED', TB_TASK_STARTED : '$TB_TASK_STARTED', TB_TASK_COMPLETED : '$TB_TASK_COMPLETED', TB_TASK_CANCELLED : '$TB_TASK_CANCELLED', TB_TEST_NOT_RUN : '$TB_TEST_NOT_RUN', TB_TEST_PASSED : '$TB_TEST_PASSED', TB_TEST_FAILED : '$TB_TEST_FAILED', TESTS_TAB_TEST_NOT_RUN_VALUE : '$TESTS_TAB_TEST_NOT_RUN_VALUE', TESTS_TAB_TEST_PASSED_VALUE : '$TESTS_TAB_TEST_PASSED_VALUE', TESTS_TAB_TEST_FAILED_VALUE : '$TESTS_TAB_TEST_FAILED_VALUE', T_UPCOMING : '$T_UPCOMING', T_STARTED : '$T_STARTED', T_COMPLETED : '$T_COMPLETED', T_CANCELLED : '$T_CANCELLED', S_NOTSTARTED : '$S_NOTSTARTED', S_STARTED : '$S_STARTED', S_BUILT : '$S_BUILT', S_ACCEPTED : '$S_ACCEPTED', S_DEPLOYED : '$S_DEPLOYED', S_CANCELLED : '$S_CANCELLED', T_TESTNOTRUN : '$T_TESTNOTRUN', T_TESTPASSED : '$T_TESTPASSED', T_TESTFAILED : '$T_TESTFAILED' } var Eos = { // namespace for Eos domain objects version: '0.2.3 2008-04-24', corkboardEl: null, agents: [], projects: [], iterations: [], stories: [], tasks: [], errors: [], storyStatusMap: [], storyStatusById: [], taskStatusMap: [], gTBVSortOrder : 'byrank', loadMask : null, syncMask : null }; // Note: These will be overridden when the team board is loaded // with the most current enumeration. Eos.storyStatuses = [ { id: EOSLabels.S_NOTSTARTED, value:EOSLabels.TB_NOTSTARTED, seq: '1'}, { id: EOSLabels.S_STARTED, value:EOSLabels.TB_STARTED, seq: '2'}, { id: EOSLabels.S_BUILT, value:EOSLabels.TB_BUILT, seq: '3'}, { id: EOSLabels.S_ACCEPTED, value:EOSLabels.TB_CUSTOMERACCEPTED, seq: '4'}, { id: EOSLabels.S_DEPLOYED, value:EOSLabels.TB_DEPLOYED, seq: '5'}, { id: EOSLabels.S_CANCELLED, value:EOSLabels.TB_CANCELLED, seq: '6'} ]; // TODO: Fix when task statuses are normalized. Eos.taskStatuses = [ { id: EOSLabels.T_UPCOMING, value:'Upcoming', seq: '1'}, { id: EOSLabels.T_STARTED, value:'Started', seq: '2'}, { id: EOSLabels.T_COMPLETED, value:'Completed', seq: '3'}, { id: EOSLabels.T_CANCELLED, value:'Cancelled', seq: '4'} ]; /** * Initialization of Corkboard tab. */ Eos.init = function( ) { var tbPanel = teamboard.teamboardPanel; // Add a content panel to the layout of the corkboard. var scbpanel = new Ext.Panel({ //layout: 'absolute', layout : 'fit', baseCls : 'board', autoScroll : true, autoShow: true, renderTo: tbPanel.getEl() }); tbPanel.add(scbpanel); Eos.corkboardEl = scbpanel.getEl(); Eos.corkboardEl.on('contextmenu',Eos.showContextMenu); tbPanel.getEl().on('contextmenu',Eos.showContextMenu); // Hack: TODO: consolidate tbPanel and scbpanel. Eos.loadMask = new Ext.LoadMask(scbpanel.getEl(),{}); Eos.syncMask = new Ext.LoadMask(scbpanel.getEl(),{ msg: EOSLabels.TB_SYNCTASK_SYNCING}); // initialize status lookups for sorting and status icons. Eos.mapStoryStatus(); Eos.mapTaskStatus(); }; Eos.mapStoryStatus = function() { var slist = Eos.storyStatuses; Eos.storyStatusMap = []; Eos.storyStatysById = []; for(var i = 0; i < slist.length; i++) { var stat = slist[i]; Eos.storyStatusMap[stat.id] = stat.seq; Eos.storyStatusById[stat.id] = stat; } }; Eos.mapTaskStatus = function() { var tlist = Eos.taskStatuses; Eos.taskStatusMap = []; for(var i = 0; i < tlist.length; i++) { var stat = tlist[i]; Eos.taskStatusMap[stat.id] = stat.seq; } }; Eos.extend = function(sub, sup) { // inherit subclass from superclass, delegating through prototype chaining var P = function() {}; P.prototype = sup.prototype; sub.prototype = new P(); sub.prototype.constructor = sub; sub.superclass = sup.prototype; if (sup.prototype.constructor == Object.prototype.constructor) { sup.prototype.constructor = sup; } }; Eos.augment = function(sub, sup) { // add superclass methods to subclass, unless already defined in subclass if (typeof sub != 'function') { return Eos.error(EOSLabels.INVALID_SUBCLASS); } if (typeof sup != 'function') { return Eos.error(EOSLabels.INVALID_SUPERCLASS); } var subP = sub.prototype; var supP = sup.prototype; for (var name in supP) { if (!subP[name]) { subP[name] = supP[name]; } } }; // ContextMenu on Team Board Eos.synctaskMenu = new Ext.menu.Item({ text: EOSLabels.TB_SYNCTASK, disabled: true, handler: function() { teamboard.syncTasksWarn();}}); Eos.contextMenu = new Ext.menu.Menu({ items: [ new Ext.menu.CheckItem({ text: EOSLabels.TB_SORT_BYSTATUS, handler: function() { Eos.sortStories ("bystatus");}, group: 'sgrp', checked: (Eos.gTBVSortOrder == 'bystatus') }), new Ext.menu.CheckItem({ text: EOSLabels.TB_SORT_BYRANK, handler: function() { Eos.sortStories("byrank");}, group: 'sgrp', checked:(Eos.gTBVSortOrder == 'byrank') }), "-", { text: EOSLabels.TB_ADD_TASK, #if (!$canManageTasks) disabled: true, #end handler: function() { teamboard.addNewTask(Eos.sprint.id,null,null, Eos.loadMask); } }, { text: EOSLabels.TB_ADD_STORY, #if (!$canManageStories) disabled: true, #end handler: function() { teamboard.addNewStory(Eos.project.backlogid, null, Eos.loadMask);} }, "-", Eos.synctaskMenu ] }) Eos.showtooltip = function(el_id,text){ var el = document.getElementById(el_id); if(el==null) { el = el_id;//??? any idea why } //split lines and calculate width var max = 1; var lines = text.split("
"); for (var iIndex = 0; iIndex < lines.length; iIndex++) { var l = lines[iIndex]; max = l.length > max ? l.length: max; } return new Ext.ToolTip({ target: el, autoDestroy: true, title: '', autoHide: true, showDelay: 60, hideDelay: 0, autoHeight: true, width: max*7, trackMouse: true, html: text }); }; /* * Class Hierarchy: * Entity <-- CardView <-- Sprint * <-- Story * <-- Task * */ // // ---------- Base Class: Entity // /** * Entity class is an abstract base class which holds the Id and the * properties of the object. */ Eos.Entity = function() {}; Eos.Entity.prototype.getData = function() { if (!this.data) { this.data = {}; } return this.data; }; Eos.Entity.prototype.setData = function(data) { var self = this.getData(); if (data && (typeof data == 'object')) { for (var property in data) { self[property] = data[property]; } } else if ((typeof data == 'string') || (typeof data == 'number')) { self['id'] = BxtUtil.formatId(data); } }; Eos.Entity.prototype.set = function(property, value) { var self = this.getData(); self[property] = value; }; Eos.Entity.prototype.get = function(property, defaultValue) { var self = this.getData(); var value = self[property]; if (value === undefined) { value = defaultValue; } return value; }; // // ---------- CardView // /** * CardView is a subclass of Entity which represents the view of the * each of the Stories and Tasks. It hodls the title, dom properties and * the X,Y location of the object. */ Eos.CardView = function() {}; Eos.extend(Eos.CardView, Eos.Entity); Eos.CardView.prototype.getElProps = function() { // override to customize element properties if (!this.domProps) { this.domProps = { 'id': 'CARD_' + this.get('id'), 'class': 'card' }; } return this.domProps; }; Eos.CardView.prototype.getEl = function() { if (!this.extEl) { this.extEl = Eos.corkboardEl.createChild(this.getElProps()); } return this.extEl; }; // // ---------- Sprint // /** * Sprint is a subclass of CardView which display the sprint information and burndown chart. * It is a singleton object with methods to add Stories and Tasks to the Iteration * and to do story layout. */ Eos.Sprint = function(data) { this.setData(data); Eos.Sprint.INSTANCE = this; // remember all instances var stories = this.get('stories'); if (stories) { var N = stories.length; for (var i = 0; i < N; ++i) { var story = new Eos.Story(stories[i]); story.addMenu(); } } var sprintTasks = this.get('tasks'); if (sprintTasks) { var N = sprintTasks.length; for (var i = 0; i < N; ++i) { var task = new Eos.Task(sprintTasks[i]); task.addMenu(); } } }; Eos.extend(Eos.Sprint, Eos.CardView); Eos.Sprint.findById = function(id) { var it = Eos.Sprint.INSTANCE; if (it && (it.get('id') == id)) { return it; } return null; }; Eos.Sprint.layoutCards = function() { Eos.Sprint.INSTANCE.layoutCards(); }; Eos.Sprint.prototype.getElProps = function() { // overridden if (!this.domProps) { this.domProps = { 'id': 'SPRINT_' + this.get('id'), 'class': 'sprint' }; } return this.domProps; }; Eos.Sprint.prototype.addTask = function(task) { // friend method between Iteration and Task if (!this.tasks) { this.tasks = []; } this.tasks.push(task); }; Eos.Sprint.prototype.addStory = function(story) { // friend method between Iteration and Story if (!this.stories) { this.stories = []; } //alert('story=' + story); this.stories.push(story); }; Eos.Sprint.prototype.getTooltip = function() { var obttip = this.get("obstaclettip",''); var obcount = this.get("obstacleCount",''); var blocked = this.get("blocked",'false'); var name = this.get("title",''); if( !blocked ) { return name; } else { var overlayttip = '' + name + ': ' + EOSLabels.TB_SPBLOCKED + '
' + obttip; return overlayttip; } } Eos.Sprint.prototype.buildHtml = function() { var days = this.get('daysremaining', '0'); if(days == '') { days = 0; } var label = (days == 1) ? EOSLabels.TB_BD_DAY : EOSLabels.TB_BD_DAYS; var id = this.get('id'); var imageid = "Sprint_" + BxtUtil.formatId(this.get('id')); var onclickEvent = ''; var chartName = this.get("chart",''); var title = EOSLabels.BDCHART; if(chartName != null && ('' != chartName)) { if(chartName.indexOf('TASKTIME') > 0) { title = EOSLabels.BDCHARTBYTASK; } else { title = EOSLabels.BDCHARTBYSTORY; } } var blocked = this.get("blocked",'false'); var ttip = this.getTooltip(); var blockedDiv = ''; if(blocked == true) { if(Ext.isIE6) { blockedDiv = ''; } else { blockedDiv = ''; } } var html = '
' + blockedDiv + '
' + ' ' + days + '' + ' ' + label + '' + '
'; if(chartName == '') { chartName = "../themes/default/images/tbvnullchart2.jpg"; html += '
'; } else { // use ImageServlet to get uptodate image chartName='ImageServlet?command=returnImage&filename=/temp/'+ chartName; html += '
'; } html += ' ' + '
' + '
'; return html; }; Eos.Sprint.prototype.updateElValue = function(data) { this.setData(data); var sprintEl = this.getEl(); sprintEl.dom.innerHTML = this.buildHtml(); return sprintEl; }; Eos.Sprint.prototype.updateEl = function(data) { var sprintEl = this.updateElValue(data); var scroll = Eos.corkboardEl.getScroll(); var x = Eos.corkboardEl.getX() + this.get('x', 0) + 16; var y = Eos.corkboardEl.getY() + this.get('y', 0) + 16; sprintEl.moveTo(x-scroll.left, y-scroll.top, true); this.shadowAt(x + 1, y - Eos.corkboardEl.getY() + 2); return sprintEl; }; Eos.Sprint.prototype.shadowAt = function(x,y) { var shadow = this.get('shadow'); if(!shadow) { shadow = new Ext.Shadow(); this.setData({ shadow : shadow}); shadow.show(this.getEl()); shadow.setZIndex(0); } shadow.realign(x, y, 130, 70); } // Rich2 Eos.Sprint.prototype.layoutCards = function() { this.updateEl(); if (!this.tasks) { this.tasks = []; } this.layoutTasks(); // layout stories (and their tasks) if (!this.stories) { this.stories = []; } var M = this.stories.length; var comparator = (Eos.gTBVSortOrder == "byrank") ? Eos.storyRankComparator : Eos.storyStatusComparator; this.stories.sort(comparator); for (var j = 0; j < M; ++j) { var story = this.stories[j]; var position = { x: 1, y: 6 + (5 * j) }; story.updateEl(position); story.layoutCards(); } teamboard.teamboardPanel.doLayout(); }; Eos.Sprint.prototype.layoutTasks = function() { if (!this.tasks) { this.tasks = []; } var N = this.tasks.length; this.tasks.sort(Eos.taskStatusComparator); var j = 0; for (var i = 0; i < N; ++i) { var task = this.tasks[i]; if(task.get('status', 'new') != '000000000104') { var position = { x: 10 + (7 * j), y: 1 }; task.updateEl(position); j++; } } } Eos.Sprint.prototype.removeTask = function(rtask) { if(!this.tasks) { return; } var N = this.tasks.length; for (var i = 0; i < N; i++) { var task = this.tasks[i]; if(task.get('id') == rtask.get('id')){ this.tasks.splice(i,1); return; } } } Eos.Sprint.prototype.getContextMenu = function() { if (!this.contextMenu) { this.contextMenu = new Ext.menu.Menu({ items: [ { id:'addtask', text: EOSLabels.TB_ADD_TASK, #if (!$canManageTasks) disabled: true, #end handler: function() { teamboard.addNewTask(Eos.sprint.id,null,null); } }, { id:'addstory', text: EOSLabels.TB_ADD_STORY, #if (!$canManageStories) disabled: true, #end handler: function() { teamboard.addNewStory(Eos.project.backlogid, null);} }, '-', new Ext.menu.CheckItem({ id:'tasktime', text: EOSLabels.BDCHARTBYTASK, group:'bdgrp', checked: true, handler: function() { Eos.Sprint.setChart('TASKTIME')}}), new Ext.menu.CheckItem({ id:'storysize', text: EOSLabels.BDCHARTBYSTORY, group: 'bdgrp', handler: function() { Eos.Sprint.setChart('STORYSIZE')}} ), '-', { text: EOSLabels.TB_VIEW_OBSTACLES, #if (!$canManageObstacles) disabled: true, #end handler: function() { teamboard.showObstacleMgmtDialog(Eos.project.id,Eos.sprint.id,null,null, Eos.loadMask); } } ] }); } return this.contextMenu; }; Eos.Sprint.prototype.addMenu = function() { var sprintEl = this.getEl(); sprintEl.on('contextmenu', this.showContextMenu.createDelegate(this)); return sprintEl; } Eos.Sprint.prototype.showContextMenu = function(e) { e.stopEvent(); var menu = this.getContextMenu(); // set the current selection on burndown charts var chartName = this.get("chart",''); if(chartName.indexOf('STORYSIZE')>= 0) { menu.items.get('storysize').setChecked(true); } else { menu.items.get('tasktime').setChecked(true); } menu.showAt(e.getXY()); }; Eos.Sprint.setChart = function(chartType) { var chartName = Eos.Sprint.INSTANCE.get("chart",''); if(chartName === null || '' === chartName) { return; } if(chartName.indexOf(chartType) >= 0 ) { // already set return; } var ochartType = (chartType === 'TASKTIME') ? 'STORYSIZE' : 'TASKTIME'; var chName = chartName.replace(ochartType, chartType); Eos.Sprint.INSTANCE.updateElValue( { chart : chName }); // Remember the burndown chart selection Ext.Ajax.request({ url: 'pbv/PBVAsyncHandler?action=SAVE-USER-SETTINGS', params: {start:0, limit:25, userId:'$currentUserId', viewId:'$_FOCUS_TEAMBOARD_VIEW', keyId:'BurndownChart', selection: chartType} }); }; Eos.storyIntStatus = function (stat) { var val = Eos.storyStatusMap[stat]; return (!val) ? 999 : parseInt(val,10); } Eos.storyStatusComparator = function(s1, s2) { var v1 = Eos.storyIntStatus(s1.get('status', '000000000101')); var v2 = Eos.storyIntStatus(s2.get('status', '000000000101')); if(v1 == v2) { var r1 = s1.get('rank', 0.0); var r2 = s2.get('rank', 0.0); if( r1 == r2 ) { var id1 = s1.get('id'); var id2 = s2.get('id'); return (id1 < id2) ? -1 : 1; } return (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0; } return (v1 < v2) ? -1 : (v1> v2)? 1 : 0; } Eos.storyRankComparator = function(s1, s2) { var v1 = s1.get('rank', 0.0); var v2 = s2.get('rank', 0.0); if(v1 == v2) { var r1 = s1.get('id'); var r2 = s2.get('id'); return (r1 < r2) ? -1 : 1; } return (v1 < v2) ? -1 : (v1> v2)? 1 : 0; } // // ---------- Story // /** * Story class is a subclass of CardView. It tracks all the stories of the iteration * in the Eos.Story.INSTANCE_MAP. * @param {Object} data to set up the Story */ Eos.Story = function(data) { this.setData(data); Eos.Story.INSTANCE_MAP[this.get('id')] = this; // remember all instances // link to iteration (if any) var iteration_id = this.get('iteration_id'); if (iteration_id) { this.iteration = Eos.Sprint.findById(iteration_id); } // add story to "parent" if (this.iteration) { this.iteration.addStory(this); } var tasks = this.get('tasks'); if (tasks) { var N = tasks.length; for (var i = 0; i < N; ++i) { var task = new Eos.Task(tasks[i]); task.addMenu(); } } }; Eos.extend(Eos.Story, Eos.CardView); Eos.Story.INSTANCE_MAP = {}; // map of all Story instances Eos.Story.findById = function(id) { return Eos.Story.INSTANCE_MAP[id]; }; Eos.Story.prototype.getElProps = function() { // overridden if (!this.domProps) { this.domProps = { 'id': 'STORY_' + this.get('id'), 'class': 'story' }; } return this.domProps; }; Eos.Story.prototype.buildHtml = function() { var stat = this.get('status', '000000000101'); var obcount = this.get('obstacleCount', '0'); var btcount = this.get('blockedTaskCount', '0'); var sc = Eos.getStoryCls(stat, obcount,btcount); var title = this.get('title', ''); var id = BxtUtil.formatId(this.get('id')); var html = '
' + stat + '' + '
' + id + '
' + '
' + '
' + title + '
' + '
' + this.get('size', '?') + '
' + '
'; return html; }; Eos.Story.prototype.updateElValue = function(data) { this.setData(data); var storyEl = this.getEl(); if(data == null || data == '') { storyEl.dom.HTML = ''; } else { storyEl.dom.innerHTML = this.buildHtml(); } return storyEl; }; Eos.Story.prototype.updateEl = function(data) { var storyEl = this.updateElValue(data); var scroll = Eos.corkboardEl.getScroll(); var elx = Eos.corkboardEl.getX(); var ely = Eos.corkboardEl.getY(); var x = elx + (16 * this.get('x', 0)); var y = ely + (16 * this.get('y', 0)); storyEl.moveTo(x-scroll.left, y-scroll.top, true); this.shadowAt(x - elx + 2,y - ely + 2); return storyEl; }; Eos.Story.prototype.addMenu = function() { var storyEl = this.getEl(); storyEl.on('contextmenu', this.showContextMenu.createDelegate(this)); return storyEl; }; Eos.Story.prototype.shadowAt = function(x,y) { var shadow = this.get('shadow'); if(!shadow) { shadow = new Ext.Shadow(); this.setData({ shadow : shadow}); shadow.show(this.getEl()); shadow.setZIndex(0); } //shadow.realign(x-1,y-35+2,130,70); shadow.realign(x,y, 130,70); } Eos.Story.prototype.getContextMenu = function() { if (!this.contextMenu) { this.contextMenu = new Ext.menu.Menu({ items: [ { text: EOSLabels.TB_EDIT_STORY, handler: this.launchEditor.createDelegate(this) }, { text: EOSLabels.TB_COPY_STORY, #if (!$canManageStories) disabled: true, #end handler: this.copyStoryDialog.createDelegate(this) }, '-', { text: EOSLabels.TB_ADD_TASK_TOSTORY, #if (!$canManageTasks) disabled: true, #end handler: this.newTaskDialog.createDelegate(this) }, { text: EOSLabels.TB_ADD_STORY, #if (!$canManageStories) disabled: true, #end handler: function() { teamboard.addNewStory(Eos.project.backlogid, null, Eos.loadMask);} } ] }); } return this.contextMenu; }; Eos.Story.prototype.showContextMenu = function(e) { e.stopEvent(); this.getContextMenu().showAt(e.getXY()); }; Eos.Story.prototype.addTask = function(task) { // friend method between Story and Task if (!this.tasks) { this.tasks = []; } this.tasks.push(task); }; Eos.Story.prototype.removeTask = function(rtask) { if(!this.tasks) { return; } var N = this.tasks.length; for (var i = 0; i < N; i++) { var task = this.tasks[i]; if(task.get('id') == rtask.get('id')){ this.tasks.splice(i,1); return; } } } // Rich3 Eos.Story.prototype.layoutCards = function() { if (!this.tasks) { this.tasks = []; } var sx = this.get('x', 0) + 9; var sy = this.get('y', 0); var N = this.tasks.length; this.tasks.sort(Eos.taskStatusComparator); var j = 0; for (var i = 0; i < N; ++i) { var task = this.tasks[i]; if(task.get('status', 'new') != '000000000104') { // cancel var position = { x: sx + (7 * j), y: sy }; task.updateEl(position); j++; } } }; Eos.Story.prototype.launchEditor = function() { var storyId = this.get('id'); teamboard.editStory(this.get('backlogid', ''), storyId, false, this.getEl(), Eos.loadMask); }; Eos.Story.prototype.newTaskDialog = function() { teamboard.addNewTask(Eos.sprint.id, this.get('id'),null, Eos.loadMask); } Eos.Story.prototype.newStoryDialog = function() { teamboard.addNewStory(Eos.project.backlogid, this.getEl(), Eos.loadMask); } Eos.Story.prototype.copyStoryDialog = function() { var storyId = this.get('id'); teamboard.editStory(Eos.project.backlogid, storyId, true, this.getEl(), Eos.loadMask); // backlog id MUST be null. } Eos.taskIntStatus = function (stat) { var val = Eos.taskStatusMap[stat]; return (!val) ? 999 : parseInt(val,10); } Eos.taskStatusComparator = function(t1, t2) { var v1 = Eos.taskIntStatus(t1.get('status', 'new')); var v2 = Eos.taskIntStatus(t2.get('status', 'new')); if(v1 == v2){ var i1 = t1.get('id'); var i2 = t2.get('id'); return (i1 < i2)? -1 : 1 ; } return (v1 < v2) ? -1 : (v1> v2)? 1 : 0; } // // ---------- Task // /** * Task is a subclass of Entity which represents a task of a story. * All tasks are tracked in Eos.Task.INSTANCE_MAP. * @param {Object} data to set the Task. */ Eos.Task = function(data) { this.setData(data); Eos.Task.INSTANCE_MAP[this.get('id')] = this; // remember all instances // link to iteration (if any) var iteration_id = this.get('iteration_id'); if (iteration_id) { this.sprint = Eos.Sprint.findById(iteration_id); } // link to story (if any) var story_id = this.get('story_id'); if (story_id) { this.story = Eos.Story.findById(story_id); } // add task to appropriate "parent" if (this.story) { this.story.addTask(this); } else if (this.sprint) { this.sprint.addTask(this); } }; Eos.extend(Eos.Task, Eos.CardView); Eos.Task.INSTANCE_MAP = {}; // map of all Task instances Eos.Task.findById = function(id) { return Eos.Task.INSTANCE_MAP[id]; }; Eos.Task.prototype.getElProps = function() { // overridden if (!this.domProps) { this.domProps = { 'id': 'TASK_' + this.get('id'), 'class': 'task' }; } return this.domProps; }; Eos.Task.prototype.buildHtml = function() { var stat = this.get('status', 'new'); var title = g_encoder.unescapeBT(this.get('title', '')); var html_title = g_encoder.encodeHTML(title); var tc = Eos.getTaskCls(stat, g_encoder.encodeHTML(g_encoder.escape(title)), this.get('obstacleCount', '0'), g_encoder.encodeHTML(g_encoder.escape(this.get('obstaclettip', '')))); var id = BxtUtil.formatId(this.get('id')); var teststatus = this.get('teststatus', ''); var html = '
' + '
' + id + '
' + '
' + '
' + ' ' + '
' ; html = html + ' ' + html_title + ' ' + '
' + '
' + this.get('remaining', '?') + '
' + '
'; return html; }; /** * Method to update and render Task data without moving. * @param {Object} data */ Eos.Task.prototype.updateElValue = function(data) { this.setData(data); var taskEl = this.getEl(); taskEl.dom.innerHTML = this.buildHtml(); return taskEl; }; /** * method to update and render Task data and then move to new location * @param {Object} data */ Eos.Task.prototype.updateEl = function(data) { var taskEl = this.updateElValue(data); var scroll = Eos.corkboardEl.getScroll(); var elx = Eos.corkboardEl.getX(); var ely = Eos.corkboardEl.getY(); var x = elx + (16 * this.get('x', 0)); var y = ely + 5 + (16 * this.get('y', 0)); taskEl.moveTo(x - scroll.left, y - scroll.top, true); this.shadowAt(x - elx + 2, y - ely + 2); return taskEl; }; Eos.Task.prototype.addMenu = function() { var taskEl = this.getEl(); taskEl.on('contextmenu', this.showContextMenu.createDelegate(this)); return taskEl; }; Eos.Task.prototype.shadowAt = function(x,y) { var shadow = this.get('shadow'); if(!shadow) { shadow = new Ext.Shadow(); this.setData({ shadow : shadow}); shadow.show(this.getEl()); shadow.setZIndex(0); } shadow.realign(x,y,100,60); } Eos.Task.prototype.launchEditor = function() { var taskId = this.get('id'); teamboard.editTask(Eos.project.id, taskId, Eos.sprint.id, false, this.getEl(), Eos.loadMask); } Eos.Task.prototype.launchCopyEditor = function() { var taskId = this.get('id'); teamboard.editTask(Eos.project.id, taskId, Eos.sprint.id, true, this.getEl(), Eos.loadMask); } Eos.Task.prototype.createObstacleMenus = function(){ Eos.Task.prototype.addObstacleMenu = new Ext.menu.Item({id: 'addObstacleMenu',text: EOSLabels.TB_ADD_OBSTACLE, #if (!$canManageObstacles) disabled: true, #end handler: this.newObstacleDialog.createDelegate(this)}); Eos.Task.prototype.editObstacleMenu = new Ext.menu.Item({id: 'editObstacleMenu',text: EOSLabels.TB_EDIT_OBSTACLE, #if (!$canManageObstacles) disabled: true, #end handler: this.editObstacleDialog.createDelegate(this)}); Eos.Task.prototype.obstaclesMgmtMenu = new Ext.menu.Item({ id: 'editObstaclesMenu', text: EOSLabels.TB_EDIT_OBSTACLES, #if (!$canManageObstacles) disabled: true, #end handler: this.editObstacleDialog.createDelegate(this)}); }; Eos.Task.prototype.newObstacleDialog = function() { var taskId = this.get('id'); teamboard.addNewObstacle(Eos.project.id,Eos.sprint.id, taskId, this.getEl(), Eos.loadMask); } Eos.Task.prototype.editObstacleDialog = function() { var taskId = this.get('id'); var obstacleId = this.get('obstacles', ''); var obstacleCount = this.get('obstacleCount', '0'); if (obstacleCount == 1) { teamboard.editObstacle(Eos.project.id, Eos.sprint.id, taskId, obstacleId, null, Eos.loadMask); } else { teamboard.showObstacleMgmtDialog(Eos.project.id, Eos.sprint.id, taskId, null, Eos.loadMask); } } Eos.Task.prototype.getContextMenu = function() { if (!this.contextMenu) { this.createObstacleMenus(this); this.contextMenu = new Ext.menu.Menu({ items: [ { text: EOSLabels.TB_EDIT_TASK, handler: this.launchEditor.createDelegate(this) }, { text: EOSLabels.TB_COPY_TASK, #if (!$canManageTasks) disabled: true, #end handler: this.launchCopyEditor.createDelegate(this) }, "-", Eos.Task.prototype.editObstacleMenu, Eos.Task.prototype.obstaclesMgmtMenu, Eos.Task.prototype.addObstacleMenu ] }); } if(this.get('obstacleCount', '0')<=0) { Eos.Task.prototype.editObstacleMenu.hide(); Eos.Task.prototype.obstaclesMgmtMenu.hide(); } else if(this.get('obstacleCount', '0')==1) { Eos.Task.prototype.editObstacleMenu.show(); Eos.Task.prototype.obstaclesMgmtMenu.hide(); } else { Eos.Task.prototype.editObstacleMenu.hide(); Eos.Task.prototype.obstaclesMgmtMenu.show(); } var stat = this.get('status', 'new'); if(stat === EOSLabels.T_COMPLETED || stat === EOSLabels.T_CANCELLED) { Eos.Task.prototype.addObstacleMenu.hide(); } else { Eos.Task.prototype.addObstacleMenu.show(); } return this.contextMenu; }; Eos.Task.prototype.showContextMenu = function(e) { e.stopEvent(); this.getContextMenu().showAt(e.getXY()); }; Eos.Task.prototype.remove = function() { var shadow = this.get('shadow'); shadow.hide(); var el = this.getEl(); el.frame("d94943", 1, { duration: 0.5 }); el.fadeOut({ easing: 'easeOut', duration: 1.5, remove: true }); } // // Utility Methods // Eos.debug = function(msg) { if (Eos.debugEl) { Eos.debugEl.dom.innerHTML += msg + '<\/br>\n'; } return msg; } Eos.error = function(msg) { handleError('EOS ERRROR! ' + msg); return msg; }; /** * Create JavaScript object(s) from json string * @param {Object} s json string */ Eos.fromJson = function(s) { var json = eval('(' + s + ')'); return json; }; /** * Create json string from javascript object(s) * @param {Object} o java script object */ Eos.toJson = function(o) { return Ext.encode(o); }; /** * Displays context menu at element e. * @param {Object} e */ Eos.showContextMenu = function(e) { e.preventDefault(); var items = Eos.contextMenu.items; // make sure sorting is in sync with tbvoptions dialog. items.itemAt(0).setChecked((Eos.gTBVSortOrder == 'bystatus'), true); items.itemAt(1).setChecked((Eos.gTBVSortOrder == 'byrank'), true); Eos.contextMenu.showAt(e.getXY()); }; /** * Utility methods to add sprints, stories, tasks, and htmlId */ Eos.addSprint = function() { var eosSprint = new Eos.Sprint(Eos.sprint); eosSprint.addMenu(); } Eos.addAllStories = function() { if (Eos.stories) { var N = Eos.stories.length; for (var i = 0; i < N; ++i) { var story = new Eos.Story(Eos.stories[i]); story.addMenu(); } } }; Eos.addAllTasks = function() { if (Eos.tasks) { var N = Eos.tasks.length; for (var i = 0; i < N; ++i) { var task = new Eos.Task(Eos.tasks[i]); task.addMenu(); } } }; Eos.resetTeamboard = function() { Eos.resetLayout(); }; Eos.resetLayout = function() { Eos.corkboardEl.update(''); }; // Rich1 Eos.doLayout = function(json) { if (json.project) { Eos.project = json.project; } if(json.sprint) { Eos.sprint = json.sprint; } // if (json.stories) { // Eos.stories = json.stories; // } // if (json.tasks) { // Eos.tasks = json.tasks; // } if (json.storystatus) { Eos.storyStatuses = json.storystatus; } Eos.mapStoryStatus(); Eos.addSprint(); //Eos.addAllStories(); //Eos.addAllTasks(); #if($canManageTasks) Eos.synctaskMenu.setDisabled(!json.hasremotetask); #end Eos.Sprint.layoutCards(); } /** * Initiate date load with AJAX request to the server * @param {Object} projectId * @param {Object} releaseId * @param {Object} sprintId * @param boolean tasksync (if true, sync the tasks and reload) */ Eos.startLoad = function(projectId, releaseId, sprintId, tasksync) { Ext.Ajax.request({ url: 'ajax2/Gemini?action=focus.web.ajax.LoadTBxTableAction', success: Eos.LoadTBxTableActionSuccess , failure: Eos.LoadTBxTableActionFailure , params: { start: 0, limit: 25, userid: '$currentUserId', backlogid: '-', projectid: projectId, releaseid: releaseId, sprintid: sprintId, tasksync: tasksync } }); if(!tasksync) { Eos.loadMask.show(); } else { Eos.syncMask.show(); } } Eos.LoadTBxTableActionSuccess = function(response, options ) { Eos.loadMask.hide(); Eos.syncMask.hide(); if(response ) { if(response.responseText) { var jsondata = Eos.fromJson(response.responseText); var svrsuccess = jsondata.success; if(svrsuccess == undefined || svrsuccess) { // if success, jsondata does not contains success attribute. Eos.doLayout(jsondata); } else { ErrorHandlerLib.onAjaxFailure(response,options); teamboard.loadBoard(); } } } else { BxtUtil.showErrorMsg(EOSLabels.TB_LOAD_ERROR); teamboard.loadBoard(); } } Eos.LoadTBxTableActionFailure = function(response, options ) { Eos.loadMask.hide(); Eos.syncMask.hide(); ErrorHandlerLib.onAjaxFailure(response,options); teamboard.loadBoard(); } /** * Method to map the task status to the corresponding CSS class. * @param {Object} stat */ Eos.getTaskCls = function(ostat,title, obcount,obttip) { var stat = ostat; var tc = null; if(stat === EOSLabels.T_UPCOMING) { // assigned,not started tc = { cls : 'task-new', imgcls : 'img-new', img2cls: '../themes/default/images/IsNotStarted.gif', text:EOSLabels.TB_TASK_NOTSTARTED, img2size :'height="10" width="10"', fcolor: '#565656'}; } else if(stat === EOSLabels.T_STARTED) { // started tc = { cls : 'task-started', imgcls : 'img-started', img2cls: '../themes/default/images/InProgress.gif', text:EOSLabels.TB_TASK_STARTED, img2size :'height="9" width="10"', fcolor: '#3D3C36'} } else if(stat === EOSLabels.T_COMPLETED ) { // completed tc = { cls : 'task-completed', imgcls : 'img-completed', img2cls: '../themes/default/images/IsCompleted.gif', text:EOSLabels.TB_TASK_COMPLETED, img2size :'height="10" width="9"', fcolor: '#374D36'} } else if( stat === EOSLabels.T_CANCELLED) { // cancelled tc = { cls : 'task-cancelled', imgcls : 'img-cancelled', img2cls: '../themes/default/images/IsCancelled.png', text:EOSLabels.TB_TASK_CANCELLED, img2size :'height="8" width="8"', fcolor: '#374D36'} } else { // other additional task status tc = { cls : 'task-new', imgcls : 'img-new', img2cls: '../themes/default/images/IsNotStarted.gif', text:EOSLabels.TB_TASK_NOTSTARTED, img2size :'height="10" width="10"', fcolor: '#565656'}; } // image overlay if( obcount == 0 || stat === EOSLabels.T_COMPLETED) { tc.overlay = '../themes/default/images/slash2.gif'; tc.overlayttip = '' + title + ''; } else { if(obcount > 0) { tc.imgcls = tc.imgcls + ' img-blocked'; if(Ext.isIE6) { tc.overlay = '../themes/default/images/ObstacleOverlayTask.gif'; } else { tc.overlay = '../themes/default/images/ObstacleCardOverlay3.png'; } tc.overlayttip = '' + title + '
' + obttip; } } return tc; } /* Eos.getTestCls = function(teststatus) { var tstcls = null; if(teststatus != '' && teststatus != null) { if(teststatus === EOSLabels.T_TESTNOTRUN) { tstcls = { img:'../themes/default/images/SCTM_Notrun.png', text: EOSLabels.TB_TEST_NOT_RUN, labelText: EOSLabels.TESTS_TAB_TEST_NOT_RUN_VALUE, imgsize: 'width=\'10\' height=\'10\'' }; } if(teststatus === EOSLabels.T_TESTPASSED) { tstcls = { img:'../themes/default/images/SCTM_Pass.png', text: EOSLabels.TB_TEST_PASSED, labelText: EOSLabels.TESTS_TAB_TEST_PASSED_VALUE, imgsize: 'width=\'10\' height=\'10\'' }; } if(teststatus === EOSLabels.T_TESTFAILED ) { tstcls = { img:'../themes/default/images/SCTM_Fail.png', text: EOSLabels.TB_TEST_FAILED, labelText: EOSLabels.TESTS_TAB_TEST_FAILED_VALUE, imgsize: 'width=\'10\' height=\'10\'' }; } } return tstcls; } */ /** * Method to map the story status to the corresponding CSS class. * @param {Object} stat * @param obcount obstacle count * @param btcount blocked task count */ Eos.getStoryCls = function(stat, obcount, btcount) { var sc = null; if(stat === null || stat === '') { sc = { cls: 'story-NotStarted', imgcls: 'img-new', text:EOSLabels.TB_NOTSET, img2cls: '../themes/default/images/NoStatus.gif', img2size :'height="10" width="10"'}; } else if( stat === EOSLabels.S_NOTSTARTED) { // not started sc = { cls: 'story-NotStarted', imgcls: 'img-new', text:EOSLabels.TB_NOTSTARTED, img2cls: '../themes/default/images/IsNotStarted.gif', img2size :'height="10" width="10"'}; } else if( stat === EOSLabels.S_STARTED) { // started sc = { cls: 'story-Started', imgcls: 'img-started', text:EOSLabels.TB_STARTED, img2cls: '../themes/default/images/InProgress.gif', img2size :'height="9" width="10"'}; } else if( stat === EOSLabels.S_BUILT) { // built sc = { cls: 'story-Built', imgcls: 'img-started', text:EOSLabels.TB_BUILT, img2cls: '../themes/default/images/IsBuilt.gif', img2size :'height="4" width="16"'}; } else if( stat === EOSLabels.S_ACCEPTED) { // customer accepted sc = { cls: 'story-CustomerAccepted', imgcls: 'img-started', text:EOSLabels.TB_CUSTOMERACCEPTED, img2cls: '../themes/default/images/IsCustomerAccepted.gif', img2size :'height="4" width="16"'}; } else if( stat === EOSLabels.S_DEPLOYED) { // deployed sc = { cls: 'story-Deployed', imgcls: 'img-completed', text:EOSLabels.TB_DEPLOYED, img2cls: '../themes/default/images/IsDeployed.gif', img2size :'height="4" width="16"'}; } else if( stat === EOSLabels.S_CANCELLED ) { // cancelled sc = { cls: 'story-Cancelled', imgcls: 'img-cancelled', text:EOSLabels.TB_CANCELLED, img2cls: '../themes/default/images/IsCancelled.png', img2size :'height="8" width="8"'}; } else { // Other statuses defined var statobj = Eos.storyStatusById[ stat ]; var val = (statobj == null || statobj == undefined) ? 'Defunct status: ' + stat : statobj.value; sc = { cls: 'story-NotStarted', imgcls: 'img-new', text:val, img2cls:'../themes/default/images/CustomStatus.png', img2size:'height="10" width="10"'}; } // blocked story if(obcount > 0) { if(btcount == 1) { if( stat != EOSLabels.S_DEPLOYED) { sc = { cls: 'story-NotStarted', imgcls: 'img-new', text:EOSLabels.TB_OBSTACLESTTIP, img2cls: '../themes/default/images/tbvBlocked.gif', img2size :'height="11" width="11"'}; } else { sc = { cls: 'story-Deployed', imgcls: 'img-completed', text:EOSLabels.TB_OBSTACLESTTIP, img2cls: '../themes/default/images/tbvBlocked.gif', img2size :'height="11" width="11"'}; } } else { if( stat != EOSLabels.S_DEPLOYED) { sc = { cls: 'story-NotStarted', imgcls: 'img-new', text:EOSLabels.TB_OBSTACLESTTIP2, img2cls: '../themes/default/images/tbvMultiBlocked.gif', img2size :'height="13" width="14"'}; } else { sc = { cls: 'story-Deployed', imgcls: 'img-completed', text:EOSLabels.TB_OBSTACLESTTIP2, img2cls: '../themes/default/images/tbvMultiBlocked.gif', img2size :'height="13" width="14"'}; } } } return sc; } Eos.sortStories = function(sortOrder) { Eos.gTBVSortOrder = sortOrder; if(Eos.Sprint.INSTANCE) { Eos.Sprint.INSTANCE.layoutCards(); } } Eos.onStorySave = function(form, action) { // get story info from response var respText = action.response.responseText; var respObj = Ext.util.JSON.decode(respText); var jstory = respObj.story; var story = Eos.Story.findById(jstory.id); var layout = false; if(story) { // existing story layout = (jstory.rank != story.get('rank', 0.0)) || (jstory.status != story.get('status', '000000000101')) && (Eos.gTBVSortOrder === 'bystatus'); story.updateElValue(jstory); } else { //new story story = new Eos.Story(jstory); story.addMenu(); layout = true; } if(layout) { Eos.Sprint.INSTANCE.layoutCards(); } } Eos.onTaskSave = function(form, action) { var respText = action.response.responseText; var respObj = Ext.util.JSON.decode(respText); var jtask = respObj.task; var task = Eos.Task.findById(jtask.id); var layout = false; if(task) { // existing task (edit) layout = (jtask.status !== task.get('status', 'new')); var tstoryId = jtask.story_id; // to story var fstoryId = task.get('story_id', ''); // from-story if(tstoryId !== fstoryId) { var fstory = (fstoryId === '(Unassigned)') ? null : Eos.Story.findById(fstoryId); var tstory = (tstoryId === '(Unassigned)') ? null : Eos.Story.findById(tstoryId); if(!fstory) { fstory = Eos.Sprint.INSTANCE; } if(!tstory) { tstory = Eos.Sprint.INSTANCE; } task.updateElValue(jtask); fstory.removeTask(task); tstory.addTask(task); fstory.layoutCards(); tstory.layoutCards(); layout = false; // no need to redo layout any more } else { task.updateElValue(jtask); } } else { //new task task = new Eos.Task(jtask); task.addMenu(); layout = true; } if(layout) { var story = Eos.Story.findById(jtask.story_id); if(story) { story.layoutCards(); } else { Eos.Sprint.INSTANCE.layoutCards(); } } } Eos.onObstacleSave = function(form, action) { /* var respText = action.response.responseText; var respObj = Ext.util.JSON.decode(respText); var jobstacle = respObj.obstacle; var obstacleid = jobstacle.id; var N = this.tasks.length; for (var i = 0; i < N; i++) { var task = this.tasks[i]; if (task.get('obstacles', '').indexof(obstacleid) > 0) { var taskid = task.get('id'); } } */ } /** * Method to load make an ajax call to the server to retrieve the stories and tasks * for the sprint. * @param {Object} projectGuid GUID of the project * @param {Object} releaseGuid GUID of the release * @param {Object} sprintGuid GUID of the sprint */ Eos.load = function(projectId, releaseId, sprintId, tasksync) { /*if(sprintId === "" || sprintId === null) { return; }*/ if(tasksync == undefined) { tasksync = false; } Eos.startLoad(projectId, releaseId, sprintId, tasksync); }; Eos.getSprintBurndownDialog = function(id, sprintObj) { var chartName = Eos.Sprint.INSTANCE.get("chart",''); var url = ''; if(chartName !== null && ('' !== chartName)) { url = '/temp/' + chartName.replace('sprintSummary2', 'sprintDetail'); } else { return; } var title = (url.indexOf('TASKTIME') >= 0) ? EOSLabels.TB_BDDLG_TASK : EOSLabels.TB_BDDLG_STORY; burndownDialog.showDialog(url, title); }; //WATIR Support Eos.getTaskIdByName = function(taskname) { var it = Eos.Sprint.INSTANCE; var N1 = it.stories.length; for (var i = 0;i < N1; i++) { var story = it.stories[i]; var tasks = story.get('tasks'); if (tasks) { var N = tasks.length; for (var i = 0; i < N; ++i) { var task = new Eos.Task(tasks[i]); var name = task.get('title', ''); name = g_encoder.unescapeBT(name); if(taskname == name) { var s = task.get('id'); return s; } } } } return ""; }; Eos.getStoryIdByName = function(storyname) { var N = Eos.stories.length; for (var j = 0;j < N; j++) { var st = new Eos.Story(Eos.stories[j]); var name = st.get('title', ''); name = g_encoder.unescapeBT(name); if(storyname == name) { var s = st.get('id'); return s; } } return ""; }; ///////////////////////////////////////////////// var burndownDialog = { dialog: null, store : null, showDialog : function( url, title ) { var imgsrc='ImageServlet?command=returnImage&filename='+ url; if(!burndownDialog.dialog){ burndownDialog.dialog = new Ext.Window({ title: title, id: 'bd-details', width: 615, height: 470, resizable: false, plain: true, modal: true, closeAction: 'hide', html: '', buttons: [{ text: 'Close' , handler: function() { burndownDialog.dialog.hide(); } }] }); } else { burndownDialog.dialog.setTitle(title); burndownDialog.dialog.body.update(''); } burndownDialog.dialog.show(); } }; /* Ext.onReady(function() { Eos.init(); Eos.load(); }); */