1'use strict' 2var util = require('util') 3var TrackerBase = require('./tracker-base.js') 4var Tracker = require('./tracker.js') 5var TrackerStream = require('./tracker-stream.js') 6 7var TrackerGroup = module.exports = function (name) { 8 TrackerBase.call(this, name) 9 this.parentGroup = null 10 this.trackers = [] 11 this.completion = {} 12 this.weight = {} 13 this.totalWeight = 0 14 this.finished = false 15 this.bubbleChange = bubbleChange(this) 16} 17util.inherits(TrackerGroup, TrackerBase) 18 19function bubbleChange (trackerGroup) { 20 return function (name, completed, tracker) { 21 trackerGroup.completion[tracker.id] = completed 22 if (trackerGroup.finished) return 23 trackerGroup.emit('change', name || trackerGroup.name, trackerGroup.completed(), trackerGroup) 24 } 25} 26 27TrackerGroup.prototype.nameInTree = function () { 28 var names = [] 29 var from = this 30 while (from) { 31 names.unshift(from.name) 32 from = from.parentGroup 33 } 34 return names.join('/') 35} 36 37TrackerGroup.prototype.addUnit = function (unit, weight) { 38 if (unit.addUnit) { 39 var toTest = this 40 while (toTest) { 41 if (unit === toTest) { 42 throw new Error( 43 'Attempted to add tracker group ' + 44 unit.name + ' to tree that already includes it ' + 45 this.nameInTree(this)) 46 } 47 toTest = toTest.parentGroup 48 } 49 unit.parentGroup = this 50 } 51 this.weight[unit.id] = weight || 1 52 this.totalWeight += this.weight[unit.id] 53 this.trackers.push(unit) 54 this.completion[unit.id] = unit.completed() 55 unit.on('change', this.bubbleChange) 56 if (!this.finished) this.emit('change', unit.name, this.completion[unit.id], unit) 57 return unit 58} 59 60TrackerGroup.prototype.completed = function () { 61 if (this.trackers.length === 0) return 0 62 var valPerWeight = 1 / this.totalWeight 63 var completed = 0 64 for (var ii = 0; ii < this.trackers.length; ii++) { 65 var trackerId = this.trackers[ii].id 66 completed += valPerWeight * this.weight[trackerId] * this.completion[trackerId] 67 } 68 return completed 69} 70 71TrackerGroup.prototype.newGroup = function (name, weight) { 72 return this.addUnit(new TrackerGroup(name), weight) 73} 74 75TrackerGroup.prototype.newItem = function (name, todo, weight) { 76 return this.addUnit(new Tracker(name, todo), weight) 77} 78 79TrackerGroup.prototype.newStream = function (name, todo, weight) { 80 return this.addUnit(new TrackerStream(name, todo), weight) 81} 82 83TrackerGroup.prototype.finish = function () { 84 this.finished = true 85 if (!this.trackers.length) this.addUnit(new Tracker(), 1, true) 86 for (var ii = 0; ii < this.trackers.length; ii++) { 87 var tracker = this.trackers[ii] 88 tracker.finish() 89 tracker.removeListener('change', this.bubbleChange) 90 } 91 this.emit('change', this.name, 1, this) 92} 93 94var buffer = ' ' 95TrackerGroup.prototype.debug = function (depth) { 96 depth = depth || 0 97 var indent = depth ? buffer.substr(0, depth) : '' 98 var output = indent + (this.name || 'top') + ': ' + this.completed() + '\n' 99 this.trackers.forEach(function (tracker) { 100 if (tracker instanceof TrackerGroup) { 101 output += tracker.debug(depth + 1) 102 } else { 103 output += indent + ' ' + tracker.name + ': ' + tracker.completed() + '\n' 104 } 105 }) 106 return output 107} 108