Repository: sbstjn/timesheet.js Branch: master Commit: 1354b057500d Files: 19 Total size: 30.1 KB Directory structure: gitextract_snamz3o5/ ├── .gitignore ├── .jshintignore ├── .jshintrc ├── Gruntfile.js ├── LICENSE.md ├── README.md ├── bower.json ├── package.json ├── serve.js ├── source/ │ ├── index.haml │ ├── javascripts/ │ │ ├── lib.js │ │ ├── main.js │ │ └── timesheet.js │ ├── snippets/ │ │ └── example-date.js │ └── stylesheets/ │ ├── _normalize.sass │ ├── style.sass │ ├── timesheet-white.sass │ └── timesheet.sass └── test/ └── timesheet.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # See http://help.github.com/ignore-files/ for more about ignoring files. # # If you find yourself ignoring temporary files generated by your text editor # or operating system, you probably want to add a global ignore instead: # git config --global core.excludesfile ~/.gitignore_global # Ignore bundler config /.bundle # Ignore the build directory /build # Ignore cache /.sass-cache /.cache # Built gh-pages /gh-pages # Ignore .DS_store file .DS_Store # Ignore Node.js modules node_modules ================================================ FILE: .jshintignore ================================================ ================================================ FILE: .jshintrc ================================================ { "bitwise": false, "boss": true, "browser": true, "camelcase": true, "curly": true, "devel": true, "eqeqeq": true, "es3": true, "expr": true, "forin": true, "freeze": true, "globalstrict": false, "immed": true, "indent": 2, "jquery": true, "latedef": true, "laxcomma": true, "multistr": false, "newcap": true, "noarg": true, "noempty": true, "nonew": true, "notypeof": false, "plusplus": false, "quotmark": "single", "regexp": true, "strict": true, "trailing": true, "undef": true, "unused": true, "maxparams": 5, "maxdepth": 4, "maxstatements": 20, "maxcomplexity": 10, "globals": {"module": true, "require": true, "suite": true, "test": true, "__dirname": true} } ================================================ FILE: Gruntfile.js ================================================ module.exports = function(grunt) { 'use strict'; var fs = require('fs'); grunt.initConfig({ uglify: { options: { mangle: false }, my_target: { files: { 'dist/timesheet.min.js': ['source/javascripts/timesheet.js'] } } }, sass: { gh: { options: { style: 'compressed' }, files: { 'gh-pages/styles/style.css': 'source/stylesheets/style.sass' } }, dist: { options: { style: 'compressed' }, files: { 'dist/timesheet.min.css': 'source/stylesheets/timesheet.sass', 'dist/timesheet-white.min.css': 'source/stylesheets/timesheet-white.sass' } } }, jshint: { all: { src: [ 'source/javascripts/*.js' ], options: { jshintrc: '.jshintrc' } } }, simplemocha: { options: { globals: ['should'], timeout: 3000, ignoreLeaks: false, grep: '', ui: 'tdd', reporter: 'spec' }, all: { src: ['test/**/*.js'] } }, express: { options: { port: 8080 }, dev: { options: { script: __dirname + '/serve.js' } } }, watch: { scripts: { files: 'source/javascripts/*.js', tasks: ['simplemocha', 'jshint', 'uglify'], options: { interrupt: true, } }, styles: { files: 'source/stylesheets/*.sass', tasks: ['sass'], options: { interrupt: true, } } }, haml: { gh: { files: { 'gh-pages/index.html': 'source/index.haml' }, options: { context: { code: fs.readFileSync(__dirname + '/source/snippets/example-date.js') } } } }, copy: { gh: { files: [ {expand: false, src: __dirname + '/source/javascripts/lib.js', dest: __dirname + '/gh-pages/script/lib.js'}, {expand: false, src: __dirname + '/source/javascripts/main.js', dest: __dirname + '/gh-pages/script/main.js'}, {expand: false, src: __dirname + '/dist/timesheet.min.js', dest: __dirname + '/gh-pages/script/timesheet.min.js'}, {expand: false, src: __dirname + '/dist/timesheet.min.css', dest: __dirname + '/gh-pages/styles/timesheet.css'}, {expand: false, src: __dirname + '/dist/timesheet-white.min.css', dest: __dirname + '/gh-pages/styles/timesheet-white.css'}, {expand: false, src: __dirname + '/dist/timesheet.min.css.map', dest: __dirname + '/gh-pages/styles/timesheet.css.map'}, {expand: false, src: __dirname + '/dist/timesheet-white.min.css.map', dest: __dirname + '/gh-pages/styles/timesheet-white.css.map'} ] } } }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-simple-mocha'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-sass'); grunt.loadNpmTasks('grunt-express-server'); grunt.loadNpmTasks('grunt-haml'); // Default task grunt.registerTask('default', ['build']); grunt.registerTask('build', ['simplemocha', 'jshint', 'uglify', 'sass']); grunt.registerTask('server', ['express:dev', 'watch' ]) grunt.registerTask('gh', ['build', 'haml:gh', 'sass:gh', 'copy:gh']); }; ================================================ FILE: LICENSE.md ================================================ # The MIT License (MIT) Copyright (c) 2014-2015 Sebastian Müller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Timesheet.js Simple JavaScript library to create HTML time sheets. Wrapped in an example project using Middleman … ![https://sbstjn.github.io/timesheet.js](https://raw.githubusercontent.com/sbstjn/timesheet.js/master/screen.png) You only have to include `dist/timesheet.js` and `dist/timesheet.css` in your HTML and initialize Timesheet.js with: ```HTML
``` ```javascript new Timesheet('timesheet', 2002, 2013, [ ['2002', '09/2002', 'A freaking awesome time', 'lorem'], ['06/2002', '09/2003', 'Some great memories', 'ipsum'], ['2003', 'Had very bad luck'], ['10/2003', '2006', 'At least had fun', 'dolor'], ['02/2005', '05/2006', 'Enjoyed those times as well', 'ipsum'], ['07/2005', '09/2005', 'Bad luck again', 'default'], ['10/2005', '2008', 'For a long time nothing happened', 'dolor'], ['01/2008', '05/2009', 'LOST Season #4', 'lorem'], ['01/2009', '05/2009', 'LOST Season #4', 'lorem'], ['02/2010', '05/2010', 'LOST Season #5', 'lorem'], ['09/2008', '06/2010', 'FRINGE #1 & #2', 'ipsum'] ]); ``` ### Bower `$ > bower install https://github.com/sbstjn/timesheet.js.git` ## Grunt commands Use `grunt` to build all JavaScript and StyleSheet files located inside `dist/`. Use `grunt server` to start a local web server on [localhost:8080](http://localhost:8080) to customize Timesheet.js, afterwards run `grunt` to compile all needed files. Use `grunt gh` to generate the site and files available at [sbstjn.github.io/timesheet.js](http://sbstjn.github.io/timesheet.js) into the `gh-pages` folder. ## License Timesheet.js is licensed under MIT License. ================================================ FILE: bower.json ================================================ { "name": "timesheet.js", "homepage": "https://sbstjn.github.io/timesheet.js/", "authors": [ "sbstjn " ], "description": "With Timesheet.js you can easily create simple time and data sheets or timelines using HTML5, JavaScript and CSS3. Yep, it's a Vanilla JS library!", "main": ["dist/timesheet.min.js", "dist/timesheet.min.css"], "moduleType": [ "amd" ], "keywords": [ "timeline", "timesheet", "timebar", "line", "bar", "graph", "visualize", "chart" ], "license": "MIT", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests", "screen.png", "source", "Gruntfile.js", "package.json" ] } ================================================ FILE: package.json ================================================ { "name": "timesheet.js", "version": "1.0.1", "description": "With Timesheet.js you can easily create simple time and data sheets or timelines using HTML5, JavaScript and CSS3. Yep, it's a Vanilla JS library!", "author": "sbstjn ", "devDependencies": { "consolidate": "^0.13.1", "express": "^4.12.4", "grunt": "~0.4.1", "grunt-cli": "~0.1.11", "grunt-contrib-copy": "^0.8.0", "grunt-contrib-jshint": "~0.7.2", "grunt-contrib-sass": "^0.7.3", "grunt-contrib-uglify": "^0.4.0", "grunt-contrib-watch": "^0.6.1", "grunt-express-server": "^0.5.1", "grunt-haml": "^0.9.0", "grunt-jslint": "~1.1.1", "grunt-simple-mocha": "^0.4.0", "haml": "^0.4.3", "hamljs": "^0.6.2", "node-sass": "^3.2.0" } } ================================================ FILE: serve.js ================================================ var express = require('express'); var sass = require('node-sass'); var fs = require('fs'); var app = express(); var engines = require('consolidate'); app.engine('haml', engines.haml); app.set('views', __dirname + '/source'); app.get('/script/lib.js', function(req, res) { res.end(fs.readFileSync(__dirname + '/source/javascripts/lib.js')); }); app.get('/script/main.js', function(req, res) { res.end(fs.readFileSync(__dirname + '/source/javascripts/main.js')); }); app.get('/script/timesheet.min.js', function(req, res) { res.end(fs.readFileSync(__dirname + '/dist/timesheet.min.js')); }); app.get('/styles/timesheet.css', function(req, res) { res.end(fs.readFileSync(__dirname + '/dist/timesheet.min.css')); }); app.get('/styles/timesheet-white.css', function(req, res) { res.end(fs.readFileSync(__dirname + '/dist/timesheet-white.min.css')); }); app.get('/styles/style.css', function(req, res) { sass.render({ file: __dirname + '/source/stylesheets/style.sass', outputStyle: req.query.style || "" }, function(err, result) { res.end(result.css.toString()); }); }); app.get('/', function (req, res) { res.render('index.haml', { code: fs.readFileSync(__dirname + '/source/snippets/example-date.js') + '' }); }); var server = app.listen(process.env.PORT || 3000, function () { var host = server.address().address; var port = server.address().port; console.log('Example app listening at http://%s:%s', host, port); }); ================================================ FILE: source/index.haml ================================================ !!! 5 %html %head %title Timesheet.js - Beautiful time tables with HTML, JavaScript and CSS … %meta{charset: 'utf-8'} %meta{content: 'IE=edge,chrome=1', 'http-equiv': 'X-UA-Compatible'} %meta{property: 'og:title', content: 'Timesheet.js - Beautiful time tables with HTML, JavaScript and CSS …'} %meta{property: 'og:description', content: 'With Timesheet.js you can easily create simple time and data sheets or timelines using HTML5, JavaScript and CSS3. Yep, it\'s a Vanilla JS library!'} %meta{property: 'og:url', content: 'https://sbstjn.github.io/timesheet.js/'} %meta{property: 'og:image', content: 'https://raw.githubusercontent.com/sbstjn/timesheet.js/master/screen.png'} %meta{property: 'fb:admins', content: '669118929'} %script{type: 'text/javascript', src: './script/lib.js'} %script{type: 'text/javascript', src: './script/timesheet.min.js'} %script{type: 'text/javascript', src: './script/main.js'} %link{rel: 'stylesheet', type: "text/css", href: "./styles/style.css"} %link{rel: 'stylesheet', type: "text/css", href: "./styles/timesheet.css"} %link{rel: 'stylesheet', type: "text/css", href: "./styles/timesheet-white.css"} %link{rel: 'stylesheet', type: "text/css", href: "https://fonts.jimstatic.com/css?family=Oxygen+Mono:400,600,800"} %link{rel: 'stylesheet', type: "text/css", href: "https://fonts.jimstatic.com/css?family=Open+Sans:400,600,800"} %link{rel: 'stylesheet', type: "text/css", href: "https://fonts.jimstatic.com/css?family=Signika+Negative:300,400,600,700"} %body.black #box #box-inner %h1 Timesheet.js %p Visualize your data and events with sexy HTML5 and CSS3. Create simple time sheets with sneaky JavaScript. Style them with CSS and have mobile fun as well … #timesheet-default %p Just include Timesheet.js and configure your data. No external dependencies, no jQuery needed and of course no Angular.JS! Just a few lines JavaScript to generate a beautiful HTML5 layout and some really delicious CSS to be customized by almighty you. %code %pre!='<script src="/javascripts/timesheet.js" type="text/javascript" />' %p Create a simple time sheet based on a JS array of events: %code %pre!=code %p It's that simple to use Timesheet.js. So, have a nice day, thank you for smoking and maybe try using Timesheet.js with custom styles … %p.footer Timesheet.js is licensed under MIT License
Cheers to Cheeaun and Ilya




dark | light %span Fork Timesheet.js on GitHub ================================================ FILE: source/javascripts/lib.js ================================================ (function() { 'use strict'; var Lib = { /* http://www.dustindiaz.com/smallest-domready-ever */ /* jshint -W030 */ ready: function r(f){ /in/.test(document.readyState)?setTimeout(function () {Lib.ready(f);},9):f(); } }; window.Lib = Lib; })(); ================================================ FILE: source/javascripts/main.js ================================================ /* global Lib, Timesheet */ (function(){ 'use strict'; Lib.ready(function() { console.log('ads'); /* jshint -W031 */ new Timesheet('timesheet-default', 2002, 2013, [ ['2002', '09/2002', 'A freaking awesome time', 'lorem'], ['06/2002', '09/2003', 'Some great memories', 'ipsum'], ['2003', 'Had very bad luck'], ['10/2003', '2006', 'At least had fun', 'dolor'], ['02/2005', '05/2006', 'Enjoyed those times as well', 'ipsum'], ['07/2005', '09/2005', 'Bad luck again', 'default'], ['10/2005', '2008', 'For a long time nothing happened', 'dolor'], ['01/2008', '05/2009', 'LOST Season #4', 'lorem'], ['01/2009', '05/2009', 'LOST Season #4', 'sit'], ['02/2010', '05/2010', 'LOST Season #5', 'lorem'], ['09/2008', '06/2010', 'FRINGE #1 & #2', 'ipsum'] ]); document.querySelector('#switch-dark').addEventListener('click', function() { document.querySelector('body').className = 'index black'; }); document.querySelector('#switch-light').addEventListener('click', function() { document.querySelector('body').className = 'index white'; }); }); })(); ================================================ FILE: source/javascripts/timesheet.js ================================================ (function() { 'use strict'; /** * Initialize a Timesheet */ var Timesheet = function(container, min, max, data) { this.data = []; this.year = { min: min, max: max }; this.parse(data || []); if (typeof document !== 'undefined') { this.container = (typeof container === 'string') ? document.querySelector('#'+container) : container; this.drawSections(); this.insertData(); } }; /** * Insert data into Timesheet */ Timesheet.prototype.insertData = function() { var html = []; var widthMonth = this.container.querySelector('.scale section').offsetWidth; for (var n = 0, m = this.data.length; n < m; n++) { var cur = this.data[n]; var bubble = this.createBubble(widthMonth, this.year.min, cur.start, cur.end); var line = [ '', '' + bubble.getDateLabel() + ' ', '' + cur.label + '' ].join(''); html.push('
  • ' + line + '
  • '); } this.container.innerHTML += ''; }; /** * Draw section labels */ Timesheet.prototype.drawSections = function() { var html = []; for (var c = this.year.min; c <= this.year.max; c++) { html.push('
    ' + c + '
    '); } this.container.className = 'timesheet color-scheme-default'; this.container.innerHTML = '
    ' + html.join('') + '
    '; }; /** * Parse data string */ Timesheet.prototype.parseDate = function(date) { if (date.indexOf('/') === -1) { date = new Date(parseInt(date, 10), 0, 1); date.hasMonth = false; } else { date = date.split('/'); date = new Date(parseInt(date[1], 10), parseInt(date[0], 10)-1, 1); date.hasMonth = true; } return date; }; /** * Parse passed data */ Timesheet.prototype.parse = function(data) { for (var n = 0, m = data.length; n this.year.max) { this.year.max = end.getFullYear(); } else if (beg.getFullYear() > this.year.max) { this.year.max = beg.getFullYear(); } this.data.push({start: beg, end: end, label: lbl, type: cat}); } }; /** * Wrapper for adding bubbles */ Timesheet.prototype.createBubble = function(wMonth, min, start, end) { return new Bubble(wMonth, min, start, end); }; /** * Timesheet Bubble */ var Bubble = function(wMonth, min, start, end) { this.min = min; this.start = start; this.end = end; this.widthMonth = wMonth; }; /** * Format month number */ Bubble.prototype.formatMonth = function(num) { num = parseInt(num, 10); return num >= 10 ? num : '0' + num; }; /** * Calculate starting offset for bubble */ Bubble.prototype.getStartOffset = function() { return (this.widthMonth/12) * (12 * (this.start.getFullYear() - this.min) + this.start.getMonth()); }; /** * Get count of full years from start to end */ Bubble.prototype.getFullYears = function() { return ((this.end && this.end.getFullYear()) || this.start.getFullYear()) - this.start.getFullYear(); }; /** * Get count of all months in Timesheet Bubble */ Bubble.prototype.getMonths = function() { var fullYears = this.getFullYears(); var months = 0; if (!this.end) { months += !this.start.hasMonth ? 12 : 1; } else { if (!this.end.hasMonth) { months += 12 - (this.start.hasMonth ? this.start.getMonth() : 0); months += 12 * (fullYears-1 > 0 ? fullYears-1 : 0); } else { months += this.end.getMonth() + 1; months += 12 - (this.start.hasMonth ? this.start.getMonth() : 0); months += 12 * (fullYears-1); } } return months; }; /** * Get bubble's width in pixel */ Bubble.prototype.getWidth = function() { return (this.widthMonth/12) * this.getMonths(); }; /** * Get the bubble's label */ Bubble.prototype.getDateLabel = function() { return [ (this.start.hasMonth ? this.formatMonth(this.start.getMonth() + 1) + '/' : '' ) + this.start.getFullYear(), (this.end ? '-' + ((this.end.hasMonth ? this.formatMonth(this.end.getMonth() + 1) + '/' : '' ) + this.end.getFullYear()) : '') ].join(''); }; window.Timesheet = Timesheet; })(); ================================================ FILE: source/snippets/example-date.js ================================================ new Timesheet('timesheet-default', 2002, 2013, [ ['2002', '09/2002', 'A freaking awesome time', 'lorem'], ['06/2002', '09/2003', 'Some great memories', 'ipsum'], ['2003', 'Had very bad luck'], ['10/2003', '2006', 'At least had fun', 'dolor'], ['02/2005', '05/2006', 'Enjoyed those times as well', 'ipsum'], ['07/2005', '09/2005', 'Bad luck again', 'default'], ['10/2005', '2008', 'For a long time nothing happened', 'dolor'], ['01/2008', '05/2009', 'LOST Season #4', 'lorem'], ['01/2009', '05/2009', 'LOST Season #4', 'lorem'], ['02/2010', '05/2010', 'LOST Season #5', 'lorem'], ['09/2008', '06/2010', 'FRINGE #1 & #2', 'ipsum'] ]); ================================================ FILE: source/stylesheets/_normalize.sass ================================================ article, aside, details, figcaption, figure, footer, header, hgroup, nav, section, summary display: block audio, canvas, video display: inline-block audio:not([controls]) display: none height: 0 [hidden] display: none html font-family: sans-serif -webkit-text-size-adjust: 100% -ms-text-size-adjust: 100% html, body, p, ul, li, h1, h2, h3, h4, h5, img, fieldset, input, textarea, select margin: 0 padding: 0 border: 0 a &:focus outline: 0 &:active, &:hover outline: 0 h1 font-size: 2em h2 font-size: 1em strong font-weight: bold dfn font-style: italic code, kbd, pre, samp font-family: monospace, serif font-size: 1em pre white-space: pre white-space: pre-wrap word-wrap: break-word q quotes: "\201C" "\201D" "\2018" "\2019" small font-size: 80% sub font-size: 75% line-height: 0 position: relative vertical-align: baseline bottom: -0.25em sup font-size: 75% line-height: 0 position: relative vertical-align: baseline top: -0.5em img border: 0 legend border: 0 padding: 0 button, input, select, textarea font-size: 100% textarea, input outline: none input line-height: normal ul, ul li list-style-type: none margin: 0 padding: 0 button::-moz-focus-inner, input::-moz-focus-inner border: 0 padding: 0 textarea overflow: auto vertical-align: top table border-collapse: collapse border-spacing: 0 ================================================ FILE: source/stylesheets/style.sass ================================================ @import 'normalize' html, body margin: 0 padding: 0 width: 100% height: 100% * transition: all 0.5s ease body.black background-color: rgba(61, 61, 61, 1) body.white background-color: rgba(230, 230, 230, 1) #box width: 100% height: 100% #box-inner height: 100% text-align: center vertical-align: middle h1 font-family: 'Open Sans' font-size: 60pt font-weight: 800 line-height: 240px p width: 680px text-align: left font-family: 'Open Sans' font-weight: 400 margin: 20px auto line-height: 24px code pre margin: 0 auto width: 670px padding: 10px 8px font-family: 'Oxygen Mono' font-size: 11px font-weight: 600 code, pre text-align: left .footer margin-top: 65px text-align: center font-size: 12px font-weight: 300 a font-weight: 300 a, a:hover, a:active, a:visited font-weight: 300 a, a:hover, a:visited, a:active font-weight: 600 text-decoration: none .center text-align: center .white h1 color: RGBA(29, 175, 234, 1) p color: rgba(100, 100, 100, 1) p span color: RGBA(29, 175, 234, 1) font-weight: 600 code pre color: rgba(40, 40, 40, 1) background-color: rgba(250, 250, 250, 1) .footer color: rgba(100, 100, 100, 1) a color: rgba(100, 100, 100, 1) span a color: RGBA(29, 175, 234, 1) .black h1 color: rgba(255, 145, 18, 1) p color: rgba(220, 220, 220, 1) code pre background-color: rgba(100, 100, 100, 1) color: rgba(190, 190, 190, 1) a, a:hover, a:visited, a:active, p span color: rgba(255, 145, 18, 1) .footer color: rgba(100, 100, 100, 1) a color: rgba(100, 100, 100, 1) span a color: rgba(255, 145, 18, 1) #example-data margin: 0 auto width: 680px height: 300px background-color: rgba(100, 100, 100, 1) p text-align: right font-size: 12px a color: rgba(255, 145, 18, 1) text-decoration: none .version font-size: 12px line-height: 17px margin: 80px auto a, a:visited, a:hover, a:active font-family: 'Open Sans' color: rgba(255, 145, 18, 1) text-decoration: none span font-weight: 600 color: white span color: rgba(255, 145, 18, 1) font-weight: 600 ================================================ FILE: source/stylesheets/timesheet-white.sass ================================================ .white .timesheet width: 720px height: 292px margin: 0 auto .timesheet border-top: 1px solid rgba(60, 60, 60, 0.3) background-color: rgba(251, 251, 251, 1) position: relative &.color-scheme-default .bubble-default background-color: RGBA(252, 70, 74, 1) // red .bubble-lorem background-color: RGBA(154, 202, 39, 1) // green .bubble-ipsum background-color: RGBA(60, 182, 227, 1) // blue .bubble-dolor background-color: RGBA(244, 207, 48, 1) // yellow .bubble-sit background-color: RGBA(169, 105, 202, 1) // cyan &.color-scheme-alternative .bubble-default background-color: rgba(243, 85, 46, 1) .bubble-lorem background-color: rgba(136, 195, 58, 1) .bubble-ipsum background-color: rgba(67, 106, 224, 1) .bubble-dolor background-color: rgba(244, 210, 52, 1) .bubble-sit background-color: rgba(112, 125, 134, 1) .scale height: 100% position: absolute top: 0 left: 0 float: left section float: left width: 59px color: rgba(50, 50, 50, 0.8) font-family: 'Signika Negative' font-size: 13px line-height: 24px font-weight: 300 border-left: 1px dashed rgba(50, 50, 50, 0.1) height: 100% .data margin: 28px 0 0 0 padding: 0 text-align: left list-style-type: none color: rgba(250, 250, 250, 0.8) font-family: 'Signika Negative' font-size: 13px overflow: hidden li margin: 0 0 3px 0 line-height: 22px height: 21px display: block cursor: pointer clear: both position: relative white-space: nowrap &:hover .bubble opacity: 1 .date color: rgba(121, 121, 121, 1) font-size: 14px .label font-weight: lighter font-size: 14px padding-left: 5px line-height: 21px color: rgba(51, 51, 50, 1) white-space: nowrap .bubble width: 24px height: 7px display: block float: left position: relative top: 7px border-radius: 4px margin: 0 10px 0 0 opacity: 0.7 ================================================ FILE: source/stylesheets/timesheet.sass ================================================ .timesheet width: 720px height: 292px margin: 0 auto .timesheet border-top: 1px solid rgba(250, 250, 250, 0.5) background-color: rgba(51, 51, 51, 1) position: relative &.color-scheme-default .bubble-default background-color: RGBA(252, 70, 74, 1) // red .bubble-lorem background-color: RGBA(154, 202, 39, 1) // green .bubble-ipsum background-color: RGBA(60, 182, 227, 1) // blue .bubble-dolor background-color: RGBA(244, 207, 48, 1) // yellow .bubble-sit background-color: RGBA(169, 105, 202, 1) // cyan &.color-scheme-alternative .bubble-default background-color: rgba(243, 85, 46, 1) .bubble-lorem background-color: rgba(136, 195, 58, 1) .bubble-ipsum background-color: rgba(67, 106, 224, 1) .bubble-dolor background-color: rgba(244, 210, 52, 1) .bubble-sit background-color: rgba(112, 125, 134, 1) .scale height: 100% position: absolute top: 0 left: 0 float: left section float: left width: 59px text-align: center color: rgba(250, 250, 250, 0.8) font-family: 'Signika Negative' font-size: 13px line-height: 24px font-weight: lighter border-left: 1px dashed rgba(250, 250, 250, 0.2) height: 100% .data margin: 28px 0 0 0 padding: 0 text-align: left list-style-type: none color: rgba(250, 250, 250, 0.8) font-family: 'Signika Negative' font-size: 13px overflow: hidden li margin: 0 0 3px 0 line-height: 22px height: 21px display: block clear: both position: relative white-space: nowrap &:hover .bubble opacity: 1 .date color: rgba(181, 181, 181, 1) font-size: 14px .label font-weight: lighter font-size: 14px padding-left: 5px line-height: 21px color: rgba(151, 151, 150, 1) white-space: nowrap .bubble width: 24px height: 7px display: block float: left position: relative top: 7px border-radius: 4px margin: 0 10px 0 0 opacity: 0.7 #timesheet-alternative background-color: RGBA(247, 247, 247, 1) border-radius: 5px section color: RGBA(63, 68, 72, 1) border-left: 1px dashed RGBA(63, 68, 72, 0.2) &:first-child border-left: 1px dashed transparent .date display: none .bubble margin-right: 7px .label padding-left: 0px color: RGBA(48, 48, 48, 1) ================================================ FILE: test/timesheet.js ================================================ /** * Load Timesheet lib and fake a window object … */ window = {}; require(__dirname + '/../source/javascripts/timesheet.js'); var assert = require('assert'); suite('Timesheet', function() { test('Calculation', function(done) { var TS = new window.Timesheet(); assert.equal(12, (TS.createBubble(60, 2012, TS.parseDate('2002'), TS.parseDate('2002'))).getMonths()); assert.equal(12, (TS.createBubble(60, 2012, TS.parseDate('2002'), TS.parseDate('2003'))).getMonths()); assert.equal(24, (TS.createBubble(60, 2012, TS.parseDate('2002'), TS.parseDate('2004'))).getMonths()); assert.equal(9, (TS.createBubble(60, 2012, TS.parseDate('04/2002'), TS.parseDate('2002'))).getMonths()); assert.equal(9, (TS.createBubble(60, 2012, TS.parseDate('04/2002'), TS.parseDate('2003'))).getMonths()); assert.equal(21, (TS.createBubble(60, 2012, TS.parseDate('04/2002'), TS.parseDate('2004'))).getMonths()); assert.equal(13, (TS.createBubble(60, 2012, TS.parseDate('04/2002'), TS.parseDate('04/2003'))).getMonths()); assert.equal(25, (TS.createBubble(60, 2012, TS.parseDate('04/2002'), TS.parseDate('04/2004'))).getMonths()); assert.equal(1, (TS.createBubble(60, 2012, TS.parseDate('04/2002'))).getMonths()); assert.equal(12, (TS.createBubble(60, 2012, TS.parseDate('2002'))).getMonths()); done(); }); });