make[1]: Entering directory `/home/bill/dev/schedule'
| Line | Hits | Source |
|---|---|---|
| 1 | // indexOf compares searchElement to elements of the Array using strict | |
| 2 | // equality (the same method used by the ===, or triple-equals, operator). | |
| 3 | // | |
| 4 | // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf | |
| 5 | // | |
| 6 | 1 | if (!Array.prototype.indexOf) { |
| 7 | 0 | Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { |
| 8 | 0 | "use strict"; |
| 9 | 0 | if (this == null) { |
| 10 | 0 | throw new TypeError(); |
| 11 | } | |
| 12 | 0 | var t = Object(this); |
| 13 | 0 | var len = t.length >>> 0; |
| 14 | 0 | if (len === 0) { |
| 15 | 0 | return -1; |
| 16 | } | |
| 17 | 0 | var n = 0; |
| 18 | 0 | if (arguments.length > 1) { |
| 19 | 0 | n = Number(arguments[1]); |
| 20 | 0 | if (n != n) { // shortcut for verifying if it's NaN |
| 21 | 0 | n = 0; |
| 22 | 0 | } else if (n != 0 && n != Infinity && n != -Infinity) { |
| 23 | 0 | n = (n > 0 || -1) * Math.floor(Math.abs(n)); |
| 24 | } | |
| 25 | } | |
| 26 | 0 | if (n >= len) { |
| 27 | 0 | return -1; |
| 28 | } | |
| 29 | 0 | var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); |
| 30 | 0 | for (; k < len; k++) { |
| 31 | 0 | if (k in t && t[k] === searchElement) { |
| 32 | 0 | return k; |
| 33 | } | |
| 34 | } | |
| 35 | 0 | return -1; |
| 36 | } | |
| 37 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | // Returns true if an object is an array, false if it is not. | |
| 2 | // | |
| 3 | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray | |
| 4 | ||
| 5 | ||
| 6 | 1 | if(!Array.isArray) { |
| 7 | 0 | Array.isArray = function (vArg) { |
| 8 | 0 | return Object.prototype.toString.call(vArg) === "[object Array]"; |
| 9 | }; | |
| 10 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Schedule create | |
| 3 | * (c) 2013 Bill, BunKat LLC. | |
| 4 | * | |
| 5 | * Creates a schedule for each task that respects the task schedules, task | |
| 6 | * dependencies, resource schedules, project schedule, and start date provided. | |
| 7 | * | |
| 8 | * Schedule is freely distributable under the MIT license. | |
| 9 | * For all details and documentation: | |
| 10 | * http://github.com/bunkat/schedule | |
| 11 | */ | |
| 12 | ||
| 13 | 1 | schedule.create = function(tasks, resources, sched, startDate) { |
| 14 | ||
| 15 | 22 | var taskGraph = schedule.dependencyGraph(tasks), |
| 16 | resMgr = schedule.resourceManager(resources, startDate), | |
| 17 | scheduledTasks = {}; | |
| 18 | ||
| 19 | /** | |
| 20 | * Main function, coordinates the process of creating a schedule. | |
| 21 | */ | |
| 22 | 22 | function generateSchedule() { |
| 23 | 22 | var range, failedTasks = []; |
| 24 | ||
| 25 | // add required resources not supplied in resources array, the project | |
| 26 | // schedule and all task schedules to the resource manager (these will | |
| 27 | // be treated as resources to calculate valid reservations with) | |
| 28 | 22 | resMgr.addResource(taskGraph.resources, '', startDate); |
| 29 | 22 | resMgr.addResource([{id: '_proj', schedule: sched}], '', startDate); |
| 30 | 22 | resMgr.addResource(tasks, '_task', startDate); |
| 31 | ||
| 32 | 22 | forwardPass(taskGraph.roots); |
| 33 | 22 | range = getSummary(tasks, failedTasks); |
| 34 | 22 | backwardPass(taskGraph.leaves, range[1]); |
| 35 | ||
| 36 | 22 | return { |
| 37 | scheduledTasks: scheduledTasks, | |
| 38 | failedTasks: failedTasks.length ? failedTasks : null, | |
| 39 | success: failedTasks.length === 0, | |
| 40 | start: range[0], | |
| 41 | end: range[1] | |
| 42 | }; | |
| 43 | } | |
| 44 | ||
| 45 | /** | |
| 46 | * Schedules each task as their dependencies are met, tracking dependency | |
| 47 | * end dates in the dependencies map. | |
| 48 | */ | |
| 49 | 22 | function forwardPass(roots) { |
| 50 | 22 | var readyTasks = roots.slice(0), |
| 51 | dependencies = {}; // holds count and earliest start date of dependencies | |
| 52 | ||
| 53 | 22 | for(var i = 0, len = roots.length; i < len; i++) { |
| 54 | 46 | dependencies[roots[i]] = [0, startDate.getTime()]; |
| 55 | } | |
| 56 | ||
| 57 | 22 | while(readyTasks.length) { |
| 58 | 69 | schedule.sort.tasks(taskGraph, readyTasks); |
| 59 | ||
| 60 | 69 | var task = taskGraph.tasks[readyTasks.pop()], |
| 61 | start = dependencies[task.id][1], | |
| 62 | end = forwardPassTask(task, start); | |
| 63 | ||
| 64 | 69 | if(end && task.requiredBy) { |
| 65 | 20 | updateDependencies(readyTasks, dependencies, task.requiredBy, end); |
| 66 | 20 | resMgr.optimize(getMinStart(dependencies)); // clean up expired exceptions |
| 67 | } | |
| 68 | } | |
| 69 | } | |
| 70 | ||
| 71 | /** | |
| 72 | * Finds the next available time that all of a tasks constraints are met and | |
| 73 | * makes the appropriate resource reservations. A task may be scheduled in a | |
| 74 | * single contiguous block or multiple blocks of time. | |
| 75 | */ | |
| 76 | 22 | function forwardPassTask(task, start) { |
| 77 | 69 | var resAll = ['_proj', '_task' + task.id], |
| 78 | resources = task.resources ? resAll.concat(task.resources) : resAll, | |
| 79 | duration = task.duration, | |
| 80 | next = start, | |
| 81 | scheduledTask = {schedule: [], duration: task.duration}; | |
| 82 | ||
| 83 | 69 | while(duration) { |
| 84 | 73 | var r = resMgr.makeReservation(resources, next, task.minSchedule || 1, duration); |
| 85 | 75 | if(!r.success) return undefined; |
| 86 | ||
| 87 | 71 | scheduledTask.earlyStart = scheduledTask.earlyStart || r.start; |
| 88 | 71 | scheduledTask.schedule.push(r); |
| 89 | 71 | duration -= r.duration; |
| 90 | 71 | next = r.end; |
| 91 | } | |
| 92 | ||
| 93 | 67 | scheduledTask.earlyFinish = next; |
| 94 | 67 | scheduledTasks[task.id] = scheduledTask; |
| 95 | ||
| 96 | 67 | return next; |
| 97 | } | |
| 98 | ||
| 99 | /** | |
| 100 | * Finds the start and end date of the schedule and adds any tasks that were | |
| 101 | * scheduled to the failedTasks array. | |
| 102 | */ | |
| 103 | 22 | function getSummary(tasks, failedTasks) { |
| 104 | 22 | var start, end; |
| 105 | ||
| 106 | 22 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 107 | 72 | var t = scheduledTasks[tasks[i].id]; |
| 108 | 72 | if(t) { |
| 109 | 67 | start = !start || t.earlyStart < start ? t.earlyStart : start; |
| 110 | 67 | end = !end || t.earlyFinish > end ? t.earlyFinish : end; |
| 111 | } | |
| 112 | else { | |
| 113 | 5 | failedTasks.push(tasks[i].id); |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 117 | 22 | return [start, end]; |
| 118 | } | |
| 119 | ||
| 120 | /** | |
| 121 | * As tasks are scheduled, the information is tracked in the dependencies | |
| 122 | * array. As a tasks dependencies are all met, the task is pushed onto the | |
| 123 | * readyTasks array which means it is available to be scheduled. | |
| 124 | */ | |
| 125 | 22 | function updateDependencies(readyTasks, dependencies, tasks, end) { |
| 126 | 20 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 127 | 34 | var tid = tasks[i], |
| 128 | dependsOn = taskGraph.tasks[tid].dependsOn, | |
| 129 | metDeps = dependencies[tid] || (dependencies[tid] = [0, 0]); | |
| 130 | ||
| 131 | 34 | metDeps[0] += 1; |
| 132 | 34 | metDeps[1] = end > metDeps[1] ? end : metDeps[1]; |
| 133 | ||
| 134 | 34 | if(!dependsOn || metDeps[0] >= dependsOn.length) { |
| 135 | 23 | readyTasks.push(tid); |
| 136 | } | |
| 137 | } | |
| 138 | } | |
| 139 | ||
| 140 | /** | |
| 141 | * Finds the earliest time that any of the remaining tasks could be scheduled | |
| 142 | * for. It is used to optimize the resource manager since nothing can be | |
| 143 | * scheduled before this time. | |
| 144 | */ | |
| 145 | 22 | function getMinStart(dependencies) { |
| 146 | 20 | var min; |
| 147 | 20 | for(var id in dependencies) { |
| 148 | 89 | if(!min || min > dependencies[id][1]) { |
| 149 | 20 | min = dependencies[id][1]; |
| 150 | } | |
| 151 | } | |
| 152 | 20 | return min; |
| 153 | } | |
| 154 | ||
| 155 | /** | |
| 156 | * Calculates when a task must be completed by before it ends up slipping | |
| 157 | * one of its dependencies or the schedule. Tasks with zero float amount | |
| 158 | * are in the critical path. | |
| 159 | */ | |
| 160 | 22 | function backwardPass(tasks, finishDate) { |
| 161 | 59 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 162 | 114 | var sTask = scheduledTasks[tasks[i]], |
| 163 | dependsOn = taskGraph.tasks[tasks[i]].dependsOn; | |
| 164 | ||
| 165 | 114 | if(sTask) { |
| 166 | 112 | sTask.lateFinish = finishDate; |
| 167 | 112 | sTask.floatAmt = (sTask.lateFinish - sTask.earlyFinish) / later.MIN; |
| 168 | ||
| 169 | 112 | if(dependsOn) { |
| 170 | 37 | backwardPass(dependsOn, sTask.earlyStart); |
| 171 | } | |
| 172 | } | |
| 173 | } | |
| 174 | } | |
| 175 | ||
| 176 | 22 | return generateSchedule(); |
| 177 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Dependency graph | |
| 3 | * (c) 2013 Bill, BunKat LLC. | |
| 4 | * | |
| 5 | * Generates a dependency graph from a set of tasks and finds the root nodes, | |
| 6 | * leaf nodes, depth, and optimistic float (time between when a schedule starts | |
| 7 | * and when it must start to prevent a schedule slip). This information is used | |
| 8 | * by the schedule generator to schedule tasks against an actual timeline. | |
| 9 | * | |
| 10 | * Schedule is freely distributable under the MIT license. | |
| 11 | * For all details and documentation: | |
| 12 | * http://github.com/bunkat/schedule | |
| 13 | */ | |
| 14 | ||
| 15 | 1 | schedule.dependencyGraph = function(taskArr) { |
| 16 | ||
| 17 | /** | |
| 18 | * Starting point for creating the dependency graph, clones the tasks and | |
| 19 | * then fills out the graph properties. | |
| 20 | */ | |
| 21 | 33 | function createDependencyGraph(tasks) { |
| 22 | 33 | var graph = { |
| 23 | tasks: {}, | |
| 24 | roots: [], | |
| 25 | leaves: [], | |
| 26 | resources: [], | |
| 27 | depth: 0, | |
| 28 | end : 0 | |
| 29 | }; | |
| 30 | ||
| 31 | 33 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 32 | 107 | var t = tasks[i]; |
| 33 | 107 | graph.tasks[t.id] = { |
| 34 | id: t.id, | |
| 35 | duration: t.duration, | |
| 36 | priority: t.priority, | |
| 37 | schedule: t.schedule, | |
| 38 | minSchedule: t.minSchedule, | |
| 39 | dependsOn: t.dependsOn, | |
| 40 | resources: t.resources | |
| 41 | }; | |
| 42 | } | |
| 43 | ||
| 44 | 33 | setResources(graph); |
| 45 | 33 | setRequiredBy(graph.tasks); |
| 46 | 33 | setRootsAndLeaves(graph); |
| 47 | ||
| 48 | 33 | setDepth(graph, graph.leaves, 0); |
| 49 | 33 | graph.depth += 1; // increment depth so it is 1 based |
| 50 | ||
| 51 | 33 | forwardPass(graph, {}, graph.roots, 0); |
| 52 | 33 | setEnd(graph, graph.leaves); |
| 53 | 33 | backwardPass(graph, {}, graph.leaves, graph.end); |
| 54 | ||
| 55 | 33 | return graph; |
| 56 | } | |
| 57 | ||
| 58 | /** | |
| 59 | * Creates an array of all the unique resources that are used by the tasks. | |
| 60 | */ | |
| 61 | 33 | function setResources(graph) { |
| 62 | 33 | for(var id in graph.tasks) { |
| 63 | 107 | var task = graph.tasks[id]; |
| 64 | 107 | if(!isEmpty(task.resources)) { |
| 65 | 38 | for(var i = 0, len = task.resources.length; i < len; i++) { |
| 66 | 40 | var resId = task.resources[i]; |
| 67 | 40 | if(graph.resources.indexOf(resId) === -1) { |
| 68 | 13 | graph.resources.push(resId); |
| 69 | } | |
| 70 | } | |
| 71 | } | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | /** | |
| 76 | * Creates the back links from child to parent based on the dependsOn property. | |
| 77 | */ | |
| 78 | 33 | function setRequiredBy(tasks) { |
| 79 | 33 | for(var id in tasks) { |
| 80 | 107 | var child = tasks[id], |
| 81 | dependsOn = child.dependsOn; | |
| 82 | ||
| 83 | 107 | if(!isEmpty(dependsOn)) { |
| 84 | 48 | for(var i = 0, len = dependsOn.length; i < len; i++) { |
| 85 | 63 | var parent = tasks[dependsOn[i]]; |
| 86 | 63 | (parent.requiredBy || (parent.requiredBy = [])).push(child.id); |
| 87 | } | |
| 88 | } | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | /** | |
| 93 | * Finds the roots and leaves of the dependency graph. | |
| 94 | */ | |
| 95 | 33 | function setRootsAndLeaves(graph) { |
| 96 | 33 | for(var id in graph.tasks) { |
| 97 | 107 | var task = graph.tasks[id]; |
| 98 | ||
| 99 | 107 | if(isEmpty(task.dependsOn)) { |
| 100 | 59 | graph.roots.push(task.id); |
| 101 | } | |
| 102 | ||
| 103 | 107 | if(isEmpty(task.requiredBy)) { |
| 104 | 66 | graph.leaves.push(task.id); |
| 105 | } | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | /** | |
| 110 | * Determines the depth (maximum number of nodes that depend on the current | |
| 111 | * node) of each node in the dependency graph. | |
| 112 | */ | |
| 113 | 33 | function setDepth(graph, tasks, depth) { |
| 114 | 96 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 115 | 162 | var task = graph.tasks[tasks[i]], |
| 116 | dependsOn = task.dependsOn; | |
| 117 | ||
| 118 | 162 | task.depth = !task.depth || depth > task.depth ? depth : task.depth; |
| 119 | 162 | graph.depth = depth > graph.depth ? depth : graph.depth; |
| 120 | ||
| 121 | 162 | if(!isEmpty(dependsOn)) { |
| 122 | 63 | setDepth(graph, dependsOn, task.depth + 1); |
| 123 | } | |
| 124 | } | |
| 125 | } | |
| 126 | ||
| 127 | /** | |
| 128 | * Generates an optimistic (assume all resources are available when needed) | |
| 129 | * forward schedule for each node in the graph, respecting node dependencies. | |
| 130 | */ | |
| 131 | 33 | function forwardPass(graph, depEnds, tasks, start) { |
| 132 | 71 | updateDependencies(depEnds, tasks, start); |
| 133 | 71 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 134 | 119 | var tid = tasks[i], |
| 135 | task = graph.tasks[tid], | |
| 136 | dependsOn = task.dependsOn, | |
| 137 | dep = depEnds[tid]; | |
| 138 | ||
| 139 | 119 | if(!task.earlyFinish && (isEmpty(dependsOn) || (dep && dep[0] === dependsOn.length))) { |
| 140 | 104 | task.earlyStart = dep[1]; |
| 141 | 104 | task.earlyFinish = dep[1] + task.duration; |
| 142 | ||
| 143 | 104 | if(!isEmpty(task.requiredBy)) { |
| 144 | 38 | forwardPass(graph, depEnds, task.requiredBy, task.earlyFinish); |
| 145 | } | |
| 146 | } | |
| 147 | } | |
| 148 | } | |
| 149 | ||
| 150 | /** | |
| 151 | * Finds the end of the optimistic forward pass schedule. | |
| 152 | */ | |
| 153 | 33 | function setEnd(graph, tasks) { |
| 154 | 33 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 155 | 66 | var finish = graph.tasks[tasks[i]].earlyFinish; |
| 156 | 66 | graph.end = finish > graph.end ? finish : graph.end; |
| 157 | } | |
| 158 | } | |
| 159 | ||
| 160 | /** | |
| 161 | * Generates an optimistic (assume all resources are available when needed) | |
| 162 | * backward schedule for each node in the graph, respecting node dependencies. | |
| 163 | * Computes the float (time between earliest finish and latest finish). | |
| 164 | */ | |
| 165 | 33 | function backwardPass(graph, depEnds, tasks, end) { |
| 166 | 78 | updateDependencies(depEnds, tasks, end, true); |
| 167 | 78 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 168 | 125 | var tid = tasks[i], |
| 169 | task = graph.tasks[tid], | |
| 170 | requiredBy = task.requiredBy, | |
| 171 | dep = depEnds[tid]; | |
| 172 | ||
| 173 | 125 | if(isEmpty(requiredBy) || (dep && dep[0] === requiredBy.length)) { |
| 174 | 103 | task.lateStart = dep[1] - task.duration; |
| 175 | 103 | task.lateFinish = dep[1]; |
| 176 | 103 | task.floatAmt = task.lateFinish - task.earlyFinish; |
| 177 | ||
| 178 | 103 | if(!isEmpty(task.dependsOn)) { |
| 179 | 45 | backwardPass(graph, depEnds, task.dependsOn, task.lateStart); |
| 180 | } | |
| 181 | } | |
| 182 | } | |
| 183 | } | |
| 184 | ||
| 185 | /** | |
| 186 | * Tracks dependencies between nodes to ensure nodes are only scheduled once | |
| 187 | * their dependencies have completed. | |
| 188 | */ | |
| 189 | 33 | function updateDependencies(deps, tasks, start, rev) { |
| 190 | 171 | var compare = rev ? function(a,b) { return b > a; } : |
| 191 | 14 | function(a,b) { return a > b; }; |
| 192 | ||
| 193 | 149 | for(var i = 0, len = tasks.length; i < len; i++) { |
| 194 | 244 | var id = tasks[i]; |
| 195 | ||
| 196 | 244 | if(deps[id]) { |
| 197 | 36 | deps[id][0] = deps[id][0] + 1; |
| 198 | 36 | deps[id][1] = compare(start, deps[id][1]) ? start : deps[id][1]; |
| 199 | } | |
| 200 | else { | |
| 201 | 208 | deps[id] = [1, start]; |
| 202 | } | |
| 203 | } | |
| 204 | } | |
| 205 | ||
| 206 | /** | |
| 207 | * Returns true if the array is undefined or empty. | |
| 208 | */ | |
| 209 | 33 | function isEmpty(arr) { |
| 210 | 1041 | return !arr || arr.length === 0; |
| 211 | } | |
| 212 | ||
| 213 | 33 | return createDependencyGraph(taskArr); |
| 214 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Resource manager | |
| 3 | * (c) 2013 Bill, BunKat LLC. | |
| 4 | * | |
| 5 | * Manages all of the resources and schedule constraints (project schedule, | |
| 6 | * task schedule, and resource schedules) and reserves resources as needed. Finds | |
| 7 | * the earliest time that a set of resources can be reserved. | |
| 8 | * | |
| 9 | * Schedule is freely distributable under the MIT license. | |
| 10 | * For all details and documentation: | |
| 11 | * http://github.com/bunkat/schedule | |
| 12 | */ | |
| 13 | ||
| 14 | 1 | schedule.resourceManager = function(resourceDefinitions, startDate) { |
| 15 | ||
| 16 | 36 | var defaultSched = {schedules: [{fd_a: [startDate.getTime()]}]}, |
| 17 | rMap = buildResourceMap(resourceDefinitions, startDate); | |
| 18 | ||
| 19 | /** | |
| 20 | * Creates a map from the resource definitions that contains the schedule | |
| 21 | * information for each of the resources (specifically when the resource | |
| 22 | * will be next available and how to calculate future availability). | |
| 23 | */ | |
| 24 | 36 | function buildResourceMap(resourceDefinitions, start) { |
| 25 | 36 | var map = {}; |
| 26 | 36 | if(resourceDefinitions) { |
| 27 | 35 | for(var i = 0, len = resourceDefinitions.length; i < len; i++) { |
| 28 | 90 | addResourceToMap(map, resourceDefinitions[i], start); |
| 29 | } | |
| 30 | } | |
| 31 | ||
| 32 | 36 | return map; |
| 33 | } | |
| 34 | ||
| 35 | /** | |
| 36 | * Adds a resource to the resource map. | |
| 37 | */ | |
| 38 | 36 | function addResourceToMap(map, def, start) { |
| 39 | 188 | var sched = JSON.parse(JSON.stringify(def.schedule || defaultSched)), |
| 40 | nextFn = schedule.memoizedRangeFn(later.schedule(sched).nextRange); | |
| 41 | ||
| 42 | 188 | map[def.id] = { schedule: sched, next: nextFn, nextAvail: nextFn(start) }; |
| 43 | } | |
| 44 | ||
| 45 | /** | |
| 46 | * Attempts to find the next time that all resources are available, starting | |
| 47 | * from the start time, with a duration of at least min minutes but no more | |
| 48 | * than max minutes. | |
| 49 | */ | |
| 50 | 36 | function getReservation(resources, start, min, max) { |
| 51 | 92 | var reservation, schedules = [], delays = {}; |
| 52 | 92 | maxTries = 50; |
| 53 | ||
| 54 | 92 | initRanges(resources, start, schedules, delays); |
| 55 | 92 | while(!(reservation = tryReservation(schedules, min, max)).success && --maxTries) { |
| 56 | 199 | updateRanges(schedules, nextValidStart(schedules), delays); |
| 57 | } | |
| 58 | ||
| 59 | 92 | reservation.delays = delays; |
| 60 | 92 | return reservation; |
| 61 | } | |
| 62 | ||
| 63 | /** | |
| 64 | * Initializes the resource schedule availablity based on the start date | |
| 65 | * provided. Resources that were not immediately available are captured in | |
| 66 | * the delays array to be reported with the reservation. | |
| 67 | */ | |
| 68 | 36 | function initRanges(resources, start, ranges, delays) { |
| 69 | 115 | for(var i = 0, len = resources.length; i < len; i++) { |
| 70 | 282 | var resId = resources[i]; |
| 71 | ||
| 72 | // handles nested resources (OR) | |
| 73 | 282 | if(Array.isArray(resId)) { |
| 74 | 23 | var subRanges = [], subDelays = {}; |
| 75 | 23 | initRanges(resId, start, subRanges, subDelays); |
| 76 | ||
| 77 | 23 | var longDelay = getLongestDelay(subDelays); |
| 78 | 23 | if(longDelay) { |
| 79 | 21 | delays[longDelay] = subDelays[longDelay]; |
| 80 | } | |
| 81 | ||
| 82 | 23 | var schedule = {subRanges: subRanges}; |
| 83 | 23 | setEarliestSubRange(schedule); |
| 84 | 23 | ranges.push(schedule); |
| 85 | } | |
| 86 | else { | |
| 87 | 259 | var res = rMap[resId], |
| 88 | range = res.nextAvail[0] >= start ? res.nextAvail : | |
| 89 | res.next(start); | |
| 90 | ||
| 91 | 259 | if(range[0] > start && resId !== '_proj') { |
| 92 | 89 | delays[resId] = { needed: start, available: range[0] }; |
| 93 | } | |
| 94 | ||
| 95 | 259 | ranges.push({id: resId, range: range}); |
| 96 | } | |
| 97 | } | |
| 98 | } | |
| 99 | ||
| 100 | /** | |
| 101 | * Determines if the current schedules overlap for at least min minutes. If | |
| 102 | * they do, a reservation is created, otherwise a failure is reported. | |
| 103 | */ | |
| 104 | 36 | function tryReservation(schedules, min,max) { |
| 105 | 291 | var reservation = {success: false}, |
| 106 | resources = [], start, end; | |
| 107 | ||
| 108 | 291 | for(var i = 0, len = schedules.length; i < len; i++) { |
| 109 | 733 | var schedule = schedules[i], |
| 110 | range = schedule.range; | |
| 111 | ||
| 112 | 733 | if(!isInternal(schedule)) { |
| 113 | 239 | resources.push(schedule.id); |
| 114 | } | |
| 115 | ||
| 116 | 733 | start = !start || range[0] > start ? range[0] : start; |
| 117 | 733 | end = !end || range[1] < end ? range[1] : end; |
| 118 | } | |
| 119 | ||
| 120 | 291 | var duration = (end - start) / later.MIN; |
| 121 | 291 | if(duration >= min) { |
| 122 | 89 | duration = max && duration > max ? max : duration; |
| 123 | 89 | reservation = createReservation(resources, start, duration); |
| 124 | } | |
| 125 | ||
| 126 | 291 | return reservation; |
| 127 | } | |
| 128 | ||
| 129 | /** | |
| 130 | * Generates a new reservation object and reserves the associated resources. | |
| 131 | */ | |
| 132 | 36 | function createReservation(resources, start, duration) { |
| 133 | 89 | var end = start + (duration * later.MIN), |
| 134 | reservation = { | |
| 135 | resources: resources, | |
| 136 | start: start, | |
| 137 | end: end, | |
| 138 | duration: duration, | |
| 139 | success: true | |
| 140 | }; | |
| 141 | ||
| 142 | 89 | applyReservation(resources, start, end); |
| 143 | 89 | return reservation; |
| 144 | } | |
| 145 | ||
| 146 | /** | |
| 147 | * Updates ranges after a failed reservation attempt. Resources that were not | |
| 148 | * immediately available are captured in the delays array to be reported with | |
| 149 | * the reservation. | |
| 150 | */ | |
| 151 | 36 | function updateRanges(resources, start, delays) { |
| 152 | 206 | for(var i = 0, len = resources.length; i < len; i++) { |
| 153 | 511 | var res = resources[i]; |
| 154 | 789 | if(res.range[1] > start) continue; |
| 155 | ||
| 156 | 233 | if(res.subRanges) { |
| 157 | 7 | updateRanges(res.subRanges, start, {}); |
| 158 | 7 | setEarliestSubRange(res); |
| 159 | } | |
| 160 | else { | |
| 161 | 226 | res.range = rMap[res.id].next(start); |
| 162 | ||
| 163 | 226 | if(res.id !== '_proj' && !delays[res.id]) { |
| 164 | 15 | delays[res.id] = { needed: start, available: res.range[0] }; |
| 165 | } | |
| 166 | } | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | /** | |
| 171 | * Applies a schedule reservation (by adding schedule exceptions) to any | |
| 172 | * reservable resources that are indicated. | |
| 173 | */ | |
| 174 | 36 | function applyReservation(resources, start, end) { |
| 175 | 89 | for(var i = 0, len = resources.length; i < len; i++) { |
| 176 | 69 | var res = rMap[resources[i]]; |
| 177 | ||
| 178 | // skip if this resource should not be reserved for single use | |
| 179 | 69 | if(res.isNotReservable) continue; |
| 180 | ||
| 181 | 69 | if(start !== res.nextAvail[0]) { |
| 182 | 48 | if(!res.schedule.exceptions) res.schedule.exceptions = []; |
| 183 | 38 | res.schedule.exceptions.push({fd_a: [start], fd_b: [end] }); |
| 184 | 38 | res.next = schedule.memoizedRangeFn(later.schedule(res.schedule).nextRange); |
| 185 | 38 | end = res.nextAvail[0]; |
| 186 | } | |
| 187 | ||
| 188 | 69 | res.nextAvail = res.next(end); |
| 189 | } | |
| 190 | } | |
| 191 | ||
| 192 | /** | |
| 193 | * Determines the earliest time that a schedule goes invalid which is the | |
| 194 | * time that should be used to update resource ranges from. | |
| 195 | */ | |
| 196 | 36 | function nextValidStart(schedules) { |
| 197 | 199 | var latest; |
| 198 | 199 | for(var i = 0, len = schedules.length; i < len; i++) { |
| 199 | 497 | var end = schedules[i].range[1]; |
| 200 | 497 | latest = !latest || end < latest ? end : latest; |
| 201 | } | |
| 202 | ||
| 203 | 199 | return latest; |
| 204 | } | |
| 205 | ||
| 206 | /** | |
| 207 | * Resources that are OR'd together (using a nested array) are treated as a | |
| 208 | * single resource with sub resources. This function determines the resource | |
| 209 | * that has the earliest start date which is then used for future calculations. | |
| 210 | */ | |
| 211 | 36 | function setEarliestSubRange(schedule) { |
| 212 | 30 | var minId, minRange; |
| 213 | 30 | for(var i = 0, len = schedule.subRanges.length; i < len; i++) { |
| 214 | 60 | var sub = schedule.subRanges[i]; |
| 215 | ||
| 216 | 60 | if(!minId || (sub.range[0] < minRange[0])) { |
| 217 | 40 | minId = sub.id; |
| 218 | 40 | minRange = sub.range; |
| 219 | } | |
| 220 | } | |
| 221 | ||
| 222 | 30 | schedule.id = minId; |
| 223 | 30 | schedule.range = minRange; |
| 224 | } | |
| 225 | ||
| 226 | /** | |
| 227 | * Determines the longest delay amongst a set of delays. Used to determine | |
| 228 | * which resource to report for resources that are OR'd together. | |
| 229 | */ | |
| 230 | 36 | function getLongestDelay(delays) { |
| 231 | 23 | var latest, lid; |
| 232 | 23 | for(var id in delays) { |
| 233 | 40 | var available = delays[id].available; |
| 234 | 40 | if(!latest || available < latest) { |
| 235 | 27 | latest = available; |
| 236 | 27 | lid = id; |
| 237 | } | |
| 238 | } | |
| 239 | ||
| 240 | 23 | return lid; |
| 241 | } | |
| 242 | ||
| 243 | /** | |
| 244 | * Returns true if resource provided is an internal (not user specified) | |
| 245 | * resource. | |
| 246 | */ | |
| 247 | 36 | function isInternal(resource) { |
| 248 | 733 | return resource.id[0] === '_'; |
| 249 | } | |
| 250 | ||
| 251 | 36 | return { |
| 252 | ||
| 253 | /** | |
| 254 | * Returns the current resource schedule state for the specified resoruce id. | |
| 255 | */ | |
| 256 | getResource: function(id) { | |
| 257 | 4 | return rMap[id]; |
| 258 | }, | |
| 259 | ||
| 260 | /** | |
| 261 | * Adds a new resource to the resource map if a resource doesn't already exist | |
| 262 | * with that id. Expects resources to be passed in as an array and will | |
| 263 | * prefix each resource with the prefix specified. | |
| 264 | */ | |
| 265 | addResource: function(arr, prefix, start) { | |
| 266 | 66 | for(var i = 0, len = arr.length; i < len; i++) { |
| 267 | 107 | var def = typeof arr[i] !== 'object' ? |
| 268 | { id: prefix + arr[i] } : | |
| 269 | { id: prefix + arr[i].id, schedule: arr[i].schedule, isNotReservable: arr[i].isNotReservable }; | |
| 270 | ||
| 271 | 107 | if(!rMap[def.id]) { |
| 272 | 98 | addResourceToMap(rMap, def, start); |
| 273 | } | |
| 274 | } | |
| 275 | }, | |
| 276 | ||
| 277 | /** | |
| 278 | * Attempts to reserve the set of resources at the earliest possible time from | |
| 279 | * start time provide with a duration of at least min and no more than max | |
| 280 | * minutes. | |
| 281 | */ | |
| 282 | makeReservation: function(resources, start, min, max) { | |
| 283 | 92 | start = start ? new Date(start) : new Date(); |
| 284 | 92 | return getReservation(resources, start.getTime(), min || 1, max); |
| 285 | }, | |
| 286 | ||
| 287 | /** | |
| 288 | * Optimizes the resource schedules by eliminating schedule reservations that | |
| 289 | * occur before the start date provided (i.e. ones that can never occur | |
| 290 | * again). | |
| 291 | */ | |
| 292 | optimize: function(start) { | |
| 293 | 23 | for(var id in rMap) { |
| 294 | 174 | var res = rMap[id]; |
| 295 | ||
| 296 | 174 | if(res.schedule.exceptions) { |
| 297 | 28 | var curExceptions = res.schedule.exceptions; |
| 298 | 28 | res.schedule.exceptions = []; |
| 299 | ||
| 300 | 28 | for(var i = 0, len = curExceptions.length; i < len; i++) { |
| 301 | 33 | if(!curExceptions[i].fd_b || curExceptions[i].fd_b > start) { |
| 302 | 26 | res.schedule.exceptions.push(curExceptions[i]); |
| 303 | } | |
| 304 | } | |
| 305 | 28 | res.next = schedule.memoizedRangeFn(later.schedule(res.schedule).nextRange); |
| 306 | } | |
| 307 | ||
| 308 | 174 | if(res.nextAvail[0] < start) { |
| 309 | 18 | res.nextAvail = res.next(start); |
| 310 | } | |
| 311 | } | |
| 312 | } | |
| 313 | }; | |
| 314 | ||
| 315 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Resources | |
| 3 | * (c) 2013 Bill, BunKat LLC. | |
| 4 | * | |
| 5 | * Takes an array of objects and generates an array of valid schedule resources | |
| 6 | * objects. | |
| 7 | * | |
| 8 | * Schedule is freely distributable under the MIT license. | |
| 9 | * For all details and documentation: | |
| 10 | * http://github.com/bunkat/schedule | |
| 11 | */ | |
| 12 | ||
| 13 | 1 | schedule.resources = function() { |
| 14 | 9 | var id = resourcesId, |
| 15 | sched = resourcesSched, | |
| 16 | isNotReservable = resourcesIsNotReservable; | |
| 17 | ||
| 18 | /** | |
| 19 | * Takes an array of objects and returns an array of schedule resource objects. | |
| 20 | */ | |
| 21 | 9 | function resources(data) { |
| 22 | 6 | var items = [], |
| 23 | fid = schedule.functor(id), | |
| 24 | fsched = schedule.functor(sched), | |
| 25 | freserve = schedule.functor(isNotReservable); | |
| 26 | ||
| 27 | 6 | for(var i = 0, len = data.length; i < len; i++) { |
| 28 | 16 | var resource = data[i], |
| 29 | rId = fid.call(this, resource, i), | |
| 30 | rSched = fsched.call(this, resource, i), | |
| 31 | rReserve = freserve.call(this, resource, i); | |
| 32 | ||
| 33 | 16 | items.push({id: rId, schedule: rSched, isNotReservable: rReserve}); |
| 34 | } | |
| 35 | ||
| 36 | 6 | return items; |
| 37 | } | |
| 38 | ||
| 39 | /** | |
| 40 | * The function or value that should be used to generate the resource id. Sets the | |
| 41 | * value to the argument passed in, returns current value if no arguments are | |
| 42 | * passed in. | |
| 43 | */ | |
| 44 | 9 | resources.id = function(_) { |
| 45 | 3 | if (!arguments.length) return id; |
| 46 | 3 | id = _; |
| 47 | 3 | return resources; |
| 48 | }; | |
| 49 | ||
| 50 | /** | |
| 51 | * The function or value that should be used to generate the resource schedule. The | |
| 52 | * schedule must be a valid Later.js schedule. Sets the value to the argument | |
| 53 | * passed in, returns current value if no arguments are passed in. | |
| 54 | */ | |
| 55 | 9 | resources.schedule = function(_) { |
| 56 | 3 | if (!arguments.length) return sched; |
| 57 | 3 | sched = _; |
| 58 | 3 | return resources; |
| 59 | }; | |
| 60 | ||
| 61 | /** | |
| 62 | * The function or value that should be used to generate the resource is not | |
| 63 | * reservable value. Sets the value to the argument passed in, returns current | |
| 64 | * value if no arguments are passed in. | |
| 65 | */ | |
| 66 | 9 | resources.isNotReservable = function(_) { |
| 67 | 1 | if (!arguments.length) return isNotReservable; |
| 68 | 1 | isNotReservable = _; |
| 69 | 1 | return resources; |
| 70 | }; | |
| 71 | ||
| 72 | 9 | return resources; |
| 73 | }; | |
| 74 | ||
| 75 | /** | |
| 76 | * The default id function. | |
| 77 | */ | |
| 78 | 1 | function resourcesId(d) { |
| 79 | 9 | return d.id; |
| 80 | } | |
| 81 | ||
| 82 | /** | |
| 83 | * The default schedule function. | |
| 84 | */ | |
| 85 | 1 | function resourcesSched(d) { |
| 86 | 9 | return d.schedule; |
| 87 | } | |
| 88 | ||
| 89 | /** | |
| 90 | * The default is not reservable function. | |
| 91 | */ | |
| 92 | 1 | function resourcesIsNotReservable(d) { |
| 93 | 13 | return d.isNotReservable || false; |
| 94 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Tasks | |
| 3 | * (c) 2013 Bill, BunKat LLC. | |
| 4 | * | |
| 5 | * Takes an array of objects and generates an of array valid schedule task objects. | |
| 6 | * | |
| 7 | * Schedule is freely distributable under the MIT license. | |
| 8 | * For all details and documentation: | |
| 9 | * http://github.com/bunkat/schedule | |
| 10 | */ | |
| 11 | ||
| 12 | 1 | schedule.tasks = function() { |
| 13 | 9 | var id = tasksId, |
| 14 | duration = tasksDuration, | |
| 15 | sched = tasksSched, | |
| 16 | resources = tasksResources, | |
| 17 | dependsOn = tasksDependsOn, | |
| 18 | minSchedule = tasksMinSchedule, | |
| 19 | priority = tasksPriority; | |
| 20 | ||
| 21 | /** | |
| 22 | * Takes an array of objects and returns an array of schedule task objects. | |
| 23 | */ | |
| 24 | 9 | function tasks(data) { |
| 25 | 3 | var items = [], |
| 26 | fid = schedule.functor(id), | |
| 27 | fduration = schedule.functor(duration), | |
| 28 | fsched = schedule.functor(sched), | |
| 29 | fresources = schedule.functor(resources), | |
| 30 | fdependsOn = schedule.functor(dependsOn), | |
| 31 | fminschedule = schedule.functor(minSchedule), | |
| 32 | fpriority = schedule.functor(priority); | |
| 33 | ||
| 34 | 3 | for(var i = 0, len = data.length; i < len; i++) { |
| 35 | 32 | var task = data[i], |
| 36 | item = { | |
| 37 | id: fid.call(this, task, i), | |
| 38 | duration: fduration.call(this, task, i), | |
| 39 | schedule: fsched.call(this, task, i), | |
| 40 | resources: fresources.call(this, task, i), | |
| 41 | dependsOn: fdependsOn.call(this, task, i), | |
| 42 | minSchedule: fminschedule.call(this, task, i), | |
| 43 | priority: fpriority.call(this, task, i) | |
| 44 | }; | |
| 45 | ||
| 46 | 32 | items.push(item); |
| 47 | } | |
| 48 | ||
| 49 | 3 | return items; |
| 50 | } | |
| 51 | ||
| 52 | /** | |
| 53 | * The function or value that should be used to generate the task id. Sets the | |
| 54 | * value to the argument passed in, returns current value if no arguments are | |
| 55 | * passed in. | |
| 56 | */ | |
| 57 | 9 | tasks.id = function(_) { |
| 58 | 2 | if (!arguments.length) return id; |
| 59 | 2 | id = _; |
| 60 | 2 | return tasks; |
| 61 | }; | |
| 62 | ||
| 63 | /** | |
| 64 | * The function or value that should be used to generate the task duration. Sets the | |
| 65 | * value to the argument passed in, returns current value if no arguments are | |
| 66 | * passed in. | |
| 67 | */ | |
| 68 | 9 | tasks.duration = function(_) { |
| 69 | 2 | if (!arguments.length) return duration; |
| 70 | 2 | duration = _; |
| 71 | 2 | return tasks; |
| 72 | }; | |
| 73 | ||
| 74 | /** | |
| 75 | * The function or value that should be used to generate the task schedule. The | |
| 76 | * schedule must be a valid Later.js schedule. Sets the value to the argument | |
| 77 | * passed in, returns current value if no arguments are passed in. | |
| 78 | */ | |
| 79 | 9 | tasks.schedule = function(_) { |
| 80 | 2 | if (!arguments.length) return sched; |
| 81 | 2 | sched = _; |
| 82 | 2 | return tasks; |
| 83 | }; | |
| 84 | ||
| 85 | /** | |
| 86 | * The function or value that should be used to generate the resources array. Sets the | |
| 87 | * value to the argument passed in, returns current value if no arguments are | |
| 88 | * passed in. | |
| 89 | */ | |
| 90 | 9 | tasks.resources = function(_) { |
| 91 | 3 | if (!arguments.length) return resources; |
| 92 | 3 | resources = _; |
| 93 | 3 | return tasks; |
| 94 | }; | |
| 95 | ||
| 96 | /** | |
| 97 | * The function or value that should be used to generate the dependency array. Sets the | |
| 98 | * value to the argument passed in, returns current value if no arguments are | |
| 99 | * passed in. | |
| 100 | */ | |
| 101 | 9 | tasks.dependsOn = function(_) { |
| 102 | 0 | if (!arguments.length) return dependsOn; |
| 103 | 0 | dependsOn = _; |
| 104 | 0 | return tasks; |
| 105 | }; | |
| 106 | ||
| 107 | /** | |
| 108 | * The function or value that should be used to generate the min schedule. Sets the | |
| 109 | * value to the argument passed in, returns current value if no arguments are | |
| 110 | * passed in. | |
| 111 | */ | |
| 112 | 9 | tasks.minSchedule = function(_) { |
| 113 | 2 | if (!arguments.length) return minSchedule; |
| 114 | 2 | minSchedule = _; |
| 115 | 2 | return tasks; |
| 116 | }; | |
| 117 | ||
| 118 | /** | |
| 119 | * The function or value that should be used to generate the priority. Sets the | |
| 120 | * value to the argument passed in, returns current value if no arguments are | |
| 121 | * passed in. | |
| 122 | */ | |
| 123 | 9 | tasks.priority = function(_) { |
| 124 | 1 | if (!arguments.length) return priority; |
| 125 | 1 | priority = _; |
| 126 | 1 | return tasks; |
| 127 | }; | |
| 128 | ||
| 129 | 9 | return tasks; |
| 130 | }; | |
| 131 | ||
| 132 | /** | |
| 133 | * The default id function. | |
| 134 | */ | |
| 135 | 1 | function tasksId(d) { |
| 136 | 0 | return d.id; |
| 137 | } | |
| 138 | ||
| 139 | /** | |
| 140 | * The default duration function. | |
| 141 | */ | |
| 142 | 1 | function tasksDuration(d) { |
| 143 | 0 | return d.duration; |
| 144 | } | |
| 145 | ||
| 146 | /** | |
| 147 | * The default schedule function. | |
| 148 | */ | |
| 149 | 1 | function tasksSched(d) { |
| 150 | 0 | return d.schedule; |
| 151 | } | |
| 152 | ||
| 153 | /** | |
| 154 | * The default resources function. | |
| 155 | */ | |
| 156 | 1 | function tasksResources(d) { |
| 157 | 0 | return d.resources; |
| 158 | } | |
| 159 | ||
| 160 | /** | |
| 161 | * The default depends on function. | |
| 162 | */ | |
| 163 | 1 | function tasksDependsOn(d) { |
| 164 | 32 | return d.dependsOn; |
| 165 | } | |
| 166 | ||
| 167 | /** | |
| 168 | * The default min schedule function. | |
| 169 | */ | |
| 170 | 1 | function tasksMinSchedule(d) { |
| 171 | 0 | return d.minSchedule; |
| 172 | } | |
| 173 | ||
| 174 | /** | |
| 175 | * The default priority function. | |
| 176 | */ | |
| 177 | 1 | function tasksPriority(d) { |
| 178 | 10 | return d.priority; |
| 179 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | schedule.date = {}; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Timezone | |
| 3 | * (c) 2013 Bill, BunKat LLC. | |
| 4 | * | |
| 5 | * Configures Schedule to use local time or UTC. Schedule uses UTC time by default. | |
| 6 | * | |
| 7 | * Schedule is freely distributable under the MIT license. | |
| 8 | * For all details and documentation: | |
| 9 | * http://github.com/bunkat/schedule | |
| 10 | */ | |
| 11 | ||
| 12 | // pass through to Later to configure timezones | |
| 13 | 1 | schedule.date.UTC = function() { later.date.UTC(); }; |
| 14 | 5 | schedule.date.localTime = function() { later.date.localTime(); }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | schedule.sort = {}; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Sort tasks | |
| 3 | * (c) 2013 Bill, BunKat LLC. | |
| 4 | * | |
| 5 | * Determines the order that tasks are scheduled in when multiple tasks can be | |
| 6 | * scheduled in parallel. Default it to do highest priority tasks first, then | |
| 7 | * tasks that have been determined to have the largest float. | |
| 8 | * | |
| 9 | * Schedule is freely distributable under the MIT license. | |
| 10 | * For all details and documentation: | |
| 11 | * http://github.com/bunkat/schedule | |
| 12 | */ | |
| 13 | ||
| 14 | 1 | schedule.sort.tasks = function(taskGraph, readyTasks) { |
| 15 | 69 | readyTasks.sort(function(a,b) { |
| 16 | 155 | var ta = taskGraph.tasks[a], |
| 17 | tb = taskGraph.tasks[b]; | |
| 18 | ||
| 19 | 155 | if(tb.priority && (!ta.priority || tb.priority > ta.priority)) { |
| 20 | 118 | return -1; |
| 21 | } | |
| 22 | ||
| 23 | 37 | if(ta.priority && (!tb.priority || ta.priority > tb.priority)) { |
| 24 | 19 | return 1; |
| 25 | } | |
| 26 | ||
| 27 | 18 | return taskGraph.tasks[b].floatAmt > taskGraph.tasks[a].floatAmt; |
| 28 | }); | |
| 29 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Functor | |
| 3 | * | |
| 4 | * Wraps values in functions so that they can be called. Usage inspired by | |
| 5 | * Mike Bostock in d3. | |
| 6 | * | |
| 7 | * Schedule is freely distributable under the MIT license. | |
| 8 | * For all details and documentation: | |
| 9 | * http://github.com/bunkat/schedule | |
| 10 | */ | |
| 11 | ||
| 12 | 1 | schedule.functor = function(v) { |
| 13 | 64 | return typeof v === "function" ? v : function() { return v; }; |
| 14 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * MemoizedRangeFn | |
| 3 | * (c) 2013 Bill, BunKat LLC. | |
| 4 | * | |
| 5 | * Wraps later.schedule().nextRange to provide memoization of results. Calculating | |
| 6 | * valid occurrences can be expensive and so we want to reduce the amount of times | |
| 7 | * we calculate them as much as possible. Also cleans up undefined values so that | |
| 8 | * we don't have to deal with them later. | |
| 9 | * | |
| 10 | * Schedule is freely distributable under the MIT license. | |
| 11 | * For all details and documentation: | |
| 12 | * http://github.com/bunkat/schedule | |
| 13 | */ | |
| 14 | ||
| 15 | 1 | schedule.memoizedRangeFn = function(fn) { |
| 16 | 254 | var cache = {}; // local store for memoization results |
| 17 | ||
| 18 | 254 | return function(start) { |
| 19 | 566 | if(!cache[start]) { |
| 20 | 550 | var result = fn(1, start); |
| 21 | 550 | cache[start] = [ |
| 22 | result[0] ? result[0].getTime() : 4102444800000,// Jan 1, 2100 | |
| 23 | result[1] ? result[1].getTime() : 4102444800000 // Jan 1, 2100 | |
| 24 | ]; | |
| 25 | } | |
| 26 | ||
| 27 | 566 | return cache[start]; |
| 28 | }; | |
| 29 | }; |