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