├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── app
├── app.js
├── utils
│ └── transform.js
└── views
│ ├── app.jsx
│ ├── layout.hbs
│ ├── person.jsx
│ ├── timezone.jsx
│ └── timezoneList.jsx
├── assets
└── stylesheets
│ └── index.styl
├── index.js
├── package.json
└── public
├── favicon.png
├── images
├── arrow-keys.png
└── worldmap.png
└── js
└── bundle.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | *.log
4 | public/stylesheets/*.css
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 Dan Farrelly
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
16 | SOFTWARE.
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node ./index.js
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Timezone
2 |
3 | > **Note** For the repo for the SaaS app **[Timezone.io](http://timezone.io)**
4 | head over to **[timezoneio/timezoneio](https://github.com/timezoneio/timezoneio)**.
5 | This project is the original, simple version meant to be self-hosted. Please
6 | feel free to sign up for free at **[Timezone.io](http://timezone.io)** and
7 | contribute issues and pull requests on that project's repo!
8 |
9 | Timezone is an application aimed at helping remote teams by making it easier
10 | to see where and **when** their coworkers are. This project was the basis for
11 | the larger [Timezone.io](https://github.com/timezoneio/timezoneio) project and
12 | is meant for self-hosting.
13 |
14 | 
15 |
16 | # Setup
17 |
18 | Clone this repo and add a `people.json` file in the repo's root directory.
19 | Timezone codes for the `tz` field can be found [here](http://momentjs.com/timezone/).
20 | Each person object should have data in the following format:
21 |
22 | ```json
23 | [
24 | {
25 | "name": "Dan",
26 | "avatar": "https://d389zggrogs7qo.cloudfront.net/images/team/dan.jpg",
27 | "city": "NYC",
28 | "tz": "America/New_York"
29 | },
30 | {
31 | "name": "Niel",
32 | "avatar": "https://d389zggrogs7qo.cloudfront.net/images/team/niel.jpg",
33 | "city": "Cape Town",
34 | "tz": "Africa/Johannesburg"
35 | }
36 | ]
37 | ```
38 |
39 | # Configuration
40 |
41 | By default, timezone uses port 3000. This port can be changed by setting
42 | the environment variable, `PORT`. i.e. `PORT=80` to use port 80.
43 |
44 | # Deploy
45 |
46 | This project is designed with a Procfile to deploy to a Heroku instance. Please
47 | check with Heroku's up to date documentation for any latest changes. You should
48 | be able to commit your changes in your forked repo (including adding your own
49 | people.json file) then run:
50 |
51 | ```bash
52 | $ heroku create
53 | $ git push heroku master
54 | ```
55 |
56 |
57 | # Development
58 |
59 | You must have [Node.js](http://nodejs.org/) and [Browserify](http://browserify.org/)
60 | installed on your system to compile the project assets. After install Node.js, run:
61 |
62 | ```bash
63 | $ npm install -g browserify
64 | ```
65 |
66 | To run the server and download all dependencies for the project run this in the
67 | project root directory:
68 |
69 | ```bash
70 | $ npm install
71 | ```
72 |
73 | `bundle.js` contains all of the necessary scripts and data for the client.
74 | To re-build this file with Browserify run:
75 |
76 | ```bash
77 | $ npm run build
78 | ```
79 |
80 | Now to start the server on localhost:3000 you can run:
81 |
82 | ```bash
83 | $ node ./index.js
84 | ```
85 |
86 | **Note:** These docs are very basic and need some more love. I'll add more info
87 | soon :)
88 |
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var moment = require('moment-timezone');
3 |
4 | var transform = require('./utils/transform.js');
5 | var App = require('./views/app.jsx');
6 |
7 |
8 | // Organize into timezones
9 | var time = moment();
10 | var timezones = transform(time, window.people);
11 |
12 | // Add the component to the DOM
13 | var targetNode = document.querySelector('#app');
14 |
15 | React.render(
16 | React.createElement(App, {
17 | time: time,
18 | timezones: timezones
19 | }),
20 | targetNode
21 | );
22 |
23 |
24 | var KEY = {
25 | LEFT: 37,
26 | RIGHT: 39
27 | };
28 |
29 | // Listen to keyup for timechange
30 | window.addEventListener('keyup', function(e){
31 |
32 | if (e.keyCode === KEY.RIGHT){
33 | time.add(1, 'h');
34 | } else if (e.keyCode === KEY.LEFT){
35 | time.subtract(1, 'h');
36 | }
37 |
38 | // Push new data to re-render component
39 | React.render(
40 | React.createElement(App, {
41 | time: time,
42 | timezones: timezones
43 | }),
44 | targetNode
45 | );
46 |
47 | });
48 |
49 | function reRender() {
50 |
51 | var now = moment();
52 | if (now.hour() === time.hour() && now.minute() === time.minute()) return;
53 |
54 | time.hour( now.hour() );
55 | time.minute( now.minute() );
56 |
57 | React.render(
58 | React.createElement(App, {
59 | time: time,
60 | timezones: timezones
61 | }),
62 | targetNode
63 | );
64 |
65 | }
66 |
67 | // Check every 10 seconds for an updated time
68 | setInterval(reRender, 1000 * 10);
69 |
70 | // Check on window focus
71 | window.onfocus = reRender;
72 |
--------------------------------------------------------------------------------
/app/utils/transform.js:
--------------------------------------------------------------------------------
1 | var moment = require('moment-timezone');
2 |
3 |
4 | function appendTime(time, person) {
5 | person.time = moment( time ).tz( person.tz );
6 | }
7 |
8 | function sortByTimezone(a, b){
9 | return a.time.utcOffset() - b.time.utcOffset();
10 | }
11 |
12 |
13 | module.exports = function transform(time, people) {
14 |
15 | // Append a moment date to each person
16 | people.forEach(appendTime.bind(people, time));
17 | people.sort(sortByTimezone);
18 |
19 | var timezones = people.reduce(function(zones, person){
20 | var last = zones[ zones.length - 1 ];
21 | var offset = last && last.people[0].time.utcOffset();
22 |
23 | if (last && offset === person.time.utcOffset()) {
24 | last.people.push(person);
25 | } else {
26 | zones.push({
27 | tz: person.tz,
28 | people: [ person ]
29 | });
30 | }
31 |
32 | return zones;
33 | }, []);
34 |
35 | timezones.forEach(function(timezone){
36 | if (timezone.people.length / people.length > 0.2)
37 | timezone.major = true;
38 | });
39 |
40 | return timezones;
41 |
42 | };
--------------------------------------------------------------------------------
/app/views/app.jsx:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var TimezoneList = require('./timezoneList.jsx');
3 |
4 | module.exports = React.createClass({
5 | getInitialState: function() {
6 | return {
7 | timeFormat: 'h:mm a'
8 | };
9 | },
10 | toggleTimeFormat: function() {
11 | this.setState({
12 | timeFormat: this.state.timeFormat === 'h:mm a' ? 'H:mm' : 'h:mm a'
13 | });
14 | },
15 | render: function() {
16 | return (
17 |