Path: blob/master/webroot/rsrc/js/application/projects/WorkboardColumn.js
12242 views
/**1* @provides javelin-workboard-column2* @requires javelin-install3* javelin-workboard-card4* javelin-workboard-header5* @javelin6*/78JX.install('WorkboardColumn', {910construct: function(board, phid, root) {11this._board = board;12this._phid = phid;13this._root = root;1415this._panel = JX.DOM.findAbove(root, 'div', 'workpanel');16this._pointsNode = JX.DOM.find(this._panel, 'span', 'column-points');1718this._pointsContentNode = JX.DOM.find(19this._panel,20'span',21'column-points-content');2223this._cards = {};24this._headers = {};25this._objects = [];26this._naturalOrder = [];27this._dropEffects = [];28},2930properties: {31triggerPreviewEffect: null32},3334members: {35_phid: null,36_root: null,37_board: null,38_cards: null,39_headers: null,40_naturalOrder: null,41_orderVectors: null,42_panel: null,43_pointsNode: null,44_pointsContentNode: null,45_dirty: true,46_objects: null,47_dropEffects: null,4849getPHID: function() {50return this._phid;51},5253getRoot: function() {54return this._root;55},5657getCards: function() {58return this._cards;59},6061_getObjects: function() {62return this._objects;63},6465getCard: function(phid) {66return this._cards[phid];67},6869getBoard: function() {70return this._board;71},7273setNaturalOrder: function(order) {74this._naturalOrder = order;75this._orderVectors = null;76return this;77},7879setDropEffects: function(effects) {80this._dropEffects = effects;81return this;82},8384getDropEffects: function() {85return this._dropEffects;86},8788getPointsNode: function() {89return this._pointsNode;90},9192getPointsContentNode: function() {93return this._pointsContentNode;94},9596getWorkpanelNode: function() {97return this._panel;98},99100newCard: function(phid) {101var card = new JX.WorkboardCard(this, phid);102103this._cards[phid] = card;104this._naturalOrder.push(phid);105this._orderVectors = null;106107return card;108},109110removeCard: function(phid) {111var card = this._cards[phid];112delete this._cards[phid];113114for (var ii = 0; ii < this._naturalOrder.length; ii++) {115if (this._naturalOrder[ii] == phid) {116this._naturalOrder.splice(ii, 1);117this._orderVectors = null;118break;119}120}121122return card;123},124125addCard: function(card, after) {126var phid = card.getPHID();127128card.setColumn(this);129this._cards[phid] = card;130131var index = 0;132133if (after) {134for (var ii = 0; ii < this._naturalOrder.length; ii++) {135if (this._naturalOrder[ii] == after) {136index = ii + 1;137break;138}139}140}141142if (index > this._naturalOrder.length) {143this._naturalOrder.push(phid);144} else {145this._naturalOrder.splice(index, 0, phid);146}147148this._orderVectors = null;149150return this;151},152153getDropTargetNodes: function() {154var objects = this._getObjects();155156var nodes = [];157for (var ii = 0; ii < objects.length; ii++) {158var object = objects[ii];159nodes.push(object.getNode());160}161162return nodes;163},164165getCardPHIDs: function() {166return JX.keys(this.getCards());167},168169getPointLimit: function() {170return JX.Stratcom.getData(this.getRoot()).pointLimit;171},172173markForRedraw: function() {174this._dirty = true;175},176177isMarkedForRedraw: function() {178return this._dirty;179},180181getHeader: function(key) {182if (!this._headers[key]) {183this._headers[key] = new JX.WorkboardHeader(this, key);184}185return this._headers[key];186},187188handleDragGhost: function(default_handler, ghost, node) {189// If the column has headers, don't let the user drag a card above190// the topmost header: for example, you can't change a task to have191// a priority higher than the highest possible priority.192193if (this._hasColumnHeaders()) {194if (!node) {195return false;196}197}198199return default_handler(ghost, node);200},201202_hasColumnHeaders: function() {203var board = this.getBoard();204var order = board.getOrder();205206return board.getOrderTemplate(order).getHasHeaders();207},208209redraw: function() {210var board = this.getBoard();211var order = board.getOrder();212213var list = this._getCardsSortedByKey(order);214215var ii;216var objects = [];217218var has_headers = this._hasColumnHeaders();219var header_keys = [];220var seen_headers = {};221if (has_headers) {222var header_templates = board.getHeaderTemplatesForOrder(order);223for (var k in header_templates) {224header_keys.push(header_templates[k].getHeaderKey());225}226header_keys.reverse();227}228229var header_key;230var next;231for (ii = 0; ii < list.length; ii++) {232var card = list[ii];233234// If a column has a "High" priority card and a "Low" priority card,235// we need to add the "Normal" header in between them. This allows236// you to change priority to "Normal" even if there are no "Normal"237// cards in a column.238239if (has_headers) {240header_key = board.getCardTemplate(card.getPHID())241.getHeaderKey(order);242243if (!seen_headers[header_key]) {244while (header_keys.length) {245next = header_keys.pop();246247var header = this.getHeader(next);248objects.push(header);249seen_headers[header_key] = true;250251if (next === header_key) {252break;253}254}255}256}257258objects.push(card);259}260261// Add any leftover headers at the bottom of the column which don't have262// any cards in them. In particular, empty columns don't have any cards263// but should still have headers.264265while (header_keys.length) {266next = header_keys.pop();267268if (seen_headers[next]) {269continue;270}271272objects.push(this.getHeader(next));273}274275this._objects = objects;276277var content = [];278for (ii = 0; ii < this._objects.length; ii++) {279var object = this._objects[ii];280281var node = object.getNode();282content.push(node);283}284285JX.DOM.setContent(this.getRoot(), content);286287this._redrawFrame();288289this._dirty = false;290},291292compareHandler: function(src_list, src_node, dst_list, dst_node) {293var board = this.getBoard();294var order = board.getOrder();295296var u_vec = this._getNodeOrderVector(src_node, order);297var v_vec = this._getNodeOrderVector(dst_node, order);298299return board.compareVectors(u_vec, v_vec);300},301302_getNodeOrderVector: function(node, order) {303var board = this.getBoard();304var data = JX.Stratcom.getData(node);305306if (data.objectPHID) {307return this._getOrderVector(data.objectPHID, order);308}309310return board.getHeaderTemplate(data.headerKey).getVector();311},312313setIsDropTarget: function(is_target) {314var node = this.getWorkpanelNode();315JX.DOM.alterClass(node, 'workboard-column-drop-target', is_target);316},317318_getCardsSortedByKey: function(order) {319var cards = this.getCards();320321var list = [];322for (var k in cards) {323list.push(cards[k]);324}325326list.sort(JX.bind(this, this._sortCards, order));327328return list;329},330331_sortCards: function(order, u, v) {332var board = this.getBoard();333var u_vec = this._getOrderVector(u.getPHID(), order);334var v_vec = this._getOrderVector(v.getPHID(), order);335336return board.compareVectors(u_vec, v_vec);337},338339_getOrderVector: function(phid, order) {340var board = this.getBoard();341342if (!this._orderVectors) {343this._orderVectors = {};344}345346if (!this._orderVectors[order]) {347var cards = this.getCards();348var vectors = {};349350for (var k in cards) {351var card_phid = cards[k].getPHID();352var vector = board.getCardTemplate(card_phid)353.getSortVector(order);354355vectors[card_phid] = [].concat(vector);356357// Push a "card" type, so cards always sort after headers; headers358// have a "0" in this position.359vectors[card_phid].push(1);360}361362for (var ii = 0; ii < this._naturalOrder.length; ii++) {363var natural_phid = this._naturalOrder[ii];364if (vectors[natural_phid]) {365vectors[natural_phid].push(ii);366}367}368369this._orderVectors[order] = vectors;370}371372if (!this._orderVectors[order][phid]) {373// In this case, we're comparing a card being dragged in from another374// column to the cards already in this column. We're just going to375// build a temporary vector for it.376var incoming_vector = board.getCardTemplate(phid)377.getSortVector(order);378incoming_vector = [].concat(incoming_vector);379380// Add a "card" type to sort this after headers.381incoming_vector.push(1);382383// Add a "0" for the natural ordering to put this on top. A new card384// has no natural ordering on a column it isn't part of yet.385incoming_vector.push(0);386387return incoming_vector;388}389390return this._orderVectors[order][phid];391},392393_redrawFrame: function() {394var cards = this.getCards();395var board = this.getBoard();396397var points = {};398var count = 0;399var decimal_places = 0;400for (var phid in cards) {401var card = cards[phid];402403var card_points;404if (board.getPointsEnabled()) {405card_points = card.getPoints();406} else {407card_points = 1;408}409410if (card_points !== null) {411var status = card.getStatus();412if (!points[status]) {413points[status] = 0;414}415points[status] += card_points;416417// Count the number of decimal places in the point value with the418// most decimal digits. We'll use the same precision when rendering419// the point sum. This avoids rounding errors and makes the display420// a little more consistent.421var parts = card_points.toString().split('.');422if (parts[1]) {423decimal_places = Math.max(decimal_places, parts[1].length);424}425}426427count++;428}429430var total_points = 0;431for (var k in points) {432total_points += points[k];433}434total_points = total_points.toFixed(decimal_places);435436var limit = this.getPointLimit();437438var display_value;439if (limit !== null && limit !== 0) {440display_value = total_points + ' / ' + limit;441} else {442display_value = total_points;443}444445if (board.getPointsEnabled()) {446display_value = count + ' | ' + display_value;447}448449var over_limit = ((limit !== null) && (total_points > limit));450451var content_node = this.getPointsContentNode();452var points_node = this.getPointsNode();453454JX.DOM.setContent(content_node, display_value);455456// Only put the "empty" style on the column (which just adds some empty457// space so it's easier to drop cards into an empty column) if it has no458// cards and no headers.459460var is_empty =461(!this.getCardPHIDs().length) &&462(!this._hasColumnHeaders());463464var panel = JX.DOM.findAbove(this.getRoot(), 'div', 'workpanel');465JX.DOM.alterClass(panel, 'project-panel-empty', is_empty);466467468JX.DOM.alterClass(panel, 'project-panel-over-limit', over_limit);469470var color_map = {471'phui-tag-disabled': (total_points === 0),472'phui-tag-blue': (total_points > 0 && !over_limit),473'phui-tag-red': (over_limit)474};475476for (var c in color_map) {477JX.DOM.alterClass(points_node, c, !!color_map[c]);478}479480JX.DOM.show(points_node);481}482483}484485});486487488