diff --git a/bower.json b/bower.json
index f0c2326..9b7fe82 100644
--- a/bower.json
+++ b/bower.json
@@ -22,6 +22,7 @@
"bootstrap": "~3.3.4",
"sweetalert": "~0.5.0",
"angular": "~1.3.15",
- "angular-ui-router": "~0.2.13"
+ "angular-ui-router": "~0.2.13",
+ "async": "~0.9.2"
}
}
diff --git a/gulpfile.js b/gulpfile.js
index b41d89e..7c5d339 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -35,6 +35,7 @@ gulp.task('js',function(){
jsFiles = ["src/*.js",
"bower_components/jquery/dist/jquery.js",
"bower_components/moment/moment.js",
+ "bower_components/async/lib/async.js",
"bower_components/bootstrap/dist/js/bootstrap.js",
"bower_components/bootstrap/js/collapse.js",
"bower_components/angular/angular.js",
diff --git a/src/PBSlib.coffee b/src/PBSlib.coffee
index fc5b89e..31388b9 100644
--- a/src/PBSlib.coffee
+++ b/src/PBSlib.coffee
@@ -1,6 +1,7 @@
class PBS
constructor: (@list, @verbose) ->
@days = []
+ @verbose = yes
@criticalPaths = []
log: (x...) ->
@@ -25,68 +26,89 @@ class PBS
return item
# Compute the item's end day
- calculateEndDay: (item) =>
- if !item.startDay?
+ calculateEndDay: (item,cb) =>
+ if item.endDay? then cb null, item.endDay
+ else if !item.startDay?
@log "calculating start day of",item.id
- item.startDay = @calculateStartDay item
- @log "start day of",item.id,"is",item.startDay
- item.endDay = item.startDay + item.duration
- @log "end day of",item.id,"is",item.endDay
- @insertDay item.endDay
- return item.endDay
+ @calculateStartDay item, => @calculateEndDay item, cb
+ else
+ @log "start day of",item.id,"is",item.startDay
+ item.endDay = item.startDay + item.duration
+ @log "end day of",item.id,"is",item.endDay
+ @insertDay item.endDay
+ cb null, item.endDay
# Find out which day the activity starts
- calculateStartDay: (item) =>
- if !item.depends? or item.depends.length is 0
+ calculateStartDay: (item,cb) =>
+ if item.startDay? then cb null, item.startDay
+ else if !item.depends? or item.depends.length is 0
@insertDay 0
- return item.startDay = 0
- item.startDay = @maxa item.depends.map(@toActivity).map @calculateEndDay
- @log "start day of",item.id,"is",item.startDay
- # write max delay time to each depend
- for j,x of item.depends
- @log "checking permittedDelay to dependency", x, "of", item
- i = @toActivity x
- if !i.dependant? then i.dependant = [item.id]
- else i.dependant.push item.id
- if !i.permittedDelay?
- i.permittedDelay = item.startDay - @calculateEndDay i
- @log "written permittedDelay to dependency", x, "of", item, "as", i.permittedDelay
- else @log "aborting permittedDelay: already calculated"
- @log "permitted delay of",x,"is",i.permittedDelay
- @insertDay item.startDay
- return item.startDay
+ cb null, item.startDay = 0
+ else
+ async.map item.depends.map(@toActivity), @calculateEndDay.bind(@), (er, r) =>
+ item.startDay = @maxa r
+ @log "start day of",item.id,"is",item.startDay
+ # write max delay time to each depend
+ checkPermittedDelay = (x,c) =>
+ @log "checking permittedDelay to dependency", x, "of", item
+ i = @toActivity x
+ if !i.dependant? then i.dependant = [item.id]
+ else i.dependant.push item.id
+ if !i.permittedDelay?
+ @calculateEndDay i, (e,d) =>
+ i.permittedDelay = item.startDay - d
+ @log "written permittedDelay to dependency", x, "of", item, "as", i.permittedDelay
+ @log "permitted delay of",x,"is",i.permittedDelay
+ c()
+ else
+ @log "aborting permittedDelay: already calculated"
+ @log "permitted delay of",x,"is",i.permittedDelay
+ c()
+ async.each item.depends, checkPermittedDelay.bind(@), =>
+ @insertDay item.startDay
+ cb null, item.startDay
- calculateDelays: (item) =>
- if !item.dependant? or item.dependant.length is 0 then return no
- lowestFDelay = 0; fDelay = no; cDelay = 0
- for j,i of item.dependant
- x = @toActivity i
- if !isNaN(x.permittedDelay) or x.permittedDelay < lowestFDelay or fDelay is no
- @log "activity", i, "dependant on", item.id, "has the lowest delay for now ("+(x.permittedDelay or 0)+")"
- lowestFDelay = x.permittedDelay or 0
- cDelay = x.chainedDelay or 0
- fDelay = yes
- olDelay = item.chainedDelay
- item.chainedDelay = lowestFDelay + cDelay
- @log "chained delay of", item.id, "is", item.chainedDelay
- return item.chainedDelay isnt olDelay
+ calculateDelays: (item,cb) =>
+ if !item.dependant? or item.dependant.length is 0 then cb null, no
+ else
+ lowestFDelay = 0; fDelay = no; cDelay = 0
+ checkDependant = (i,c) =>
+ x = @toActivity i
+ if !isNaN(x.permittedDelay) or x.permittedDelay < lowestFDelay or fDelay is no
+ @log "activity", i, "dependant on", item.id, "has the lowest delay for now ("+(x.permittedDelay or 0)+")"
+ lowestFDelay = x.permittedDelay or 0
+ cDelay = x.chainedDelay or 0
+ fDelay = yes
+ c()
+ async.each item.dependant, checkDependant.bind(@), =>
+ olDelay = item.chainedDelay
+ item.chainedDelay = lowestFDelay + cDelay
+ @log "chained delay of", item.id, "is", item.chainedDelay
+ cb null, item.chainedDelay isnt olDelay
- calculateCriticalPaths: (path) ->
+ calculateCriticalPaths: (path,cb) ->
@log "calculating path from",path
lastID = path[path.length - 1]
last = @toActivity lastID
if last.dependant? and last.dependant.length > 0
- last.dependant.forEach (x) =>
+ checkDependant = (x,cb2) =>
ii = @toActivity x
delay = ii.permittedDelay or 0
if delay is 0
- @calculateCriticalPaths path.concat x
+ cb2 null, yes
+ @calculateCriticalPaths path.concat(x), cb
else
@log "dead end at", lastID, "-->", x, "because delay is", delay
+ cb2 null, no
+ async.each last.dependant, checkDependant.bind(@), (a,b) -> return
else
- path.forEach (x) => @toActivity(x).critical = yes
- @log "calculated path", path
- @criticalPaths.push path
+ setCritical = (x,c) =>
+ @toActivity(x).critical = yes
+ c null, yes
+ async.each path, setCritical.bind(@), =>
+ @log "calculated path", path
+ @criticalPaths.push path
+ cb null, path
# Find out which activity has the highest id
highestID: => return @maxa(@list.map (x) -> x.id)
@@ -100,26 +122,41 @@ class PBS
setData: (data) ->
@list = data
return @
-
+
calculate: (options,cb) ->
- h = @highestID()
- for x,i in @list
- @log '('+x.id+'/'+h+')'
- @calculateEndDay x
- finished = no; i = 0
- while !finished
- i++; finished = yes
- for x,i in @list
- if @calculateDelays x
- finished = no
- @log "Done calculating delays. Took", i, "iterations"
- for x,i in @list
- if !x.depends? or x.depends.length is 0
- @calculateCriticalPaths [x.id]
- results = activities: @list, days: @days, criticalPaths: @criticalPaths
- if options?.json
- if cb? then cb(JSON.stringify results)
- JSON.stringify results
- else
- if cb? then cb(results)
- results
+ @log "(Step 1) calculating startDay, endDay, freeDelay"
+ async.each @list, @calculateEndDay.bind(@), =>
+ @log "(Step 2) Starting chained delay calculations."
+ cont = yes; i = 0
+ notDone = ->
+ console.log cont
+ cont
+ chainedDelayChanged = (x,res) =>
+ i++
+ cont = no
+ @calculateDelays x, (e,r) =>
+ @log "cDelay calc result:",r
+ res null, r
+ calculateAllDelays = (cb2) =>
+ async.map @list, chainedDelayChanged, (err,r) ->
+ iterator = (acc,x,cb3) ->
+ if x
+ cb3 null, yes
+ else cb3 null, acc
+ async.reduce r, no, iterator, (err,res) ->
+ cont = res
+ cb2 null
+ #TODO: check WHILST
+ async.whilst notDone, calculateAllDelays, =>
+ @log "Done calculating delays. Took", i, "iterations"
+ #TODO: check THIS vvvvv
+ calculateCriticalPathIfApplicable = (x, c) =>
+ if !x.depends? or x.depends.length is 0
+ @calculateCriticalPaths [x.id], c
+ async.each @list, calculateCriticalPathIfApplicable.bind(@), =>
+ results = activities: @list, days: @days, criticalPaths: @criticalPaths
+ @log "DONE:", results
+ if options?.json
+ cb JSON.stringify results
+ else
+ cb results
diff --git a/src/app.coffee b/src/app.coffee
index 46db041..eaedbf9 100644
--- a/src/app.coffee
+++ b/src/app.coffee
@@ -46,13 +46,15 @@ pertController = ($scope) ->
localStorage.setItem 'ganttpert', sdata
unless options.silent
swal 'Ok', 'Data updated', 'success'
- $scope.pbs = new PBS(data).calculate()
- $scope.$broadcast 'dataChanged'
+ $scope.pbs = new PBS(data).calculate {}, ->
+ $scope.$broadcast 'dataChanged'
catch e
swal 'Error', e, 'error'
- $scope.fromLocalStorage = (options) ->
+ $scope.fromLocalStorage = (options,cb) ->
options = options || {}
+ if options.call? then cb = options
+ else unless cb?.call? then return console.log "fromLocalStorage called without callback"
data = localStorage.getItem options.name || 'ganttpert'
if data is null then data = "[]"
try
@@ -63,13 +65,15 @@ pertController = ($scope) ->
swal 'JSON Error', e, 'error'
if options.raw
#console.log 'Loading: []'
- return []
+ cb []
else
#console.log 'Loading: {list: [], days: []}'
- return list: [], days: []
+ cb list: [], days: []
if options.raw
#console.log 'Loading: '+jdata
- return jdata
+ cb jdata
else
#console.log 'Loading: '+$scope.pbs
- return $scope.pbs ?= new PBS(jdata).calculate()
+ new PBS(jdata).calculate (x) ->
+ $scope.pbs = x
+ cb x
diff --git a/src/controllers.coffee b/src/controllers.coffee
index f50b620..1697c9d 100644
--- a/src/controllers.coffee
+++ b/src/controllers.coffee
@@ -1,9 +1,8 @@
pertApp.controller 'tableController', ($scope) ->
$scope.list = []
$scope.refreshTable = ->
- ls = $scope.fromLocalStorage()
- if ls?
- $scope.list = ls.activities
+ $scope.fromLocalStorage (ls) ->
+ if ls? then $scope.list = ls.activities
$scope.$on 'dataChanged', $scope.refreshTable
$scope.refreshTable()
@@ -32,7 +31,7 @@ pertApp.controller 'pertDiagController', ($scope) ->
network = new vis.Network (document.getElementById 'pertDiagram'), { nodes: nodes, edges: connections }, options
$scope.rebuild = ->
console.log 'rebuild'
- $scope.buildGraph $scope.fromLocalStorage()
+ $scope.fromLocalStorage (r) -> $scope.buildGraph r
$scope.$on 'dataChanged', $scope.rebuild
$scope.rebuild()
@@ -47,8 +46,8 @@ pertApp.controller 'ganttDiagController', ($scope) ->
if !data? then return
timeline = new vis.Timeline (document.getElementById 'timeline'), ($scope.toDates data.activities), {}
$scope.$on 'dataChanged', ->
- $scope.buildTimeline $scope.fromLocalStorage()
- $scope.buildTimeline $scope.fromLocalStorage()
+ $scope.fromLocalStorage (r) -> $scope.buildTimeline r
+ $scope.fromLocalStorage (r) -> $scope.buildTimeline r
pertApp.controller 'rawEditorController', ($scope) ->
$scope.reset = ->
@@ -60,10 +59,9 @@ pertApp.controller 'rawEditorController', ($scope) ->
swal 'Invalid Data', e, 'error'
$scope.toLocalStorage data
$scope.reloadData = ->
- $scope.taData = JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes
- $scope.$on 'dataChanged', ->
- $scope.reloadData()
- #$('#ta').val JSON.stringify $scope.fromLocalStorage silent: yes, raw: yes
+ $scope.fromLocalStorage { silent: yes, raw: yes }, (x) ->
+ $scope.taData = JSON.stringify x
+ $scope.$on 'dataChanged', -> $scope.reloadData()
$scope.reloadData()
pertApp.controller 'editorController', ($scope) ->
@@ -77,18 +75,18 @@ pertApp.controller 'editorController', ($scope) ->
swal 'Ops', 'could not find '+id, 'warning'
$scope.delete = (index,id) ->
- newdata = $scope.fromLocalStorage raw: yes
- l = []
- if id? then for i,j of newdata
- if id isnt j.id
- l.push j
- else for i,j of newdata
- if parseInt(i) isnt index
- l.push j
- diff = newdata.length - l.length
- $scope.toLocalStorage l, silent: yes
- if diff isnt 1
- swal 'Done', diff+' item(s) deleted', 'warning'
+ $scope.fromLocalStorage { raw: yes }, (newdata) ->
+ l = []
+ if id? then for i,j of newdata
+ if id isnt j.id
+ l.push j
+ else for i,j of newdata
+ if parseInt(i) isnt index
+ l.push j
+ diff = newdata.length - l.length
+ $scope.toLocalStorage l, silent: yes
+ if diff isnt 1
+ swal 'Done', diff+' item(s) deleted', 'warning'
$scope.addNew = (id, dur, deps) ->
dur ?= $('#new-duration').val().trim()
@@ -109,14 +107,14 @@ pertApp.controller 'editorController', ($scope) ->
unless isNaN dep
deps[i] = parseInt dep
catch e
- newdata = $scope.fromLocalStorage silent: yes, raw: yes
- if !newdata? or newdata is null or !newdata.push?
- newdata = []
- newdata.push { id: id, duration: dur, depends: deps }
- $scope.toLocalStorage newdata, silent: yes
+ $scope.fromLocalStorage { silent: yes, raw: yes }, (newdata) ->
+ if !newdata? or newdata is null or !newdata.push?
+ newdata = []
+ newdata.push { id: id, duration: dur, depends: deps }
+ $scope.toLocalStorage newdata, silent: yes
$scope.refreshEditor = ->
- data = $scope.fromLocalStorage { silent: yes, raw: yes }
- $scope.list = data || []
+ $scope.fromLocalStorage { silent: yes, raw: yes }, (data) ->
+ $scope.list = data || []
$scope.$on 'dataChanged', $scope.refreshEditor
$scope.refreshEditor()
diff --git a/src/index.html b/src/index.html
index 47df66a..18f044d 100644
--- a/src/index.html
+++ b/src/index.html
@@ -9,6 +9,7 @@
+