108 | );
109 | },
110 |
111 | handleCancel: function(e) {
112 | if (this.props.onCancel)
113 | this.props.onCancel(e);
114 | e.preventDefault();
115 | this.close();
116 | },
117 |
118 | handleConfirm: function(e) {
119 | if (this.props.onConfirm)
120 | this.props.onConfirm(e);
121 | e.preventDefault();
122 | }
123 | });
124 |
125 | return BootstrapModal;
126 | });
127 |
--------------------------------------------------------------------------------
/src/js/components/header.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @jsx React.DOM
3 | */
4 | /*jshint quotmark:false */
5 | /*jshint white:false */
6 | /*jshint trailing:false */
7 | /*jshint newcap:false */
8 | /*global React, Router*/
9 |
10 | /*
11 | Copyright (c) 2015 - Andreas Dewes
12 |
13 | This file is part of Gitboard.
14 |
15 | Gitboard is free software: you can redistribute it and/or modify
16 | it under the terms of the GNU Affero General Public License as
17 | published by the Free Software Foundation, either version 3 of the
18 | License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU Affero General Public License for more details.
24 |
25 | You should have received a copy of the GNU Affero General Public License
26 | along with this program. If not, see .
27 | */
28 |
29 | define(["react","js/utils",
30 | "js/request_notifier",
31 | "js/components/generic/flash_messages",
32 | ],
33 | function (React,
34 | Utils,
35 | RequestNotifier,
36 | FlashMessages
37 | ){
38 | 'use'+' strict';
39 |
40 | var RequestIndicator = React.createClass({
41 |
42 | displayName: 'RequestIndicator',
43 |
44 | getInitialState : function(){
45 | return {hidden : false};
46 | },
47 |
48 | hideMessage : function(){
49 | this.setState({hidden : true});
50 | return false;
51 | },
52 |
53 | componentWillReceiveProps : function(props){
54 | if (props.activeRequestCount !== undefined && props.activeRequestCount == 0)
55 | this.setState({hidden : false});
56 | },
57 |
58 | render : function(){
59 | if (this.props.connectionError == true)
60 | return
;
109 | }
110 | });
111 |
112 | return Menu;
113 | });
114 |
--------------------------------------------------------------------------------
/src/js/components/milestones.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @jsx React.DOM
3 | */
4 | /*jshint quotmark:false */
5 | /*jshint white:false */
6 | /*jshint trailing:false */
7 | /*jshint newcap:false */
8 | /*global React, Router*/
9 |
10 | /*
11 | Copyright (c) 2015 - Andreas Dewes
12 |
13 | This file is part of Gitboard.
14 |
15 | Gitboard is free software: you can redistribute it and/or modify
16 | it under the terms of the GNU Affero General Public License as
17 | published by the Free Software Foundation, either version 3 of the
18 | License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU Affero General Public License for more details.
24 |
25 | You should have received a copy of the GNU Affero General Public License
26 | along with this program. If not, see .
27 | */
28 |
29 | define(["react",
30 | "js/utils",
31 | "js/components/mixins/loader",
32 | "js/components/mixins/github_error_handler",
33 | "jquery",
34 | "moment"
35 | ],
36 | function (React,Utils,LoaderMixin,GithubErrorHandlerMixin,$,Moment) {
37 | 'use'+' strict';
38 |
39 |
40 | var MilestoneItem = React.createClass({
41 |
42 | render : function(){
43 | var due;
44 | if (this.props.milestone.due_on !== null){
45 | var datestring = Moment(new Date(this.props.milestone.due_on)).fromNow();
46 | due = [,' ',datestring];
47 | }
48 | return
292 | Sorry for the inconvenience. Please try reloading this page. If the problem persists
293 | please contact us, we will do our best to fix this issue for you.
294 |
295 |
;
98 | }
99 | });
100 |
101 | return Organizations;
102 | });
103 |
--------------------------------------------------------------------------------
/src/js/components/repositories.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @jsx React.DOM
3 | */
4 | /*jshint quotmark:false */
5 | /*jshint white:false */
6 | /*jshint trailing:false */
7 | /*jshint newcap:false */
8 | /*global React, Router*/
9 |
10 | /*
11 | Copyright (c) 2015 - Andreas Dewes
12 |
13 | This file is part of Gitboard.
14 |
15 | Gitboard is free software: you can redistribute it and/or modify
16 | it under the terms of the GNU Affero General Public License as
17 | published by the Free Software Foundation, either version 3 of the
18 | License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU Affero General Public License for more details.
24 |
25 | You should have received a copy of the GNU Affero General Public License
26 | along with this program. If not, see .
27 | */
28 |
29 | define(["react",
30 | "js/utils",
31 | "js/components/mixins/loader",
32 | "js/components/mixins/github_error_handler",
33 | "jquery"
34 | ],
35 | function (React,Utils,LoaderMixin,GithubErrorHandlerMixin,$) {
36 | 'use'+' strict';
37 |
38 | var RepositoryItem = React.createClass({
39 |
40 | render : function(){
41 | return
]
119 |
120 | var data = this.state.data;
121 | var title;
122 | if (data.organization)
123 | title = 'Repositories - '+(data.organization.name || data.organization.login);
124 | else
125 | title = 'Your repositories';
126 |
127 | return
128 |
129 |
130 |
{title}
131 |
132 |
133 |
134 | {repositoryItems}
135 |
136 |
;
137 | }
138 | });
139 |
140 | return Repositories;
141 | });
142 |
--------------------------------------------------------------------------------
/src/js/components/user/logout.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @jsx React.DOM
3 | */
4 | /*jshint quotmark:false */
5 | /*jshint white:false */
6 | /*jshint trailing:false */
7 | /*jshint newcap:false */
8 | /*global React, Router*/
9 |
10 | /*
11 | Copyright (c) 2015 - Andreas Dewes
12 |
13 | This file is part of Gitboard.
14 |
15 | Gitboard is free software: you can redistribute it and/or modify
16 | it under the terms of the GNU Affero General Public License as
17 | published by the Free Software Foundation, either version 3 of the
18 | License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU Affero General Public License for more details.
24 |
25 | You should have received a copy of the GNU Affero General Public License
26 | along with this program. If not, see .
27 | */
28 |
29 | define(["js/settings",
30 | "js/utils",
31 | "js/flash_messages",
32 | "react"],function (
33 | settings,
34 | Utils,
35 | FlashMessagesService,
36 | React
37 | ) {
38 | 'use strict';
39 |
40 | var Logout = React.createClass({
41 |
42 | displayName: "LogoutComponent",
43 |
44 |
45 | componentWillMount : function(){
46 | Utils.logout();
47 | },
48 |
49 | render: function () {
50 |
51 | var statusMessage;
52 |
53 | return
54 |
55 |
56 |
57 |
58 |
59 |
60 |
You have been logged out
61 |
62 |
63 |
Security notice
64 |
65 |
66 |
We cannot delete your authorization from Github without your username and password. If you want to delete it, you can do so manually here (look for the gitboard token)
67 |
68 |
69 |
70 |
71 |
72 |
73 | },
74 |
75 | });
76 |
77 | return Logout;
78 |
79 | });
80 |
--------------------------------------------------------------------------------
/src/js/config.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 | var require ={
20 | paths: {
21 | "text" : "assets/js/text",
22 | "jquery" : "bower_components/jquery/dist/jquery",
23 | "bootstrap" : "bower_components/bootstrap/dist/js/bootstrap",
24 | "moment" : "bower_components/momentjs/moment",
25 | "director" : "bower_components/director/build/director",
26 | "react": "bower_components/react/react",
27 | "sprintf" :"bower_components/sprintf/src/sprintf",
28 | "markdown":"bower_components/markdown/lib/markdown",
29 | "marked" : "bower_components/marked/lib/marked",
30 | },
31 | shim : {
32 | "director" : {
33 | exports : 'Router'
34 | },
35 | "bootstrap" : {
36 | deps : ['jquery']
37 | },
38 | "prism" : {
39 | exports : 'Prism'
40 | },
41 | "marked" : {
42 | exports : 'marked',
43 | },
44 | },
45 | baseUrl : "static",
46 | urlArgs: "bust=" + (new Date()).getTime()
47 | };
48 |
--------------------------------------------------------------------------------
/src/js/flash_messages.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 | define(["js/utils","js/subject"],function (Utils,Subject) {
20 | 'use strict';
21 |
22 | var FlashMessages = function(){
23 | Subject.Subject.call(this);
24 | this.currentMessages = {};
25 | this.messageStream = [];
26 | this.messageCount = 0;
27 | };
28 |
29 | var instance;
30 |
31 | function getInstance()
32 | {
33 | if (instance === undefined)
34 | instance = new FlashMessages();
35 | return instance;
36 | }
37 |
38 | FlashMessages.prototype = new Subject.Subject();
39 | FlashMessages.prototype.constructor = FlashMessages;
40 |
41 | FlashMessages.prototype.postMessage = function(data){
42 | var messageId = this.messageCount;
43 | this.messageCount++;
44 | var messageData = {id: messageId,data : data,receivedAt : new Date()};
45 | if (messageData.duration === undefined)
46 | messageData.duration = 2500;
47 | this.currentMessages[messageId] = messageData;
48 | this.messageStream.push(messageId);
49 | this.notify("newMessage",messageData);
50 | return messageId;
51 | }
52 |
53 | return getInstance();
54 |
55 | });
56 |
--------------------------------------------------------------------------------
/src/js/helpers/issue_manager.js:
--------------------------------------------------------------------------------
1 | define(["js/utils","js/api/all","js/flash_messages"],function (Utils,Apis,FlashMessagesService) {
2 | 'use strict';
3 |
4 | var IssueManager = function(params){
5 | this.params = params;
6 | };
7 |
8 | IssueManager.prototype.hasLabel = function(issue,label){
9 | for (var i in issue.labels){
10 | var issueLabel = issue.labels[i];
11 | if (label.toLowerCase() == issueLabel.name.toLowerCase())
12 | return true;
13 | }
14 | return false;
15 | }
16 |
17 | IssueManager.prototype._setStateImmediately = function(issue,state){
18 |
19 | if (issue.state != state)
20 | issue.state = state;
21 |
22 | };
23 |
24 | IssueManager.prototype._setState = function(issue,state,onSuccess,onError){
25 |
26 | Apis.issue.updateIssue(this.params.repositoryId,issue.number,{state : state},onSuccess,onError);
27 | };
28 |
29 | var categoryLabels = ['doing','to-do','awaiting-review','done'];
30 |
31 | IssueManager.prototype._setLabelsImmediately = function(issue,labels){
32 |
33 | var labelsToRemove = issue.labels.filter(function(label){
34 | return Object.keys(labels).indexOf(label.name) !== -1 && labels[label.name] == false;
35 | })
36 | .map(function(label){return label.name;});
37 |
38 | issue.labels = issue.labels.filter(function(label){
39 | return (labelsToRemove.indexOf(label.name) == -1) ? true: false
40 | });
41 |
42 | var issueLabelNames= issue.labels.map(function(label){return label.name});
43 |
44 | var labelsToAdd = Object.keys(labels).filter(function(label){
45 | if (!labels[label])
46 | return false;
47 | if (issueLabelNames.indexOf(label) == -1)
48 | return true;
49 | return false;});
50 |
51 | for(var i in labelsToAdd){
52 | var labelToAdd = labelsToAdd[i];
53 | issue.labels.push(this.params.labelsByName[labelToAdd] || {name : labelToAdd});
54 | }
55 |
56 | return {remove : labelsToRemove,add : labelsToAdd};
57 |
58 | }
59 |
60 | IssueManager.prototype._setLabels = function(issue,labelsToRemove,labelsToAdd,onSuccess,onError){
61 |
62 | var removeCallback = onSuccess;
63 |
64 | if (labelsToRemove.length)
65 | for(var i in labelsToRemove){
66 | removeCallback = function(oldCallback){
67 | Apis.label.removeLabel(this.params.repositoryId,issue.number,labelsToRemove[i],oldCallback,onError);
68 | }.bind(this,removeCallback);
69 | }
70 | if (labelsToAdd.length)
71 | Apis.label.addLabels(this.params.repositoryId,issue.number,labelsToAdd,removeCallback,onError);
72 | else if (removeCallback)
73 | removeCallback();
74 |
75 | }
76 |
77 | IssueManager.prototype.getMinutes = function(timeString){
78 | var re = /([\d]+)(m|h|d)/i;
79 | var res = re.exec(timeString);
80 | if (res){
81 | var number = parseInt(res[1]);
82 | switch(res[2]){
83 | case 'm':return number;
84 | case 'h':return number*60;
85 | case 'd':return number*60*8;
86 | }
87 | }
88 | return undefined;
89 | };
90 |
91 | IssueManager.prototype.formatMinutes = function(minutes){
92 | if (minutes < 60)
93 | return minutes+'m';
94 | else if (minutes < 8*60){
95 | var hours = Math.floor(minutes/60);
96 | var minutes = minutes % 60;
97 | var str = hours+'h';
98 | if (minutes)
99 | str+=' '+minutes+'m';
100 | return str;
101 | }
102 | var days = Math.floor(minutes/60/8);
103 | var hours = Math.floor((minutes%(60*8))/60);
104 | var minutes = minutes % 60;
105 | str = days+'d';
106 | if (hours)
107 | str+=' '+hours+'h';
108 | if (minutes)
109 | str+=' '+minutes+'m';
110 | return str;
111 | };
112 |
113 | IssueManager.prototype.getTime = function(issue,type){
114 | for (var i in issue.labels){
115 | var label = issue.labels[i];
116 | var re;
117 | if (type == 'estimate')
118 | re = /^time-estimate-([\d\w]+)$/i;
119 | else
120 | re = /^time-spent-([\d\w]+)$/i;
121 | var res = re.exec(label.name);
122 | if (res){
123 | return res[1];
124 | }
125 | }
126 | return null;
127 | };
128 |
129 | IssueManager.prototype.setTime = function(issue,time,type){
130 | var labels = {};
131 | for(var i in issue.labels){
132 | var label = issue.labels[i];
133 | if (type == 'estimate'){
134 | if (/^time-estimate-/i.exec(label.name))
135 | labels[label.name] = false;
136 | }else{
137 | if (/^time-spent-/i.exec(label.name))
138 | labels[label.name] = false;
139 | }
140 | }
141 | if (time){
142 | if (type == 'estimate')
143 | labels['time-estimate-'+time] = true;
144 | else
145 | labels['time-spent-'+time] = true;
146 | }
147 |
148 | var labelOps = this._setLabelsImmediately(issue,labels);
149 |
150 | if (this.params.onImmediateChange)
151 | this.params.onImmediateChange();
152 |
153 | this._setLabels(issue,labelOps.remove,labelOps.add,this.params.onResourceChange,this.params.onError);
154 | }
155 |
156 | IssueManager.prototype.assignTo = function(issue,collaborator){
157 | issue.assignee = collaborator;
158 | if (this.params.onImmediateChange)
159 | this.params.onImmediateChange();
160 | Apis.issue.updateIssue(this.params.repositoryId,
161 | issue.number,
162 | {assignee : collaborator ? collaborator.login : null},
163 | this.params.onResourceChange,
164 | this.params.onError);
165 | };
166 |
167 | IssueManager.prototype.setMilestone = function(issue,milestone){
168 | issue.milestone = milestone;
169 | if (this.params.onImmediateChange)
170 | this.params.onImmediateChange();
171 | Apis.issue.updateIssue(this.params.repositoryId,
172 | issue.number,
173 | {milestone : milestone ? milestone.number : null},
174 | this.params.onResourceChange,
175 | this.params.onError);
176 | };
177 |
178 | IssueManager.prototype.isMemberOf = function(issue,category){
179 | var closed = false;
180 | var labels = [];
181 | switch(category){
182 | case 'toDo':labels=['to-do'];break;
183 | case 'doing':labels=['doing'];break;
184 | case 'awaitingReview':labels=['awaiting-review'];break;
185 | case 'done':closed = true;break;
186 | default:return false;
187 | }
188 | if (labels.length){
189 | var found = false;
190 | for (var i in labels){
191 | var label = labels[i];
192 | if (this.hasLabel(issue,label))
193 | found = true;
194 | }
195 | if (!found)
196 | return false;
197 | }
198 | if ((closed && issue.state == 'open') || ((!closed) && issue.state == 'closed'))
199 | return false;
200 | return true;
201 | },
202 |
203 | IssueManager.prototype.moveTo = function(issue,category){
204 | var closed = false;
205 | var labels = {
206 | 'to-do' : false,
207 | 'doing' : false,
208 | 'awaiting-review' : false,
209 | 'done' : false,
210 | }
211 | var labelsToAdd = [];
212 | var labelsToRemove = [];
213 | switch(category){
214 | case 'toDo' : labels['to-do'] = true;break;
215 | case 'doing' : labels['doing'] = true;break;
216 | case 'awaitingReview' : labels['awaiting-review'] = true;break;
217 | case 'done' : closed = true;break;
218 | default: break;
219 | };
220 | this._setStateImmediately(issue,closed ? 'closed' : 'open');
221 | if (this.params.onImmediateChange)
222 | this.params.onImmediateChange();
223 | var labelOps = this._setLabelsImmediately(issue,labels);
224 | this._setState(issue,closed ? 'closed' : 'open',
225 | function(){this._setLabels(issue,labelOps.remove,labelOps.add,this.params.onResourceChange,this.params.onError);}.bind(this),this.params.onError);
226 | },
227 |
228 | IssueManager.prototype.issueCategories = {
229 | toDo : {
230 | title : 'To Do',
231 | label : 'to-do',
232 | },
233 | doing : {
234 | title : 'Doing',
235 | label : 'doing',
236 | },
237 | awaitingReview : {
238 | title : 'Awaiting Review',
239 | label : 'awaiting-review',
240 | },
241 | done : {
242 | title : 'Done',
243 | label : null
244 | },
245 | };
246 |
247 | return IssueManager;
248 |
249 | });
--------------------------------------------------------------------------------
/src/js/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 | define(
20 | [
21 | "js/components/app",
22 | "react",
23 | "js/utils",
24 | "js/routes",
25 | "director",
26 | "js/settings",
27 | ],
28 | function (MainApp,
29 | React,
30 | Utils,
31 | Routes,
32 | Director,
33 | Settings
34 | )
35 | {
36 |
37 | var app = undefined;
38 | var appComponent = undefined;
39 | var router = new Director();
40 |
41 | function initApp(){
42 | appComponent = React.render(app,
43 | document.getElementById('app')
44 | );
45 | }
46 |
47 | var A = React.createClass({
48 | displayName: 'A',
49 |
50 | render : function(){
51 | var props = $.extend({},this.props);
52 | if (this.props.plain){
53 | delete props.plain;
54 | return React.DOM.a(props);
55 | }
56 |
57 | props.onClick = function(e){
58 | if (this.props.onClick !== undefined)
59 | this.props.onClick(e);
60 | if (e.isDefaultPrevented())
61 | return;
62 | //we only intercept the link if it's not an external one...
63 | if (props.href.substr(0,4) !== 'http'){
64 | e.preventDefault();
65 | router.setRoute(props.href);
66 | }
67 | }.bind(this);
68 | return React.DOM.a(props);
69 | }
70 | });
71 |
72 | if (Settings.html5history){
73 | window.A = A;
74 | Settings.onRedirectTo = function(url){
75 | router.setRoute(url);
76 | };
77 | }
78 | else
79 | window.A = "a";
80 |
81 | function render(props){
82 | if (window.ga !== undefined){
83 | ga('send', 'pageview', {
84 | 'page': location.pathname + location.search + location.hash
85 | });
86 | }
87 | if (props.params !== undefined)
88 | {
89 | props.stringParams = props.params.slice(1);
90 | props.params = Utils.getUrlParameters(props.params.slice(1));
91 | }
92 | else
93 | props.params = {};
94 |
95 | props.router = router;
96 |
97 | if (Settings.html5history){
98 | var re = new RegExp(Settings.frontendUrl+'(.*)$')
99 | var result = re.exec(window.location.pathname);
100 | props.baseUrl = result[1];
101 | }
102 | else{
103 | var re = /^#(.*?)(\?.*)?$/i;
104 | var result = re.exec(window.location.hash);
105 | if (result !== null){
106 | props.baseUrl = result[1];
107 | }
108 | else
109 | props.baseUrl = "/app";
110 | }
111 |
112 | if (app === undefined)
113 | {
114 | app = React.createElement(MainApp,props);
115 | initApp();
116 | }
117 | else
118 | appComponent.replaceProps(props);
119 | }
120 |
121 |
122 | router.configure({html5history : Settings.html5history,
123 | notfound : function(url){
124 | Utils.redirectTo(Utils.makeUrl('/'));
125 | },
126 | strict : false });
127 |
128 | router.param('repositoryId', /([\w\d\-\:\.\/]+)/);
129 | router.param('milestoneId', /(\d+)/);
130 | router.param('organizationId', /([\w\d\-\:\.]+)/);
131 |
132 | //We add URL-style parameters to all routes by decorating the original handler function.
133 | var routesWithParams = {};
134 |
135 | var lastUrl;
136 | var lastUrlPattern;
137 |
138 | function generateCallBack(urlPattern, urlWithParams){
139 | return function(){
140 | var urlCallback = Routes[urlPattern];
141 | var params = urlCallback.apply(
142 | this,
143 | Array.prototype.slice.call(arguments, 0, arguments.length-(Settings.html5history ? 0 : 1))
144 | );
145 |
146 | if (Utils.callbacks.onUrlChange && window.location.href != lastUrl){
147 | for(var i in Utils.callbacks.onUrlChange){
148 | var callback = Utils.callbacks.onUrlChange[i];
149 | if (callback(urlPattern,urlWithParams,window.location.href) == false){
150 | if (lastUrl)
151 | Utils.replaceUrl(lastUrl);
152 | return function(){};
153 | }
154 | }
155 | }
156 |
157 | if (Settings.html5history)
158 | params.params = window.location.search;
159 | else
160 | params.params = arguments[arguments.length-1];
161 |
162 | var url = window.location.href;
163 | params.url = url;
164 | //update title & meta tags
165 | Utils.setTitle(params.title);
166 | Utils.setMetaTag("description", (params.metaTags || {}).description);
167 | Utils.setMetaTag("keywords", ((params.metaTags || {}).keywords || []).concat(Settings.globalKeyKeywords || []).join(","));
168 | //render the view
169 | render(params);
170 | //scroll to top if we navigated to a different page
171 | if(urlPattern !== lastUrlPattern) {
172 | document.documentElement.scrollTop = 0;
173 | }
174 | //set lastUrl and lastUrlPattern
175 | lastUrl = url;;
176 | lastUrlPattern = urlPattern;
177 | };
178 | };
179 |
180 | for (var url in Routes){
181 | var urlWithParams = url+'/?(\\?.*)?';
182 |
183 | if (Settings.html5history)
184 | urlWithParams = url;
185 | var prefix = '';
186 | if (Settings.html5history)
187 | prefix = Settings.frontendUrl;
188 | routesWithParams[prefix+urlWithParams] = generateCallBack(url,urlWithParams);
189 | }
190 |
191 | router.mount(routesWithParams);
192 | router.init();
193 | Utils.setRouter(router);
194 |
195 | if (!Settings.html5history){
196 | if (window.location.hash === ''){
197 | window.location.hash="#/";
198 | }
199 | }
200 | else{
201 | if (window.location.hash.substring(0,2) == '#/'){
202 | window.location = window.location.protocol+'//'+window.location.host+Settings.frontendUrl+window.location.hash.substring(1);
203 | }
204 | }
205 |
206 | return {router: router, initApp: initApp};
207 |
208 | });
209 |
--------------------------------------------------------------------------------
/src/js/request_notifier.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 | define(["js/utils","js/subject"],function (Utils,Subject) {
20 | 'use strict';
21 |
22 | var RequestNotifier = function(){
23 | Subject.Subject.call(this);
24 | this.currentRequests = {};
25 | this.requestCount = 0;
26 | this.retryInterval = 1;
27 | };
28 |
29 | var instance;
30 |
31 | function getInstance()
32 | {
33 | if (instance === undefined)
34 | instance = new RequestNotifier();
35 | return instance;
36 | }
37 |
38 | RequestNotifier.prototype = new Subject.Subject();
39 | RequestNotifier.prototype.constructor = RequestNotifier;
40 |
41 | RequestNotifier.prototype.register = function(requestId,data){
42 | this.currentRequests[requestId] = {registeredAt : new Date(),data : data};
43 | this.notify("registerRequest",requestId);
44 | this.notify("activeRequestCount",this.activeRequestCount());
45 | return requestId;
46 | }
47 |
48 | RequestNotifier.prototype.success = function(requestId,data){
49 | if (requestId in this.currentRequests)
50 | delete this.currentRequests[requestId];
51 | this.notify("requestSucceeded",{requestId : requestId,data : data});
52 | this.notify("activeRequestCount",this.activeRequestCount());
53 | }
54 |
55 | RequestNotifier.prototype.error = function(requestId,xhr,data,e){
56 | if (requestId in this.currentRequests)
57 | delete this.currentRequests[requestId];
58 | if (xhr.readyState == 0){
59 | var requestData = Utils.requestData(requestId);
60 | if (requestData == undefined)
61 | return;
62 | this.notify("connectionError",{requestId : requestId,
63 | xhr : xhr,
64 | data : data,
65 | requestData : requestData,
66 | e :e})
67 | }
68 | else{
69 | this.notify("requestFailed",{requestId : requestId,xhr : xhr,data : data,e : e});
70 | }
71 | this.notify("activeRequestCount",this.activeRequestCount());
72 | }
73 |
74 | RequestNotifier.prototype.activeRequestCount = function(requestId){
75 | return Object.keys(this.currentRequests).length;
76 | }
77 |
78 | return {getInstance:getInstance};
79 |
80 | });
81 |
--------------------------------------------------------------------------------
/src/js/routes.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 | define(
20 | [
21 | "js/utils",
22 | "js/components/sprintboard",
23 | "js/components/milestones",
24 | "js/components/repositories",
25 | "js/components/organizations",
26 | "js/components/user/login",
27 | "js/components/user/logout"
28 | ],
29 | function (
30 | Utils,
31 | SprintBoard,
32 | Milestones,
33 | Repositories,
34 | Organizations,
35 | Login,
36 | Logout
37 | )
38 | {
39 |
40 | var routes = {
41 | '' :
42 | function(){return {
43 | data : {},
44 | component: Repositories}
45 | },
46 | '/sprintboard/:repositoryId/:milestoneId':
47 | function(repositoryId,milestoneId){return {
48 | anonOk : true,
49 | data : {repositoryId : repositoryId,milestoneId : milestoneId},
50 | component: SprintBoard,
51 | }},
52 | '/sprintboard/:repositoryId':
53 | function(repositoryId){return {
54 | anonOk : true,
55 | data : {repositoryId : repositoryId,milestoneId : null},
56 | component: SprintBoard,
57 | }},
58 | '/repositories':
59 | function(){return {
60 | data : {},
61 | component: Repositories
62 | }},
63 | '/repositories/:organizationId':
64 | function(organizationId){return {
65 | data : {organizationId : organizationId},
66 | anonOk : true,
67 | component : Repositories
68 | }},
69 | '/user_repositories/:userId':
70 | function(userId){return {
71 | data : {userId : userId},
72 | anonOk : true,
73 | component : Repositories
74 | }},
75 | '/organizations':
76 | function(){return {
77 | data : {},
78 | component : Organizations
79 | }},
80 | '/milestones/:repositoryId':
81 | function(repositoryId){return {
82 | anonOk : true,
83 | data : {repositoryId : repositoryId},
84 | component : Milestones
85 | }},
86 | '/login':
87 | function(){return {
88 | data : {},
89 | anonOk: true,
90 | component : Login
91 | }},
92 | '/logout' :
93 | function(){return {
94 | data : {},
95 | anonOk : true,
96 | component : Logout
97 | }},
98 | };
99 |
100 | return routes;
101 | });
102 |
--------------------------------------------------------------------------------
/src/js/settings.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 | define(["jquery","js/env_settings"],function ($,envSettings,Utils) {
20 | 'use strict';
21 |
22 | var settings = {
23 | scopes: ['read:org','repo'],
24 | source : 'https://api.github.com',
25 | useCache : true,
26 | cacheValidity : 3600*24, //cache expires after 24 hours
27 | cacheRefreshLimit : 0.0, //how long until we fetch the new value of something?
28 | };
29 | return $.extend($.extend({},settings),envSettings);
30 | })
31 |
--------------------------------------------------------------------------------
/src/js/settings_hashtag_navigation.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | define([],function (Utils) {
21 | 'use strict';
22 |
23 | var settings = {
24 | frontendUrl : '',
25 | html5history : false,
26 | };
27 | return settings;
28 | })
29 |
--------------------------------------------------------------------------------
/src/js/settings_html5_navigation.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 | define([],function (Utils) {
20 | 'use strict';
21 |
22 | var settings = {
23 | frontendUrl : '',
24 | html5history : true,
25 | };
26 | return settings;
27 | })
28 |
--------------------------------------------------------------------------------
/src/js/subject.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2015 - Andreas Dewes
3 |
4 | This file is part of Gitboard.
5 |
6 | Gitboard is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Affero General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU Affero General Public License for more details.
15 |
16 | You should have received a copy of the GNU Affero General Public License
17 | along with this program. If not, see .
18 | */
19 | define([],function (settings) {
20 | 'use strict';
21 |
22 | function Subject(){
23 | this.observers = [];
24 | }
25 |
26 | Subject.prototype = {
27 | subscribe : function (callback) {
28 | this.observers.push(callback);
29 | },
30 | unsubscribe : function(callback) {
31 | var new_observers = [];
32 | for (var i in this.observers)
33 | {
34 | if (this.observers[i] !== callback)
35 | new_observers.push(this.observers[i]);
36 | }
37 | this.observers = new_observers;
38 | },
39 | notify : function(property,data) {
40 | var new_observers = [];
41 | this.observers.forEach(function (cb) {
42 | try {
43 | cb(this,property,data);
44 | new_observers.push(cb);
45 | }
46 | catch(e){throw e;
47 | }}.bind(this)
48 | );
49 | this.observers = new_observers;
50 | }
51 | }
52 |
53 | return {
54 | Subject : Subject
55 | };
56 | });
57 |
--------------------------------------------------------------------------------
/src/scss/_above_the_fold.scss:
--------------------------------------------------------------------------------
1 | //Import CSS modules
2 | @import '../bower_components/bootstrap/dist/css/bootstrap.min.css';
3 | @import '../bower_components/bootstrap-material-design/dist/css/material.min.css';
4 | @import '../bower_components/font-mfizz/css/font-mfizz.css';
5 | @import '../bower_components/font-awesome/css/font-awesome.min.css';
6 | @import '../bower_components/octicons/octicons/octicons.css';
7 | @import '../assets/css/styles.css';
8 |
9 | // Import modules
10 | @import "modules/colors";
11 | @import "modules/mixins";
12 | @import "modules/typography";
13 | @import "modules/spaces";
14 | @import "modules/border_radius";
15 |
16 | // Import partials
17 | @import "partials/request_indicator";
18 | @import "partials/sprintboard";
19 | @import "partials/milestones";
20 | @import "partials/organizations";
21 | @import "partials/repositories";
22 | @import "partials/modal";
--------------------------------------------------------------------------------
/src/scss/_below_the_fold.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adewes/gitboard/4824cf068a9352959b795fddb2093e6ef6b37f3f/src/scss/_below_the_fold.scss
--------------------------------------------------------------------------------
/src/scss/main.scss:
--------------------------------------------------------------------------------
1 | // Import files
2 | @import "above_the_fold";
3 | @import "below_the_fold";
4 | @import url(https://fonts.googleapis.com/css?family=Roboto+Condensed:400,300italic,300);
5 |
6 |
7 | body{
8 | padding-bottom: 50px;
9 | }
10 |
11 | .navbar-gitboard{
12 | background: $logo-color !important;
13 | color:#fff !important;
14 | }
15 |
16 | body,h1,h2,h3,h4,h5,h6 {
17 | font-family: 'Roboto Condensed', sans-serif;
18 | font-weight:300;
19 | }
20 |
21 |
22 | img.github-ribbon{
23 | z-index:2000;
24 | position: fixed;
25 | bottom: 0;
26 | right: 0;
27 | border: 0;
28 | }
29 |
30 | @media(min-width:992px){
31 | #features{
32 | img{
33 | width:80%;
34 | }
35 | }
36 | }
37 |
38 | #features{
39 |
40 | p{
41 | margin-top:40px;
42 | text-align:left;
43 | }
44 | div.row{
45 | margin-bottom:40px;
46 | }
47 | div.diagram{
48 | text-align:center;
49 | }
50 | }
51 |
52 | footer {background:#fff; padding-top:10px;}
53 |
54 | a {color:#006; font-weight:bolder;}
55 | a:hover,a:active {color: #008; text-decoration:none;}
56 | a:visited {color:#006;}
57 |
58 | @media (max-width:991px){
59 |
60 | #features{
61 | img{
62 | width:60%;
63 | }
64 | }
65 |
66 | img.github-ribbon{
67 | display:none; //we do not show the ribbon on mobile devices since it obstructs everything...
68 | }
69 | footer{
70 | margin-top:20px;
71 | }
72 | .navbar-fixed-bottom{
73 | position:relative;
74 | }
75 | }
76 |
77 | @media (min-width: 992px) {
78 | .narrow-container {
79 | width: 970px;
80 | }
81 |
82 | .jumbotron{
83 | margin-top:30px;
84 | margin-bottom:50px !important;
85 | }
86 | }
87 |
88 | #app{
89 | padding-top:20px !important;
90 | }
91 |
92 | .left-tilt{
93 | -ms-transform: rotate(-1deg); /* IE 9 */
94 | -webkit-transform: rotate(-1deg); /* Chrome, Safari, Opera */
95 | transform: rotate(-1deg);
96 | }
97 |
98 | .right-tilt{
99 | -ms-transform: rotate(1deg); /* IE 9 */
100 | -webkit-transform: rotate(1deg); /* Chrome, Safari, Opera */
101 | transform: rotate(1deg);
102 | }
103 |
--------------------------------------------------------------------------------
/src/scss/modules/_border_radius.scss:
--------------------------------------------------------------------------------
1 | $_lg: 5px;
2 | $_sm: 2px;
3 |
4 | .top-radius-lg {
5 | @include border-top-radius($_lg);
6 | }
7 |
8 | .bottom-radius-lg {
9 | @include border-bottom-radius($_lg);
10 | }
11 |
12 | .all-radius-lg {
13 | @include border-radius($_lg);
14 | }
15 |
16 | .top-radius-sm {
17 | @include border-top-radius($_sm);
18 | }
19 |
20 | .bottom-radius-sm {
21 | @include border-bottom-radius($_sm);
22 | }
23 |
24 | .all-radius-sm {
25 | @include border-radius($_sm);
26 | }
27 |
28 | .border-radius-top-none {
29 | border-top-left-radius: 0px !important;
30 | border-top-right-radius: 0px !important;
31 | }
32 |
33 | .border-bottom-none {
34 | border-bottom: none !important;
35 | }
36 |
37 | .border-top-none {
38 | border-top: none !important;
39 | }
40 |
41 | .bordered-top {
42 | border-top: 1px solid get-color(grayscale, mid-light);
43 | }
--------------------------------------------------------------------------------
/src/scss/modules/_colors.scss:
--------------------------------------------------------------------------------
1 | // Colors
2 |
3 | /* At a minimum every palette defines a base colour, and then optionally adds tones use the following naming pattern:
4 | - x-dark
5 | - dark *
6 | - mid-dark
7 | - base (default)
8 | - mid-light
9 | - light
10 | - x-light
11 | */
12 |
13 | //Function to get palettes
14 | @function get-color($palette, $tone: 'base', $alpha: 1) {
15 | @if map-has-key($palettes, $palette) {
16 | @if map-has-key(map-get($palettes, $palette), $tone) {
17 | @return rgba(palette($palette, $tone), $alpha);
18 | }
19 | @else {
20 | @warn "No palette `#{$palette}` with tone `#{$tone}`"
21 | }
22 | }
23 | @else {
24 | @warn "No palette with name `#{$palette}` in $palettes";
25 | }
26 | @return null;
27 | }
28 |
29 | //Function to colors
30 | @function palette($palette, $tone: 'base') {
31 | @return map-get(map-get($palettes, $palette), $tone);
32 | }
33 |
34 | /* Function to get colors with transparency
35 | Usage:
36 | get-color(black);
37 | get-color(black, light);
38 | get-color(black, $alpha: 0.8)
39 | get-color(black, light, 0.8)
40 | */
41 |
42 | // Base colors of paletes
43 | $_color-base-black: #000;
44 | $_color-base-white: #fff;
45 | $_color-base-grey: #777;
46 | $_color-base-lightgrey: #ddd;
47 | $_color-base-grayscale: #999;
48 | $_color-base-red: #ff0000;
49 | $_color-base-green: #00ff00;
50 | $_color-base-blue: #0000ff;
51 | $_color-base-yellow: #ffff00;
52 | $_color-base-orange: #ffaa00;
53 | $_color-base-purple: #a020f0;
54 |
55 | $logo-color: #359EFF;
56 |
57 |
58 | $palettes: (
59 | black: (
60 | base: $_color-base-black,
61 | ),
62 | white: (
63 | base: $_color-base-white,
64 | ),
65 | grayscale: (
66 | x-dark: $_color-base-black,
67 | dark: #333,
68 | mid-dark: #666,
69 | mid-dark-2: #555,
70 | base: $_color-base-grayscale,
71 | mid-light-3: #bbb,
72 | mid-light-2: #aaa,
73 | mid-light: #ccc,
74 | light: #eee,
75 | x-light: $_color-base-white,
76 | ),
77 | qc: (
78 | base: $_color-base-white,
79 | //blue: #0079d2,
80 | blue: #135389,
81 | blueshadow: #08406F,
82 | blue-opaq: rgba(#135389, 0.5),
83 | darkblue: #005197,
84 | darkerblue: #00396a,
85 | green: #058A59,
86 | darkgreen: #04613E,
87 | grey: #44424a,
88 | darkgrey: #191919,
89 | active-grey: #e7ecf3,
90 | active-grey-2: #768096,
91 | orange: #EE5720,
92 | darkorange: #BD4519,
93 | ),
94 | severity: (
95 | 1: #991f15,
96 | 2: #dc7f23,
97 | 3: #ddb438,
98 | 4: #53b89c,
99 | ),
100 | alert: (
101 | info: #1ba2d4,
102 | success: #4ab544,
103 | warning: #ce9b11,
104 | danger: #d13d3d,
105 | info-light: #e4eef9,
106 | success-light: #eaf6e7,
107 | warning-light: #fcf5e4,
108 | danger-light: #f9e7e7,
109 | info-medium: #2cb2e4,
110 | success-medium: #20c524,
111 | warning-medium: #e1b02a,
112 | danger-medium:#e34141,
113 | ),
114 | calltoaction: (
115 | gradient-start: #008bd9,
116 | gradient-end: #006ccb,
117 | orange-gradient-start: #f08f27,
118 | orange-gradient-end: #ea7126,
119 | green-gradient-start: #2bc53d,
120 | green-gradient-end: #019b13,
121 | ),
122 | gold: (
123 | dark: #e9c973,
124 | light: #ffe8aa,
125 | ),
126 | orange: (
127 | mid-dark: #ff7700,
128 | base: $_color-base-orange,
129 | ),
130 | red: (
131 | dark: darken($_color-base-red, 10%),
132 | base: $_color-base-red,
133 | mid-light: #cc4444,
134 | light: #fb9a99,
135 | ),
136 | green: (
137 | dark: #33a029,
138 | base: $_color-base-green,
139 | mid-light: #ccebc5,
140 | light: #aaff00,
141 | ),
142 | blue: (
143 | dark: darken($_color-base-blue, 10%),
144 | mid-dark: #0044aa,
145 | base: $_color-base-blue,
146 | mid-light: #1f78b4,
147 | light: #a6cee3,
148 | ),
149 | yellow: (
150 | dark: darken($_color-base-yellow, 10%),
151 | base: $_color-base-yellow,
152 | light: lighten($_color-base-yellow, 10%),
153 | x-light: #ffffaa,
154 | ),
155 | grey: (
156 | x-dark: #333,
157 | dark: #444,
158 | mid-dark: #555,
159 | base: $_color-base-grey,
160 | mid-light: #888,
161 | light: #999,
162 | x-light: #aaa,
163 | ),
164 | lightgrey: (
165 | mid-dark: #ccc,
166 | base: $_color-base-lightgrey,
167 | mid-light: #eee,
168 | light: #f8f8f8,
169 | bg-light: #f2f2f2,
170 | x-light: #fbfbfb,
171 | xx-light: #fefefe,
172 | ),
173 | purple: (
174 | base: $_color-base-purple,
175 | mid-light: #7777aa,
176 | ),
177 | );
178 |
--------------------------------------------------------------------------------
/src/scss/modules/_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin border-radius($radius) {
2 | -webkit-border-radius: $radius;
3 | border-radius: $radius;
4 | background-clip: padding-box; /* stops bg color from leaking outside the border: */
5 | }
6 |
7 | // Single side border-radius
8 |
9 | @mixin border-top-radius($radius) {
10 | -webkit-border-top-right-radius: $radius;
11 | border-top-right-radius: $radius;
12 | -webkit-border-top-left-radius: $radius;
13 | border-top-left-radius: $radius;
14 | background-clip: padding-box;
15 | }
16 | @mixin border-right-radius($radius) {
17 | -webkit-border-bottom-right-radius: $radius;
18 | border-bottom-right-radius: $radius;
19 | -webkit-border-top-right-radius: $radius;
20 | border-top-right-radius: $radius;
21 | background-clip: padding-box;
22 | }
23 | @mixin border-bottom-radius($radius) {
24 | -webkit-border-bottom-right-radius: $radius;
25 | border-bottom-right-radius: $radius;
26 | -webkit-border-bottom-left-radius: $radius;
27 | border-bottom-left-radius: $radius;
28 | background-clip: padding-box;
29 | }
30 | @mixin border-left-radius($radius) {
31 | -webkit-border-bottom-left-radius: $radius;
32 | border-bottom-left-radius: $radius;
33 | -webkit-border-top-left-radius: $radius;
34 | border-top-left-radius: $radius;
35 | background-clip: padding-box;
36 | }
37 |
38 | @mixin logos($margin-right: 20px) {
39 | .logos {
40 | text-align: center;
41 | margin-right: $margin-right;
42 | margin-bottom: 15px;
43 | width: auto !important;
44 | }
45 |
46 | .logos-big {
47 | text-align: center;
48 | margin-right: $margin-right;
49 | margin-bottom: 15px;
50 | height: 130px !important;
51 | width: auto !important;
52 | }
53 |
54 | .logos-small {
55 | text-align: center;
56 | margin-right: $margin-right;
57 | margin-bottom: 15px;
58 | height: 30px !important;
59 | width: auto !important;
60 | }
61 | }
--------------------------------------------------------------------------------
/src/scss/modules/_spaces.scss:
--------------------------------------------------------------------------------
1 | $slug-top: space-top;
2 | $slug-bottom: space-bottom;
3 | $slug-right: space-right;
4 | $slug-left: space-left;
5 | $maximum: 30;
6 | $step: 5;
7 |
8 | // Generates space-top-x and space-bottom-x classes
9 | // x = i * steps; i(max) = maximum
10 |
11 | @for $i from 0 through $maximum {
12 | $space: $i * $step;
13 | .#{$slug-top}-#{$space} {
14 | margin-top: $space#{px !important};
15 | }
16 | .#{$slug-bottom}-#{$space} {
17 | margin-bottom: $space#{px !important};
18 | }
19 | .#{$slug-right}-#{$space} {
20 | margin-right: $space#{px !important};
21 | }
22 | .#{$slug-left}-#{$space} {
23 | margin-left: $space#{px !important};
24 | }
25 | }
--------------------------------------------------------------------------------
/src/scss/modules/_typography.scss:
--------------------------------------------------------------------------------
1 | // Typography
2 |
3 | // FONT SETTING
4 | @function font($font, $size: 'base', $weight: 'normal') {
5 | @return map-get(map-get($fonts, $font), $tone);
6 | }
7 |
8 | // $font_family:
9 |
10 | // $fonts: (
11 | // h1: (
12 | // font-family: $font_family,
13 | // font-weight: 700,
14 | // font-size: 16px,
15 | // line-height:
16 | // ),
17 | // );
18 |
19 |
20 | // font: {
21 | // family: fantasy;
22 | // size: 30em;
23 | // weight: bold;
24 | // }
25 |
26 |
--------------------------------------------------------------------------------
/src/scss/partials/_milestones.scss:
--------------------------------------------------------------------------------
1 | .milestone-item{
2 | a{
3 | text-decoration: none;
4 | color: inherit;
5 | background:inherit;
6 | }
7 | }
--------------------------------------------------------------------------------
/src/scss/partials/_modal.scss:
--------------------------------------------------------------------------------
1 | .modal{
2 | .modal-footer{
3 | background:#eee;
4 | border-top:1px solid #ddd;
5 | padding:10px !important;
6 | margin:0;
7 | text-align:left;
8 | }
9 | }
--------------------------------------------------------------------------------
/src/scss/partials/_organizations.scss:
--------------------------------------------------------------------------------
1 | .organization-item{
2 |
3 | position:relative;
4 | overflow:hidden;
5 | img.avatar{
6 | position:absolute;
7 | opacity:0.5;
8 | z-index:1;
9 | right:10px;
10 | top:10px;
11 | }
12 |
13 | a {
14 | text-decoration: none;
15 | color: inherit;
16 | background:inherit;
17 | }
18 |
19 | .panel-body {
20 | position:relative;
21 | z-index:10;
22 | }
23 |
24 | p{
25 | color:#777;
26 | font-size:10px;
27 | }
28 | }
--------------------------------------------------------------------------------
/src/scss/partials/_repositories.scss:
--------------------------------------------------------------------------------
1 | .repository-item{
2 | a{
3 | text-decoration: none;
4 | color: inherit;
5 | background:inherit;
6 | }
7 | }
--------------------------------------------------------------------------------
/src/scss/partials/_request_indicator.scss:
--------------------------------------------------------------------------------
1 |
2 | p.request-indicator {
3 |
4 | width: auto;
5 | position: absolute;
6 | top: 0;
7 | left: 50%;
8 | margin-left: -70px;
9 | border: 0px solid get-color(gold, dark);
10 | border-top: none;
11 | padding: 5px 15px;
12 | font-size: 12px;
13 | z-index: 1000;
14 | background: get-color(yellow, light);
15 | color: #000;
16 | text-align: center;
17 |
18 | -webkit-box-shadow: 0px 2px 3px 0px get-color(black, $alpha: 0.3);
19 | -moz-box-shadow: 0px 2px 3px 0px get-color(black, $alpha: 0.3);
20 | box-shadow: 0px 2px 3px 0px get-color(black, $alpha: 0.3);
21 |
22 | > a {
23 | color: #000;
24 |
25 | &:hover {
26 | text-decoration: none;
27 | }
28 | > .fa-spin, > .fa-exclamation-triangle {
29 | margin-right: 5px;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/scss/partials/_sprintboard.scss:
--------------------------------------------------------------------------------
1 | body{
2 | min-height:100%;
3 | height:100%;
4 | }
5 |
6 | .sprintboard
7 | {
8 |
9 | a.refresh-link{
10 | color:#aaa;
11 | }
12 |
13 | @media (min-width: 992px){
14 |
15 | .row{
16 | overflow: hidden;
17 | }
18 |
19 | [class*="col-"]{
20 | margin-bottom: -99999px;
21 | padding-bottom: 99999px;
22 | }
23 | }
24 |
25 | .issue-list.active{
26 | background:#ddd;
27 | }
28 |
29 | .issue-list.active .issue-item{
30 | pointer-events:none;
31 | }
32 |
33 | .issue-details img{
34 | max-width:100%;
35 | }
36 |
37 | span.due{
38 | color:#555;
39 | font-weight:100;
40 | }
41 |
42 | span.estimates{
43 | font-size:12px;
44 | }
45 |
46 | span.time-estimate,
47 | span.time-spent
48 | {
49 | color:#555;
50 | font-weight:100;
51 | }
52 |
53 | span.time-estimate:before,
54 | span.time-spent:before
55 | {
56 | font-family:FontAwesome;
57 | padding-right:4px;
58 | color:#aaa;
59 | }
60 |
61 | span.time-spent{
62 | padding-left:4px;
63 | color:#000;
64 | }
65 |
66 | span.time-estimate:before
67 | {
68 | content:'\f251';
69 | }
70 |
71 | span.time-spent:before
72 | {
73 | content:'\f253';
74 | }
75 |
76 | .issue-item.dragged{
77 | -ms-transform: rotate(-2deg); /* IE 9 */
78 | -webkit-transform: rotate(-2deg); /* Chrome, Safari, Opera */
79 | transform: rotate(-2deg);
80 | background:#fdd;
81 | }
82 |
83 | .issue-item{
84 |
85 | .modal{
86 | overflow:visible;
87 | }
88 |
89 | span.issue-labels{
90 | font-size:12px;
91 | padding:0;
92 | margin-left:10px;
93 | }
94 |
95 | .issue-label{
96 | margin-right:4px;
97 | padding:2px;
98 | font-weight:300;
99 | }
100 |
101 | .btn-group{
102 | margin-top:0;
103 | }
104 |
105 | div.selectors{
106 |
107 | clear:both;
108 | margin-bottom:10px;
109 | z-index:9999;
110 |
111 | div.btn-group{
112 | margin:0;
113 | padding:0;
114 | }
115 | span.legend{
116 | display:block;
117 | font-size:12px;
118 | color:#999;
119 | padding:0;
120 | margin:0;
121 | }
122 | }
123 |
124 | div.selectors:after {
125 | visibility: hidden;
126 | display: block;
127 | font-size: 0;
128 | content: " ";
129 | clear: both;
130 | height: 0;
131 | }
132 |
133 | a.github-link{
134 | color:#777;
135 | padding-left:4px;
136 | }
137 |
138 | a {
139 | text-decoration: none;
140 | color: inherit;
141 | background:inherit;
142 | }
143 |
144 | .panel-heading{
145 | position:relative;
146 | }
147 |
148 | .panel-footer{
149 | padding:2px;
150 | font-size:12px;
151 | }
152 |
153 | .panel-body{
154 | padding:4px;
155 | }
156 |
157 | p.right-symbols{
158 | position:absolute;
159 | right:2px;
160 | top:0;
161 | text-align: right;
162 | }
163 |
164 | img.assignee {
165 | }
166 |
167 |
168 | h5{
169 | font-weight:bold !important;
170 | }
171 |
172 | @for $i from 0 through 16{
173 | .label-#{($i)+1} {
174 | position: relative;
175 | position:absolute;
176 | top:0;
177 | left:#{$i*4}px;
178 | width:3px;
179 | height:16px;
180 |
181 | }
182 | }
183 |
184 | }
185 |
186 | }
187 |
--------------------------------------------------------------------------------
/src/templates/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Gitboard
12 |
13 |
14 |
15 |
16 |
17 |
18 |
37 |
38 |
Gitboard is a simple and intuitive Kanban board for your Github issues. Easily manage to-dos, milestones and projects and keep up-to-date on your progress. Gitboard is open-source and runs securely inside your browser.
Gitboard gives you an intuitive and beautiful overview of your Github issues. Forget navigating back and forth to see which issues are in the backlog, being worked on or closed.
73 |
74 | See who's working on what. Move issues between categories by dragging and dropping them. Define time estimates for your issues and track actual working time.
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
Keep track of your goals
84 |
Gitboard helps you to keep track of your goals and the time budget of your team. See aggregate time estimates and actual working time for each milestone. This helps you to discover problems in your project before the deadline hits you hard.
85 |
86 | Coming soon: Burndown charts. See if your progress keeps up with your schedule.
87 |
98 | It uses a simple, resource-based data synchronization scheme and tries to keep complexity to a minimum. Feel free to use Gitboard as inspiration for your own React.js-based projects.
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
Open-source license
108 |
Gitboard is licensed under the Affero General Public License (AGPL). This means that you can adapt it to your needs and freely publish your own version as long as you respect the terms of the license and make your improvements available to the community.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
Secure and transparent
117 |
Gitboard communicates directly with the Github API without using an intermediate server. This means that your Github access credentials stay on your computer all the time.
118 |
119 | Gitboard will never store your username or password in plaintext and only use them to create an access token. The access token will be held securely in the session storage or local storage of your browser.
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
Questions? Get in touch!
129 |
130 | If you have questions about Gitboard or if you experience problems using it, please open an issue. You can also get in touch with me via e-mail.
131 |
132 |
133 | Contributions and feedback are highly welcome! When contributing to the code, please make sure to check out the README first.
134 |