icons module, animation for loading
This commit is contained in:
parent
fc7d479ad3
commit
b43ee135ba
|
@ -19,6 +19,7 @@ module.exports = (grunt) ->
|
|||
'src/styles/fonts.styl'
|
||||
'src/styles/icons.styl'
|
||||
'src/styles/chart.styl'
|
||||
'src/styles/notification.styl'
|
||||
'src/styles/app.styl'
|
||||
]
|
||||
dest: 'public/css/app.css'
|
||||
|
|
|
@ -30,6 +30,8 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au
|
|||
- [ ] Check that we have not run out of requests to make
|
||||
- [ ] Show loading sign on top of [browser window](https://github.com/buunguyen/topbar) which is unobtrusive enough we can show it immediately.
|
||||
- [ ] show a countdown clock towards the end of the milestone or show overdue
|
||||
- [ ] highlight for a moment recently changed milestone
|
||||
- [ ] smooth animation when transitioning between icons
|
||||
- [x] format milestone titles prepending "Milestone" word if appropriate
|
||||
- [x] Variable document.title on different pages
|
||||
- [x] be able to go back to homepage
|
||||
|
@ -38,7 +40,6 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au
|
|||
- [x] work for `mbostock/d3`
|
||||
- [x] allow people to go straight to a URL that fetches the repo, if public, for them; to demo our app without adding a repo (add it behind the scenes); *req* cache repos
|
||||
- [x] closed issues can be moved to a newly created milestone, this messes up the chart since we assume milestone is created first!
|
||||
- [ ] highlight for a moment recently changed milestone
|
||||
|
||||
### Extras
|
||||
|
||||
|
|
38
bower.json
38
bower.json
|
@ -1,21 +1,21 @@
|
|||
{
|
||||
"name": "burnchart",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"lodash": "2.3.0",
|
||||
"normalize-css": "2.1.3",
|
||||
"ractive": "~0.5.5",
|
||||
"ractive-adaptor": "radekstepan/ractive-adaptor-ractive",
|
||||
"firebase": "~1.0.21",
|
||||
"firebase-simple-login": "~1.6.3",
|
||||
"grapnel": "~0.4.2",
|
||||
"github": "~0.9.0",
|
||||
"localforage": "~0.9.2",
|
||||
"superagent": "~0.19.0",
|
||||
"async": "~0.9.0",
|
||||
"moment": "~2.8.3",
|
||||
"d3": "~3.4.11",
|
||||
"d3-tip": "~0.6.5",
|
||||
"marked": "~0.3.2"
|
||||
}
|
||||
"name": "burnchart",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"lodash": "2.3.0",
|
||||
"normalize-css": "2.1.3",
|
||||
"ractive": "~0.5.5",
|
||||
"ractive-adaptor": "radekstepan/ractive-adaptor-ractive",
|
||||
"firebase": "~1.0.21",
|
||||
"firebase-simple-login": "~1.6.3",
|
||||
"grapnel": "~0.4.2",
|
||||
"github": "~0.9.0",
|
||||
"localforage": "~0.9.2",
|
||||
"superagent": "~0.19.0",
|
||||
"async": "~0.9.0",
|
||||
"moment": "~2.8.3",
|
||||
"d3": "~3.4.11",
|
||||
"d3-tip": "~0.6.5",
|
||||
"marked": "~0.3.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -409,18 +409,9 @@ table {
|
|||
@font-face{font-family:'MuseoSans500Regular';src:url("../fonts/museo-sans-500.eot");src:url("../fonts/museo-sans-500.eot?#iefix") format('embedded-opentype'),url("../fonts/museo-sans-500.woff") format('woff'),url("../fonts/museo-sans-500.ttf") format('truetype'),url("../fonts/museo-sans-500.svg#MuseoSans500Regular") format('svg');font-weight:normal;font-style:normal}
|
||||
@font-face{font-family:'Fontello';src:url("../fonts/fontello.eot?74672344");src:url("../fonts/fontello.eot?74672344#iefix") format('embedded-opentype'),url("../fonts/fontello.woff?74672344") format('woff'),url("../fonts/fontello.ttf?74672344") format('truetype'),url("../fonts/fontello.svg?74672344#fontello") format('svg');font-weight:normal;font-style:normal}
|
||||
|
||||
.icon{vertical-align:middle;}
|
||||
.icon:before{font-family:"Fontello";font-style:normal;font-weight:normal;speak:none;display:inline-block;text-decoration:inherit;text-align:center;font-variant:normal;text-transform:none;line-height:1em}
|
||||
.icon.cog:before{content:'\e800'}
|
||||
.icon.search:before{content:'\e801'}
|
||||
.icon.github:before{content:'\e802'}
|
||||
.icon.address:before{content:'\e803'}
|
||||
.icon.plus-circled:before{content:'\e804'}
|
||||
.icon.fire-station:before{content:'\e805'}
|
||||
.icon.sort-alphabet:before{content:'\e806'}
|
||||
.icon.down-open:before{content:'\e807'}
|
||||
.icon.spin6{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-ms-animation:spin 2s infinite linear;animation:spin 2s infinite linear;}
|
||||
.icon.spin6:before{content:'\e808'}
|
||||
.icon{vertical-align:middle;font-family:"Fontello";font-style:normal;font-weight:normal;speak:none;display:inline-block;text-decoration:inherit;text-align:center;font-variant:normal;text-transform:none;}
|
||||
.icon.spin6{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-ms-animation:spin 2s infinite linear;animation:spin 2s infinite linear}
|
||||
.icon.spin4{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-ms-animation:spin 2s infinite linear;animation:spin 2s infinite linear}
|
||||
@-moz-keyframes spin{0%{-moz-transform:rotate(0)}
|
||||
100%{-moz-transform:rotate(360deg)}
|
||||
}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0)}
|
||||
|
@ -457,12 +448,9 @@ a{text-decoration:none;color:#aaafbf;cursor:pointer}
|
|||
h1,h2,h3,p{margin:0}
|
||||
ul{list-style-type:none;margin:0;padding:0;}
|
||||
ul li{display:inline-block}
|
||||
.icon{margin-right:4px}
|
||||
.wrap{width:800px;margin:0 auto}
|
||||
#head{background:#c1041c;height:64px;}
|
||||
#head h1{font-size:26px;margin:0;padding:10px 24px;line-height:44px;height:44px;background:#77000e;display:inline-block;}
|
||||
#head h1 a{color:#c1041c;}
|
||||
#head h1 a .icon{margin:0}
|
||||
#head #icon{font-size:26px;margin:0;padding:10px 0;line-height:44px;height:44px;width:74px;background:#77000e;display:inline-block;color:#c1041c;margin:0;text-align:center}
|
||||
#head .q{position:relative;display:inline-block;margin:13px 20px 0 20px;vertical-align:top;}
|
||||
#head .q .icon{position:absolute;color:#c1041c;}
|
||||
#head .q .icon.search{top:8px;left:12px}
|
||||
|
|
|
@ -2,18 +2,9 @@
|
|||
@font-face{font-family:'MuseoSans500Regular';src:url("../fonts/museo-sans-500.eot");src:url("../fonts/museo-sans-500.eot?#iefix") format('embedded-opentype'),url("../fonts/museo-sans-500.woff") format('woff'),url("../fonts/museo-sans-500.ttf") format('truetype'),url("../fonts/museo-sans-500.svg#MuseoSans500Regular") format('svg');font-weight:normal;font-style:normal}
|
||||
@font-face{font-family:'Fontello';src:url("../fonts/fontello.eot?74672344");src:url("../fonts/fontello.eot?74672344#iefix") format('embedded-opentype'),url("../fonts/fontello.woff?74672344") format('woff'),url("../fonts/fontello.ttf?74672344") format('truetype'),url("../fonts/fontello.svg?74672344#fontello") format('svg');font-weight:normal;font-style:normal}
|
||||
|
||||
.icon{vertical-align:middle;}
|
||||
.icon:before{font-family:"Fontello";font-style:normal;font-weight:normal;speak:none;display:inline-block;text-decoration:inherit;text-align:center;font-variant:normal;text-transform:none;line-height:1em}
|
||||
.icon.cog:before{content:'\e800'}
|
||||
.icon.search:before{content:'\e801'}
|
||||
.icon.github:before{content:'\e802'}
|
||||
.icon.address:before{content:'\e803'}
|
||||
.icon.plus-circled:before{content:'\e804'}
|
||||
.icon.fire-station:before{content:'\e805'}
|
||||
.icon.sort-alphabet:before{content:'\e806'}
|
||||
.icon.down-open:before{content:'\e807'}
|
||||
.icon.spin6{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-ms-animation:spin 2s infinite linear;animation:spin 2s infinite linear;}
|
||||
.icon.spin6:before{content:'\e808'}
|
||||
.icon{vertical-align:middle;font-family:"Fontello";font-style:normal;font-weight:normal;speak:none;display:inline-block;text-decoration:inherit;text-align:center;font-variant:normal;text-transform:none;}
|
||||
.icon.spin6{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-ms-animation:spin 2s infinite linear;animation:spin 2s infinite linear}
|
||||
.icon.spin4{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-ms-animation:spin 2s infinite linear;animation:spin 2s infinite linear}
|
||||
@-moz-keyframes spin{0%{-moz-transform:rotate(0)}
|
||||
100%{-moz-transform:rotate(360deg)}
|
||||
}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0)}
|
||||
|
@ -50,12 +41,9 @@ a{text-decoration:none;color:#aaafbf;cursor:pointer}
|
|||
h1,h2,h3,p{margin:0}
|
||||
ul{list-style-type:none;margin:0;padding:0;}
|
||||
ul li{display:inline-block}
|
||||
.icon{margin-right:4px}
|
||||
.wrap{width:800px;margin:0 auto}
|
||||
#head{background:#c1041c;height:64px;}
|
||||
#head h1{font-size:26px;margin:0;padding:10px 24px;line-height:44px;height:44px;background:#77000e;display:inline-block;}
|
||||
#head h1 a{color:#c1041c;}
|
||||
#head h1 a .icon{margin:0}
|
||||
#head #icon{font-size:26px;margin:0;padding:10px 0;line-height:44px;height:44px;width:74px;background:#77000e;display:inline-block;color:#c1041c;margin:0;text-align:center}
|
||||
#head .q{position:relative;display:inline-block;margin:13px 20px 0 20px;vertical-align:top;}
|
||||
#head .q .icon{position:absolute;color:#c1041c;}
|
||||
#head .q .icon.search{top:8px;left:12px}
|
||||
|
|
Binary file not shown.
|
@ -15,6 +15,8 @@
|
|||
<glyph glyph-name="sort-alphabet" unicode="" d="m516 165q12-25-2-50t-44-25q-33 0-47 29l-37 75-249 0-37-75q-9-20-30-27t-40 3q-20 9-27 29t3 41l209 417q13 27 47 27t46-27z m-326 134l143 0-72 143z m905-209l-313 0q-30 0-45 27t4 57l250 332-209 0q-21 0-37 16t-15 37 15 36 37 16l313 0q30 0 45-28t-4-56l-250-333 209 0q21 0 37-15t15-37-15-37-37-15z m-417 209l-105 0q-21 0-36 15t-15 36 15 37 36 15l105 0q22 0 37-15t15-37-15-36-37-15z" horiz-adv-x="1147" />
|
||||
<glyph glyph-name="down-open" unicode="" d="m50 425q0 14 11 25l92 92q11 11 26 11t25-11l296-296 296 296q11 11 25 11t26-11l92-92q11-11 11-25t-11-26l-414-413q-10-11-25-11t-25 11l-414 413q-11 11-11 26z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="spin6" unicode="" d="m855 9c-189-190-520-172-705 13-190 190-200 494-28 695 11 13 21 26 35 34 36 23 85 18 117-13 30-31 35-76 16-112-5-9-9-15-16-22-140-151-145-379-8-516 153-153 407-121 542 34 106 122 142 297 77 451-83 198-305 291-510 222l0 1c236 82 492-24 588-252 71-167 37-355-72-493-11-15-23-29-36-42z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="megaphone" unicode="" d="m0 413q0 66 36 111t89 45l289 0q63 0 119 45t96 125l2 0q58 111 150 111 102 0 160-131t59-307-59-306-160-131q-92 0-150 111l-2 0q-78 155-199 168-6-2-8-2l0-2q-6-2-10-7-2-2-2-4-4-6-4-12l0-221q0-8 16-26t16-36l0-32q0-25-19-43t-44-19l-125 0q-25 0-44 19t-18 43l0 282q0 25-19 44t-44 18q-53 0-89 45t-36 111z m63 0q0-40 18-67t44-27l219 0q-31 37-31 94t31 93l-219 0q-25 0-44-27t-18-66z m169-157q18-29 18-62l0-282 125 0 0 28-2 2-2 2q-27 33-27 62l0 219q0 16 6 31l-118 0z m112 157q0-40 18-67t44-27l8 0q86 0 162-63-13 74-13 157 0 80 13 156-76-63-162-63l-8 0q-25 0-44-27t-18-66z m281 0q0-47 6-94l88 0q25 0 44 27t18 67-18 66-44 27l-88 0q-6-47-6-93z m14-157q19-97 57-158t85-60q65 0 111 109t45 266-45 265-111 109q-47 0-85-60t-57-158l80 0q52 0 89-45t36-112-36-111-89-45l-80 0z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="spin4" unicode="" d="m498 850c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
Binary file not shown.
|
@ -1,17 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="css/app.bundle.css">
|
||||
|
||||
<script src="js/app.bundle.js"></script>
|
||||
<script src="http://0.0.0.0:35729/livereload.js"></script>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="css/app.bundle.css">
|
||||
|
||||
<script src="js/app.bundle.js"></script>
|
||||
<script src="http://0.0.0.0:35729/livereload.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
var app = require('burnchart');
|
||||
app.render('body');
|
||||
</script>
|
||||
<script>
|
||||
var app = require('burnchart');
|
||||
app.render('body');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -29175,7 +29175,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
}).call(this);
|
||||
;!function() {
|
||||
var d3 = {
|
||||
version: "3.4.11"
|
||||
version: "3.4.12"
|
||||
};
|
||||
if (!Date.now) Date.now = function() {
|
||||
return +new Date();
|
||||
|
@ -29447,7 +29447,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
size: d3_map_size,
|
||||
empty: d3_map_empty,
|
||||
forEach: function(f) {
|
||||
for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) f.call(this, key.substring(1), this[key]);
|
||||
for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) f.call(this, key.slice(1), this[key]);
|
||||
}
|
||||
});
|
||||
var d3_map_prefix = "\x00", d3_map_prefixCode = d3_map_prefix.charCodeAt(0);
|
||||
|
@ -29557,7 +29557,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
size: d3_map_size,
|
||||
empty: d3_map_empty,
|
||||
forEach: function(f) {
|
||||
for (var value in this) if (value.charCodeAt(0) === d3_map_prefixCode) f.call(this, value.substring(1));
|
||||
for (var value in this) if (value.charCodeAt(0) === d3_map_prefixCode) f.call(this, value.slice(1));
|
||||
}
|
||||
});
|
||||
d3.behavior = {};
|
||||
|
@ -29574,7 +29574,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
}
|
||||
function d3_vendorSymbol(object, name) {
|
||||
if (name in object) return name;
|
||||
name = name.charAt(0).toUpperCase() + name.substring(1);
|
||||
name = name.charAt(0).toUpperCase() + name.slice(1);
|
||||
for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
|
||||
var prefixName = d3_vendorPrefixes[i] + name;
|
||||
if (prefixName in object) return prefixName;
|
||||
|
@ -29591,8 +29591,8 @@ requireModule('promise/polyfill').polyfill();
|
|||
d3_dispatch.prototype.on = function(type, listener) {
|
||||
var i = type.indexOf("."), name = "";
|
||||
if (i >= 0) {
|
||||
name = type.substring(i + 1);
|
||||
type = type.substring(0, i);
|
||||
name = type.slice(i + 1);
|
||||
type = type.slice(0, i);
|
||||
}
|
||||
if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
|
||||
if (arguments.length === 2) {
|
||||
|
@ -29733,8 +29733,8 @@ requireModule('promise/polyfill').polyfill();
|
|||
qualify: function(name) {
|
||||
var i = name.indexOf(":"), prefix = name;
|
||||
if (i >= 0) {
|
||||
prefix = name.substring(0, i);
|
||||
name = name.substring(i + 1);
|
||||
prefix = name.slice(0, i);
|
||||
name = name.slice(i + 1);
|
||||
}
|
||||
return d3_nsPrefix.hasOwnProperty(prefix) ? {
|
||||
space: d3_nsPrefix[prefix],
|
||||
|
@ -30086,7 +30086,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
};
|
||||
d3_selectionPrototype.size = function() {
|
||||
var n = 0;
|
||||
this.each(function() {
|
||||
d3_selection_each(this, function() {
|
||||
++n;
|
||||
});
|
||||
return n;
|
||||
|
@ -30183,7 +30183,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
};
|
||||
function d3_selection_on(type, listener, capture) {
|
||||
var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
|
||||
if (i > 0) type = type.substring(0, i);
|
||||
if (i > 0) type = type.slice(0, i);
|
||||
var filter = d3_selection_onFilters.get(type);
|
||||
if (filter) type = filter, wrap = d3_selection_onFilter;
|
||||
function onRemove() {
|
||||
|
@ -30291,13 +30291,13 @@ requireModule('promise/polyfill').polyfill();
|
|||
var rect = container.getBoundingClientRect();
|
||||
return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
|
||||
}
|
||||
d3.touches = function(container, touches) {
|
||||
if (arguments.length < 2) touches = d3_eventSource().touches;
|
||||
return touches ? d3_array(touches).map(function(touch) {
|
||||
var point = d3_mousePoint(container, touch);
|
||||
point.identifier = touch.identifier;
|
||||
return point;
|
||||
}) : [];
|
||||
d3.touch = function(container, touches, identifier) {
|
||||
if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
|
||||
if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
|
||||
if ((touch = touches[i]).identifier === identifier) {
|
||||
return d3_mousePoint(container, touch);
|
||||
}
|
||||
}
|
||||
};
|
||||
d3.behavior.drag = function() {
|
||||
var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_behavior_dragMouseSubject, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_behavior_dragTouchSubject, "touchmove", "touchend");
|
||||
|
@ -30357,6 +30357,14 @@ requireModule('promise/polyfill').polyfill();
|
|||
function d3_behavior_dragMouseSubject() {
|
||||
return d3_window;
|
||||
}
|
||||
d3.touches = function(container, touches) {
|
||||
if (arguments.length < 2) touches = d3_eventSource().touches;
|
||||
return touches ? d3_array(touches).map(function(touch) {
|
||||
var point = d3_mousePoint(container, touch);
|
||||
point.identifier = touch.identifier;
|
||||
return point;
|
||||
}) : [];
|
||||
};
|
||||
var π = Math.PI, τ = 2 * π, halfπ = π / 2, ε = 1e-6, ε2 = ε * ε, d3_radians = π / 180, d3_degrees = 180 / π;
|
||||
function d3_sgn(x) {
|
||||
return x > 0 ? 1 : x < 0 ? -1 : 0;
|
||||
|
@ -30552,10 +30560,11 @@ requireModule('promise/polyfill').polyfill();
|
|||
}
|
||||
}
|
||||
function touchstarted() {
|
||||
var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that).on(mousedown, null).on(touchstart, started), dragRestore = d3_event_dragSuppress();
|
||||
var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress();
|
||||
d3_selection_interrupt.call(that);
|
||||
started();
|
||||
zoomstarted(dispatch);
|
||||
subject.on(mousedown, null).on(touchstart, started);
|
||||
function relocate() {
|
||||
var touches = d3.touches(that);
|
||||
scale0 = view.k;
|
||||
|
@ -30798,7 +30807,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
}
|
||||
}
|
||||
if (color = d3_rgb_names.get(format)) return rgb(color.r, color.g, color.b);
|
||||
if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.substring(1), 16))) {
|
||||
if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
|
||||
if (format.length === 4) {
|
||||
r = (color & 3840) >> 4;
|
||||
r = r >> 4 | r;
|
||||
|
@ -31017,7 +31026,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
};
|
||||
function respond() {
|
||||
var status = request.status, result;
|
||||
if (!status && request.responseText || status >= 200 && status < 300 || status === 304) {
|
||||
if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
|
||||
try {
|
||||
result = response.call(xhr, request);
|
||||
} catch (e) {
|
||||
|
@ -31089,6 +31098,10 @@ requireModule('promise/polyfill').polyfill();
|
|||
callback(error == null ? request : null);
|
||||
} : callback;
|
||||
}
|
||||
function d3_xhrHasResponse(request) {
|
||||
var type = request.responseType;
|
||||
return type && type !== "text" ? request.response : request.responseText;
|
||||
}
|
||||
d3.dsv = function(delimiter, mimeType) {
|
||||
var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
|
||||
function dsv(url, row, callback) {
|
||||
|
@ -31141,7 +31154,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
} else if (c === 10) {
|
||||
eol = true;
|
||||
}
|
||||
return text.substring(j + 1, i).replace(/""/g, '"');
|
||||
return text.slice(j + 1, i).replace(/""/g, '"');
|
||||
}
|
||||
while (I < N) {
|
||||
var c = text.charCodeAt(I++), k = 1;
|
||||
|
@ -31149,9 +31162,9 @@ requireModule('promise/polyfill').polyfill();
|
|||
eol = true;
|
||||
if (text.charCodeAt(I) === 10) ++I, ++k;
|
||||
} else if (c !== delimiterCode) continue;
|
||||
return text.substring(j, I - k);
|
||||
return text.slice(j, I - k);
|
||||
}
|
||||
return text.substring(j);
|
||||
return text.slice(j);
|
||||
}
|
||||
while ((t = token()) !== EOF) {
|
||||
var a = [];
|
||||
|
@ -31193,14 +31206,6 @@ requireModule('promise/polyfill').polyfill();
|
|||
};
|
||||
d3.csv = d3.dsv(",", "text/csv");
|
||||
d3.tsv = d3.dsv(" ", "text/tab-separated-values");
|
||||
d3.touch = function(container, touches, identifier) {
|
||||
if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
|
||||
if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
|
||||
if ((touch = touches[i]).identifier === identifier) {
|
||||
return d3_mousePoint(container, touch);
|
||||
}
|
||||
}
|
||||
};
|
||||
var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) {
|
||||
setTimeout(callback, 17);
|
||||
};
|
||||
|
@ -31292,7 +31297,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
function d3_locale_numberFormat(locale) {
|
||||
var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping ? function(value) {
|
||||
var i = value.length, t = [], j = 0, g = locale_grouping[0];
|
||||
while (i > 0 && g > 0) {
|
||||
while (g > 0 && i > 0) {
|
||||
t.push(value.substring(i -= g, i + g));
|
||||
g = locale_grouping[j = (j + 1) % locale_grouping.length];
|
||||
}
|
||||
|
@ -31586,14 +31591,14 @@ requireModule('promise/polyfill').polyfill();
|
|||
var string = [], i = -1, j = 0, c, p, f;
|
||||
while (++i < n) {
|
||||
if (template.charCodeAt(i) === 37) {
|
||||
string.push(template.substring(j, i));
|
||||
string.push(template.slice(j, i));
|
||||
if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
|
||||
if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
|
||||
string.push(c);
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
string.push(template.substring(j, i));
|
||||
string.push(template.slice(j, i));
|
||||
return string.join("");
|
||||
}
|
||||
format.parse = function(string) {
|
||||
|
@ -31614,7 +31619,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
date.setFullYear(d.y, 0, 1);
|
||||
date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
|
||||
} else date.setFullYear(d.y, d.m, d.d);
|
||||
date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L);
|
||||
date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L);
|
||||
return localZ ? date._ : date;
|
||||
};
|
||||
format.toString = function() {
|
||||
|
@ -31760,22 +31765,22 @@ requireModule('promise/polyfill').polyfill();
|
|||
};
|
||||
function d3_time_parseWeekdayAbbrev(date, string, i) {
|
||||
d3_time_dayAbbrevRe.lastIndex = 0;
|
||||
var n = d3_time_dayAbbrevRe.exec(string.substring(i));
|
||||
var n = d3_time_dayAbbrevRe.exec(string.slice(i));
|
||||
return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseWeekday(date, string, i) {
|
||||
d3_time_dayRe.lastIndex = 0;
|
||||
var n = d3_time_dayRe.exec(string.substring(i));
|
||||
var n = d3_time_dayRe.exec(string.slice(i));
|
||||
return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseMonthAbbrev(date, string, i) {
|
||||
d3_time_monthAbbrevRe.lastIndex = 0;
|
||||
var n = d3_time_monthAbbrevRe.exec(string.substring(i));
|
||||
var n = d3_time_monthAbbrevRe.exec(string.slice(i));
|
||||
return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseMonth(date, string, i) {
|
||||
d3_time_monthRe.lastIndex = 0;
|
||||
var n = d3_time_monthRe.exec(string.substring(i));
|
||||
var n = d3_time_monthRe.exec(string.slice(i));
|
||||
return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseLocaleFull(date, string, i) {
|
||||
|
@ -31788,7 +31793,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
|
||||
}
|
||||
function d3_time_parseAmPm(date, string, i) {
|
||||
var n = d3_time_periodLookup.get(string.substring(i, i += 2).toLowerCase());
|
||||
var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase());
|
||||
return n == null ? -1 : (date.p = n, i);
|
||||
}
|
||||
return d3_time_format;
|
||||
|
@ -31812,31 +31817,31 @@ requireModule('promise/polyfill').polyfill();
|
|||
}
|
||||
function d3_time_parseWeekdayNumber(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 1));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 1));
|
||||
return n ? (date.w = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseWeekNumberSunday(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i));
|
||||
var n = d3_time_numberRe.exec(string.slice(i));
|
||||
return n ? (date.U = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseWeekNumberMonday(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i));
|
||||
var n = d3_time_numberRe.exec(string.slice(i));
|
||||
return n ? (date.W = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseFullYear(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 4));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 4));
|
||||
return n ? (date.y = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseYear(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 2));
|
||||
return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseZone(date, string, i) {
|
||||
return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5)) ? (date.Z = -string,
|
||||
return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string,
|
||||
i + 5) : -1;
|
||||
}
|
||||
function d3_time_expandYear(d) {
|
||||
|
@ -31844,46 +31849,46 @@ requireModule('promise/polyfill').polyfill();
|
|||
}
|
||||
function d3_time_parseMonthNumber(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 2));
|
||||
return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseDay(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 2));
|
||||
return n ? (date.d = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseDayOfYear(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 3));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 3));
|
||||
return n ? (date.j = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseHour24(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 2));
|
||||
return n ? (date.H = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseMinutes(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 2));
|
||||
return n ? (date.M = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseSeconds(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 2));
|
||||
return n ? (date.S = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_parseMilliseconds(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 3));
|
||||
var n = d3_time_numberRe.exec(string.slice(i, i + 3));
|
||||
return n ? (date.L = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
function d3_time_zone(d) {
|
||||
var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(abs(z) / 60), zm = abs(z) % 60;
|
||||
var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60;
|
||||
return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
|
||||
}
|
||||
function d3_time_parseLiteralPercent(date, string, i) {
|
||||
d3_time_percentRe.lastIndex = 0;
|
||||
var n = d3_time_percentRe.exec(string.substring(i, i + 1));
|
||||
var n = d3_time_percentRe.exec(string.slice(i, i + 1));
|
||||
return n ? i + n[0].length : -1;
|
||||
}
|
||||
function d3_time_formatMulti(formats) {
|
||||
|
@ -32491,35 +32496,6 @@ requireModule('promise/polyfill').polyfill();
|
|||
function d3_geo_clipSort(a, b) {
|
||||
return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
|
||||
}
|
||||
function d3_geo_pointInPolygon(point, polygon) {
|
||||
var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
|
||||
d3_geo_areaRingSum.reset();
|
||||
for (var i = 0, n = polygon.length; i < n; ++i) {
|
||||
var ring = polygon[i], m = ring.length;
|
||||
if (!m) continue;
|
||||
var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
|
||||
while (true) {
|
||||
if (j === m) j = 0;
|
||||
point = ring[j];
|
||||
var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ;
|
||||
d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
|
||||
polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
|
||||
if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
|
||||
var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
|
||||
d3_geo_cartesianNormalize(arc);
|
||||
var intersection = d3_geo_cartesianCross(meridianNormal, arc);
|
||||
d3_geo_cartesianNormalize(intersection);
|
||||
var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
|
||||
if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
|
||||
winding += antimeridian ^ dλ >= 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
if (!j++) break;
|
||||
λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
|
||||
}
|
||||
}
|
||||
return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1;
|
||||
}
|
||||
var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
|
||||
function d3_geo_clipAntimeridianLine(listener) {
|
||||
var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
|
||||
|
@ -32587,6 +32563,35 @@ requireModule('promise/polyfill').polyfill();
|
|||
listener.point(to[0], to[1]);
|
||||
}
|
||||
}
|
||||
function d3_geo_pointInPolygon(point, polygon) {
|
||||
var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
|
||||
d3_geo_areaRingSum.reset();
|
||||
for (var i = 0, n = polygon.length; i < n; ++i) {
|
||||
var ring = polygon[i], m = ring.length;
|
||||
if (!m) continue;
|
||||
var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
|
||||
while (true) {
|
||||
if (j === m) j = 0;
|
||||
point = ring[j];
|
||||
var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ;
|
||||
d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
|
||||
polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
|
||||
if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
|
||||
var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
|
||||
d3_geo_cartesianNormalize(arc);
|
||||
var intersection = d3_geo_cartesianCross(meridianNormal, arc);
|
||||
d3_geo_cartesianNormalize(intersection);
|
||||
var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
|
||||
if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
|
||||
winding += antimeridian ^ dλ >= 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
if (!j++) break;
|
||||
λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
|
||||
}
|
||||
}
|
||||
return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1;
|
||||
}
|
||||
function d3_geo_clipCircle(radius) {
|
||||
var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
|
||||
return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]);
|
||||
|
@ -34786,7 +34791,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
a = a + "", b = b + "";
|
||||
while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) {
|
||||
if ((bs = bm.index) > bi) {
|
||||
bs = b.substring(bi, bs);
|
||||
bs = b.slice(bi, bs);
|
||||
if (s[i]) s[i] += bs; else s[++i] = bs;
|
||||
}
|
||||
if ((am = am[0]) === (bm = bm[0])) {
|
||||
|
@ -34801,7 +34806,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
bi = d3_interpolate_numberB.lastIndex;
|
||||
}
|
||||
if (bi < b.length) {
|
||||
bs = b.substring(bi);
|
||||
bs = b.slice(bi);
|
||||
if (s[i]) s[i] += bs; else s[++i] = bs;
|
||||
}
|
||||
return s.length < 2 ? q[0] ? (b = q[0].x, function(t) {
|
||||
|
@ -34871,7 +34876,7 @@ requireModule('promise/polyfill').polyfill();
|
|||
}
|
||||
});
|
||||
d3.ease = function(name) {
|
||||
var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in";
|
||||
var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in";
|
||||
t = d3_ease.get(t) || d3_ease_default;
|
||||
m = d3_ease_mode.get(m) || d3_identity;
|
||||
return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
|
||||
|
@ -40084,6 +40089,21 @@ if (typeof exports === 'object') {
|
|||
|
||||
});
|
||||
|
||||
// state.coffee
|
||||
root.require.register('burnchart/src/models/state.js', function(exports, require, module) {
|
||||
|
||||
var Model;
|
||||
|
||||
Model = require('../utils/model');
|
||||
|
||||
module.exports = new Model({
|
||||
'data': {
|
||||
'loading': false
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// user.coffee
|
||||
root.require.register('burnchart/src/models/user.js', function(exports, require, module) {
|
||||
|
||||
|
@ -40688,10 +40708,12 @@ if (typeof exports === 'object') {
|
|||
// router.coffee
|
||||
root.require.register('burnchart/src/modules/router.js', function(exports, require, module) {
|
||||
|
||||
var el, mediator, route, router;
|
||||
var el, mediator, route, router, state;
|
||||
|
||||
mediator = require('./mediator');
|
||||
|
||||
state = require('../models/state');
|
||||
|
||||
el = '#page';
|
||||
|
||||
route = function(page, req, evt) {
|
||||
|
@ -40712,6 +40734,10 @@ if (typeof exports === 'object') {
|
|||
'reset': function() {
|
||||
mediator.fire('!projects/clear');
|
||||
return window.location.hash = '#';
|
||||
},
|
||||
'notify': function() {
|
||||
window.location.hash = '#';
|
||||
return state.set('loading', true);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -40725,43 +40751,55 @@ if (typeof exports === 'object') {
|
|||
// header.mustache
|
||||
root.require.register('burnchart/src/templates/header.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"head\">"," <div class=\"right\">"," {{#user.displayName}}"," {{user.displayName}} logged in"," {{else}}"," <a class=\"github\" on-click=\"!login\"><span class=\"icon github\"></span> Sign In</a>"," {{/user.displayName}}"," </div>",""," <h1><a href=\"#\"><span class=\"icon fire-station\"></span></a></h1>",""," <div class=\"q\">"," <span class=\"icon search\"></span>"," <span class=\"icon down-open\"></span>"," <input type=\"text\" placeholder=\"Jump to...\">"," </div>",""," <ul>"," <li><a href=\"#project/add\" class=\"add\"><span class=\"icon plus-circled\"></span> Add a Project</a></li>"," <li><a href=\"#\" class=\"faq\">FAQ</a></li>"," <li><a href=\"#reset\">DB Reset</a></li>"," </ul>","</div>"].join("\n");
|
||||
module.exports = ["<div id=\"head\">"," <div class=\"right\">"," {{#user.displayName}}"," {{user.displayName}} logged in"," {{else}}"," <a class=\"github\" on-click=\"!login\"><Icons icon=\"github\"/> Sign In</a>"," {{/user.displayName}}"," </div>",""," <a id=\"icon\" href=\"#\">"," <Icons icon=\"{{icon}}\"/>"," </a>",""," <div class=\"q\">"," <Icons icon=\"search\"/>"," <Icons icon=\"down-open\"/>"," <input type=\"text\" placeholder=\"Jump to...\">"," </div>",""," <ul>"," <li><a href=\"#project/add\" class=\"add\"><Icons icon=\"plus-circled\"/> Add a Project</a></li>"," <li><a href=\"#\" class=\"faq\">FAQ</a></li>"," <li><a href=\"#reset\">DB Reset</a></li>"," <li><a href=\"#notify\">Notify</a></li>"," </ul>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// hero.mustache
|
||||
root.require.register('burnchart/src/templates/hero.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["{{^projects.list}}"," <div id=\"hero\">"," <div class=\"content\">"," <span class=\"icon address\"></span>"," <h2>See your project progress</h2>"," <p>Not sure where to start? Just add a demo repo to see a chart. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>"," <div class=\"cta\">"," <a href=\"#project/add\" class=\"primary\"><span class=\"icon plus-circled\"></span> Add your project</a>"," <a href=\"#\" class=\"secondary\">Read the Guide</a>"," </div>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||
module.exports = ["{{^projects.list}}"," <div id=\"hero\">"," <div class=\"content\">"," <Icons icon=\"address\"/>"," <h2>See your project progress</h2>"," <p>Not sure where to start? Just add a demo repo to see a chart. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>"," <div class=\"cta\">"," <a href=\"#project/add\" class=\"primary\"><Icons icon=\"plus-circled\"/> Add your project</a>"," <a href=\"#\" class=\"secondary\">Read the Guide</a>"," </div>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||
});
|
||||
|
||||
// icons.mustache
|
||||
root.require.register('burnchart/src/templates/icons.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["{{#code}}"," <span class=\"icon {{icon}}\">{{{ '&#' + code + ';' }}}</span>","{{/code}}"].join("\n");
|
||||
});
|
||||
|
||||
// layout.mustache
|
||||
root.require.register('burnchart/src/templates/layout.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<Header/>","","<div id=\"page\">"," <!-- content loaded from a router -->","</div>","","<div id=\"footer\">"," <div class=\"wrap\">"," © 2012-2014 <a href=\"http://cloudfi.re\">Cloudfire Systems</a>"," </div>","</div>"].join("\n");
|
||||
module.exports = ["<Header/>","","<div id=\"page\">"," <!-- content loaded from a router -->","</div>","","<div id=\"footer\">"," <div class=\"wrap\">"," © 2012-2014 <a href=\"http://cloudfi.re\">Cloudfire Systems</a>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// notify.mustache
|
||||
root.require.register('burnchart/src/templates/notify.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<Icon icon=\"megaphone\"/>","<p>You have some interesting news in your inbox. Go <a href=\"#\">check it out</a> now.</p>"].join("\n");
|
||||
});
|
||||
|
||||
// addProject.mustache
|
||||
root.require.register('burnchart/src/templates/pages/addProject.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"add\">"," <div class=\"header\">"," <h2>Add a Project</h2>"," <p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href=\"#\">Sign In</a> first.</p>"," </div>",""," <div class=\"form\">"," <table>"," <tr>"," <td>"," <input type=\"text\" placeholder=\"user/repo\" autocomplete=\"off\" value=\"{{value}}\">"," </td>"," <td>"," <a on-click=\"submit\">Add</a>"," </td>"," </tr>"," </table>"," </div>"," </div>","</div>"].join("\n");
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"add\">"," <div class=\"header\">"," <h2>Add a Project</h2>"," <p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href=\"#\">Sign In</a> first.</p>"," </div>",""," <div class=\"form\">"," <table>"," <tr>"," <td>"," <input type=\"text\" placeholder=\"user/repo\" autocomplete=\"off\" value=\"{{value}}\">"," </td>"," <td>"," <a on-click=\"submit\">Add</a>"," </td>"," </tr>"," </table>"," </div>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// index.mustache
|
||||
root.require.register('burnchart/src/templates/pages/index.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <Hero/>"," <Projects/>","</div>"].join("\n");
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <Hero/>"," <Projects/>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// showChart.mustache
|
||||
root.require.register('burnchart/src/templates/pages/showChart.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"title\">"," <div class=\"wrap\">"," <h2 class=\"title\">{{ format.title(milestone.title) }}</h2>"," <span class=\"sub\">{{{ format.due(milestone.due_on) }}}</span>"," <p class=\"description\">{{{ format.markdown(milestone.description) }}}</p>"," </div>","</div>","","<div id=\"content\" class=\"wrap\">"," <div id=\"chart\">"," <div id=\"svg\"></div>"," </div>","</div>"].join("\n");
|
||||
module.exports = ["<div id=\"title\">"," <div class=\"wrap\">"," <h2 class=\"title\">{{ format.title(milestone.title) }}</h2>"," <span class=\"sub\">{{{ format.due(milestone.due_on) }}}</span>"," <p class=\"description\">{{{ format.markdown(milestone.description) }}}</p>"," </div>","</div>","","<div id=\"content\" class=\"wrap\">"," <div id=\"chart\">"," <div id=\"svg\"></div>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// projects.mustache
|
||||
root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["{{#projects.list.length}}"," <div id=\"projects\">"," <div class=\"header\">"," <a href=\"#\" class=\"sort\"><span class=\"icon sort-alphabet\"></span> Sorted by priority</a>"," <h2>Projects</h2>"," </div>",""," <table>"," {{#projects.list}}"," {{#milestones}}"," <tr>"," <td class=\"repo\">{{owner}}/{{name}}</td>"," <td>"," <a class=\"milestone\" href=\"#chart/{{owner}}/{{name}}/{{number}}\">{{ title }}</a>"," </td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>"," <span class=\"due\">{{{ format.due(due_on) }}}</span>"," <div class=\"outer bar\">"," <div class=\"inner bar {{format.onTime(this)}}\" style=\"width:{{format.progress(closed_issues, open_issues)}}%\"></div>"," </div>"," </div>"," </td>"," </tr>"," {{/milestones}}"," {{/projects.list}}",""," <!--"," <tr>"," <td><a class=\"repo\" href=\"#\">radekstepan/disposable</a></td>"," <td><span class=\"milestone\">Milestone 1.0 <span class=\"icon down-open\"></span></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">40%</span>"," <span class=\"due\">due on Friday</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:40%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr class=\"done\">"," <td><a class=\"repo\" href=\"#\">radekstepan/burnchart</a></td>"," <td><span class=\"milestone\">Beta Milestone <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">100%</span>"," <span class=\"due\">due tomorrow</span>"," <div class=\"outer bar\">"," <div class=\"inner bar green\" style=\"width:100%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">intermine/intermine</a></td>"," <td><span class=\"milestone\">Emma Release 96 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">27%</span>"," <span class=\"due\">due in 2 weeks</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:27%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">microsoft/windows</a></td>"," <td><span class=\"milestone\">RC 9 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">90%</span>"," <span class=\"due red\">overdue by a month</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:90%\"></div>"," </div>"," </div>"," </td>"," </tr>"," -->"," </table>",""," <div class=\"footer\">"," <a href=\"#\"><span class=\"icon cog\"></span> Edit</a>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||
module.exports = ["{{#projects.list.length}}"," <div id=\"projects\">"," <div class=\"header\">"," <a href=\"#\" class=\"sort\"><Icons icon=\"sort-alphabet\"/> Sorted by priority</a>"," <h2>Projects</h2>"," </div>",""," <table>"," {{#projects.list}}"," {{#milestones}}"," <tr>"," <td class=\"repo\">{{owner}}/{{name}}</td>"," <td>"," <a class=\"milestone\" href=\"#chart/{{owner}}/{{name}}/{{number}}\">{{ title }}</a>"," </td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>"," <span class=\"due\">{{{ format.due(due_on) }}}</span>"," <div class=\"outer bar\">"," <div class=\"inner bar {{format.onTime(this)}}\" style=\"width:{{format.progress(closed_issues, open_issues)}}%\"></div>"," </div>"," </div>"," </td>"," </tr>"," {{/milestones}}"," {{/projects.list}}",""," <!--"," <tr>"," <td><a class=\"repo\" href=\"#\">radekstepan/disposable</a></td>"," <td><span class=\"milestone\">Milestone 1.0 <span class=\"icon down-open\"></span></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">40%</span>"," <span class=\"due\">due on Friday</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:40%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr class=\"done\">"," <td><a class=\"repo\" href=\"#\">radekstepan/burnchart</a></td>"," <td><span class=\"milestone\">Beta Milestone <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">100%</span>"," <span class=\"due\">due tomorrow</span>"," <div class=\"outer bar\">"," <div class=\"inner bar green\" style=\"width:100%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">intermine/intermine</a></td>"," <td><span class=\"milestone\">Emma Release 96 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">27%</span>"," <span class=\"due\">due in 2 weeks</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:27%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">microsoft/windows</a></td>"," <td><span class=\"milestone\">RC 9 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">90%</span>"," <span class=\"due red\">overdue by a month</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:90%\"></div>"," </div>"," </div>"," </td>"," </tr>"," -->"," </table>",""," <div class=\"footer\">"," <a href=\"#\"><Icons icon=\"cog\"/> Edit</a>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||
});
|
||||
|
||||
// date.coffee
|
||||
|
@ -40818,6 +40856,9 @@ if (typeof exports === 'object') {
|
|||
} else {
|
||||
return ['Milestone', text].join(' ');
|
||||
}
|
||||
},
|
||||
hexToDecimal: function(hex) {
|
||||
return parseInt(hex, 16);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -40860,7 +40901,7 @@ if (typeof exports === 'object') {
|
|||
// header.coffee
|
||||
root.require.register('burnchart/src/views/header.js', function(exports, require, module) {
|
||||
|
||||
var firebase, mediator, user;
|
||||
var Icons, firebase, mediator, state, user;
|
||||
|
||||
firebase = require('../modules/firebase');
|
||||
|
||||
|
@ -40868,19 +40909,31 @@ if (typeof exports === 'object') {
|
|||
|
||||
user = require('../models/user');
|
||||
|
||||
state = require('../models/state');
|
||||
|
||||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../templates/header'),
|
||||
'data': {
|
||||
'user': user,
|
||||
'icon': 'fire-station'
|
||||
},
|
||||
init: function() {
|
||||
return this.on('!login', function() {
|
||||
var _this = this;
|
||||
this.on('!login', function() {
|
||||
return firebase.login(function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
return state.observe('loading', function(val) {
|
||||
return _this.set('icon', val ? 'spin4' : 'fire-station');
|
||||
});
|
||||
},
|
||||
'data': {
|
||||
user: user
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
});
|
||||
|
@ -40890,22 +40943,65 @@ if (typeof exports === 'object') {
|
|||
// hero.coffee
|
||||
root.require.register('burnchart/src/views/hero.js', function(exports, require, module) {
|
||||
|
||||
var mediator, projects;
|
||||
var Icons, mediator, projects;
|
||||
|
||||
mediator = require('../modules/mediator');
|
||||
|
||||
projects = require('../models/projects');
|
||||
|
||||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../templates/hero'),
|
||||
'data': {
|
||||
projects: projects
|
||||
},
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// icons.coffee
|
||||
root.require.register('burnchart/src/views/icons.js', function(exports, require, module) {
|
||||
|
||||
var codes, utils;
|
||||
|
||||
utils = require('../utils/format');
|
||||
|
||||
codes = {
|
||||
'cog': '\e800',
|
||||
'search': '\e801',
|
||||
'github': '\e802',
|
||||
'address': '\e803',
|
||||
'plus-circled': '\e804',
|
||||
'fire-station': '\e805',
|
||||
'sort-alphabet': '\e806',
|
||||
'down-open': '\e807',
|
||||
'spin6': '\e808',
|
||||
'megaphone': '\e809',
|
||||
'spin4': '\e80a'
|
||||
};
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../templates/icons'),
|
||||
'isolated': true,
|
||||
init: function() {
|
||||
return this.observe('icon', function(icon) {
|
||||
var hex;
|
||||
if (icon && (hex = codes[icon])) {
|
||||
return this.set('code', utils.hexToDecimal(hex));
|
||||
} else {
|
||||
return this.set('code', null);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// addProject.coffee
|
||||
root.require.register('burnchart/src/views/pages/addProject.js', function(exports, require, module) {
|
||||
|
||||
|
@ -41017,17 +41113,22 @@ if (typeof exports === 'object') {
|
|||
// projects.coffee
|
||||
root.require.register('burnchart/src/views/projects.js', function(exports, require, module) {
|
||||
|
||||
var mediator, projects;
|
||||
var Icons, mediator, projects;
|
||||
|
||||
mediator = require('../modules/mediator');
|
||||
|
||||
projects = require('../models/projects');
|
||||
|
||||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../templates/projects'),
|
||||
'data': {
|
||||
projects: projects
|
||||
},
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
});
|
||||
|
||||
|
|
124
public/js/app.js
124
public/js/app.js
|
@ -118,6 +118,21 @@
|
|||
|
||||
});
|
||||
|
||||
// state.coffee
|
||||
root.require.register('burnchart/src/models/state.js', function(exports, require, module) {
|
||||
|
||||
var Model;
|
||||
|
||||
Model = require('../utils/model');
|
||||
|
||||
module.exports = new Model({
|
||||
'data': {
|
||||
'loading': false
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// user.coffee
|
||||
root.require.register('burnchart/src/models/user.js', function(exports, require, module) {
|
||||
|
||||
|
@ -722,10 +737,12 @@
|
|||
// router.coffee
|
||||
root.require.register('burnchart/src/modules/router.js', function(exports, require, module) {
|
||||
|
||||
var el, mediator, route, router;
|
||||
var el, mediator, route, router, state;
|
||||
|
||||
mediator = require('./mediator');
|
||||
|
||||
state = require('../models/state');
|
||||
|
||||
el = '#page';
|
||||
|
||||
route = function(page, req, evt) {
|
||||
|
@ -746,6 +763,10 @@
|
|||
'reset': function() {
|
||||
mediator.fire('!projects/clear');
|
||||
return window.location.hash = '#';
|
||||
},
|
||||
'notify': function() {
|
||||
window.location.hash = '#';
|
||||
return state.set('loading', true);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -759,43 +780,55 @@
|
|||
// header.mustache
|
||||
root.require.register('burnchart/src/templates/header.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"head\">"," <div class=\"right\">"," {{#user.displayName}}"," {{user.displayName}} logged in"," {{else}}"," <a class=\"github\" on-click=\"!login\"><span class=\"icon github\"></span> Sign In</a>"," {{/user.displayName}}"," </div>",""," <h1><a href=\"#\"><span class=\"icon fire-station\"></span></a></h1>",""," <div class=\"q\">"," <span class=\"icon search\"></span>"," <span class=\"icon down-open\"></span>"," <input type=\"text\" placeholder=\"Jump to...\">"," </div>",""," <ul>"," <li><a href=\"#project/add\" class=\"add\"><span class=\"icon plus-circled\"></span> Add a Project</a></li>"," <li><a href=\"#\" class=\"faq\">FAQ</a></li>"," <li><a href=\"#reset\">DB Reset</a></li>"," </ul>","</div>"].join("\n");
|
||||
module.exports = ["<div id=\"head\">"," <div class=\"right\">"," {{#user.displayName}}"," {{user.displayName}} logged in"," {{else}}"," <a class=\"github\" on-click=\"!login\"><Icons icon=\"github\"/> Sign In</a>"," {{/user.displayName}}"," </div>",""," <a id=\"icon\" href=\"#\">"," <Icons icon=\"{{icon}}\"/>"," </a>",""," <div class=\"q\">"," <Icons icon=\"search\"/>"," <Icons icon=\"down-open\"/>"," <input type=\"text\" placeholder=\"Jump to...\">"," </div>",""," <ul>"," <li><a href=\"#project/add\" class=\"add\"><Icons icon=\"plus-circled\"/> Add a Project</a></li>"," <li><a href=\"#\" class=\"faq\">FAQ</a></li>"," <li><a href=\"#reset\">DB Reset</a></li>"," <li><a href=\"#notify\">Notify</a></li>"," </ul>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// hero.mustache
|
||||
root.require.register('burnchart/src/templates/hero.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["{{^projects.list}}"," <div id=\"hero\">"," <div class=\"content\">"," <span class=\"icon address\"></span>"," <h2>See your project progress</h2>"," <p>Not sure where to start? Just add a demo repo to see a chart. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>"," <div class=\"cta\">"," <a href=\"#project/add\" class=\"primary\"><span class=\"icon plus-circled\"></span> Add your project</a>"," <a href=\"#\" class=\"secondary\">Read the Guide</a>"," </div>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||
module.exports = ["{{^projects.list}}"," <div id=\"hero\">"," <div class=\"content\">"," <Icons icon=\"address\"/>"," <h2>See your project progress</h2>"," <p>Not sure where to start? Just add a demo repo to see a chart. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>"," <div class=\"cta\">"," <a href=\"#project/add\" class=\"primary\"><Icons icon=\"plus-circled\"/> Add your project</a>"," <a href=\"#\" class=\"secondary\">Read the Guide</a>"," </div>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||
});
|
||||
|
||||
// icons.mustache
|
||||
root.require.register('burnchart/src/templates/icons.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["{{#code}}"," <span class=\"icon {{icon}}\">{{{ '&#' + code + ';' }}}</span>","{{/code}}"].join("\n");
|
||||
});
|
||||
|
||||
// layout.mustache
|
||||
root.require.register('burnchart/src/templates/layout.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<Header/>","","<div id=\"page\">"," <!-- content loaded from a router -->","</div>","","<div id=\"footer\">"," <div class=\"wrap\">"," © 2012-2014 <a href=\"http://cloudfi.re\">Cloudfire Systems</a>"," </div>","</div>"].join("\n");
|
||||
module.exports = ["<Header/>","","<div id=\"page\">"," <!-- content loaded from a router -->","</div>","","<div id=\"footer\">"," <div class=\"wrap\">"," © 2012-2014 <a href=\"http://cloudfi.re\">Cloudfire Systems</a>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// notify.mustache
|
||||
root.require.register('burnchart/src/templates/notify.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<Icon icon=\"megaphone\"/>","<p>You have some interesting news in your inbox. Go <a href=\"#\">check it out</a> now.</p>"].join("\n");
|
||||
});
|
||||
|
||||
// addProject.mustache
|
||||
root.require.register('burnchart/src/templates/pages/addProject.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"add\">"," <div class=\"header\">"," <h2>Add a Project</h2>"," <p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href=\"#\">Sign In</a> first.</p>"," </div>",""," <div class=\"form\">"," <table>"," <tr>"," <td>"," <input type=\"text\" placeholder=\"user/repo\" autocomplete=\"off\" value=\"{{value}}\">"," </td>"," <td>"," <a on-click=\"submit\">Add</a>"," </td>"," </tr>"," </table>"," </div>"," </div>","</div>"].join("\n");
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"add\">"," <div class=\"header\">"," <h2>Add a Project</h2>"," <p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href=\"#\">Sign In</a> first.</p>"," </div>",""," <div class=\"form\">"," <table>"," <tr>"," <td>"," <input type=\"text\" placeholder=\"user/repo\" autocomplete=\"off\" value=\"{{value}}\">"," </td>"," <td>"," <a on-click=\"submit\">Add</a>"," </td>"," </tr>"," </table>"," </div>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// index.mustache
|
||||
root.require.register('burnchart/src/templates/pages/index.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <Hero/>"," <Projects/>","</div>"].join("\n");
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <Hero/>"," <Projects/>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// showChart.mustache
|
||||
root.require.register('burnchart/src/templates/pages/showChart.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"title\">"," <div class=\"wrap\">"," <h2 class=\"title\">{{ format.title(milestone.title) }}</h2>"," <span class=\"sub\">{{{ format.due(milestone.due_on) }}}</span>"," <p class=\"description\">{{{ format.markdown(milestone.description) }}}</p>"," </div>","</div>","","<div id=\"content\" class=\"wrap\">"," <div id=\"chart\">"," <div id=\"svg\"></div>"," </div>","</div>"].join("\n");
|
||||
module.exports = ["<div id=\"title\">"," <div class=\"wrap\">"," <h2 class=\"title\">{{ format.title(milestone.title) }}</h2>"," <span class=\"sub\">{{{ format.due(milestone.due_on) }}}</span>"," <p class=\"description\">{{{ format.markdown(milestone.description) }}}</p>"," </div>","</div>","","<div id=\"content\" class=\"wrap\">"," <div id=\"chart\">"," <div id=\"svg\"></div>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// projects.mustache
|
||||
root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["{{#projects.list.length}}"," <div id=\"projects\">"," <div class=\"header\">"," <a href=\"#\" class=\"sort\"><span class=\"icon sort-alphabet\"></span> Sorted by priority</a>"," <h2>Projects</h2>"," </div>",""," <table>"," {{#projects.list}}"," {{#milestones}}"," <tr>"," <td class=\"repo\">{{owner}}/{{name}}</td>"," <td>"," <a class=\"milestone\" href=\"#chart/{{owner}}/{{name}}/{{number}}\">{{ title }}</a>"," </td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>"," <span class=\"due\">{{{ format.due(due_on) }}}</span>"," <div class=\"outer bar\">"," <div class=\"inner bar {{format.onTime(this)}}\" style=\"width:{{format.progress(closed_issues, open_issues)}}%\"></div>"," </div>"," </div>"," </td>"," </tr>"," {{/milestones}}"," {{/projects.list}}",""," <!--"," <tr>"," <td><a class=\"repo\" href=\"#\">radekstepan/disposable</a></td>"," <td><span class=\"milestone\">Milestone 1.0 <span class=\"icon down-open\"></span></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">40%</span>"," <span class=\"due\">due on Friday</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:40%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr class=\"done\">"," <td><a class=\"repo\" href=\"#\">radekstepan/burnchart</a></td>"," <td><span class=\"milestone\">Beta Milestone <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">100%</span>"," <span class=\"due\">due tomorrow</span>"," <div class=\"outer bar\">"," <div class=\"inner bar green\" style=\"width:100%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">intermine/intermine</a></td>"," <td><span class=\"milestone\">Emma Release 96 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">27%</span>"," <span class=\"due\">due in 2 weeks</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:27%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">microsoft/windows</a></td>"," <td><span class=\"milestone\">RC 9 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">90%</span>"," <span class=\"due red\">overdue by a month</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:90%\"></div>"," </div>"," </div>"," </td>"," </tr>"," -->"," </table>",""," <div class=\"footer\">"," <a href=\"#\"><span class=\"icon cog\"></span> Edit</a>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||
module.exports = ["{{#projects.list.length}}"," <div id=\"projects\">"," <div class=\"header\">"," <a href=\"#\" class=\"sort\"><Icons icon=\"sort-alphabet\"/> Sorted by priority</a>"," <h2>Projects</h2>"," </div>",""," <table>"," {{#projects.list}}"," {{#milestones}}"," <tr>"," <td class=\"repo\">{{owner}}/{{name}}</td>"," <td>"," <a class=\"milestone\" href=\"#chart/{{owner}}/{{name}}/{{number}}\">{{ title }}</a>"," </td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>"," <span class=\"due\">{{{ format.due(due_on) }}}</span>"," <div class=\"outer bar\">"," <div class=\"inner bar {{format.onTime(this)}}\" style=\"width:{{format.progress(closed_issues, open_issues)}}%\"></div>"," </div>"," </div>"," </td>"," </tr>"," {{/milestones}}"," {{/projects.list}}",""," <!--"," <tr>"," <td><a class=\"repo\" href=\"#\">radekstepan/disposable</a></td>"," <td><span class=\"milestone\">Milestone 1.0 <span class=\"icon down-open\"></span></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">40%</span>"," <span class=\"due\">due on Friday</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:40%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr class=\"done\">"," <td><a class=\"repo\" href=\"#\">radekstepan/burnchart</a></td>"," <td><span class=\"milestone\">Beta Milestone <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">100%</span>"," <span class=\"due\">due tomorrow</span>"," <div class=\"outer bar\">"," <div class=\"inner bar green\" style=\"width:100%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">intermine/intermine</a></td>"," <td><span class=\"milestone\">Emma Release 96 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">27%</span>"," <span class=\"due\">due in 2 weeks</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:27%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">microsoft/windows</a></td>"," <td><span class=\"milestone\">RC 9 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">90%</span>"," <span class=\"due red\">overdue by a month</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:90%\"></div>"," </div>"," </div>"," </td>"," </tr>"," -->"," </table>",""," <div class=\"footer\">"," <a href=\"#\"><Icons icon=\"cog\"/> Edit</a>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||
});
|
||||
|
||||
// date.coffee
|
||||
|
@ -852,6 +885,9 @@
|
|||
} else {
|
||||
return ['Milestone', text].join(' ');
|
||||
}
|
||||
},
|
||||
hexToDecimal: function(hex) {
|
||||
return parseInt(hex, 16);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -894,7 +930,7 @@
|
|||
// header.coffee
|
||||
root.require.register('burnchart/src/views/header.js', function(exports, require, module) {
|
||||
|
||||
var firebase, mediator, user;
|
||||
var Icons, firebase, mediator, state, user;
|
||||
|
||||
firebase = require('../modules/firebase');
|
||||
|
||||
|
@ -902,19 +938,31 @@
|
|||
|
||||
user = require('../models/user');
|
||||
|
||||
state = require('../models/state');
|
||||
|
||||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../templates/header'),
|
||||
'data': {
|
||||
'user': user,
|
||||
'icon': 'fire-station'
|
||||
},
|
||||
init: function() {
|
||||
return this.on('!login', function() {
|
||||
var _this = this;
|
||||
this.on('!login', function() {
|
||||
return firebase.login(function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
return state.observe('loading', function(val) {
|
||||
return _this.set('icon', val ? 'spin4' : 'fire-station');
|
||||
});
|
||||
},
|
||||
'data': {
|
||||
user: user
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
});
|
||||
|
@ -924,22 +972,65 @@
|
|||
// hero.coffee
|
||||
root.require.register('burnchart/src/views/hero.js', function(exports, require, module) {
|
||||
|
||||
var mediator, projects;
|
||||
var Icons, mediator, projects;
|
||||
|
||||
mediator = require('../modules/mediator');
|
||||
|
||||
projects = require('../models/projects');
|
||||
|
||||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../templates/hero'),
|
||||
'data': {
|
||||
projects: projects
|
||||
},
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// icons.coffee
|
||||
root.require.register('burnchart/src/views/icons.js', function(exports, require, module) {
|
||||
|
||||
var codes, utils;
|
||||
|
||||
utils = require('../utils/format');
|
||||
|
||||
codes = {
|
||||
'cog': '\e800',
|
||||
'search': '\e801',
|
||||
'github': '\e802',
|
||||
'address': '\e803',
|
||||
'plus-circled': '\e804',
|
||||
'fire-station': '\e805',
|
||||
'sort-alphabet': '\e806',
|
||||
'down-open': '\e807',
|
||||
'spin6': '\e808',
|
||||
'megaphone': '\e809',
|
||||
'spin4': '\e80a'
|
||||
};
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../templates/icons'),
|
||||
'isolated': true,
|
||||
init: function() {
|
||||
return this.observe('icon', function(icon) {
|
||||
var hex;
|
||||
if (icon && (hex = codes[icon])) {
|
||||
return this.set('code', utils.hexToDecimal(hex));
|
||||
} else {
|
||||
return this.set('code', null);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// addProject.coffee
|
||||
root.require.register('burnchart/src/views/pages/addProject.js', function(exports, require, module) {
|
||||
|
||||
|
@ -1051,17 +1142,22 @@
|
|||
// projects.coffee
|
||||
root.require.register('burnchart/src/views/projects.js', function(exports, require, module) {
|
||||
|
||||
var mediator, projects;
|
||||
var Icons, mediator, projects;
|
||||
|
||||
mediator = require('../modules/mediator');
|
||||
|
||||
projects = require('../models/projects');
|
||||
|
||||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../templates/projects'),
|
||||
'data': {
|
||||
projects: projects
|
||||
},
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
( require "./#{key}" for key in [
|
||||
'utils/mixins'
|
||||
'models/projects'
|
||||
'utils/mixins'
|
||||
'models/projects'
|
||||
] )
|
||||
|
||||
|
||||
|
@ -8,12 +8,12 @@ Router = require './modules/router'
|
|||
Header = require './views/header'
|
||||
|
||||
App = Ractive.extend
|
||||
|
||||
'template': require './templates/layout'
|
||||
|
||||
'template': require './templates/layout'
|
||||
|
||||
'components': { Header }
|
||||
'components': { Header }
|
||||
|
||||
init: ->
|
||||
new Router()
|
||||
init: ->
|
||||
new Router()
|
||||
|
||||
module.exports = new App()
|
|
@ -2,32 +2,32 @@ Model = require '../utils/model'
|
|||
|
||||
module.exports = new Model
|
||||
|
||||
"data":
|
||||
# Firebase app name.
|
||||
"firebase": "burnchart"
|
||||
# Data source provider.
|
||||
"provider": "github"
|
||||
# Fields to keep from GH responses.
|
||||
"fields":
|
||||
"milestone": [
|
||||
"closed_issues"
|
||||
"created_at"
|
||||
"description"
|
||||
"due_on"
|
||||
"number"
|
||||
"open_issues"
|
||||
"title"
|
||||
"updated_at"
|
||||
]
|
||||
# Chart configuration.
|
||||
"chart":
|
||||
# Days we are not working.
|
||||
"off_days": [ ]
|
||||
# How do we parse GitHub dates?
|
||||
"datetime": /^(\d{4}-\d{2}-\d{2})T(.*)/
|
||||
# How does a size label look like?
|
||||
"size_label": /^size (\d+)$/
|
||||
# How do we specify which user/repo/(milestone) we want?
|
||||
"location": /^#!((\/[^\/]+){2,3})$/
|
||||
# Process all issues as one size or use labels.
|
||||
"points": 'ONE_SIZE'
|
||||
"data":
|
||||
# Firebase app name.
|
||||
"firebase": "burnchart"
|
||||
# Data source provider.
|
||||
"provider": "github"
|
||||
# Fields to keep from GH responses.
|
||||
"fields":
|
||||
"milestone": [
|
||||
"closed_issues"
|
||||
"created_at"
|
||||
"description"
|
||||
"due_on"
|
||||
"number"
|
||||
"open_issues"
|
||||
"title"
|
||||
"updated_at"
|
||||
]
|
||||
# Chart configuration.
|
||||
"chart":
|
||||
# Days we are not working.
|
||||
"off_days": [ ]
|
||||
# How do we parse GitHub dates?
|
||||
"datetime": /^(\d{4}-\d{2}-\d{2})T(.*)/
|
||||
# How does a size label look like?
|
||||
"size_label": /^size (\d+)$/
|
||||
# How do we specify which user/repo/(milestone) we want?
|
||||
"location": /^#!((\/[^\/]+){2,3})$/
|
||||
# Process all issues as one size or use labels.
|
||||
"points": 'ONE_SIZE'
|
|
@ -7,38 +7,38 @@ user = require './user'
|
|||
|
||||
module.exports = new Model
|
||||
|
||||
'data':
|
||||
'list': []
|
||||
'data':
|
||||
'list': []
|
||||
|
||||
init: ->
|
||||
# Initialize with items stored locally.
|
||||
localforage.getItem 'projects', (projects=[]) =>
|
||||
# Fetch milestones for each of these projects.
|
||||
async.each projects, (project, cb) ->
|
||||
mediator.fire '!projects/add', project
|
||||
, (err) ->
|
||||
throw err if err
|
||||
init: ->
|
||||
# Initialize with items stored locally.
|
||||
localforage.getItem 'projects', (projects=[]) =>
|
||||
# Fetch milestones for each of these projects.
|
||||
async.each projects, (project, cb) ->
|
||||
mediator.fire '!projects/add', project
|
||||
, (err) ->
|
||||
throw err if err
|
||||
|
||||
# Persist projects in local storage.
|
||||
@observe 'list', (projects) ->
|
||||
localforage.setItem 'projects', projects
|
||||
# Persist projects in local storage.
|
||||
@observe 'list', (projects) ->
|
||||
localforage.setItem 'projects', projects
|
||||
|
||||
mediator.on '!projects/add', (repo, done) =>
|
||||
# TODO: warn when we are adding an existing repo (or
|
||||
# silently go to index again).
|
||||
mediator.on '!projects/add', (repo, done) =>
|
||||
# TODO: warn when we are adding an existing repo (or
|
||||
# silently go to index again).
|
||||
|
||||
# Fetch milestones (which validates repo too).
|
||||
request.allMilestones repo, (err, res) =>
|
||||
return done err if err
|
||||
# Fetch milestones (which validates repo too).
|
||||
request.allMilestones repo, (err, res) =>
|
||||
return done err if err
|
||||
|
||||
# Pluck these fields for milestones.
|
||||
milestones = _.pluckMany res, config.get('fields.milestone')
|
||||
# Pluck these fields for milestones.
|
||||
milestones = _.pluckMany res, config.get('fields.milestone')
|
||||
|
||||
# Push to the stack.
|
||||
@push 'list', _.merge repo, { milestones }
|
||||
# Push to the stack.
|
||||
@push 'list', _.merge repo, { milestones }
|
||||
|
||||
# Call back.
|
||||
do done
|
||||
# Call back.
|
||||
do done
|
||||
|
||||
mediator.on '!projects/clear', =>
|
||||
@set 'list', []
|
||||
mediator.on '!projects/clear', =>
|
||||
@set 'list', []
|
|
@ -0,0 +1,7 @@
|
|||
Model = require '../utils/model'
|
||||
|
||||
module.exports = new Model
|
||||
|
||||
'data':
|
||||
# Are we loading stuff?
|
||||
'loading': no
|
|
@ -1,12 +1,12 @@
|
|||
mediator = require '../modules/mediator'
|
||||
Model = require '../utils/model'
|
||||
Model = require '../utils/model'
|
||||
|
||||
# Currently logged-in user.
|
||||
module.exports = new Model
|
||||
|
||||
# Default to a local user.
|
||||
'data':
|
||||
'provider': "local"
|
||||
'id': "0"
|
||||
'uid': "local:0"
|
||||
'token': null
|
||||
# Default to a local user.
|
||||
'data':
|
||||
'provider': "local"
|
||||
'id': "0"
|
||||
'uid': "local:0"
|
||||
'token': null
|
|
@ -2,243 +2,243 @@
|
|||
config = require '../models/config'
|
||||
|
||||
module.exports =
|
||||
|
||||
# A graph of closed issues.
|
||||
# `collection`: issues
|
||||
# `created_at`: milestone start date
|
||||
# `total`: total number of points (open & closed issues)
|
||||
'actual': (collection, created_at, total, cb) ->
|
||||
head = [ {
|
||||
'date': new Date created_at
|
||||
'points': total
|
||||
} ]
|
||||
|
||||
# A graph of closed issues.
|
||||
# `collection`: issues
|
||||
# `created_at`: milestone start date
|
||||
# `total`: total number of points (open & closed issues)
|
||||
'actual': (collection, created_at, total, cb) ->
|
||||
head = [ {
|
||||
'date': new Date created_at
|
||||
'points': total
|
||||
} ]
|
||||
|
||||
min = +Infinity ; max = -Infinity
|
||||
min = +Infinity ; max = -Infinity
|
||||
|
||||
# Generate the actual closes.
|
||||
rest = _.map collection, (issue) ->
|
||||
{ size, closed_at } = issue
|
||||
# Determine the range.
|
||||
min = size if size < min
|
||||
max = size if size > max
|
||||
# Generate the actual closes.
|
||||
rest = _.map collection, (issue) ->
|
||||
{ size, closed_at } = issue
|
||||
# Determine the range.
|
||||
min = size if size < min
|
||||
max = size if size > max
|
||||
|
||||
# Dropping points remaining.
|
||||
issue.date = new Date closed_at
|
||||
issue.points = total -= size
|
||||
issue
|
||||
|
||||
# Now add a radius in a range (will be used for a circle).
|
||||
range = d3.scale.linear().domain([ min, max ]).range([ 5, 8 ])
|
||||
# Dropping points remaining.
|
||||
issue.date = new Date closed_at
|
||||
issue.points = total -= size
|
||||
issue
|
||||
|
||||
# Now add a radius in a range (will be used for a circle).
|
||||
range = d3.scale.linear().domain([ min, max ]).range([ 5, 8 ])
|
||||
|
||||
rest = _.map rest, (issue) ->
|
||||
issue.radius = range issue.size
|
||||
issue
|
||||
rest = _.map rest, (issue) ->
|
||||
issue.radius = range issue.size
|
||||
issue
|
||||
|
||||
cb null, [].concat head, rest
|
||||
cb null, [].concat head, rest
|
||||
|
||||
# A graph of an ideal progression..
|
||||
# `a`: milestone start date
|
||||
# `b`: milestone end date
|
||||
# `total`: total number of points (open & closed issues)
|
||||
'ideal': (a, b, total, cb) ->
|
||||
# Swap?
|
||||
[ b, a ] = [ a, b ] if b < a
|
||||
# A graph of an ideal progression..
|
||||
# `a`: milestone start date
|
||||
# `b`: milestone end date
|
||||
# `total`: total number of points (open & closed issues)
|
||||
'ideal': (a, b, total, cb) ->
|
||||
# Swap?
|
||||
[ b, a ] = [ a, b ] if b < a
|
||||
|
||||
# We start here adding days to `d`.
|
||||
[ y, m, d ] = _.map a.match(config.get('chart.datetime'))[1].split('-'), (v) -> parseInt v
|
||||
# We want to end here.
|
||||
cutoff = new Date(b)
|
||||
# We start here adding days to `d`.
|
||||
[ y, m, d ] = _.map a.match(config.get('chart.datetime'))[1].split('-'), (v) -> parseInt v
|
||||
# We want to end here.
|
||||
cutoff = new Date(b)
|
||||
|
||||
# Go through the beginning to the end skipping off days.
|
||||
days = [] ; length = 0
|
||||
do once = (inc = 0) ->
|
||||
# A new day.
|
||||
day = new Date y, m - 1, d + inc
|
||||
|
||||
# Does this day count?
|
||||
day_of = 7 if !day_of = day.getDay()
|
||||
if day_of in config.get('chart.off_days')
|
||||
days.push { date: day, off_day: yes }
|
||||
else
|
||||
length += 1
|
||||
days.push { date: day }
|
||||
|
||||
# Go again?
|
||||
once(inc + 1) unless day > cutoff
|
||||
# Go through the beginning to the end skipping off days.
|
||||
days = [] ; length = 0
|
||||
do once = (inc = 0) ->
|
||||
# A new day.
|
||||
day = new Date y, m - 1, d + inc
|
||||
|
||||
# Does this day count?
|
||||
day_of = 7 if !day_of = day.getDay()
|
||||
if day_of in config.get('chart.off_days')
|
||||
days.push { date: day, off_day: yes }
|
||||
else
|
||||
length += 1
|
||||
days.push { date: day }
|
||||
|
||||
# Go again?
|
||||
once(inc + 1) unless day > cutoff
|
||||
|
||||
# Map points on the array of days now.
|
||||
velocity = total / (length - 1)
|
||||
# Map points on the array of days now.
|
||||
velocity = total / (length - 1)
|
||||
|
||||
days = _.map days, (day, i) ->
|
||||
day.points = total
|
||||
total -= velocity if days[i] and not days[i].off_day
|
||||
day
|
||||
days = _.map days, (day, i) ->
|
||||
day.points = total
|
||||
total -= velocity if days[i] and not days[i].off_day
|
||||
day
|
||||
|
||||
# Do we need to make a link to right now?
|
||||
days.push { date: now, points: 0 } if (now = new Date()) > cutoff
|
||||
# Do we need to make a link to right now?
|
||||
days.push { date: now, points: 0 } if (now = new Date()) > cutoff
|
||||
|
||||
cb null, days
|
||||
cb null, days
|
||||
|
||||
# Graph representing a trendling of actual issues.
|
||||
'trendline': (actual, created_at, due_on) ->
|
||||
start = +actual[0].date
|
||||
# Graph representing a trendling of actual issues.
|
||||
'trendline': (actual, created_at, due_on) ->
|
||||
start = +actual[0].date
|
||||
|
||||
# Values is a list of time from the start and points remaining.
|
||||
values = _.map actual, ({ date, points }) ->
|
||||
[ +date - start, points ]
|
||||
# Values is a list of time from the start and points remaining.
|
||||
values = _.map actual, ({ date, points }) ->
|
||||
[ +date - start, points ]
|
||||
|
||||
# Now is an actual point too.
|
||||
last = actual[actual.length - 1]
|
||||
values.push [ + new Date() - start, last.points ]
|
||||
# Now is an actual point too.
|
||||
last = actual[actual.length - 1]
|
||||
values.push [ + new Date() - start, last.points ]
|
||||
|
||||
# http://classroom.synonym.com/calculate-trendline-2709.html
|
||||
b1 = 0 ; e = 0 ; c1 = 0
|
||||
a = (l = values.length) * _.reduce(values, (sum, [ a, b ]) ->
|
||||
b1 += a ; e += b
|
||||
c1 += Math.pow(a, 2)
|
||||
sum + (a * b)
|
||||
, 0)
|
||||
# http://classroom.synonym.com/calculate-trendline-2709.html
|
||||
b1 = 0 ; e = 0 ; c1 = 0
|
||||
a = (l = values.length) * _.reduce(values, (sum, [ a, b ]) ->
|
||||
b1 += a ; e += b
|
||||
c1 += Math.pow(a, 2)
|
||||
sum + (a * b)
|
||||
, 0)
|
||||
|
||||
slope = (a - (b1 * e)) / ((l * c1) - (Math.pow(b1, 2)))
|
||||
intercept = (e - (slope * b1)) / l
|
||||
fn = (x) -> slope * x + intercept
|
||||
slope = (a - (b1 * e)) / ((l * c1) - (Math.pow(b1, 2)))
|
||||
intercept = (e - (slope * b1)) / l
|
||||
fn = (x) -> slope * x + intercept
|
||||
|
||||
# Milestone always has a creation date.
|
||||
created_at = new Date created_at
|
||||
# Due date can be empty.
|
||||
due_on = if due_on then new Date(due_on) else new Date()
|
||||
# Milestone always has a creation date.
|
||||
created_at = new Date created_at
|
||||
# Due date can be empty.
|
||||
due_on = if due_on then new Date(due_on) else new Date()
|
||||
|
||||
a = created_at - start
|
||||
b = due_on - start
|
||||
a = created_at - start
|
||||
b = due_on - start
|
||||
|
||||
[
|
||||
{
|
||||
date: created_at
|
||||
points: fn(a)
|
||||
}, {
|
||||
date: due_on
|
||||
points: fn(b)
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
date: created_at
|
||||
points: fn(a)
|
||||
}, {
|
||||
date: due_on
|
||||
points: fn(b)
|
||||
}
|
||||
]
|
||||
|
||||
# The graph as a whole.
|
||||
'render': ([ actual, ideal, trendline ], cb) ->
|
||||
document.querySelector('#svg').innerHTML = ''
|
||||
# The graph as a whole.
|
||||
'render': ([ actual, ideal, trendline ], cb) ->
|
||||
document.querySelector('#svg').innerHTML = ''
|
||||
|
||||
# Get available space.
|
||||
{ height, width } = document.querySelector('#chart').getBoundingClientRect()
|
||||
# Get available space.
|
||||
{ height, width } = document.querySelector('#chart').getBoundingClientRect()
|
||||
|
||||
margin = { 'top': 30, 'right': 30, 'bottom': 40, 'left': 50 }
|
||||
width -= margin.left + margin.right
|
||||
height -= margin.top + margin.bottom
|
||||
margin = { 'top': 30, 'right': 30, 'bottom': 40, 'left': 50 }
|
||||
width -= margin.left + margin.right
|
||||
height -= margin.top + margin.bottom
|
||||
|
||||
# Scales.
|
||||
x = d3.time.scale().range([ 0, width ])
|
||||
y = d3.scale.linear().range([ height, 0 ])
|
||||
# Scales.
|
||||
x = d3.time.scale().range([ 0, width ])
|
||||
y = d3.scale.linear().range([ height, 0 ])
|
||||
|
||||
# Axes.
|
||||
xAxis = d3.svg.axis().scale(x)
|
||||
.orient("bottom")
|
||||
# Show vertical lines...
|
||||
.tickSize(-height)
|
||||
# ...with day of the month...
|
||||
.tickFormat( (d) -> d.getDate() )
|
||||
# ...and give us a spacer.
|
||||
.tickPadding(10)
|
||||
# Axes.
|
||||
xAxis = d3.svg.axis().scale(x)
|
||||
.orient("bottom")
|
||||
# Show vertical lines...
|
||||
.tickSize(-height)
|
||||
# ...with day of the month...
|
||||
.tickFormat( (d) -> d.getDate() )
|
||||
# ...and give us a spacer.
|
||||
.tickPadding(10)
|
||||
|
||||
yAxis = d3.svg.axis().scale(y)
|
||||
.orient("left")
|
||||
.tickSize(-width)
|
||||
.ticks(5)
|
||||
.tickPadding(10)
|
||||
|
||||
# Line generator.
|
||||
line = d3.svg.line()
|
||||
.interpolate("linear")
|
||||
.x( (d) -> x(d.date) )
|
||||
.y( (d) -> y(d.points) )
|
||||
yAxis = d3.svg.axis().scale(y)
|
||||
.orient("left")
|
||||
.tickSize(-width)
|
||||
.ticks(5)
|
||||
.tickPadding(10)
|
||||
|
||||
# Line generator.
|
||||
line = d3.svg.line()
|
||||
.interpolate("linear")
|
||||
.x( (d) -> x(d.date) )
|
||||
.y( (d) -> y(d.points) )
|
||||
|
||||
# Get the minimum and maximum date, and initial points.
|
||||
x.domain([ ideal[0].date, ideal[ideal.length - 1].date ])
|
||||
y.domain([ 0, ideal[0].points ]).nice()
|
||||
# Get the minimum and maximum date, and initial points.
|
||||
x.domain([ ideal[0].date, ideal[ideal.length - 1].date ])
|
||||
y.domain([ 0, ideal[0].points ]).nice()
|
||||
|
||||
# Add an SVG element with the desired dimensions and margin.
|
||||
svg = d3.select("#svg").append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
|
||||
# Add an SVG element with the desired dimensions and margin.
|
||||
svg = d3.select("#svg").append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
|
||||
|
||||
# Add the days x-axis.
|
||||
svg.append("g")
|
||||
.attr("class", "x axis day")
|
||||
.attr("transform", "translate(0,#{height})")
|
||||
.call(xAxis)
|
||||
# Add the days x-axis.
|
||||
svg.append("g")
|
||||
.attr("class", "x axis day")
|
||||
.attr("transform", "translate(0,#{height})")
|
||||
.call(xAxis)
|
||||
|
||||
# Add the months x-axis.
|
||||
m = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
]
|
||||
# Add the months x-axis.
|
||||
m = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
]
|
||||
|
||||
mAxis = xAxis
|
||||
.orient("top")
|
||||
.tickSize(height)
|
||||
.tickFormat( (d) -> m[d.getMonth()] )
|
||||
.ticks(2)
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "x axis month")
|
||||
.attr("transform", "translate(0,#{height})")
|
||||
.call(mAxis)
|
||||
mAxis = xAxis
|
||||
.orient("top")
|
||||
.tickSize(height)
|
||||
.tickFormat( (d) -> m[d.getMonth()] )
|
||||
.ticks(2)
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "x axis month")
|
||||
.attr("transform", "translate(0,#{height})")
|
||||
.call(mAxis)
|
||||
|
||||
# Add the y-axis.
|
||||
svg.append("g")
|
||||
.attr("class", "y axis")
|
||||
.call(yAxis)
|
||||
# Add the y-axis.
|
||||
svg.append("g")
|
||||
.attr("class", "y axis")
|
||||
.call(yAxis)
|
||||
|
||||
# Add a line showing where we are now.
|
||||
svg.append("svg:line")
|
||||
.attr("class", "today")
|
||||
.attr("x1", x(new Date()))
|
||||
.attr("y1", 0)
|
||||
.attr("x2", x(new Date()))
|
||||
.attr("y2", height)
|
||||
# Add a line showing where we are now.
|
||||
svg.append("svg:line")
|
||||
.attr("class", "today")
|
||||
.attr("x1", x(new Date()))
|
||||
.attr("y1", 0)
|
||||
.attr("x2", x(new Date()))
|
||||
.attr("y2", height)
|
||||
|
||||
# Add the ideal line path.
|
||||
svg.append("path")
|
||||
.attr("class", "ideal line")
|
||||
.attr("d", line.interpolate("basis")(ideal))
|
||||
# Add the ideal line path.
|
||||
svg.append("path")
|
||||
.attr("class", "ideal line")
|
||||
.attr("d", line.interpolate("basis")(ideal))
|
||||
|
||||
# Add the trendline path.
|
||||
svg.append("path")
|
||||
.attr("class", "trendline line")
|
||||
.attr("d", line.interpolate("linear")(trendline))
|
||||
# Add the trendline path.
|
||||
svg.append("path")
|
||||
.attr("class", "trendline line")
|
||||
.attr("d", line.interpolate("linear")(trendline))
|
||||
|
||||
# Add the actual line path.
|
||||
svg.append("path")
|
||||
.attr("class", "actual line")
|
||||
.attr("d", line.interpolate("linear").y( (d) -> y(d.points) )(actual))
|
||||
# Add the actual line path.
|
||||
svg.append("path")
|
||||
.attr("class", "actual line")
|
||||
.attr("d", line.interpolate("linear").y( (d) -> y(d.points) )(actual))
|
||||
|
||||
# Collect the tooltip here.
|
||||
tooltip = d3.tip().attr('class', 'd3-tip').html ({ number, title }) ->
|
||||
"##{number}: #{title}"
|
||||
# Collect the tooltip here.
|
||||
tooltip = d3.tip().attr('class', 'd3-tip').html ({ number, title }) ->
|
||||
"##{number}: #{title}"
|
||||
|
||||
svg.call(tooltip)
|
||||
svg.call(tooltip)
|
||||
|
||||
# Show when we closed an issue.
|
||||
svg.selectAll("a.issue")
|
||||
.data(actual.slice(1)) # skip the starting point
|
||||
.enter()
|
||||
# A wrapping link.
|
||||
.append('svg:a')
|
||||
.attr("xlink:href", ({ html_url }) -> html_url )
|
||||
.attr("xlink:show", 'new')
|
||||
.append('svg:circle')
|
||||
.attr("cx", ({ date }) -> x date )
|
||||
.attr("cy", ({ points }) -> y points )
|
||||
.attr("r", ({ radius }) -> 5 ) # fixed for now
|
||||
.on('mouseover', tooltip.show)
|
||||
.on('mouseout', tooltip.hide)
|
||||
# Show when we closed an issue.
|
||||
svg.selectAll("a.issue")
|
||||
.data(actual.slice(1)) # skip the starting point
|
||||
.enter()
|
||||
# A wrapping link.
|
||||
.append('svg:a')
|
||||
.attr("xlink:href", ({ html_url }) -> html_url )
|
||||
.attr("xlink:show", 'new')
|
||||
.append('svg:circle')
|
||||
.attr("cx", ({ date }) -> x date )
|
||||
.attr("cy", ({ points }) -> y points )
|
||||
.attr("r", ({ radius }) -> 5 ) # fixed for now
|
||||
.on('mouseover', tooltip.show)
|
||||
.on('mouseout', tooltip.hide)
|
||||
|
||||
cb null
|
||||
cb null
|
|
@ -3,36 +3,36 @@ config = require '../models/config'
|
|||
|
||||
# Default "silent" callback for auth.
|
||||
class Class
|
||||
|
||||
constructor: ->
|
||||
# Setup a new client.
|
||||
@client = new Firebase "https://#{config.get('firebase')}.firebaseio.com"
|
||||
|
||||
constructor: ->
|
||||
# Setup a new client.
|
||||
@client = new Firebase "https://#{config.get('firebase')}.firebaseio.com"
|
||||
|
||||
# Check if we have a user in session.
|
||||
@auth = new FirebaseSimpleLogin @client, (err, obj) =>
|
||||
return @authCb err if err or not obj
|
||||
# Check if we have a user in session.
|
||||
@auth = new FirebaseSimpleLogin @client, (err, obj) =>
|
||||
return @authCb err if err or not obj
|
||||
|
||||
# Save user.
|
||||
user.set obj
|
||||
# Save user.
|
||||
user.set obj
|
||||
|
||||
# Default "blank" callback.
|
||||
authCb: ->
|
||||
# Default "blank" callback.
|
||||
authCb: ->
|
||||
|
||||
# Login a user.
|
||||
login: (cb) ->
|
||||
return cb 'Client is not setup' unless @client
|
||||
|
||||
# Override the default auth callback.
|
||||
@authCb = cb
|
||||
# Login a user.
|
||||
login: (cb) ->
|
||||
return cb 'Client is not setup' unless @client
|
||||
|
||||
# Override the default auth callback.
|
||||
@authCb = cb
|
||||
|
||||
# Login.
|
||||
@auth.login config.get('provider'),
|
||||
'rememberMe': yes
|
||||
'scope': 'public_repo'
|
||||
# Login.
|
||||
@auth.login config.get('provider'),
|
||||
'rememberMe': yes
|
||||
'scope': 'public_repo'
|
||||
|
||||
# Logout a user.
|
||||
logout: ->
|
||||
@auth?.logout
|
||||
do user.reset
|
||||
# Logout a user.
|
||||
logout: ->
|
||||
@auth?.logout
|
||||
do user.reset
|
||||
|
||||
module.exports = new Class()
|
|
@ -4,67 +4,67 @@ request = require './request'
|
|||
|
||||
module.exports =
|
||||
|
||||
# Used on an initial fetch of issues for a repo.
|
||||
'get_all': (opts, cb) ->
|
||||
# For each state...
|
||||
one_status = (state, cb) ->
|
||||
# Concat them here.
|
||||
results = []
|
||||
# One pageful fetch (next pages in series).
|
||||
do fetch_page = (page = 1) ->
|
||||
request.allIssues opts, {
|
||||
'milestone': opts.milestone.number,
|
||||
state, page
|
||||
}, (err, data) ->
|
||||
# Errors?
|
||||
return cb err if err
|
||||
# Empty?
|
||||
return cb null, results unless data.length
|
||||
# Concat sorted (API does not sort on closed_at!).
|
||||
results = results.concat _.sortBy data, 'closed_at'
|
||||
# < 100 results?
|
||||
return cb null, results if data.length < 100
|
||||
# Fetch the next page then.
|
||||
fetch_page page + 1
|
||||
# Used on an initial fetch of issues for a repo.
|
||||
'get_all': (opts, cb) ->
|
||||
# For each state...
|
||||
one_status = (state, cb) ->
|
||||
# Concat them here.
|
||||
results = []
|
||||
# One pageful fetch (next pages in series).
|
||||
do fetch_page = (page = 1) ->
|
||||
request.allIssues opts, {
|
||||
'milestone': opts.milestone.number,
|
||||
state, page
|
||||
}, (err, data) ->
|
||||
# Errors?
|
||||
return cb err if err
|
||||
# Empty?
|
||||
return cb null, results unless data.length
|
||||
# Concat sorted (API does not sort on closed_at!).
|
||||
results = results.concat _.sortBy data, 'closed_at'
|
||||
# < 100 results?
|
||||
return cb null, results if data.length < 100
|
||||
# Fetch the next page then.
|
||||
fetch_page page + 1
|
||||
|
||||
# For each `open` and `closed` issues in parallel.
|
||||
async.parallel [
|
||||
_.partial one_status, 'open'
|
||||
_.partial one_status, 'closed'
|
||||
], cb
|
||||
# For each `open` and `closed` issues in parallel.
|
||||
async.parallel [
|
||||
_.partial one_status, 'open'
|
||||
_.partial one_status, 'closed'
|
||||
], cb
|
||||
|
||||
# Filter an array of incoming issues based on a regex & save size on them.
|
||||
'filter': (collection, cb) ->
|
||||
# The total size of all issues.
|
||||
total = 0
|
||||
# Filter an array of incoming issues based on a regex & save size on them.
|
||||
'filter': (collection, cb) ->
|
||||
# The total size of all issues.
|
||||
total = 0
|
||||
|
||||
# Which point counting mode are we in?
|
||||
switch config.get 'chart.points'
|
||||
# All issues are the same size
|
||||
when 'ONE_SIZE'
|
||||
total = collection.length
|
||||
filtered = _.map collection, (issue) ->
|
||||
issue.size = 1
|
||||
issue
|
||||
|
||||
# Take the points size from issue label.
|
||||
when 'LABELS'
|
||||
filtered = _.filter collection, (issue) ->
|
||||
# Skip if no labels exist.
|
||||
return no unless labels = issue.labels
|
||||
# Which point counting mode are we in?
|
||||
switch config.get 'chart.points'
|
||||
# All issues are the same size
|
||||
when 'ONE_SIZE'
|
||||
total = collection.length
|
||||
filtered = _.map collection, (issue) ->
|
||||
issue.size = 1
|
||||
issue
|
||||
|
||||
# Take the points size from issue label.
|
||||
when 'LABELS'
|
||||
filtered = _.filter collection, (issue) ->
|
||||
# Skip if no labels exist.
|
||||
return no unless labels = issue.labels
|
||||
|
||||
# Determine the total issue size from all labels.
|
||||
issue.size = _.reduce labels, (sum, label) ->
|
||||
# Not matching.
|
||||
return sum unless matches = label.name.match config.get 'chart.size_label'
|
||||
# Increase sum.
|
||||
sum += parseInt matches[1]
|
||||
, 0
|
||||
|
||||
# Increase the total.
|
||||
total += issue.size
|
||||
# Determine the total issue size from all labels.
|
||||
issue.size = _.reduce labels, (sum, label) ->
|
||||
# Not matching.
|
||||
return sum unless matches = label.name.match config.get 'chart.size_label'
|
||||
# Increase sum.
|
||||
sum += parseInt matches[1]
|
||||
, 0
|
||||
|
||||
# Increase the total.
|
||||
total += issue.size
|
||||
|
||||
# Are we saving it?
|
||||
!!issue.size
|
||||
# Are we saving it?
|
||||
!!issue.size
|
||||
|
||||
cb null, filtered, total
|
||||
cb null, filtered, total
|
|
@ -2,34 +2,34 @@
|
|||
request = require './request'
|
||||
|
||||
module.exports =
|
||||
# Get current/specified milestone for a repo.
|
||||
get: (repo, cb) ->
|
||||
# Get a specific milestone.
|
||||
if repo.milestone
|
||||
request.oneMilestone repo, repo.milestone, (err, m) ->
|
||||
# Errors?
|
||||
return cb err if err
|
||||
# Empty milestone?
|
||||
if m.open_issues + m.closed_issues is 0
|
||||
return cb null, "No issues for milestone `#{m.title}`"
|
||||
# Get current/specified milestone for a repo.
|
||||
get: (repo, cb) ->
|
||||
# Get a specific milestone.
|
||||
if repo.milestone
|
||||
request.oneMilestone repo, repo.milestone, (err, m) ->
|
||||
# Errors?
|
||||
return cb err if err
|
||||
# Empty milestone?
|
||||
if m.open_issues + m.closed_issues is 0
|
||||
return cb null, "No issues for milestone `#{m.title}`"
|
||||
|
||||
cb null, null, m
|
||||
cb null, null, m
|
||||
|
||||
# Get the current milestone out of many.
|
||||
else
|
||||
request.allMilestones repo, (err, data) ->
|
||||
# Errors?
|
||||
return cb err if err
|
||||
# Empty warning?
|
||||
return cb null, "No open milestones for repo #{repo.path}" unless data.length
|
||||
# The first milestone should be ending soonest.
|
||||
m = data[0]
|
||||
# Filter milestones without due date.
|
||||
m = _.rest data, { 'due_on' : null }
|
||||
# The first milestone should be ending soonest. Prefer milestones with due dates.
|
||||
m = if m[0] then m[0] else data[0]
|
||||
# Empty milestone?
|
||||
if m.open_issues + m.closed_issues is 0
|
||||
return cb null, "No issues for milestone `#{m.title}`"
|
||||
# Get the current milestone out of many.
|
||||
else
|
||||
request.allMilestones repo, (err, data) ->
|
||||
# Errors?
|
||||
return cb err if err
|
||||
# Empty warning?
|
||||
return cb null, "No open milestones for repo #{repo.path}" unless data.length
|
||||
# The first milestone should be ending soonest.
|
||||
m = data[0]
|
||||
# Filter milestones without due date.
|
||||
m = _.rest data, { 'due_on' : null }
|
||||
# The first milestone should be ending soonest. Prefer milestones with due dates.
|
||||
m = if m[0] then m[0] else data[0]
|
||||
# Empty milestone?
|
||||
if m.open_issues + m.closed_issues is 0
|
||||
return cb null, "No issues for milestone `#{m.title}`"
|
||||
|
||||
cb null, null, m
|
||||
cb null, null, m
|
|
@ -1,62 +1,62 @@
|
|||
#!/usr/bin/env coffee
|
||||
issues = require './issues'
|
||||
chart = require './chart'
|
||||
issues = require './issues'
|
||||
chart = require './chart'
|
||||
|
||||
# Setup a project and render it.
|
||||
module.exports = (opts, cb) ->
|
||||
|
||||
# Get all issues.
|
||||
async.waterfall [ (cb) ->
|
||||
issues.get_all opts, cb
|
||||
|
||||
# Filter them to labeled ones.
|
||||
(all, cb) ->
|
||||
async.map all, (array, cb) ->
|
||||
issues.filter array, (err, filtered, total) ->
|
||||
cb err, [ filtered, total ]
|
||||
, (err, [ open, closed ]) ->
|
||||
return cb err if err
|
||||
# Empty?
|
||||
return cb 'No matching issues found' if open[1] + closed[1] is 0
|
||||
# Save the open/closed on us first.
|
||||
opts.issues =
|
||||
'closed': { 'points': closed[1], 'data': closed[0] }
|
||||
'open': { 'points': open[1], 'data': open[0] }
|
||||
# Do we need to move the milestone start date?
|
||||
if (start = closed[0][0].closed_at) < opts.milestone.created_at
|
||||
opts.milestone.created_at = start
|
||||
# Get all issues.
|
||||
async.waterfall [ (cb) ->
|
||||
issues.get_all opts, cb
|
||||
|
||||
# Filter them to labeled ones.
|
||||
(all, cb) ->
|
||||
async.map all, (array, cb) ->
|
||||
issues.filter array, (err, filtered, total) ->
|
||||
cb err, [ filtered, total ]
|
||||
, (err, [ open, closed ]) ->
|
||||
return cb err if err
|
||||
# Empty?
|
||||
return cb 'No matching issues found' if open[1] + closed[1] is 0
|
||||
# Save the open/closed on us first.
|
||||
opts.issues =
|
||||
'closed': { 'points': closed[1], 'data': closed[0] }
|
||||
'open': { 'points': open[1], 'data': open[0] }
|
||||
# Do we need to move the milestone start date?
|
||||
if (start = closed[0][0].closed_at) < opts.milestone.created_at
|
||||
opts.milestone.created_at = start
|
||||
|
||||
cb null
|
||||
|
||||
# Create actual and ideal lines & render.
|
||||
(cb) ->
|
||||
total = opts.issues.open.points + opts.issues.closed.points
|
||||
cb null
|
||||
|
||||
# Create actual and ideal lines & render.
|
||||
(cb) ->
|
||||
total = opts.issues.open.points + opts.issues.closed.points
|
||||
|
||||
async.parallel [
|
||||
_.partial(
|
||||
chart.actual, # actual line
|
||||
opts.issues.closed.data,
|
||||
opts.milestone.created_at,
|
||||
total
|
||||
)
|
||||
_.partial(
|
||||
chart.ideal, # ideal line
|
||||
opts.milestone.created_at,
|
||||
opts.milestone.due_on,
|
||||
total
|
||||
)
|
||||
], (err, values) ->
|
||||
# Generate a trendline?
|
||||
values.push(chart.trendline(
|
||||
values[0],
|
||||
opts.milestone.created_at,
|
||||
opts.milestone.due_on
|
||||
)) if values[0].length
|
||||
async.parallel [
|
||||
_.partial(
|
||||
chart.actual, # actual line
|
||||
opts.issues.closed.data,
|
||||
opts.milestone.created_at,
|
||||
total
|
||||
)
|
||||
_.partial(
|
||||
chart.ideal, # ideal line
|
||||
opts.milestone.created_at,
|
||||
opts.milestone.due_on,
|
||||
total
|
||||
)
|
||||
], (err, values) ->
|
||||
# Generate a trendline?
|
||||
values.push(chart.trendline(
|
||||
values[0],
|
||||
opts.milestone.created_at,
|
||||
opts.milestone.due_on
|
||||
)) if values[0].length
|
||||
|
||||
# Render the chart.
|
||||
chart.render values, cb
|
||||
# Render the chart.
|
||||
chart.render values, cb
|
||||
|
||||
# Watch window resize from now on?
|
||||
# window.onresize = doit if 'onresize' of window
|
||||
# Watch window resize from now on?
|
||||
# window.onresize = doit if 'onresize' of window
|
||||
|
||||
], cb
|
||||
], cb
|
|
@ -2,124 +2,124 @@ user = require '../models/user'
|
|||
|
||||
# Custom JSON parser.
|
||||
superagent.parse =
|
||||
'application/json': (res) ->
|
||||
try
|
||||
JSON.parse res
|
||||
catch e
|
||||
{} # it was not to be...
|
||||
'application/json': (res) ->
|
||||
try
|
||||
JSON.parse res
|
||||
catch e
|
||||
{} # it was not to be...
|
||||
|
||||
# Default args.
|
||||
defaults =
|
||||
'github':
|
||||
'host': 'api.github.com'
|
||||
'protocol': 'https'
|
||||
'github':
|
||||
'host': 'api.github.com'
|
||||
'protocol': 'https'
|
||||
|
||||
# Public api.
|
||||
module.exports =
|
||||
|
||||
# Get a repo.
|
||||
'repo': (repo, cb) ->
|
||||
data = _.defaults
|
||||
'path': "/repos/#{repo.owner}/#{repo.name}"
|
||||
'headers': headers user.get('token')
|
||||
, defaults.github
|
||||
|
||||
# Get a repo.
|
||||
'repo': (repo, cb) ->
|
||||
data = _.defaults
|
||||
'path': "/repos/#{repo.owner}/#{repo.name}"
|
||||
'headers': headers user.get('token')
|
||||
, defaults.github
|
||||
|
||||
request data, cb
|
||||
request data, cb
|
||||
|
||||
# Get all open milestones.
|
||||
'allMilestones': (repo, cb) ->
|
||||
data = _.defaults
|
||||
'path': "/repos/#{repo.owner}/#{repo.name}/milestones"
|
||||
'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' }
|
||||
'headers': headers user.get('token')
|
||||
, defaults.github
|
||||
# Get all open milestones.
|
||||
'allMilestones': (repo, cb) ->
|
||||
data = _.defaults
|
||||
'path': "/repos/#{repo.owner}/#{repo.name}/milestones"
|
||||
'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' }
|
||||
'headers': headers user.get('token')
|
||||
, defaults.github
|
||||
|
||||
request data, cb
|
||||
|
||||
# Get one open milestone.
|
||||
'oneMilestone': (repo, number, cb) ->
|
||||
data = _.defaults
|
||||
'path': "/repos/#{repo.owner}/#{repo.name}/milestones/#{number}"
|
||||
'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' }
|
||||
'headers': headers user.get('token')
|
||||
, defaults.github
|
||||
request data, cb
|
||||
|
||||
# Get one open milestone.
|
||||
'oneMilestone': (repo, number, cb) ->
|
||||
data = _.defaults
|
||||
'path': "/repos/#{repo.owner}/#{repo.name}/milestones/#{number}"
|
||||
'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' }
|
||||
'headers': headers user.get('token')
|
||||
, defaults.github
|
||||
|
||||
request data, cb
|
||||
request data, cb
|
||||
|
||||
# Get all issues for a state.
|
||||
'allIssues': (repo, query, cb) ->
|
||||
data = _.defaults
|
||||
'path': "/repos/#{repo.owner}/#{repo.name}/issues"
|
||||
'query': _.extend query, { 'per_page': '100' }
|
||||
'headers': headers user.get('token')
|
||||
, defaults.github
|
||||
# Get all issues for a state.
|
||||
'allIssues': (repo, query, cb) ->
|
||||
data = _.defaults
|
||||
'path': "/repos/#{repo.owner}/#{repo.name}/issues"
|
||||
'query': _.extend query, { 'per_page': '100' }
|
||||
'headers': headers user.get('token')
|
||||
, defaults.github
|
||||
|
||||
request data, cb
|
||||
request data, cb
|
||||
|
||||
# Make a request using SuperAgent.
|
||||
request = ({ protocol, host, path, query, headers }, cb) ->
|
||||
exited = no
|
||||
exited = no
|
||||
|
||||
# Make the query params.
|
||||
q = if query then '?' + ( "#{k}=#{v}" for k, v of query ).join('&') else ''
|
||||
# Make the query params.
|
||||
q = if query then '?' + ( "#{k}=#{v}" for k, v of query ).join('&') else ''
|
||||
|
||||
# The URI.
|
||||
req = superagent.get("#{protocol}://#{host}#{path}#{q}")
|
||||
# Add headers.
|
||||
( req.set(k, v) for k, v of headers )
|
||||
|
||||
# Timeout for requests that do not finish... see #32.
|
||||
timeout = setTimeout ->
|
||||
exited = yes
|
||||
cb 'Request has timed out'
|
||||
, 1e4 # give us 10s
|
||||
# The URI.
|
||||
req = superagent.get("#{protocol}://#{host}#{path}#{q}")
|
||||
# Add headers.
|
||||
( req.set(k, v) for k, v of headers )
|
||||
|
||||
# Timeout for requests that do not finish... see #32.
|
||||
timeout = setTimeout ->
|
||||
exited = yes
|
||||
cb 'Request has timed out'
|
||||
, 1e4 # give us 10s
|
||||
|
||||
# Send.
|
||||
req.end (err, data) ->
|
||||
# Arrived too late.
|
||||
return if exited
|
||||
# All fine.
|
||||
exited = yes
|
||||
clearTimeout timeout
|
||||
# Actually process the response.
|
||||
response err, data, cb
|
||||
# Send.
|
||||
req.end (err, data) ->
|
||||
# Arrived too late.
|
||||
return if exited
|
||||
# All fine.
|
||||
exited = yes
|
||||
clearTimeout timeout
|
||||
# Actually process the response.
|
||||
response err, data, cb
|
||||
|
||||
# How do we respond to a response?
|
||||
response = (err, data, cb) ->
|
||||
return cb error err if err
|
||||
# 2xx?
|
||||
if data.statusType isnt 2
|
||||
# Do we have a message from GitHub?
|
||||
return cb data.body.message if data?.body?.message?
|
||||
# Use SA one.
|
||||
return cb data.error.message
|
||||
# All good.
|
||||
cb null, data.body
|
||||
return cb error err if err
|
||||
# 2xx?
|
||||
if data.statusType isnt 2
|
||||
# Do we have a message from GitHub?
|
||||
return cb data.body.message if data?.body?.message?
|
||||
# Use SA one.
|
||||
return cb data.error.message
|
||||
# All good.
|
||||
cb null, data.body
|
||||
|
||||
# Give us headers.
|
||||
headers = (token) ->
|
||||
# The defaults.
|
||||
h = _.extend {},
|
||||
'Content-Type': 'application/json'
|
||||
'Accept': 'application/vnd.github.v3'
|
||||
# Add token?
|
||||
h.Authorization = "token #{token}" if token?
|
||||
h
|
||||
# The defaults.
|
||||
h = _.extend {},
|
||||
'Content-Type': 'application/json'
|
||||
'Accept': 'application/vnd.github.v3'
|
||||
# Add token?
|
||||
h.Authorization = "token #{token}" if token?
|
||||
h
|
||||
|
||||
# Parse an error.
|
||||
error = (err) ->
|
||||
switch
|
||||
when _.isString err
|
||||
message = err
|
||||
when _.isArray err
|
||||
message = err[1]
|
||||
when _.isObject(err) and _.isString(err.message)
|
||||
message = err.message
|
||||
switch
|
||||
when _.isString err
|
||||
message = err
|
||||
when _.isArray err
|
||||
message = err[1]
|
||||
when _.isObject(err) and _.isString(err.message)
|
||||
message = err.message
|
||||
|
||||
unless message
|
||||
try
|
||||
message = JSON.stringify err
|
||||
catch
|
||||
message = do err.toString
|
||||
unless message
|
||||
try
|
||||
message = JSON.stringify err
|
||||
catch
|
||||
message = do err.toString
|
||||
|
||||
message
|
||||
message
|
|
@ -1,22 +1,26 @@
|
|||
mediator = require './mediator'
|
||||
state = require '../models/state'
|
||||
|
||||
el = '#page'
|
||||
|
||||
route = (page, req, evt) ->
|
||||
Page = require "../views/pages/#{page}"
|
||||
new Page { el, 'data': { 'route': req.params } }
|
||||
Page = require "../views/pages/#{page}"
|
||||
new Page { el, 'data': { 'route': req.params } }
|
||||
|
||||
router =
|
||||
'': _.partial route, 'index'
|
||||
'project/add': _.partial route, 'addProject'
|
||||
'chart/:owner/:name/:milestone': _.partial route, 'showChart'
|
||||
# TODO: remove in production.
|
||||
'reset': ->
|
||||
mediator.fire '!projects/clear'
|
||||
window.location.hash = '#'
|
||||
'': _.partial route, 'index'
|
||||
'project/add': _.partial route, 'addProject'
|
||||
'chart/:owner/:name/:milestone': _.partial route, 'showChart'
|
||||
# TODO: remove in production.
|
||||
'reset': ->
|
||||
mediator.fire '!projects/clear'
|
||||
window.location.hash = '#'
|
||||
'notify': ->
|
||||
window.location.hash = '#'
|
||||
state.set 'loading', yes
|
||||
|
||||
module.exports = ->
|
||||
# Init the routes.
|
||||
Grapnel.listen router
|
||||
# Init the routes.
|
||||
Grapnel.listen router
|
||||
|
||||
router
|
||||
router
|
|
@ -7,321 +7,316 @@ $strong_color = #C1041C
|
|||
$highlight_color = #FFBB2A
|
||||
|
||||
body
|
||||
color: #3E4457
|
||||
font-family: $sans_serif_font
|
||||
color: #3E4457
|
||||
font-family: $sans_serif_font
|
||||
|
||||
a
|
||||
text-decoration: none
|
||||
color: #AAAFBF
|
||||
cursor: pointer
|
||||
text-decoration: none
|
||||
color: #AAAFBF
|
||||
cursor: pointer
|
||||
|
||||
h1, h2, h3, p
|
||||
margin: 0
|
||||
margin: 0
|
||||
|
||||
ul
|
||||
list-style-type: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
list-style-type: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
li
|
||||
display: inline-block
|
||||
|
||||
.icon
|
||||
margin-right: 4px
|
||||
li
|
||||
display: inline-block
|
||||
|
||||
.wrap
|
||||
width: 800px
|
||||
margin: 0 auto
|
||||
width: 800px
|
||||
margin: 0 auto
|
||||
|
||||
#head
|
||||
background: $strong_color
|
||||
height: 64px
|
||||
background: $strong_color
|
||||
height: 64px
|
||||
|
||||
h1
|
||||
font-size: 26px
|
||||
margin: 0
|
||||
padding: 10px 24px
|
||||
line-height: 44px
|
||||
height: 44px
|
||||
background: #77000E
|
||||
display: inline-block
|
||||
#icon
|
||||
font-size: 26px
|
||||
margin: 0
|
||||
padding: 10px 0
|
||||
line-height: 44px
|
||||
height: 44px
|
||||
width: 74px
|
||||
background: #77000E
|
||||
display: inline-block
|
||||
color: $strong_color
|
||||
margin: 0
|
||||
text-align: center
|
||||
|
||||
a
|
||||
color: $strong_color
|
||||
.q
|
||||
position: relative
|
||||
display: inline-block
|
||||
margin: 13px 20px 0 20px
|
||||
vertical-align: top
|
||||
|
||||
.icon
|
||||
margin: 0
|
||||
.icon
|
||||
position: absolute
|
||||
color: $strong_color
|
||||
|
||||
.q
|
||||
position: relative
|
||||
display: inline-block
|
||||
margin: 13px 20px 0 20px
|
||||
vertical-align: top
|
||||
&.search
|
||||
top: 8px
|
||||
left: 12px
|
||||
|
||||
.icon
|
||||
position: absolute
|
||||
color: $strong_color
|
||||
&.down-open
|
||||
top: 8px
|
||||
right: 12px
|
||||
|
||||
&.search
|
||||
top: 8px
|
||||
left: 12px
|
||||
input
|
||||
background: #77000E
|
||||
border: 0
|
||||
padding: 10px 12px 10px 36px
|
||||
font-size: 14px
|
||||
border-radius: 2px
|
||||
color: #FFF
|
||||
width: 220px
|
||||
|
||||
&.down-open
|
||||
top: 8px
|
||||
right: 12px
|
||||
ul
|
||||
display: inline-block
|
||||
|
||||
input
|
||||
background: #77000E
|
||||
border: 0
|
||||
padding: 10px 12px 10px 36px
|
||||
font-size: 14px
|
||||
border-radius: 2px
|
||||
color: #FFF
|
||||
width: 220px
|
||||
li
|
||||
margin-left: 30px
|
||||
|
||||
ul
|
||||
display: inline-block
|
||||
a
|
||||
color: #E0808D
|
||||
font-weight: bold
|
||||
|
||||
li
|
||||
margin-left: 30px
|
||||
&.active, &:hover
|
||||
color: #FFF
|
||||
|
||||
.right
|
||||
float: right
|
||||
margin-right: 20px
|
||||
line-height: 64px
|
||||
color: #E0808D
|
||||
|
||||
a
|
||||
color: #E0808D
|
||||
font-weight: bold
|
||||
|
||||
&.active, &:hover
|
||||
color: #FFF
|
||||
|
||||
.right
|
||||
float: right
|
||||
margin-right: 20px
|
||||
line-height: 64px
|
||||
color: #E0808D
|
||||
|
||||
a
|
||||
border-radius: 2px
|
||||
background: $highlight_color
|
||||
color: $strong_color
|
||||
padding: 11px 20px
|
||||
border-radius: 2px
|
||||
background: $highlight_color
|
||||
color: $strong_color
|
||||
padding: 11px 20px
|
||||
|
||||
#title
|
||||
border-bottom: 3px solid #F3F4F8
|
||||
border-bottom: 3px solid #F3F4F8
|
||||
|
||||
.title
|
||||
border-bottom: 3px solid #AAAFBF
|
||||
margin: 30px 0 -3px 0
|
||||
display: inline-block
|
||||
padding-bottom: 20px
|
||||
.title
|
||||
border-bottom: 3px solid #AAAFBF
|
||||
margin: 30px 0 -3px 0
|
||||
display: inline-block
|
||||
padding-bottom: 20px
|
||||
|
||||
.sub
|
||||
font-size: 16px
|
||||
font-weight: bold
|
||||
margin: 0 20px
|
||||
.sub
|
||||
font-size: 16px
|
||||
font-weight: bold
|
||||
margin: 0 20px
|
||||
|
||||
.description
|
||||
display: inline-block
|
||||
font-family: $serif_font
|
||||
white-space: nowrap
|
||||
color: #B1B6C4
|
||||
.description
|
||||
display: inline-block
|
||||
font-family: $serif_font
|
||||
white-space: nowrap
|
||||
color: #B1B6C4
|
||||
|
||||
#content
|
||||
padding: 20px
|
||||
margin-top: 20px
|
||||
padding: 20px
|
||||
margin-top: 20px
|
||||
|
||||
#hero
|
||||
background: url('../img/hires/2.jpg') center
|
||||
background-size: cover
|
||||
border-radius: 2px
|
||||
margin-bottom: 30px
|
||||
#hero
|
||||
background: url('../img/hires/2.jpg') center
|
||||
background-size: cover
|
||||
border-radius: 2px
|
||||
margin-bottom: 30px
|
||||
|
||||
.content
|
||||
border-radius: 2px
|
||||
.content
|
||||
border-radius: 2px
|
||||
color: #FFF
|
||||
padding: 30px
|
||||
background: rgba(#000, 30%)
|
||||
box-shadow: inset 0 1px 2px rgba(#000, 20%)
|
||||
|
||||
h2
|
||||
margin-bottom: 20px
|
||||
margin-left: 140px
|
||||
|
||||
p
|
||||
font-family: $serif_font
|
||||
font-size: 18px
|
||||
line-height: 24px
|
||||
margin-left: 140px
|
||||
text-align: justify
|
||||
text-justify: inter-word
|
||||
|
||||
.address
|
||||
font-size: 120px
|
||||
float: left
|
||||
|
||||
.cta
|
||||
text-align: center
|
||||
margin-top: 10px
|
||||
|
||||
a
|
||||
font-family: $serif_font
|
||||
padding: 11px 20px
|
||||
border-radius: 2px
|
||||
display: inline-block
|
||||
margin: 0 4px
|
||||
|
||||
&.primary
|
||||
font-weight: bold
|
||||
background: $strong_color
|
||||
color: #FFF
|
||||
padding: 30px
|
||||
background: rgba(#000, 30%)
|
||||
box-shadow: inset 0 1px 2px rgba(#000, 20%)
|
||||
|
||||
&.secondary
|
||||
background: #FFF
|
||||
color: $strong_color
|
||||
|
||||
h2
|
||||
margin-bottom: 20px
|
||||
margin-left: 140px
|
||||
#add
|
||||
h2
|
||||
color: #3E4457
|
||||
|
||||
p
|
||||
font-family: $serif_font
|
||||
font-size: 18px
|
||||
line-height: 24px
|
||||
margin-left: 140px
|
||||
text-align: justify
|
||||
text-justify: inter-word
|
||||
p
|
||||
font-family: $serif_font
|
||||
color: #B1B6C4
|
||||
margin-top: 10px
|
||||
line-height: 20px
|
||||
text-align: justify
|
||||
text-justify: inter-word
|
||||
|
||||
.address
|
||||
font-size: 120px
|
||||
float: left
|
||||
a
|
||||
color: #3E4457
|
||||
|
||||
.cta
|
||||
text-align: center
|
||||
margin-top: 10px
|
||||
.form
|
||||
margin-top: 20px
|
||||
|
||||
table
|
||||
width: 100%
|
||||
|
||||
a
|
||||
font-family: $serif_font
|
||||
padding: 11px 20px
|
||||
border-radius: 2px
|
||||
display: inline-block
|
||||
margin: 0 4px
|
||||
tr
|
||||
td
|
||||
&:first-child
|
||||
width: 100%
|
||||
|
||||
&.primary
|
||||
font-weight: bold
|
||||
background: $strong_color
|
||||
color: #FFF
|
||||
|
||||
&.secondary
|
||||
background: #FFF
|
||||
color: $strong_color
|
||||
input
|
||||
box-sizing: border-box
|
||||
padding: 10px
|
||||
width: 100%
|
||||
border-radius: 2px 0 0 2px
|
||||
border: 1px solid #DDE1ED
|
||||
border-right: 0
|
||||
box-shadow: inset 0 1px 2px rgba(#000, 20%)
|
||||
|
||||
#add
|
||||
h2
|
||||
color: #3E4457
|
||||
a
|
||||
margin-left: -2px
|
||||
font-family: $serif_font
|
||||
padding: 11px 20px
|
||||
border-radius: 0 2px 2px 0
|
||||
display: inline-block
|
||||
font-weight: bold
|
||||
background: $strong_color
|
||||
color: #FFF
|
||||
|
||||
p
|
||||
font-family: $serif_font
|
||||
color: #B1B6C4
|
||||
margin-top: 10px
|
||||
line-height: 20px
|
||||
text-align: justify
|
||||
text-justify: inter-word
|
||||
#projects
|
||||
border: 1px solid #CDCECF
|
||||
border-radius: 2px
|
||||
|
||||
a
|
||||
color: #3E4457
|
||||
h2
|
||||
color: #3E4457
|
||||
display: inline-block
|
||||
|
||||
.form
|
||||
margin-top: 20px
|
||||
|
||||
table
|
||||
width: 100%
|
||||
.sort
|
||||
float: right
|
||||
line-height: 30px
|
||||
|
||||
tr
|
||||
td
|
||||
&:first-child
|
||||
width: 100%
|
||||
table
|
||||
width: 100%
|
||||
|
||||
input
|
||||
box-sizing: border-box
|
||||
padding: 10px
|
||||
width: 100%
|
||||
border-radius: 2px 0 0 2px
|
||||
border: 1px solid #DDE1ED
|
||||
border-right: 0
|
||||
tr
|
||||
td
|
||||
background: #FCFCFC
|
||||
padding: 20px 30px
|
||||
border-bottom: 1px solid #EAECF2
|
||||
|
||||
.milestone
|
||||
.icon
|
||||
font-size: 10px
|
||||
margin: 0
|
||||
|
||||
.progress
|
||||
width: 200px
|
||||
|
||||
.percent, .due
|
||||
color: darken(#C1C4D0, 20%)
|
||||
font-size: 13px
|
||||
|
||||
.percent
|
||||
float: right
|
||||
|
||||
.bar
|
||||
border-radius: 4px
|
||||
background: #EAECF2
|
||||
height: 10px
|
||||
width: 100%
|
||||
|
||||
&.inner
|
||||
box-shadow: inset 0 1px 2px rgba(#000, 20%)
|
||||
|
||||
a
|
||||
margin-left: -2px
|
||||
font-family: $serif_font
|
||||
padding: 11px 20px
|
||||
border-radius: 0 2px 2px 0
|
||||
display: inline-block
|
||||
font-weight: bold
|
||||
&.red
|
||||
background: $strong_color
|
||||
color: #FFF
|
||||
|
||||
#projects
|
||||
border: 1px solid #CDCECF
|
||||
border-radius: 2px
|
||||
&.green
|
||||
background: #00B361
|
||||
|
||||
h2
|
||||
.due
|
||||
&.red
|
||||
color: $strong_color
|
||||
font-weight: bold
|
||||
|
||||
&:first-child
|
||||
color: #3E4457
|
||||
display: inline-block
|
||||
font-weight: bold
|
||||
|
||||
.sort
|
||||
float: right
|
||||
line-height: 30px
|
||||
&:nth-child(2)
|
||||
td
|
||||
background: lighten(#FCFCFC, 60%)
|
||||
|
||||
table
|
||||
width: 100%
|
||||
&:last-child
|
||||
td
|
||||
border: 0
|
||||
|
||||
tr
|
||||
td
|
||||
background: #FCFCFC
|
||||
padding: 20px 30px
|
||||
border-bottom: 1px solid #EAECF2
|
||||
&.done
|
||||
td
|
||||
background: #EBF6F1
|
||||
|
||||
.milestone
|
||||
.icon
|
||||
font-size: 10px
|
||||
margin: 0
|
||||
.milestone, .percent, .due
|
||||
color: #00B361
|
||||
|
||||
.progress
|
||||
width: 200px
|
||||
.header, .footer
|
||||
padding: 20px 30px
|
||||
|
||||
.percent, .due
|
||||
color: darken(#C1C4D0, 20%)
|
||||
font-size: 13px
|
||||
.header
|
||||
box-shadow: 0 1px 2px rgba(#DDE1ED, 50%)
|
||||
margin-bottom: 2px
|
||||
border-bottom: 1px solid #DDE1ED
|
||||
|
||||
.percent
|
||||
float: right
|
||||
a
|
||||
font-family: $serif_font
|
||||
|
||||
.bar
|
||||
border-radius: 4px
|
||||
background: #EAECF2
|
||||
height: 10px
|
||||
width: 100%
|
||||
.footer
|
||||
background: #F9FAFB
|
||||
color: #AAAFBF
|
||||
box-shadow: inset 0 1px 2px rgba(#DDE1ED, 20%)
|
||||
border-top: 1px solid #DDE1ED
|
||||
text-align: right
|
||||
font-family: $serif_font
|
||||
|
||||
&.inner
|
||||
box-shadow: inset 0 1px 2px rgba(#000, 20%)
|
||||
|
||||
&.red
|
||||
background: $strong_color
|
||||
|
||||
&.green
|
||||
background: #00B361
|
||||
|
||||
.due
|
||||
&.red
|
||||
color: $strong_color
|
||||
font-weight: bold
|
||||
|
||||
&:first-child
|
||||
color: #3E4457
|
||||
font-weight: bold
|
||||
|
||||
&:nth-child(2)
|
||||
td
|
||||
background: lighten(#FCFCFC, 60%)
|
||||
|
||||
&:last-child
|
||||
td
|
||||
border: 0
|
||||
|
||||
&.done
|
||||
td
|
||||
background: #EBF6F1
|
||||
|
||||
.milestone, .percent, .due
|
||||
color: #00B361
|
||||
|
||||
.header, .footer
|
||||
padding: 20px 30px
|
||||
|
||||
.header
|
||||
box-shadow: 0 1px 2px rgba(#DDE1ED, 50%)
|
||||
margin-bottom: 2px
|
||||
border-bottom: 1px solid #DDE1ED
|
||||
|
||||
a
|
||||
font-family: $serif_font
|
||||
|
||||
.footer
|
||||
background: #F9FAFB
|
||||
color: #AAAFBF
|
||||
box-shadow: inset 0 1px 2px rgba(#DDE1ED, 20%)
|
||||
border-top: 1px solid #DDE1ED
|
||||
text-align: right
|
||||
font-family: $serif_font
|
||||
|
||||
.icon
|
||||
color: #AAAFBF
|
||||
.icon
|
||||
color: #AAAFBF
|
||||
|
||||
#footer
|
||||
border-top: 1px solid #F3F4F8
|
||||
text-align: center
|
||||
padding: 30px
|
||||
margin-top: 30px
|
||||
font-family: $serif_font
|
||||
border-top: 1px solid #F3F4F8
|
||||
text-align: center
|
||||
padding: 30px
|
||||
margin-top: 30px
|
||||
font-family: $serif_font
|
|
@ -1,95 +1,95 @@
|
|||
@import 'nib'
|
||||
|
||||
// color definitions
|
||||
$closed = #4DAF7C
|
||||
$opened = #E55F3A
|
||||
$grey = #CACACA
|
||||
$brown = #64584C
|
||||
$closed = #4DAF7C
|
||||
$opened = #E55F3A
|
||||
$grey = #CACACA
|
||||
$brown = #64584C
|
||||
$background1 = #D7BCAB
|
||||
$background2 = #CC9485
|
||||
|
||||
// where D3 renders to
|
||||
#chart
|
||||
height: 300px
|
||||
position: relative
|
||||
height: 300px
|
||||
position: relative
|
||||
|
||||
// position will be adjusted dynamically
|
||||
#tooltip
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
// position will be adjusted dynamically
|
||||
#tooltip
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
|
||||
svg
|
||||
path
|
||||
&.line
|
||||
fill: none
|
||||
stroke-width: 1px
|
||||
clip-path: url(#clip)
|
||||
svg
|
||||
path
|
||||
&.line
|
||||
fill: none
|
||||
stroke-width: 1px
|
||||
clip-path: url(#clip)
|
||||
|
||||
// actual progress
|
||||
&.actual
|
||||
stroke: $brown
|
||||
stroke-width: 3px
|
||||
// actual progress
|
||||
&.actual
|
||||
stroke: $brown
|
||||
stroke-width: 3px
|
||||
|
||||
// ideal velocity throughout the sprint
|
||||
&.ideal
|
||||
stroke: $grey
|
||||
stroke-width: 3px
|
||||
// ideal velocity throughout the sprint
|
||||
&.ideal
|
||||
stroke: $grey
|
||||
stroke-width: 3px
|
||||
|
||||
// trend of actual issue closures
|
||||
&.trendline
|
||||
stroke: $brown
|
||||
stroke-width: 1.5px
|
||||
stroke-dasharray: 5,5
|
||||
// trend of actual issue closures
|
||||
&.trendline
|
||||
stroke: $brown
|
||||
stroke-width: 1.5px
|
||||
stroke-dasharray: 5,5
|
||||
|
||||
// right now
|
||||
line
|
||||
&.today
|
||||
stroke: $grey
|
||||
stroke-width: 1px
|
||||
shape-rendering: crispEdges
|
||||
stroke-dasharray: 5,5
|
||||
// right now
|
||||
line
|
||||
&.today
|
||||
stroke: $grey
|
||||
stroke-width: 1px
|
||||
shape-rendering: crispEdges
|
||||
stroke-dasharray: 5,5
|
||||
|
||||
// represents one issue closed
|
||||
circle
|
||||
fill: $brown
|
||||
// make it easier to click
|
||||
stroke: transparent
|
||||
stroke-width: 15px
|
||||
cursor: pointer
|
||||
// represents one issue closed
|
||||
circle
|
||||
fill: $brown
|
||||
// make it easier to click
|
||||
stroke: transparent
|
||||
stroke-width: 15px
|
||||
cursor: pointer
|
||||
|
||||
// axes...
|
||||
.axis
|
||||
shape-rendering: crispEdges
|
||||
|
||||
line
|
||||
stroke: rgba($grey, 0.25)
|
||||
shape-rendering: crispEdges
|
||||
// axes...
|
||||
.axis
|
||||
shape-rendering: crispEdges
|
||||
|
||||
line
|
||||
stroke: rgba($grey, 0.25)
|
||||
shape-rendering: crispEdges
|
||||
|
||||
text
|
||||
font-weight: bold
|
||||
fill: $grey
|
||||
text
|
||||
font-weight: bold
|
||||
fill: $grey
|
||||
|
||||
path
|
||||
display: none
|
||||
path
|
||||
display: none
|
||||
|
||||
// tooltips
|
||||
.d3-tip
|
||||
margin-top: -10px
|
||||
font-size: 11px
|
||||
padding: 8px 10px 7px 10px
|
||||
text-align: center
|
||||
background: rgba(0,0,0,0.75)
|
||||
color: #fff
|
||||
border-radius: 3px
|
||||
margin-top: -10px
|
||||
font-size: 11px
|
||||
padding: 8px 10px 7px 10px
|
||||
text-align: center
|
||||
background: rgba(0,0,0,0.75)
|
||||
color: #fff
|
||||
border-radius: 3px
|
||||
|
||||
&:after
|
||||
width: 100%
|
||||
color: rgba(0,0,0,0.8)
|
||||
content: "\25BC"
|
||||
position: absolute
|
||||
&:after
|
||||
width: 100%
|
||||
color: rgba(0,0,0,0.8)
|
||||
content: "\25BC"
|
||||
position: absolute
|
||||
|
||||
&.n:after
|
||||
margin: -3px 0 0 0
|
||||
top: 100%
|
||||
left: 0
|
||||
&.n:after
|
||||
margin: -3px 0 0 0
|
||||
top: 100%
|
||||
left: 0
|
|
@ -1,32 +1,32 @@
|
|||
@font-face {
|
||||
font-family: 'MuseoSlab500Regular';
|
||||
src: url('../fonts/museo-slab-500.eot');
|
||||
src: url('../fonts/museo-slab-500.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/museo-slab-500.woff') format('woff'),
|
||||
url('../fonts/museo-slab-500.ttf') format('truetype'),
|
||||
url('../fonts/museo-slab-500.svg#MuseoSlab500Regular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: 'MuseoSlab500Regular';
|
||||
src: url('../fonts/museo-slab-500.eot');
|
||||
src: url('../fonts/museo-slab-500.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/museo-slab-500.woff') format('woff'),
|
||||
url('../fonts/museo-slab-500.ttf') format('truetype'),
|
||||
url('../fonts/museo-slab-500.svg#MuseoSlab500Regular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'MuseoSans500Regular';
|
||||
src: url('../fonts/museo-sans-500.eot');
|
||||
src: url('../fonts/museo-sans-500.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/museo-sans-500.woff') format('woff'),
|
||||
url('../fonts/museo-sans-500.ttf') format('truetype'),
|
||||
url('../fonts/museo-sans-500.svg#MuseoSans500Regular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: 'MuseoSans500Regular';
|
||||
src: url('../fonts/museo-sans-500.eot');
|
||||
src: url('../fonts/museo-sans-500.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/museo-sans-500.woff') format('woff'),
|
||||
url('../fonts/museo-sans-500.ttf') format('truetype'),
|
||||
url('../fonts/museo-sans-500.svg#MuseoSans500Regular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Fontello';
|
||||
src: url('../fonts/fontello.eot?74672344');
|
||||
src: url('../fonts/fontello.eot?74672344#iefix') format('embedded-opentype'),
|
||||
url('../fonts/fontello.woff?74672344') format('woff'),
|
||||
url('../fonts/fontello.ttf?74672344') format('truetype'),
|
||||
url('../fonts/fontello.svg?74672344#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: 'Fontello';
|
||||
src: url('../fonts/fontello.eot?74672344');
|
||||
src: url('../fonts/fontello.eot?74672344#iefix') format('embedded-opentype'),
|
||||
url('../fonts/fontello.woff?74672344') format('woff'),
|
||||
url('../fonts/fontello.ttf?74672344') format('truetype'),
|
||||
url('../fonts/fontello.svg?74672344#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
|
@ -1,72 +1,35 @@
|
|||
@import 'nib'
|
||||
|
||||
.icon
|
||||
vertical-align: middle
|
||||
vertical-align: middle
|
||||
font-family: "Fontello"
|
||||
font-style: normal
|
||||
font-weight: normal
|
||||
speak: none
|
||||
|
||||
&:before
|
||||
font-family: "Fontello"
|
||||
font-style: normal
|
||||
font-weight: normal
|
||||
speak: none
|
||||
display: inline-block
|
||||
text-decoration: inherit
|
||||
text-align: center
|
||||
|
||||
display: inline-block
|
||||
text-decoration: inherit
|
||||
text-align: center
|
||||
/* For safety - reset parent styles, that can break glyph codes */
|
||||
font-variant: normal
|
||||
text-transform: none
|
||||
|
||||
/* For safety - reset parent styles, that can break glyph codes */
|
||||
font-variant: normal
|
||||
text-transform: none
|
||||
|
||||
/* fix buttons height, for twitter bootstrap */
|
||||
line-height: 1em
|
||||
&.spin6
|
||||
animation: spin 2s infinite linear
|
||||
|
||||
&.cog
|
||||
&:before
|
||||
content: '\e800'
|
||||
|
||||
&.search
|
||||
&:before
|
||||
content: '\e801'
|
||||
|
||||
&.github
|
||||
&:before
|
||||
content: '\e802'
|
||||
|
||||
&.address
|
||||
&:before
|
||||
content: '\e803'
|
||||
|
||||
&.plus-circled
|
||||
&:before
|
||||
content: '\e804'
|
||||
|
||||
&.fire-station
|
||||
&:before
|
||||
content: '\e805'
|
||||
|
||||
&.sort-alphabet
|
||||
&:before
|
||||
content: '\e806'
|
||||
|
||||
&.down-open
|
||||
&:before
|
||||
content: '\e807'
|
||||
|
||||
&.spin6
|
||||
animation: spin 2s infinite linear
|
||||
|
||||
&:before
|
||||
content: '\e808'
|
||||
&.spin4
|
||||
animation: spin 2s infinite linear
|
||||
|
||||
@-moz-keyframes spin {
|
||||
from { -moz-transform: rotate(0deg) }
|
||||
to { -moz-transform: rotate(360deg) }
|
||||
from { -moz-transform: rotate(0deg) }
|
||||
to { -moz-transform: rotate(360deg) }
|
||||
}
|
||||
@-webkit-keyframes spin {
|
||||
from { -webkit-transform: rotate(0deg) }
|
||||
to { -webkit-transform: rotate(360deg) }
|
||||
from { -webkit-transform: rotate(0deg) }
|
||||
to { -webkit-transform: rotate(360deg) }
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform:rotate(0deg) }
|
||||
to { transform:rotate(360deg) }
|
||||
from { transform:rotate(0deg) }
|
||||
to { transform:rotate(360deg) }
|
||||
}
|
|
@ -1,23 +1,26 @@
|
|||
<div id="head">
|
||||
<div class="right">
|
||||
{{#user.displayName}}
|
||||
{{user.displayName}} logged in
|
||||
{{else}}
|
||||
<a class="github" on-click="!login"><span class="icon github"></span> Sign In</a>
|
||||
{{/user.displayName}}
|
||||
</div>
|
||||
<div class="right">
|
||||
{{#user.displayName}}
|
||||
{{user.displayName}} logged in
|
||||
{{else}}
|
||||
<a class="github" on-click="!login"><Icons icon="github"/> Sign In</a>
|
||||
{{/user.displayName}}
|
||||
</div>
|
||||
|
||||
<h1><a href="#"><span class="icon fire-station"></span></a></h1>
|
||||
<a id="icon" href="#">
|
||||
<Icons icon="{{icon}}"/>
|
||||
</a>
|
||||
|
||||
<div class="q">
|
||||
<span class="icon search"></span>
|
||||
<span class="icon down-open"></span>
|
||||
<input type="text" placeholder="Jump to...">
|
||||
</div>
|
||||
<div class="q">
|
||||
<Icons icon="search"/>
|
||||
<Icons icon="down-open"/>
|
||||
<input type="text" placeholder="Jump to...">
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<li><a href="#project/add" class="add"><span class="icon plus-circled"></span> Add a Project</a></li>
|
||||
<li><a href="#" class="faq">FAQ</a></li>
|
||||
<li><a href="#reset">DB Reset</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="#project/add" class="add"><Icons icon="plus-circled"/> Add a Project</a></li>
|
||||
<li><a href="#" class="faq">FAQ</a></li>
|
||||
<li><a href="#reset">DB Reset</a></li>
|
||||
<li><a href="#notify">Notify</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,13 +1,13 @@
|
|||
{{^projects.list}}
|
||||
<div id="hero">
|
||||
<div class="content">
|
||||
<span class="icon address"></span>
|
||||
<h2>See your project progress</h2>
|
||||
<p>Not sure where to start? Just add a demo repo to see a chart. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>
|
||||
<div class="cta">
|
||||
<a href="#project/add" class="primary"><span class="icon plus-circled"></span> Add your project</a>
|
||||
<a href="#" class="secondary">Read the Guide</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hero">
|
||||
<div class="content">
|
||||
<Icons icon="address"/>
|
||||
<h2>See your project progress</h2>
|
||||
<p>Not sure where to start? Just add a demo repo to see a chart. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>
|
||||
<div class="cta">
|
||||
<a href="#project/add" class="primary"><Icons icon="plus-circled"/> Add your project</a>
|
||||
<a href="#" class="secondary">Read the Guide</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/projects.list}}
|
|
@ -0,0 +1,3 @@
|
|||
{{#code}}
|
||||
<span class="icon {{icon}}">{{{ '&#' + code + ';' }}}</span>
|
||||
{{/code}}
|
|
@ -1,11 +1,11 @@
|
|||
<Header/>
|
||||
|
||||
<div id="page">
|
||||
<!-- content loaded from a router -->
|
||||
<!-- content loaded from a router -->
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<div class="wrap">
|
||||
© 2012-2014 <a href="http://cloudfi.re">Cloudfire Systems</a>
|
||||
</div>
|
||||
<div class="wrap">
|
||||
© 2012-2014 <a href="http://cloudfi.re">Cloudfire Systems</a>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,2 @@
|
|||
<Icon icon="megaphone"/>
|
||||
<p>You have some interesting news in your inbox. Go <a href="#">check it out</a> now.</p>
|
|
@ -1,21 +1,21 @@
|
|||
<div id="content" class="wrap">
|
||||
<div id="add">
|
||||
<div class="header">
|
||||
<h2>Add a Project</h2>
|
||||
<p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href="#">Sign In</a> first.</p>
|
||||
</div>
|
||||
|
||||
<div class="form">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" placeholder="user/repo" autocomplete="off" value="{{value}}">
|
||||
</td>
|
||||
<td>
|
||||
<a on-click="submit">Add</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="add">
|
||||
<div class="header">
|
||||
<h2>Add a Project</h2>
|
||||
<p>Type in the name of the repository as you would normally. If you'd like to add a private GitHub project, <a href="#">Sign In</a> first.</p>
|
||||
</div>
|
||||
|
||||
<div class="form">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" placeholder="user/repo" autocomplete="off" value="{{value}}">
|
||||
</td>
|
||||
<td>
|
||||
<a on-click="submit">Add</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,4 +1,4 @@
|
|||
<div id="content" class="wrap">
|
||||
<Hero/>
|
||||
<Projects/>
|
||||
<Hero/>
|
||||
<Projects/>
|
||||
</div>
|
|
@ -1,13 +1,13 @@
|
|||
<div id="title">
|
||||
<div class="wrap">
|
||||
<h2 class="title">{{ format.title(milestone.title) }}</h2>
|
||||
<span class="sub">{{{ format.due(milestone.due_on) }}}</span>
|
||||
<p class="description">{{{ format.markdown(milestone.description) }}}</p>
|
||||
</div>
|
||||
<div class="wrap">
|
||||
<h2 class="title">{{ format.title(milestone.title) }}</h2>
|
||||
<span class="sub">{{{ format.due(milestone.due_on) }}}</span>
|
||||
<p class="description">{{{ format.markdown(milestone.description) }}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="content" class="wrap">
|
||||
<div id="chart">
|
||||
<div id="svg"></div>
|
||||
</div>
|
||||
<div id="chart">
|
||||
<div id="svg"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,89 +1,89 @@
|
|||
{{#projects.list.length}}
|
||||
<div id="projects">
|
||||
<div class="header">
|
||||
<a href="#" class="sort"><span class="icon sort-alphabet"></span> Sorted by priority</a>
|
||||
<h2>Projects</h2>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
{{#projects.list}}
|
||||
{{#milestones}}
|
||||
<tr>
|
||||
<td class="repo">{{owner}}/{{name}}</td>
|
||||
<td>
|
||||
<a class="milestone" href="#chart/{{owner}}/{{name}}/{{number}}">{{ title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>
|
||||
<span class="due">{{{ format.due(due_on) }}}</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar {{format.onTime(this)}}" style="width:{{format.progress(closed_issues, open_issues)}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/milestones}}
|
||||
{{/projects.list}}
|
||||
|
||||
<!--
|
||||
<tr>
|
||||
<td><a class="repo" href="#">radekstepan/disposable</a></td>
|
||||
<td><span class="milestone">Milestone 1.0 <span class="icon down-open"></span></td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">40%</span>
|
||||
<span class="due">due on Friday</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar red" style="width:40%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="done">
|
||||
<td><a class="repo" href="#">radekstepan/burnchart</a></td>
|
||||
<td><span class="milestone">Beta Milestone <span class="icon down-open"></span></a></td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">100%</span>
|
||||
<span class="due">due tomorrow</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar green" style="width:100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="repo" href="#">intermine/intermine</a></td>
|
||||
<td><span class="milestone">Emma Release 96 <span class="icon down-open"></span></a></td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">27%</span>
|
||||
<span class="due">due in 2 weeks</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar red" style="width:27%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="repo" href="#">microsoft/windows</a></td>
|
||||
<td><span class="milestone">RC 9 <span class="icon down-open"></span></a></td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">90%</span>
|
||||
<span class="due red">overdue by a month</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar red" style="width:90%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
-->
|
||||
</table>
|
||||
|
||||
<div class="footer">
|
||||
<a href="#"><span class="icon cog"></span> Edit</a>
|
||||
</div>
|
||||
<div id="projects">
|
||||
<div class="header">
|
||||
<a href="#" class="sort"><Icons icon="sort-alphabet"/> Sorted by priority</a>
|
||||
<h2>Projects</h2>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
{{#projects.list}}
|
||||
{{#milestones}}
|
||||
<tr>
|
||||
<td class="repo">{{owner}}/{{name}}</td>
|
||||
<td>
|
||||
<a class="milestone" href="#chart/{{owner}}/{{name}}/{{number}}">{{ title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>
|
||||
<span class="due">{{{ format.due(due_on) }}}</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar {{format.onTime(this)}}" style="width:{{format.progress(closed_issues, open_issues)}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/milestones}}
|
||||
{{/projects.list}}
|
||||
|
||||
<!--
|
||||
<tr>
|
||||
<td><a class="repo" href="#">radekstepan/disposable</a></td>
|
||||
<td><span class="milestone">Milestone 1.0 <span class="icon down-open"></span></td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">40%</span>
|
||||
<span class="due">due on Friday</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar red" style="width:40%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="done">
|
||||
<td><a class="repo" href="#">radekstepan/burnchart</a></td>
|
||||
<td><span class="milestone">Beta Milestone <span class="icon down-open"></span></a></td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">100%</span>
|
||||
<span class="due">due tomorrow</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar green" style="width:100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="repo" href="#">intermine/intermine</a></td>
|
||||
<td><span class="milestone">Emma Release 96 <span class="icon down-open"></span></a></td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">27%</span>
|
||||
<span class="due">due in 2 weeks</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar red" style="width:27%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="repo" href="#">microsoft/windows</a></td>
|
||||
<td><span class="milestone">RC 9 <span class="icon down-open"></span></a></td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<span class="percent">90%</span>
|
||||
<span class="due red">overdue by a month</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar red" style="width:90%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
-->
|
||||
</table>
|
||||
|
||||
<div class="footer">
|
||||
<a href="#"><Icons icon="cog"/> Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/projects.list}}
|
|
@ -1,2 +1,2 @@
|
|||
module.exports =
|
||||
now: -> new Date().toJSON()
|
||||
now: -> new Date().toJSON()
|
|
@ -2,46 +2,50 @@ config = require '../models/config'
|
|||
|
||||
module.exports =
|
||||
|
||||
# Progress in percentages.
|
||||
'progress': _.memoize (a, b) ->
|
||||
100 * (a / (b + a))
|
||||
# Progress in percentages.
|
||||
'progress': _.memoize (a, b) ->
|
||||
100 * (a / (b + a))
|
||||
|
||||
# Is a milestone on time?
|
||||
'onTime': _.memoize (milestone) ->
|
||||
# Milestones with no due date are always on track.
|
||||
return 'green' unless milestone.due_on
|
||||
# Is a milestone on time?
|
||||
'onTime': _.memoize (milestone) ->
|
||||
# Milestones with no due date are always on track.
|
||||
return 'green' unless milestone.due_on
|
||||
|
||||
# Progress in points.
|
||||
points = @progress milestone.closed_issues, milestone.open_issues
|
||||
# Progress in points.
|
||||
points = @progress milestone.closed_issues, milestone.open_issues
|
||||
|
||||
# Calculate the progress in days.
|
||||
a = +new Date milestone.created_at
|
||||
b = +new Date
|
||||
c = +new Date milestone.due_on
|
||||
# Calculate the progress in days.
|
||||
a = +new Date milestone.created_at
|
||||
b = +new Date
|
||||
c = +new Date milestone.due_on
|
||||
|
||||
# Progress in time.
|
||||
time = @progress b - a, c - b
|
||||
# Progress in time.
|
||||
time = @progress b - a, c - b
|
||||
|
||||
[ 'red', 'green' ][ +(points > time) ]
|
||||
, (m) -> # resolver
|
||||
[ m.created_at, m.number ].join '/'
|
||||
[ 'red', 'green' ][ +(points > time) ]
|
||||
, (m) -> # resolver
|
||||
[ m.created_at, m.number ].join '/'
|
||||
|
||||
# Time from now.
|
||||
'fromNow': _.memoize (jsonDate) ->
|
||||
moment(new Date(jsonDate)).fromNow()
|
||||
# Time from now.
|
||||
'fromNow': _.memoize (jsonDate) ->
|
||||
moment(new Date(jsonDate)).fromNow()
|
||||
|
||||
# When is a milestone due?
|
||||
'due': (jsonDate) ->
|
||||
return ' ' unless jsonDate
|
||||
[ 'due', @fromNow jsonDate ].join(' ')
|
||||
# When is a milestone due?
|
||||
'due': (jsonDate) ->
|
||||
return ' ' unless jsonDate
|
||||
[ 'due', @fromNow jsonDate ].join(' ')
|
||||
|
||||
# Markdown formatting.
|
||||
'markdown': (markup) ->
|
||||
marked markup
|
||||
# Markdown formatting.
|
||||
'markdown': (markup) ->
|
||||
marked markup
|
||||
|
||||
# Format milestone title.
|
||||
'title': (text) ->
|
||||
if text.toLowerCase().indexOf('milestone') > -1
|
||||
text
|
||||
else
|
||||
[ 'Milestone', text ].join(' ')
|
||||
# Format milestone title.
|
||||
'title': (text) ->
|
||||
if text.toLowerCase().indexOf('milestone') > -1
|
||||
text
|
||||
else
|
||||
[ 'Milestone', text ].join(' ')
|
||||
|
||||
# Hex to decimal.
|
||||
hexToDecimal: (hex) ->
|
||||
parseInt hex, 16
|
|
@ -1,8 +1,8 @@
|
|||
_.mixin
|
||||
'pluckMany': (source, keys) ->
|
||||
throw '`keys` needs to be an Array' unless _.isArray keys
|
||||
_.map source, (item) ->
|
||||
obj = {}
|
||||
_.each keys, (key) ->
|
||||
obj[key] = item[key]
|
||||
obj
|
||||
'pluckMany': (source, keys) ->
|
||||
throw '`keys` needs to be an Array' unless _.isArray keys
|
||||
_.map source, (item) ->
|
||||
obj = {}
|
||||
_.each keys, (key) ->
|
||||
obj[key] = item[key]
|
||||
obj
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = (opts) ->
|
||||
Model = Ractive.extend(opts)
|
||||
model = new Model()
|
||||
model.render()
|
||||
model
|
||||
Model = Ractive.extend(opts)
|
||||
model = new Model()
|
||||
model.render()
|
||||
model
|
|
@ -1,17 +1,28 @@
|
|||
firebase = require '../modules/firebase'
|
||||
mediator = require '../modules/mediator'
|
||||
user = require '../models/user'
|
||||
state = require '../models/state'
|
||||
Icons = require './icons'
|
||||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'template': require '../templates/header'
|
||||
'template': require '../templates/header'
|
||||
|
||||
init: ->
|
||||
# Login user.
|
||||
@on '!login', ->
|
||||
firebase.login (err) ->
|
||||
throw err if err
|
||||
'data':
|
||||
'user': user
|
||||
# Default app icon.
|
||||
'icon': 'fire-station'
|
||||
|
||||
'data': { user }
|
||||
init: ->
|
||||
# Login user.
|
||||
@on '!login', ->
|
||||
firebase.login (err) ->
|
||||
throw err if err
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
# Switch loading icon with app icon.
|
||||
state.observe 'loading', (val) =>
|
||||
@set 'icon', if val then 'spin4' else 'fire-station'
|
||||
|
||||
'components': { Icons }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
|
@ -1,10 +1,13 @@
|
|||
mediator = require '../modules/mediator'
|
||||
projects = require '../models/projects'
|
||||
Icons = require './icons'
|
||||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'template': require '../templates/hero'
|
||||
'template': require '../templates/hero'
|
||||
|
||||
'data': { projects }
|
||||
'data': { projects }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
'components': { Icons }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
|
@ -0,0 +1,28 @@
|
|||
utils = require '../utils/format'
|
||||
|
||||
# Fontello icon hex codes.
|
||||
codes =
|
||||
'cog': '\e800'
|
||||
'search': '\e801'
|
||||
'github': '\e802'
|
||||
'address': '\e803'
|
||||
'plus-circled': '\e804'
|
||||
'fire-station': '\e805'
|
||||
'sort-alphabet': '\e806'
|
||||
'down-open': '\e807'
|
||||
'spin6': '\e808'
|
||||
'megaphone': '\e809'
|
||||
'spin4': '\e80a'
|
||||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'template': require '../templates/icons'
|
||||
|
||||
'isolated': yes
|
||||
|
||||
init: ->
|
||||
@observe 'icon', (icon) ->
|
||||
if icon and hex = codes[icon]
|
||||
@set 'code', utils.hexToDecimal hex
|
||||
else
|
||||
@set 'code', null
|
|
@ -3,29 +3,29 @@ user = require '../../models/user'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'template': require '../../templates/pages/addProject'
|
||||
'template': require '../../templates/pages/addProject'
|
||||
|
||||
'data': { 'value': 'radekstepan/disposable', user }
|
||||
'data': { 'value': 'radekstepan/disposable', user }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
|
||||
init: ->
|
||||
document.title = 'Add a new project'
|
||||
init: ->
|
||||
document.title = 'Add a new project'
|
||||
|
||||
# TODO: autocomplete on our username if we are logged in or based
|
||||
# on repos we already have.
|
||||
autocomplete = (value) ->
|
||||
# TODO: autocomplete on our username if we are logged in or based
|
||||
# on repos we already have.
|
||||
autocomplete = (value) ->
|
||||
|
||||
@observe 'value', _.debounce(autocomplete, 200), { 'init': no }
|
||||
@observe 'value', _.debounce(autocomplete, 200), { 'init': no }
|
||||
|
||||
# TODO: focus on the input field
|
||||
# TODO: focus on the input field
|
||||
|
||||
# TODO: listen to Enter keypress.
|
||||
@on 'submit', ->
|
||||
[ owner, name ] = @get('value').split('/')
|
||||
# TODO: listen to Enter keypress.
|
||||
@on 'submit', ->
|
||||
[ owner, name ] = @get('value').split('/')
|
||||
|
||||
# TODO: save repo & persist.
|
||||
mediator.fire '!projects/add', { owner, name }, ->
|
||||
# Redirect to the dashboard.
|
||||
# TODO: trigger a named route
|
||||
window.location.hash = '#'
|
||||
# TODO: save repo & persist.
|
||||
mediator.fire '!projects/add', { owner, name }, ->
|
||||
# Redirect to the dashboard.
|
||||
# TODO: trigger a named route
|
||||
window.location.hash = '#'
|
|
@ -4,11 +4,11 @@ format = require '../../utils/format'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'template': require '../../templates/pages/index'
|
||||
'template': require '../../templates/pages/index'
|
||||
|
||||
'components': { Hero, Projects }
|
||||
'components': { Hero, Projects }
|
||||
|
||||
'data': { format }
|
||||
'data': { format }
|
||||
|
||||
init: ->
|
||||
document.title = 'BurnChart: GitHub Burndown Chart as a Service'
|
||||
init: ->
|
||||
document.title = 'BurnChart: GitHub Burndown Chart as a Service'
|
|
@ -4,24 +4,24 @@ format = require '../../utils/format'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'template': require '../../templates/pages/showChart'
|
||||
'template': require '../../templates/pages/showChart'
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
|
||||
'data': { format }
|
||||
'data': { format }
|
||||
|
||||
init: ->
|
||||
route = @get 'route'
|
||||
|
||||
document.title = "#{route.owner}/#{route.name}"
|
||||
init: ->
|
||||
route = @get 'route'
|
||||
|
||||
document.title = "#{route.owner}/#{route.name}"
|
||||
|
||||
milestone.get route, (err, warn, obj) =>
|
||||
throw err if err
|
||||
throw warn if warn
|
||||
# Save the milestone on the route.
|
||||
@set 'milestone', obj
|
||||
route.milestone = obj
|
||||
milestone.get route, (err, warn, obj) =>
|
||||
throw err if err
|
||||
throw warn if warn
|
||||
# Save the milestone on the route.
|
||||
@set 'milestone', obj
|
||||
route.milestone = obj
|
||||
|
||||
project route, (err) ->
|
||||
throw err if err
|
||||
console.log 'Done'
|
||||
project route, (err) ->
|
||||
throw err if err
|
||||
console.log 'Done'
|
|
@ -1,10 +1,13 @@
|
|||
mediator = require '../modules/mediator'
|
||||
projects = require '../models/projects'
|
||||
Icons = require './icons'
|
||||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'template': require '../templates/projects'
|
||||
'template': require '../templates/projects'
|
||||
|
||||
'data': { projects }
|
||||
'data': { projects }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
'components': { Icons }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
Loading…
Reference in New Issue