├── lib ├── side-view │ ├── template.jade │ ├── change.jade │ ├── content.jade │ ├── styles.styl │ ├── component.json │ └── view.js ├── text-section │ ├── template.jade │ ├── change.jade │ ├── styles.styl │ ├── component.json │ └── view.js ├── bill-view │ ├── template.jade │ ├── header.jade │ ├── component.json │ ├── styles.styl │ └── view.js ├── homepage │ ├── template.jade │ ├── homepage.js │ ├── component.json │ └── view.js ├── config │ ├── .gitignore │ ├── sample.json │ ├── component.json │ ├── env.js │ └── index.js ├── db-api │ ├── index.js │ └── bill.js ├── boot │ ├── images │ │ └── favicon.ico │ ├── index.jade │ ├── component.json │ ├── boot.js │ ├── index.styl │ └── index.js ├── bill-diff │ ├── template.jade │ ├── component.json │ ├── styles.styl │ ├── view.js │ └── BillSection.js ├── bill-workflow │ ├── images │ │ ├── hexagon.png │ │ └── time_line.png │ ├── template.jade │ ├── component.json │ ├── view.js │ └── styles.styl ├── translations │ ├── lib │ │ └── en.json │ ├── index.js │ └── component.json ├── view │ ├── styles.styl │ ├── component.json │ └── view.js ├── user │ ├── component.json │ ├── index.js │ └── user.js ├── dashboard │ ├── item.jade │ ├── template.jade │ ├── component.json │ ├── style.styl │ └── view.js ├── user-model │ ├── component.json │ └── model.js ├── render │ ├── component.json │ └── render.js ├── lorem │ └── index.js ├── models │ ├── step.js │ ├── index.js │ ├── bill.js │ └── user.js ├── utils │ └── index.js ├── build │ ├── stylus.js │ ├── index.js │ └── jade.js ├── registration │ └── index.js ├── bill-api │ └── index.js ├── setup │ └── index.js └── fixtures │ ├── bill-terrazas-verdes.json │ └── bills.json ├── Procfile ├── .gitignore ├── bin ├── billtracker ├── billtracker-config ├── billtracker-build ├── billtracker-install └── billtracker-db ├── index.js ├── component.json ├── CONTRIBUTORS.md ├── README.md ├── Makefile ├── LICENSE.txt ├── History.md ├── package.json └── CONTRIBUTING.md /lib/side-view/template.jade: -------------------------------------------------------------------------------- 1 | .side-view -------------------------------------------------------------------------------- /lib/text-section/template.jade: -------------------------------------------------------------------------------- 1 | p 2 | -------------------------------------------------------------------------------- /lib/bill-view/template.jade: -------------------------------------------------------------------------------- 1 | #bill-container -------------------------------------------------------------------------------- /lib/side-view/change.jade: -------------------------------------------------------------------------------- 1 | span(class='{{type}}') {{text}} 2 | -------------------------------------------------------------------------------- /lib/homepage/template.jade: -------------------------------------------------------------------------------- 1 | section.site-content 2 | #homepage 3 | -------------------------------------------------------------------------------- /lib/text-section/change.jade: -------------------------------------------------------------------------------- 1 | span(class='{{type}}') {{text}} 2 | -------------------------------------------------------------------------------- /lib/bill-view/header.jade: -------------------------------------------------------------------------------- 1 | div 2 | h1 {{name}} 3 | p {{description}} 4 | .authors -------------------------------------------------------------------------------- /lib/config/.gitignore: -------------------------------------------------------------------------------- 1 | development.json 2 | testing.json 3 | production.json 4 | client.js -------------------------------------------------------------------------------- /lib/db-api/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Expose bill's database api 3 | */ 4 | 5 | exports.bill = require('./bill'); 6 | -------------------------------------------------------------------------------- /lib/boot/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemocracyOS/bill-tracker/development/lib/boot/images/favicon.ico -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node ./bin/billtracker-install && node ./bin/billtracker-config && node ./bin/billtracker-build && node index.js -------------------------------------------------------------------------------- /lib/bill-diff/template.jade: -------------------------------------------------------------------------------- 1 | div 2 | #bill-diff 3 | .text 4 | 5 | #references 6 | .added Added 7 | .removed Removed 8 | -------------------------------------------------------------------------------- /lib/bill-workflow/images/hexagon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemocracyOS/bill-tracker/development/lib/bill-workflow/images/hexagon.png -------------------------------------------------------------------------------- /lib/bill-workflow/images/time_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemocracyOS/bill-tracker/development/lib/bill-workflow/images/time_line.png -------------------------------------------------------------------------------- /lib/bill-workflow/template.jade: -------------------------------------------------------------------------------- 1 | #workflow-container 2 | .historyWrapper 3 | .scrollbar 4 | .handle 5 | .mousearea 6 | .frame 7 | ul.clearfix -------------------------------------------------------------------------------- /lib/translations/lib/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Bill Tracker", 3 | "about": "Also went here.", 4 | "proposed-by": "Proposed by: ", 5 | "See-this": "See this bill flow" 6 | } -------------------------------------------------------------------------------- /lib/view/styles.styl: -------------------------------------------------------------------------------- 1 | /* Color palette */ 2 | main_blue = #0199c8; 3 | grey_background = #d6d6d6; 4 | page_background = #f8f8f8; 5 | black_color = #636363; 6 | font_color = #565656; 7 | -------------------------------------------------------------------------------- /lib/translations/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var en = require('./lib/en'); 6 | 7 | module.exports.help = function(t) { 8 | // English 9 | t.en = en; 10 | } -------------------------------------------------------------------------------- /lib/side-view/content.jade: -------------------------------------------------------------------------------- 1 | .side-container 2 | button(click-at="toggle").btn.btn-default 3 | i.glyphicon.glyphicon-transfer 4 | div(style='visibility: {{status}}').text-container 5 | p.text {{text}} -------------------------------------------------------------------------------- /lib/config/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 3000, 3 | "mongoUrl": "mongodb://localhost/bill-tracker", 4 | "client": [ 5 | "organization name", 6 | "locale" 7 | ], 8 | "organization name": "bill-tracker", 9 | "locale": "en" 10 | } -------------------------------------------------------------------------------- /lib/translations/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "translations", 3 | "description": "Translations dictionary for DemocracyOS", 4 | "dependencies": {}, 5 | "scripts": [ "index.js" ], 6 | "json": [ "lib/en.json" ], 7 | "main": "index.js" 8 | } -------------------------------------------------------------------------------- /lib/config/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "config", 3 | "description": "Client config component generated dynamicaly by ./bin/billtracker-build", 4 | "dependencies": {}, 5 | "local": [], 6 | "scripts": [ "client.js" ], 7 | "main": "client.js" 8 | } -------------------------------------------------------------------------------- /lib/homepage/homepage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var page = require('page'); 6 | var HomepageView = require('./view'); 7 | 8 | page('/', function(ctx, next) { 9 | 10 | var view = new HomepageView(); 11 | view.replace('body'); 12 | }); -------------------------------------------------------------------------------- /lib/user/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user", 3 | "description": "User component.", 4 | "dependencies": { 5 | "visionmedia/page.js": "1.3.7" 6 | }, 7 | "local": ["user-model"], 8 | "scripts": [ "user.js" ], 9 | "main": "user.js", 10 | "license": "MIT" 11 | } -------------------------------------------------------------------------------- /lib/text-section/styles.styl: -------------------------------------------------------------------------------- 1 | p 2 | position: relative 3 | font-size: 16px; 4 | 5 | &.low-point 6 | opacity: 0.2 7 | 8 | &.changes 9 | opacity: 1 10 | 11 | .add 12 | border-radius: 4px; 13 | color: #333333; 14 | padding: 1px 2px; 15 | background-color: #EAFFEA; 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/github/gitignore/blob/master/Node.gitignore 2 | lib-cov 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | pids 12 | logs 13 | results 14 | 15 | npm-debug.log 16 | node_modules 17 | 18 | # Extra ignores 19 | components 20 | public 21 | .DS_Store 22 | .idea -------------------------------------------------------------------------------- /lib/dashboard/item.jade: -------------------------------------------------------------------------------- 1 | div.item.col-md-4 2 | div.inner 3 | a.law-title-link 4 | h2.law-title {{name}} 5 | 6 | div.author-wrapper {{author}} 7 | 8 | 9 | p.description {{description}} 10 | 11 | a(href='#', click-at="{{index}}").btn.btn-block.btn-primary.btn-discuss {{enter}} 12 | -------------------------------------------------------------------------------- /lib/user-model/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-model", 3 | "description": "User model component.", 4 | "dependencies": { 5 | "component/emitter": "1.1.2", 6 | "visionmedia/superagent": "0.18.0" 7 | }, 8 | "local": [], 9 | "scripts": [ "model.js" ], 10 | "main": "model.js", 11 | "license": "MIT" 12 | } -------------------------------------------------------------------------------- /bin/billtracker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var program = require('commander'); 8 | 9 | program 10 | .version('0.0.1') 11 | .command('install', "Install client components.") 12 | .command('build', "Builds client application and compiles assets.") 13 | .parse(process.argv) -------------------------------------------------------------------------------- /lib/config/env.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var env = process.env; 6 | 7 | /** 8 | * Expose heroku helper 9 | */ 10 | 11 | module.exports = { 12 | port: env.PORT, 13 | mongoUrl: env.MONGOHQ_URL, 14 | client: env.CLIENT_CONF ? env.CLIENT_CONF.split(',') : [ "organization name", "locale" ], 15 | locale: env.LOCALE 16 | } -------------------------------------------------------------------------------- /lib/render/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "render", 3 | "description": "Component render utility for Bill Tracker", 4 | "dependencies": { 5 | "component/domify": "1.2.2", 6 | "cristiandouce/merge-util": "0.1.0", 7 | "component/t": "1.0.0" 8 | }, 9 | "local": [ "config" ], 10 | "scripts": [ "render.js" ], 11 | "main": "render.js" 12 | } -------------------------------------------------------------------------------- /lib/lorem/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('express'); 6 | var app = module.exports = express(); 7 | var lorem = require('lorem-ipsum'); 8 | 9 | app.get("/", function(req, res, next) { 10 | var options = { 11 | units:'paragraphs', 12 | count: '10' 13 | }; 14 | res.send(lorem(options)); 15 | }); 16 | 17 | -------------------------------------------------------------------------------- /lib/user/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('express'); 6 | var mongoose = require('mongoose'); 7 | var app = module.exports = express(); 8 | var User = mongoose.model('User'); 9 | 10 | app.get("/users/me", function(req, res, next) { 11 | if (req.isAuthenticated()) return res.json(req.user); 12 | res.json({}); 13 | }); -------------------------------------------------------------------------------- /lib/view/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "view", 3 | "description": "Base view component", 4 | "dependencies": { 5 | "component/dom": "1.0.5", 6 | "component/emitter": "1.1.1", 7 | "component/inherit": "0.0.3", 8 | "cristiandouce/query": "0.0.1", 9 | "yields/empty": "0.0.1" 10 | }, 11 | "local": [ "render" ], 12 | "scripts": [ "view.js" ], 13 | "main": "view.js" 14 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var http = require('http'); 6 | var express = require('express'); 7 | var app = module.exports = require('lib/boot'); 8 | var debug = require('debug')('billtracker'); 9 | 10 | /** 11 | * Launch server 12 | */ 13 | 14 | http.createServer(app).listen(app.get('port'), function() { 15 | debug('Application started on port %d', app.get('port')); 16 | }); -------------------------------------------------------------------------------- /lib/models/step.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Extend module's NODE_PATH 3 | * HACK: temporary solution 4 | */ 5 | require('node-path')(module); 6 | 7 | var mongoose = require('mongoose'); 8 | var Schema = mongoose.Schema; 9 | 10 | /** 11 | * Step Schema 12 | * @type {Schema} 13 | */ 14 | StepSchema = new Schema({ 15 | number : { type: Number, required: true, default: 0 } 16 | , arriveAt : { type: Date, default: Date.now } 17 | }); 18 | -------------------------------------------------------------------------------- /lib/dashboard/template.jade: -------------------------------------------------------------------------------- 1 | .nav 2 | 3 | .about 4 | h1.title Bill Tracker 5 | p.content Track bills as they move through the legislative process. Compare versions and follow changes as they are introduced. This tracker is developed by the 6 | a(href='http://democracyos.org/') DemocracyOS team 7 | | and is open source. 8 | a(href='https://github.com/DemocracyOS/bill-tracker') Fork it 9 | |, try it, improve it. 10 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bill-tracker", 3 | "version": "0.0.4", 4 | "description": "Bill Tracker is an online tool for tracking bills in their lifespan.", 5 | "dependencies": {}, 6 | "paths": [ 7 | "lib" 8 | ], 9 | "local": [ 10 | "boot" 11 | ], 12 | "keywords": [ 13 | "nodejs", 14 | "express", 15 | "mongoose", 16 | "passport", 17 | "component", 18 | "jade" 19 | ], 20 | "license": "MIT" 21 | } 22 | -------------------------------------------------------------------------------- /lib/boot/index.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html 3 | head 4 | title Bill tracker 5 | meta(name="viewport",content="width=device-width, initial-scale=1.0") 6 | link(href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css", rel="stylesheet", media="screen") 7 | link(rel='stylesheet', href='/app.css') 8 | 9 | body 10 | section.site-content 11 | 12 | aside.nav 13 | 14 | script(src='/app.js') 15 | script. 16 | require('boot') -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | ## Contributors 2 | 3 | ### Active Contributors 4 | 5 | #### Development 6 | 7 | * [Mariano Vicente](https://github.com/vmariano) 8 | * [Guido Vilariño](http://twitter.com/gvilarino) 9 | * [Sacha Lifszyc](https://twitter.com/slifszyc) 10 | 11 | #### Original Development from [NodeJS Starter](https://github.com/gravityonmars/nodejs-starter) 12 | 13 | * [Ricardo Rauch](http://twitter.com/gravityonmars) 14 | * [Cristian Douce Suarez](http://twitter.com/cristiandouce) -------------------------------------------------------------------------------- /lib/boot/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "boot", 3 | "description": "Application Booting component.", 4 | "dependencies": { 5 | "component/t": "1.0.0", 6 | "visionmedia/page.js": "1.3.7" 7 | }, 8 | "local": [ 9 | "homepage", 10 | "user", 11 | "translations", 12 | "config" 13 | ], 14 | "scripts": [ "boot.js" ], 15 | "styles": ["index.styl"], 16 | "images": [ 17 | "images/favicon.ico" 18 | ], 19 | "main": "boot.js", 20 | "license": "MIT" 21 | } -------------------------------------------------------------------------------- /lib/text-section/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "text-section", 3 | "description": "", 4 | "dependencies": { 5 | "visionmedia/jade": "1.3.1", 6 | "component/dom": "1.0.5" 7 | , "ripplejs/ripple": "0.5.3" 8 | }, 9 | "local": [ 10 | "view" 11 | , "side-view" 12 | ], 13 | "scripts": [ 14 | "view.js" 15 | ], 16 | "styles": ["styles.styl"], 17 | "templates": [ 18 | "template.jade" 19 | , "change.jade" 20 | ], 21 | "main": "view.js" 22 | } -------------------------------------------------------------------------------- /lib/homepage/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homepage", 3 | "description": "Homepage page.", 4 | "dependencies": { 5 | "visionmedia/page.js": "1.3.7" 6 | , "visionmedia/jade": "1.3.1" 7 | , "component/emitter": "1.1.1" 8 | }, 9 | "local": [ 10 | "view", 11 | "bill-workflow", 12 | "bill-view", 13 | "dashboard", 14 | "bill-diff" 15 | ], 16 | "scripts": [ 17 | "homepage.js", 18 | "view.js" 19 | ], 20 | "templates": [ "template.jade" ], 21 | "main": "homepage.js" 22 | } -------------------------------------------------------------------------------- /lib/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var has = Object.prototype.hasOwnProperty; 6 | var crypto = require('crypto'); 7 | var log = require('debug')('billtracker:utils'); 8 | 9 | /** 10 | * HOP ref. 11 | */ 12 | 13 | exports.has = has; 14 | 15 | /** 16 | * MD5 hash generator. 17 | * 18 | * @param {String} string String to encrypt. 19 | * @return {String} MD5 encrypted string. 20 | * @api public 21 | */ 22 | 23 | exports.md5 = function md5(string) { 24 | return crypto.createHash('md5').update(string).digest("hex"); 25 | } -------------------------------------------------------------------------------- /lib/bill-diff/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bill-diff", 3 | "description": "Diff for bills page.", 4 | "dependencies": { 5 | "visionmedia/jade": "1.3.1", 6 | "component/dom": "1.0.5", 7 | "visionmedia/superagent" : "0.18.0", 8 | "visionmedia/move.js": "0.4.0", 9 | "vmariano/jsdiff": "*" 10 | }, 11 | "local": [ 12 | "view" 13 | , "text-section" 14 | ], 15 | "scripts": [ 16 | "view.js", 17 | "BillSection.js" 18 | ], 19 | "styles": ["styles.styl"], 20 | "templates": [ 21 | "template.jade" 22 | ], 23 | "main": "view.js" 24 | } 25 | -------------------------------------------------------------------------------- /bin/billtracker-config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var fs = require('fs'); 8 | var write = fs.writeFileSync; 9 | var path = require('path'); 10 | var resolve = path.resolve; 11 | 12 | // Little hack to include `NODE_PATH=.` 13 | require('node-path')(module, [resolve('.')]); 14 | 15 | // Compile client's config file 16 | var config = require('lib/config'); 17 | var client = {}; 18 | 19 | config.client.forEach(function(k) { 20 | client[k] = config[k]; 21 | }); 22 | 23 | write(resolve('lib/config/client.js'), 'module.exports = ' + JSON.stringify(client)); 24 | -------------------------------------------------------------------------------- /lib/boot/boot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var translations = require('translations'); 6 | var t = require('t'); 7 | var page = require('page'); 8 | var user = require('user'); 9 | var config = require('config'); 10 | 11 | /** 12 | * Load localization dictionaries to translation application 13 | */ 14 | 15 | translations.help(t); 16 | 17 | /** 18 | * Init `t` component with locale as `es` 19 | */ 20 | 21 | t.lang(config['locale']); 22 | 23 | /** 24 | * Boot components 25 | * and pages. 26 | */ 27 | 28 | require('homepage'); 29 | 30 | /** 31 | * Boot page.js 32 | */ 33 | 34 | page(); -------------------------------------------------------------------------------- /lib/render/render.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var domify = require('domify'); 6 | var merge = require('merge-util'); 7 | 8 | /** 9 | * Render default modules 10 | */ 11 | 12 | var translation = require('t'); 13 | var config = require('config'); 14 | 15 | exports = module.exports = render; 16 | exports.dom = dom; 17 | 18 | function render(template, options) { 19 | var defaults = { 20 | t: translation, 21 | config: config 22 | }; 23 | 24 | return template(merge(defaults, options, true)); 25 | } 26 | 27 | function dom(template, options) { 28 | return domify(render(template, options)); 29 | } 30 | -------------------------------------------------------------------------------- /lib/build/stylus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var stylus = require('stylus'); 6 | 7 | module.exports = function (options) { 8 | options = options || {}; 9 | 10 | return function plugin(file, done) { 11 | if (file.extension !== 'styl') return done(); 12 | file.read(function (err, string) { 13 | if (err) return done(err); 14 | 15 | var renderer = stylus(string); 16 | file.extension = 'css'; 17 | renderer.render(function (err, css) { 18 | if(err) { 19 | done(err); 20 | } else { 21 | file.string = css; 22 | done(); 23 | } 24 | }); 25 | }); 26 | } 27 | } -------------------------------------------------------------------------------- /lib/models/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Module dependencies 3 | */ 4 | 5 | var mongoose = require('mongoose'); 6 | 7 | /** 8 | * Expose models linker helper 9 | * 10 | * @param {Express} app `Express` instance 11 | */ 12 | 13 | module.exports = function models (app) { 14 | 15 | /* 16 | * Connect to mongo 17 | */ 18 | 19 | mongoose.connect(app.get('config').mongoUrl, { db: { safe: true }}); 20 | 21 | /** 22 | * Register `User` model 23 | */ 24 | 25 | require('./user'); 26 | 27 | /** 28 | * Register 'Bill' model 29 | */ 30 | require('./bill'); 31 | 32 | /** 33 | * Register 'Step' model. 34 | */ 35 | require('./step'); 36 | 37 | }; 38 | -------------------------------------------------------------------------------- /bin/billtracker-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var fs = require('fs'); 8 | var write = fs.writeFileSync; 9 | var path = require('path'); 10 | var resolve = path.resolve; 11 | 12 | // Little hack to include `NODE_PATH=.` 13 | require('node-path')(module, [resolve('.')]); 14 | 15 | // Compile client application `./public/app.js` and `./public/app.css` 16 | var build = require('lib/build'); 17 | var builder = build.createBuilder(); 18 | builder.build(function(err, res) { 19 | if (err) return console.log(err), process.exit(1); 20 | write('public/app.js', res.require + res.js); 21 | write('public/app.css', res.css); 22 | }); 23 | -------------------------------------------------------------------------------- /lib/user/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var page = require('page'); 6 | var User = require('user-model'); 7 | 8 | /** 9 | * Instantiate and expose user 10 | */ 11 | var user = module.exports = new User(); 12 | 13 | user.load("me"); 14 | 15 | user.required = function(ctx, next) { 16 | if ("unloaded" === user.state()) { 17 | setTimeout(loggedout, 0); 18 | } else if ("loading" === user.state()) { 19 | user.once('error', loggedout); 20 | } 21 | user.ready(function() { 22 | user.off('error', loggedout); 23 | next(); 24 | }); 25 | }; 26 | 27 | function loggedout () { 28 | console.log('user logged out'); 29 | page('/') 30 | } 31 | -------------------------------------------------------------------------------- /lib/side-view/styles.styl: -------------------------------------------------------------------------------- 1 | .side-view 2 | padding-bottom: 20px 3 | position: absolute 4 | right: 0 5 | top: 0 6 | width: 45px 7 | min-height: 100% 8 | height: 100% 9 | left: 462px; 10 | 11 | .side-container 12 | button 13 | display: inline 14 | 15 | .text-container 16 | position: absolute 17 | background-color: #ffffff 18 | display: block 19 | left: 46px 20 | border-radius: 6px; 21 | top: 0 22 | 23 | p 24 | padding: 6px 25 | margin: 0 26 | opacity: 1 27 | 28 | .remove 29 | border-radius: 4px 30 | color: #333333 31 | padding: 1px 2px; 32 | background-color: #FFECEC -------------------------------------------------------------------------------- /lib/side-view/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "side-view", 3 | "description": "Side view for show up text over the difference of bills.", 4 | "dependencies": { 5 | "visionmedia/jade": "1.3.1", 6 | "component/closest": "0.1.3", 7 | "component/emitter": "1.1.1", 8 | "ripplejs/ripple": "0.5.3", 9 | "ripplejs/each": "0.3.0", 10 | "moment/moment": "2.7.0", 11 | "visionmedia/superagent" : "0.18.0" 12 | , "component/dom": "1.0.5" 13 | }, 14 | "local": [ 15 | "view" 16 | ], 17 | "scripts": [ 18 | "view.js" 19 | ], 20 | "styles": ["styles.styl"], 21 | "templates": [ 22 | "template.jade" 23 | , "content.jade" 24 | , "change.jade" 25 | ], 26 | "main": "view.js" 27 | } -------------------------------------------------------------------------------- /lib/bill-workflow/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bill-workflow", 3 | "description": "Workflow for bills page.", 4 | "dependencies": { 5 | "visionmedia/jade": "1.3.1", 6 | "component/dom": "1.0.5", 7 | "component/closest": "0.1.3", 8 | "component/emitter": "1.1.1", 9 | "components/font-awesome": "4.1.0", 10 | "ripplejs/ripple": "0.5.3", 11 | "moment/moment": "2.7.0", 12 | "visionmedia/superagent" : "0.18.0" 13 | }, 14 | "local": [ 15 | "view" 16 | ], 17 | "scripts": [ 18 | "view.js" 19 | ], 20 | "styles": ["styles.styl"], 21 | "images": [ 22 | "images/hexagon.png", 23 | "images/time_line.png" 24 | ], 25 | "templates": [ "template.jade" ], 26 | "main": "view.js" 27 | } -------------------------------------------------------------------------------- /lib/bill-view/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bill-view", 3 | "description": "Basic bill information for bills page.", 4 | "dependencies": { 5 | "visionmedia/jade": "1.3.1", 6 | "component/dom": "1.0.5", 7 | "component/closest": "0.1.3", 8 | "component/emitter": "1.1.1", 9 | "ripplejs/ripple": "0.5.3", 10 | "ripplejs/each": "0.3.0", 11 | "moment/moment": "2.7.0", 12 | "visionmedia/superagent" : "0.18.0" 13 | , "DemocracyOs/proposer" : "0.0.2" 14 | , "component/t": "1.0.0" 15 | 16 | }, 17 | "local": [ 18 | "view" 19 | ], 20 | "scripts": [ 21 | "view.js" 22 | ], 23 | "styles": ["styles.styl"], 24 | "templates": [ 25 | "template.jade" 26 | , "header.jade" 27 | ], 28 | "main": "view.js" 29 | } -------------------------------------------------------------------------------- /lib/dashboard/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard", 3 | "description": "Dashboard of all items", 4 | "dependencies": { 5 | "component/domify": "1.2.0", 6 | "component/events": "1.0.5", 7 | "component/emitter": "1.1.1", 8 | "visionmedia/debug": "0.7.4", 9 | "visionmedia/jade": "1.1.5", 10 | "component/t": "1.0.0", 11 | "cristiandouce/query": "0.0.1", 12 | "visionmedia/superagent" : "0.18.0", 13 | "ripplejs/ripple": "0.5.3", 14 | "DemocracyOs/proposer": "0.0.2", 15 | "DemocracyOS/clamp.js": "0.0.1" 16 | }, 17 | "local": [ 18 | "view" 19 | ], 20 | "scripts": ["view.js" ], 21 | "templates": [ 22 | "item.jade", 23 | "template.jade" 24 | ], 25 | "styles": [ "style.styl" ], 26 | "main": "view.js" 27 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bill Tracker 2 | Bill Tracker is an online tool for tracking bills in their lifespan 3 | 4 | ## Installation 5 | 6 | Please refer to the [Installation](https://github.com/DemocracyOS/bill-tracker/wiki/Installation) wiki page for detailed instructions on how to install and setup your instance of Bill Tracker. 7 | 8 | ## Contributing 9 | 10 | Please see [CONTRIBUTING.md](https://github.com/DemocracyOS/bill-tracker/blob/development/CONTRIBUTING.md) for further details. 11 | 12 | ## Contributors 13 | 14 | See [CONTRIBUTORS.md](https://github.com/DemocracyOS/bill-tracker/blob/development/CONTRIBUTORS.md) to get to know the Bill Tracker team and contributors. 15 | 16 | ## Browser support 17 | 18 | We support real browsers and IE10+ 19 | 20 | ## License 21 | 22 | Bill Tracker is open source software under the MIT license. Please see full terms in the [LICENSE.txt](https://github.com/DemocracyOS/bill-tracker/blob/development/LICENSE.txt) file. 23 | -------------------------------------------------------------------------------- /lib/dashboard/style.styl: -------------------------------------------------------------------------------- 1 | .nav 2 | 3 | .about 4 | margin 0 auto 5 | width 50%; 6 | 7 | h1.title 8 | margin 0 auto 9 | max-width 250px 10 | 11 | p.content 12 | padding-bottom 30px 13 | padding-top 20px 14 | line-height 1.3em 15 | font-size 20px 16 | 17 | .item 18 | min-height: 590px 19 | display: inline-block 20 | 21 | .inner 22 | padding: 10px 23 | background-color: #EBEFF0 24 | margin-bottom 10px 25 | min-height 320px; 26 | 27 | .law-title 28 | overflow: hidden 29 | text-overflow: ellipsis 30 | width: 400px 31 | white-space: nowrap; 32 | 33 | .author-wrapper 34 | margin 5px 0; 35 | 36 | .proposer 37 | padding 0 10px 38 | 39 | .description 40 | display -webkit-box 41 | overflow: hidden 42 | text-overflow: ellipsis 43 | line-height 1.3em 44 | max-height: 320px -------------------------------------------------------------------------------- /lib/bill-diff/styles.styl: -------------------------------------------------------------------------------- 1 | #bill-diff 2 | margin: 0 auto; 3 | max-width: 1100px; 4 | 5 | .text 6 | border-radius: 6px; 7 | padding: 52px 20px; 8 | display: block; 9 | background: #ffffff; 10 | font-size: 16px; 11 | line-height: 28px; 12 | -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.1); 13 | -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.1); 14 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.1); 15 | width: 500px 16 | margin: 0 auto; 17 | 18 | #references:after, #references:before 19 | content: ' '; 20 | display: table; 21 | 22 | #references:after 23 | clear: both; 24 | 25 | #references 26 | margin: 10px auto 0; 27 | max-width: 1100px; 28 | padding: 0 30px; 29 | 30 | .added,.removed 31 | float: right; 32 | padding: 5px 10px; 33 | margin: 15px 0 0; 34 | color: #333333; 35 | -webkit-border-radius: 4px; 36 | -moz-border-radius: 4px; 37 | border-radius: 4px; 38 | 39 | .added 40 | background-color: #EAFFEA; 41 | 42 | .removed 43 | background-color: #FFECEC -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifndef DEBUG 2 | DEBUG="billtracker*" 3 | endif 4 | 5 | ifndef NODE_ENV 6 | NODE_ENV="development" 7 | endif 8 | 9 | run: node_modules components config build 10 | @echo "Booting application..." 11 | @NODE_PATH=. DEBUG=$(DEBUG) node index.js 12 | 13 | node_modules: 14 | @echo "Installing dependencies..." 15 | @npm install 16 | 17 | components: 18 | @echo "Installing components..." 19 | @node ./bin/billtracker-install --config 20 | 21 | config: 22 | @echo "Updating config settings..." 23 | @node ./bin/billtracker-config 24 | 25 | build: 26 | @echo "Compiling components to ./public..." 27 | @node ./bin/billtracker-build 28 | 29 | clean: 30 | @echo "Removing dependencies, components and built assets." 31 | @rm -rf components node_modules public 32 | @echo "Done.\n" 33 | 34 | dropdb: 35 | @node ./bin/billtracker-db drop 36 | @echo "Data droped" 37 | 38 | initdb: 39 | @echo "Bootstrapping db" 40 | @node ./bin/billtracker-db load bill ./lib/fixtures/bills.json 41 | 42 | reloaddb: dropdb initdb 43 | 44 | .PHONY: run build clean dropdb initdb 45 | 46 | -------------------------------------------------------------------------------- /lib/bill-view/styles.styl: -------------------------------------------------------------------------------- 1 | /*TODO: replace with https://github.com/rschmukler/component-stylus-plugin */ 2 | @import "./lib/view/styles.styl" 3 | 4 | #bill-container 5 | margin: 0 auto; 6 | max-width: 1100px; 7 | padding: 0 30px; 8 | 9 | h1 10 | margin: 0 0 10px; 11 | color: #0099c7; 12 | 13 | h1:before 14 | content: "\f15c"; 15 | margin: 0 10px 0 0; 16 | display: inline-block; 17 | font-family: FontAwesome; 18 | font-style: normal; 19 | font-weight: normal; 20 | line-height: 1; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale; 23 | 24 | .authors 25 | margin: 10px 0; 26 | 27 | div 28 | margin: 0 10px; 29 | p 30 | margin: 0; 31 | padding: 0; 32 | 33 | p:first-child 34 | color: #0099c7; 35 | margin: 0; 36 | 37 | p:last-child 38 | color: #777777; 39 | font-size: 14px; 40 | line-height: 22px; 41 | margin: 0 0 0 6px; 42 | 43 | #references 44 | margin: 0 auto; 45 | display: block 46 | position: fixed 47 | height: 50px 48 | left: 0; 49 | right: 0; 50 | bottom: 0; 51 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014, [Democracia en Red](http://democraciaenred.org) - Bill Tracker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/config/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Extend module's NODE_PATH 3 | * HACK: temporary solution 4 | */ 5 | 6 | require('node-path')(module); 7 | 8 | /** 9 | * Module dependencies. 10 | */ 11 | 12 | var log = require('debug')('billtracker:config'); 13 | var environment = process.env.NODE_ENV || 'development'; 14 | var resolve = require('path').resolve; 15 | var merge = require('merge-util'); 16 | var utils = require('lib/utils'); 17 | var has = utils.has; 18 | var md5 = utils.md5; 19 | var envConf = require('./env'); 20 | 21 | var filepath = resolve(__dirname, environment + ".json"); 22 | 23 | var localConf = {}; 24 | 25 | try { 26 | log('Load local configuration from %s', filepath); 27 | localConf = require(filepath); 28 | } catch (e) { 29 | log('Found error: %s', e.message); 30 | } 31 | 32 | log('Merge environment set configuration variables'); 33 | var conf = merge(localConf, envConf); 34 | conf.env = environment; 35 | 36 | log('Loaded config object for env %s with sha %j', environment, md5(JSON.stringify(conf))); 37 | 38 | module.exports = config; 39 | 40 | function config(key) { 41 | if (has.call(conf, key)) return conf[key]; 42 | log('Invalid config key "%s"', key); 43 | throw new Error('Invalid config key "' + key + '"'); 44 | } 45 | 46 | for (var key in conf) config[key] = conf[key]; -------------------------------------------------------------------------------- /lib/build/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var Builder = require('component-builder'); 6 | var stylus = require('component-stylus-plugin'); 7 | var jade = require('./jade'); 8 | var fs = require('fs'); 9 | var write = fs.writeFileSync; 10 | var debug = require('debug')('billtracker:build'); 11 | 12 | /** 13 | * Expose component 14 | * builder getter 15 | */ 16 | 17 | module.exports.createBuilder = createBuilder; 18 | 19 | /** 20 | * Expose component 21 | * build middleware. 22 | */ 23 | 24 | module.exports.middleware = middleware; 25 | 26 | /** 27 | * Creates a `Builder` instance 28 | * ready for build 29 | */ 30 | 31 | function createBuilder () { 32 | var builder = new Builder('.'); 33 | builder.copyFiles(); 34 | builder.addLookup('lib'); 35 | builder.copyAssetsTo('public'); 36 | builder.use(jade); 37 | builder.use(stylus); 38 | 39 | return builder; 40 | } 41 | 42 | /** 43 | * Express build middleware 44 | */ 45 | 46 | function middleware (req, res, next) { 47 | // Build only script and stylesheet 48 | if (!/app\.(js|css)/.test(req.path)) { 49 | return next(); 50 | }; 51 | 52 | debug('Build %s.', req.path); 53 | 54 | var builder = createBuilder(); 55 | builder.build(function(err, res){ 56 | if (err) return next(err); 57 | 58 | debug('Write script and stylesheet'); 59 | write('public/app.js', res.require + res.js); 60 | write('public/app.css', res.css); 61 | next(); 62 | }); 63 | }; -------------------------------------------------------------------------------- /lib/boot/index.styl: -------------------------------------------------------------------------------- 1 | body 2 | background-size: cover; 3 | -webkit-font-smoothing: antialiased; 4 | background-color: #ffffff 5 | 6 | h1, h2, h3 7 | font-weight: bold; 8 | 9 | 10 | a 11 | font-weight: bold 12 | 13 | .btn 14 | letter-spacing: 2px; 15 | text-transform: uppercase 16 | 17 | header 18 | h1 19 | font-size: 60px; 20 | margin: 50px 0 30px 0; 21 | h5 22 | font-size: 11px; 23 | font-style: italic 24 | opacity: .6 25 | a 26 | font-weight: normal 27 | 28 | .site-content 29 | background: #fff 30 | margin: 0 31 | padding: 0 32 | 33 | .description 34 | font-size: 18px; 35 | line-height: 26px; 36 | margin-bottom: 30px; 37 | h4 38 | margin-top: 30px; 39 | .social-login 40 | .btn 41 | margin-right: 20px; 42 | font-size: 11px; 43 | 44 | a.btn.btn-get-started 45 | border: 0; 46 | background: white; 47 | color: black; 48 | font-size: 12px; 49 | padding: 20px 30px; 50 | 51 | pre.code-box 52 | text-align: left; 53 | border: 0; 54 | padding: 20px; 55 | border-radius: 0; 56 | margin-top: 20px; 57 | h5 58 | margin-top: 0; 59 | p 60 | padding-left: 0; 61 | 62 | .share-links 63 | iframe 64 | margin-right: 20px; 65 | 66 | #homepage 67 | margin: 20px 0; 68 | 69 | aside.nav 70 | top: 0 71 | bottom: 0 72 | width: 200px 73 | border-right: 1px #BDC3C7 74 | overflow-y: scroll 75 | background: #ECF0F1 76 | position: fixed 77 | -------------------------------------------------------------------------------- /lib/boot/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('express'); 6 | var app = module.exports = express(); 7 | var translations = require('lib/translations'); 8 | var t = require('t-component'); 9 | var favicon = require('serve-favicon'); 10 | var config = require('lib/config'); 11 | 12 | /** 13 | * Set `views` directory for module 14 | */ 15 | 16 | app.set('views', __dirname); 17 | 18 | /** 19 | * Set `view engine` to `jade`. 20 | */ 21 | 22 | app.set('view engine', 'jade'); 23 | 24 | /** 25 | * middleware for favicon 26 | */ 27 | 28 | app.use(favicon(__dirname + '/images/favicon.ico')); 29 | 30 | /** 31 | * Config application 32 | */ 33 | 34 | require('lib/setup')(app); 35 | 36 | /** 37 | * Link models with 38 | * mongoDB database 39 | */ 40 | 41 | require('lib/models')(app); 42 | 43 | /** 44 | * Load user routes 45 | * API service 46 | */ 47 | 48 | app.use("/api", require('lib/user')); 49 | 50 | /** 51 | * To generate random lorem ipsum text. 52 | */ 53 | app.use("/lorem", require('lib/lorem')); 54 | 55 | /** 56 | * API Bill 57 | */ 58 | app.use("/api/bill", require('lib/bill-api')); 59 | 60 | /** 61 | * Load localization dictionaries to translation application 62 | */ 63 | 64 | translations.help(t); 65 | 66 | /** 67 | * Init `t-component` component with parameter locale 68 | */ 69 | 70 | t.lang(config('locale')); 71 | 72 | /** 73 | * GET index page. 74 | */ 75 | 76 | app.get('*', function(req, res) { 77 | res.render('index'); 78 | }); 79 | -------------------------------------------------------------------------------- /lib/view/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var dom = require('dom'); 6 | var empty = require('empty'); 7 | var Emitter = require('emitter'); 8 | var inherit = require('inherit'); 9 | var o = require('query'); 10 | var render = require('render'); 11 | 12 | /** 13 | * Expose View 14 | */ 15 | 16 | module.exports = View; 17 | 18 | /** 19 | * Create View class 20 | */ 21 | 22 | function View(template, locals) { 23 | if (!(this instanceof View)) 24 | return inherit(template, View); 25 | 26 | Emitter.call(this); 27 | this.template = template; 28 | this.locals = locals || {}; 29 | this.build(); 30 | } 31 | 32 | inherit(View, Emitter); 33 | 34 | /** 35 | * Build view's `el` 36 | */ 37 | View.prototype.build = function() { 38 | this.el = dom(render.dom(this.template, this.locals)); 39 | }; 40 | 41 | /** 42 | * Renders to provided `el` 43 | * or delivers view's `el` 44 | * 45 | * @param {Element} el 46 | * @return {Element} 47 | * @api public 48 | */ 49 | View.prototype.render = function(el) { 50 | return this.el; 51 | }; 52 | 53 | /** 54 | * Replace `el` content 55 | * with View's `el` 56 | */ 57 | View.prototype.replace = function(el) { 58 | // if string, then query element 59 | if ('string' === typeof el) { 60 | el = o(el); 61 | } 62 | 63 | this.wrapper = dom(el); 64 | return this.refresh(); 65 | }; 66 | 67 | /** 68 | * Render `this.el` inside 69 | * an empty `this.wrapper` 70 | */ 71 | View.prototype.refresh = function() { 72 | this.wrapper.empty().append(this.render()); 73 | return this; 74 | }; -------------------------------------------------------------------------------- /bin/billtracker-install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | var fs = require('fs'); 7 | var exists = fs.existsSync; 8 | var read = fs.readFileSync; 9 | var write = fs.writeFileSync; 10 | var parse = JSON.parse; 11 | var stringify = JSON.stringify; 12 | var path = require('path'); 13 | var resolve = path.resolve; 14 | var merge = require('merge-util'); 15 | var exec = require('child_process').exec; 16 | var program = require('commander'); 17 | var installc = resolve('./node_modules/.bin/component-install'); 18 | var env = process.env.NODE_ENV || 'development'; 19 | 20 | program 21 | .option('-c, --config', 'copy and/or merge configuration file') 22 | .option('-C, --no-components', 'ignore components install') 23 | .parse(process.argv); 24 | 25 | 26 | /** 27 | * Make sure there is a config file 28 | */ 29 | 30 | if (program.config) { 31 | var dest = resolve('./lib/config/' + env + '.json'); 32 | var orig = resolve('./lib/config/sample.json'); 33 | if (!exists(dest)) { 34 | // non-simple copying 35 | write(dest, read(orig)); 36 | } else { 37 | var conf = parse(read(dest)); 38 | var sample = parse(read(orig)); 39 | 40 | // Merge current config into original sample 41 | merge(sample, conf); 42 | 43 | // save config file 44 | write(dest, stringify(sample, null, 2)); 45 | } 46 | }; 47 | 48 | if (program.components && exists(installc)) { 49 | exec(installc, function(err, stdout, stderr) { 50 | if (stdout.length) console.log(stdout); 51 | if (stderr.length) return console.log(stderr), process.exit(1); 52 | if (err != null) return console.log(err), process.exit(1); 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /lib/side-view/view.js: -------------------------------------------------------------------------------- 1 | var View = require('view'); 2 | var template = require('./template'); 3 | var changeTemplate = require('./change'); 4 | var content = require('./content'); 5 | var ripple = require('ripple'); 6 | var dom = require('dom'); 7 | 8 | function SideView(changes) { 9 | if (!this instanceof SideView) { 10 | return new SideView(changes); 11 | } 12 | 13 | View.call(this, template); 14 | this.status = 'hidden'; 15 | this.attachedView = {}; 16 | this.diff = changes; 17 | } 18 | 19 | //TODO: maybe should remove the dependency of Global view, and use as ripple view. 20 | View(SideView); 21 | 22 | SideView.prototype.toggle = function () { 23 | var viewStatus = this.attachedView.get('status'); 24 | if ('hidden' === viewStatus) { 25 | this.attachedView.set('status', 'visible'); 26 | } else { 27 | this.attachedView.set('status', 'hidden'); 28 | } 29 | }; 30 | 31 | SideView.prototype.bindActions = function (callback) { 32 | var Template = ripple(content()); 33 | var status = 'hidden'; //Initial status 34 | 35 | Template.directive('click-at', function (index, el, view) { 36 | el.addEventListener('click', callback); 37 | }); 38 | 39 | this.attachedView = new Template({ 40 | status: status 41 | }); 42 | 43 | this.el.append(this.attachedView.el); 44 | this.addChanges(); 45 | }; 46 | 47 | SideView.prototype.addChanges = function () { 48 | var self = this; 49 | this.diff.forEach(function (change) { 50 | var changeView = ripple(changeTemplate()); 51 | var changeHtml = new changeView(change); 52 | dom('.text', self.attachedView.el).append(changeHtml.el); 53 | }); 54 | }; 55 | 56 | module.exports = SideView; 57 | -------------------------------------------------------------------------------- /lib/bill-view/view.js: -------------------------------------------------------------------------------- 1 | var View = require('view'); 2 | var template = require('./template'); 3 | var header = require('./header'); 4 | var dom = require('dom'); 5 | var request = require('superagent'); 6 | var ripple = require('ripple'); 7 | var each = require('each'); 8 | var proposer = require('proposer'); 9 | var t = require('t'); 10 | 11 | module.exports = BillView; 12 | 13 | function BillView() { 14 | if (!this instanceof BillView) { 15 | return new BillView(); 16 | } 17 | 18 | View.call(this, template); 19 | } 20 | 21 | View(BillView); 22 | 23 | /** 24 | * Request from . 25 | * @param {String} billId Bill id to request steps. 26 | */ 27 | BillView.prototype.loadBill = function (billId) { 28 | var billUrl = '/api/bill/' + billId; 29 | var self = this; 30 | request.get(billUrl).end(function (res) { 31 | var bill = res.body; 32 | self.createBillView(bill); 33 | }); 34 | }; 35 | 36 | /** 37 | * Create a Bill view from bill s data 38 | * @param {Object} bill 39 | */ 40 | BillView.prototype.createBillView = function (bill) { 41 | var Template = ripple(header()); 42 | var billTemplate = new Template(bill); 43 | 44 | //FIXME: this should be fixed with ripple template instead of add/remove mechanism. 45 | this.el.empty(); 46 | this.el.append(billTemplate.el); 47 | 48 | var self = this; 49 | bill.authors.forEach(function (author) { 50 | self.addAuthor(author); 51 | }); 52 | 53 | }; 54 | 55 | /** 56 | * Append authors of the bill to the current bill view. 57 | * @param author 58 | */ 59 | BillView.prototype.addAuthor = function (author) { 60 | author.authorTitle = t("proposed-by"); 61 | var userProfile = new proposer(author); 62 | dom('#bill-container .authors').append(userProfile.el); 63 | }; -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.0.4 / 2014-12-01 3 | ================== 4 | 5 | * Replace fixture br by \n 6 | * Add picture profile to the all fixture authors. 7 | * #37 Add about 8 | * #36 fix main pages styles. 9 | * #36 Remove unused image. 10 | * - Replace of side view for dashboard 11 | - add author view. 12 | - Added elipsis text for long descriptions. 13 | - Remove sidebar references 14 | * #39 fix colors of references. 15 | * #28 Update proposer icon 16 | * #35 Add real bill project to test the tool 17 | 18 | 0.0.3 / 2014-11-18 19 | ================== 20 | 21 | * #29 Update diff view - Add/Remove over the same text. 22 | - Change color styles. 23 | - Add the status 'add' to transform as text. 24 | - Refactor text introducing the concept of "section" 25 | - Change jade template to ripple template. 26 | - Add slide to left on toggle side view. 27 | * Add delete and reload db data. 28 | 29 | 0.0.2 / 2014-11-06 30 | ================== 31 | * #26 Remove dates bill's header. 32 | * #19 Add fancy diff for bill's text 33 | * #22 Update calls to load sidebar elements in the main view. 34 | * #20 Add DOS favicon 35 | * #15 Add sidebar to the main view. 36 | * #12 Fix reverse text comparation on bills text. 37 | * #10 Save/show structure bill's text 38 | * #5 Add new bill data. for text diference 39 | * #7 Add diff view for bill's text. 40 | * Fix MONGOHQ_URL env variable 41 | * Add missing log package 42 | * Fix billtracker-config 43 | * Log resolve path 44 | * Create/update inital styles. 45 | * #3 Initial data set 46 | * #2 Basic schema and crud data 47 | * #1 Add initial api endpoints related to 48 | * Add basic prototype view for bill workflow. 49 | * Fix load langs in client side. 50 | * Update node js startar old files and add new one 51 | * Initial commit based on NodeJS Starter 52 | -------------------------------------------------------------------------------- /lib/registration/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var User = require('mongoose').model('User'); 6 | 7 | 8 | /** 9 | * Facebook Registration 10 | * 11 | * @param {Object} profile PassportJS's profile 12 | * @param {Function} fn Callback accepting `err` and `user` 13 | * @api public 14 | */ 15 | 16 | exports.facebook = function facebook (profile, fn) { 17 | var user = new User(); 18 | 19 | user.firstName = profile.name.givenName; 20 | user.lastName = profile.name.familyName; 21 | user.email = profile.emails[0].value; 22 | user.profiles.facebook = profile; 23 | user.avatar = getImageUrl(profile, user.email) 24 | 25 | user.save(function(err) { 26 | return fn(err, user); 27 | }); 28 | } 29 | 30 | /** 31 | * Twitter Registration 32 | * 33 | * @param {Object} profile PassportJS's profile 34 | * @param {Function} fn Callback accepting `err` and `user` 35 | * @api public 36 | */ 37 | 38 | exports.twitter = function twitter (profile, fn) { 39 | var user = new User(); 40 | 41 | user.fullName = profile.displayName; 42 | user.profiles.twitter = profile; 43 | user.avatar = profile.photos[0].value; 44 | 45 | user.save(function(err) { 46 | return fn(err, user); 47 | }); 48 | } 49 | 50 | /** 51 | * Get image url for profile 52 | * 53 | * @param {Object} profile 54 | * @param {String} email 55 | * @return {String} Profile image url (or `avatar`) 56 | * @api private 57 | */ 58 | 59 | function getImageUrl (profile, email) { 60 | return profile.imageUrl 61 | || 'http://gravatar.com/avatar/'.concat(md5(email)).concat('?d=mm&size=200') 62 | || ''; 63 | } 64 | 65 | /** 66 | * MD5 67 | * 68 | * @param {String} source 69 | * @return {String} target 70 | * @api private 71 | */ 72 | 73 | function md5 (source) { 74 | return require('crypto') 75 | .createHash('md5') 76 | .update(source) 77 | .digest("hex"); 78 | } -------------------------------------------------------------------------------- /lib/text-section/view.js: -------------------------------------------------------------------------------- 1 | var View = require('view'); 2 | var template = require('./template'); 3 | var changeTemplate = require('./change'); 4 | var SideView = require('side-view'); 5 | var ripple = require('ripple'); 6 | 7 | function TextSection(paragraph) { 8 | 9 | if (!this instanceof TextSection) { 10 | return new TextSection(paragraph); 11 | } 12 | 13 | View.call(this, template); 14 | this.createSection(paragraph); 15 | } 16 | 17 | View(TextSection); 18 | 19 | /** 20 | * Add/remove class to highlight changes. 21 | */ 22 | TextSection.prototype.toggleChanges = function () { 23 | if (this.el.hasClass('changes')) { 24 | this.el.removeClass('changes'); 25 | this.emit('section:side-view:hide'); 26 | } else { 27 | this.el.addClass('changes'); 28 | this.emit('section:side-view:show'); 29 | } 30 | }; 31 | 32 | /** 33 | * This fill the section from the phagraph model. 34 | */ 35 | TextSection.prototype.createSection = function (paragraph) { 36 | var self = this; 37 | if (paragraph.mainChanges.length) { 38 | paragraph.mainChanges.forEach(function (change) { 39 | var aChangeTemplate = ripple(changeTemplate()); 40 | var changeView = new aChangeTemplate(change); 41 | self.el.append(changeView.el); 42 | }); 43 | } else { 44 | this.el.text(paragraph.current); 45 | } 46 | 47 | // To show only if there are some change. 48 | //FIXME: this shouldn't be a filter function 49 | var changes = paragraph.diff.changes.filter(function (element) { 50 | return 'unchange' !== element.type; 51 | }); 52 | //Fixme: refactor this weird relations 53 | if (changes.length) { 54 | var sideView = new SideView(paragraph.sideChanges); 55 | this.el.append(sideView.el); 56 | sideView.bindActions(function () { 57 | sideView.toggle(); 58 | self.toggleChanges(); 59 | }); 60 | } 61 | }; 62 | 63 | module.exports = TextSection; 64 | 65 | 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bill-tracker", 3 | "version": "0.0.4", 4 | "description": "Bill Tracker is an online tool for tracking bills in their lifespan.", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/DemocracyOS/bill-tracker.git" 9 | }, 10 | "keywords": [ 11 | "nodejs", 12 | "express", 13 | "mongoose", 14 | "passport", 15 | "component", 16 | "jade" 17 | ], 18 | "author": "Democracia en Red ", 19 | "contributors": [ 20 | { 21 | "name": "Mariano Vicente", 22 | "email": "m.vicent@gmail.com" 23 | }, 24 | { 25 | "name": "Guido Vilariño", 26 | "email": "gvilarino@gmail.com" 27 | }, 28 | { 29 | "name": "Sacha Lifszyc", 30 | "email": "sacha.lifszyc@gmail.com" 31 | } 32 | ], 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/DemocracyOs/bill-tracker/issues" 36 | }, 37 | "engines": { 38 | "node": "0.10.15", 39 | "npm": "1.3.5" 40 | }, 41 | "dependencies": { 42 | "passport": "~0.2.0", 43 | "express": "4.4.3", 44 | "express-session": "v1.3.1", 45 | "cookie-parser": "1.1.0", 46 | "body-parser": "1.3.1", 47 | "errorhandler": "1.0.2", 48 | "compression": "1.0.7", 49 | "nowww": "~1.1.2", 50 | "serve-favicon": "2.0.1", 51 | "jade": "~0.35.0", 52 | "passport-facebook": "~1.0.2", 53 | "passport-twitter": "~1.0.2", 54 | "mongoose": "~3.6.17", 55 | "connect-mongo": "0.4.1", 56 | "string-to-js": "0.0.1", 57 | "commander": "~2.0.0", 58 | "component-stylus-plugin": "~0.2.3", 59 | "component": "~0.19.5", 60 | "component-builder": "~0.10.1", 61 | "merge-util": "0.1.0", 62 | "node-path": "0.0.3", 63 | "crypto": "0.0.3", 64 | "debug": "~0.7.4", 65 | "t-component": "1.0.0", 66 | "lorem-ipsum": "1.0.1", 67 | "co-prompt": "~1.0.0" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/models/bill.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Extend module's NODE_PATH 3 | * HACK: temporary solution 4 | */ 5 | 6 | require('node-path')(module); 7 | /** 8 | * Module dependencies. 9 | */ 10 | var mongoose = require('mongoose'); 11 | var Schema = mongoose.Schema; 12 | 13 | var ClauseSchema = new Schema({ 14 | clauseName: { type: String, required: true } 15 | , order: { type: Number, required: true, default: 0 } 16 | , text: { type: String, required: true } 17 | }); 18 | 19 | var BillTextSchema = new Schema({ 20 | officialTitle: { type: String, required: true } 21 | , mediaTitle: { type: String, required: true } 22 | , remoteUri: { type: String, required: true } 23 | , summary: { type: String, required: true } 24 | , clauses: [ClauseSchema] 25 | }); 26 | 27 | //TODO: unify with step.js 28 | 29 | /** 30 | * Step Schema 31 | * @type {Schema} 32 | */ 33 | var StepSchema = new Schema({ 34 | number: { type: Number, required: true, default: 0 } 35 | , name: { type: String, required: true } 36 | , arriveAt : { type: Date, default: Date.now } 37 | , billText: { type: Object , ref:"BillTextSchema" } 38 | }); 39 | 40 | /** 41 | * Author Schema 42 | * Represent the participants that impulse the bill. 43 | * @type {Schema} 44 | */ 45 | var AuthorSchema = new Schema({ 46 | name : { type: String, required: true } 47 | , afiliation : { type: String } 48 | , image : { type: String } 49 | }); 50 | 51 | /** 52 | * Bill Schema 53 | * @type {Schema} 54 | */ 55 | var BillSchema = new Schema({ 56 | billId: { type: String, required: true } 57 | , name: { type: String, required: false } 58 | , status: { type: String, enum: ['started', 'stopped', 'finished'], default: 'started', required: true } 59 | , description: { type: String, required: false } 60 | , authors : [AuthorSchema] 61 | , steps: [StepSchema] 62 | }); 63 | 64 | BillSchema.index({ billId: -1 }); 65 | 66 | module.exports = mongoose.model('Bill', BillSchema); 67 | -------------------------------------------------------------------------------- /lib/homepage/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var template = require('./template'); 6 | var View = require('view'); 7 | var WorkflowView = require('bill-workflow'); 8 | var DiffView = require('bill-diff'); 9 | var BillView = require('bill-view'); 10 | var Dashboard = require('dashboard'); 11 | 12 | /** 13 | * Creates `HomepageView` 14 | */ 15 | function HomepageView() { 16 | if (!(this instanceof HomepageView)) { 17 | return new HomepageView(); 18 | } 19 | 20 | View.call(this, template); 21 | this.appendElementsToMainView(); 22 | } 23 | 24 | /** 25 | * Inherit from View 26 | */ 27 | View(HomepageView); 28 | 29 | 30 | HomepageView.prototype.appendElementsToMainView = function () { 31 | var mainView = this.el.find('section.site-content #homepage'); 32 | 33 | this.dashboard = new Dashboard(); 34 | mainView.append(this.dashboard.el); 35 | 36 | this.billView = new BillView(); 37 | mainView.append(this.billView.el); 38 | 39 | this.workflowView = new WorkflowView(); 40 | mainView.append(this.workflowView.el); 41 | 42 | this.diffView = new DiffView(); 43 | mainView.append(this.diffView.el); 44 | this.workflowView.on('step:change', this.diffView.changeBillContent.bind(this.diffView)); 45 | 46 | this.dashboard.on('dashboard:click-at', this.billView.loadBill.bind(this.billView)); 47 | this.dashboard.on('dashboard:click-at', this.workflowView.loadSteps.bind(this.workflowView)); 48 | this.dashboard.on('dashboard:click-at', this.toggleViews.bind(this)); 49 | 50 | this.hideBillView(); 51 | }; 52 | 53 | HomepageView.prototype.toggleViews = function () { 54 | this.dashboard.el.attr('style', 'display:none'); 55 | this.showBill(); 56 | }; 57 | 58 | //Fixme: hide/show should be part of the view. This is a hack. 59 | HomepageView.prototype.hideBillView = function () { 60 | this.billView.el.attr('style', 'display:none'); 61 | this.workflowView.el.attr('style', 'display:none'); 62 | this.diffView.el.attr('style', 'display:none'); 63 | }; 64 | 65 | HomepageView.prototype.showBill = function () { 66 | this.billView.el.attr('style', ''); 67 | this.workflowView.el.attr('style', ''); 68 | this.diffView.el.attr('style', ''); 69 | }; 70 | 71 | /** 72 | * Expose HomepageView 73 | */ 74 | module.exports = HomepageView; 75 | -------------------------------------------------------------------------------- /lib/bill-workflow/view.js: -------------------------------------------------------------------------------- 1 | var View = require('view'); 2 | var template = require('./template'); 3 | var dom = require('dom'); 4 | var Emitter = require('emitter'); 5 | var request = require('superagent'); 6 | var ripple = require('ripple'); 7 | var moment = require('moment'); 8 | 9 | module.exports = WorkflowView; 10 | 11 | function WorkflowView() { 12 | if (!this instanceof WorkflowView) { 13 | return new WorkflowView(); 14 | } 15 | 16 | View.call(this, template); 17 | } 18 | 19 | View(WorkflowView); 20 | 21 | /** 22 | * Click over some step to set active. 23 | * @param {Object} el Dom node. 24 | * @param {Object} step Step data. 25 | * @param {Object} prevStep previous step 26 | */ 27 | WorkflowView.prototype.onClickStep = function (el, step, prevStep) { 28 | var currentStep = step; 29 | this.el.find('li').removeClass('selected'); 30 | dom(el).addClass('selected'); 31 | this.emit('step:change', currentStep, prevStep); 32 | }; 33 | 34 | /** 35 | * Request for steps of some bill and fill template with data. 36 | * . 37 | * @param {String} billId Bill id to request steps. 38 | */ 39 | WorkflowView.prototype.loadSteps = function (billId) { 40 | var billUrl = '/api/bill/' + billId + '/step/all'; 41 | var self = this; 42 | 43 | //FIXME: This should be remove on empty 44 | this.el.find('ul').empty(); 45 | 46 | request.get(billUrl).end(function (res) { 47 | var steps = res.body; 48 | steps.forEach(function (step, index) { 49 | self.createStepView(step, steps[index - 1]); 50 | }); 51 | 52 | //FIXME: this trigger the click on the first load. 53 | //The view should came with the element flag as "current" 54 | var el = self.el.find('li')[0]; 55 | self.onClickStep(el,steps[0],null); 56 | }); 57 | }; 58 | 59 | /** 60 | * Create view and bind events in StepView from step data 61 | * @param {Object} step current step 62 | * @param {Object} prevStep Previous step 63 | */ 64 | WorkflowView.prototype.createStepView = function (step, prevStep) { 65 | var Template = ripple('
  • ' + 66 | '' + 67 | '

    {{arriveAt}}

    ' + 68 | '

    {{name}}

    ' + 69 | '
  • '); 70 | 71 | var self = this; 72 | Template.directive('on-click', function (number, el, view) { 73 | el.addEventListener('click', function () { 74 | self.onClickStep(el, step, prevStep); 75 | }); 76 | }); 77 | 78 | step.arriveAt = moment(step.arriveAt).format("DD-MM-YYYY"); 79 | var stepTemplate = new Template(step); 80 | stepTemplate.appendTo('.frame ul'); 81 | }; 82 | -------------------------------------------------------------------------------- /lib/build/jade.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var jade = require('jade'); 7 | var path = require('path'); 8 | var extname = path.extname; 9 | var fs = require('fs'); 10 | var read = fs.readFileSync; 11 | 12 | /** 13 | * HTML template plugin used to convert 14 | * .html files to require()-able javascript 15 | * on the fly. 16 | * 17 | * @param {Builder} builder 18 | * @api public 19 | */ 20 | 21 | module.exports = function(builder) { 22 | builder.hook('before scripts', jadeCompiler); 23 | builder.hook('before templates', jadeCleaner); 24 | }; 25 | 26 | /** 27 | * Compile `.jade` files into executable 28 | * `Jade` compiled functions. Hooks before 29 | * scripts execution 30 | * 31 | * @param {Package} pkg 32 | * @param {Function} done 33 | * @api private 34 | */ 35 | 36 | function jadeCompiler(pkg, done) { 37 | 38 | if (!pkg.config.templates) return done(); 39 | 40 | // check if we have .templates in component.json 41 | var files = pkg.config.templates.filter(jadeFilter); 42 | 43 | files.forEach(compile); 44 | 45 | function compile(file) { 46 | var filepath = pkg.path(file); 47 | var content = read(filepath, 'utf8'); 48 | 49 | var compiled = 'var jade = require("jade");\n' 50 | + 'module.exports = ' 51 | + jade.compile(content, { 52 | client: true, 53 | pretty: true, 54 | filename: filepath, 55 | compileDebug: false 56 | }); 57 | 58 | // consider using `.jade` extension and 59 | // then be allowed to import `jade`s like 60 | // `var template = require('template.jade');` 61 | pkg.addFile('scripts', file.slice(0, -5) + ".js", compiled); 62 | } 63 | 64 | done(); 65 | } 66 | 67 | /** 68 | * Clean `.jade` files from `templates` 69 | * avoiding to register jade strings 70 | * and sent to client 71 | * 72 | * @param {Package} pkg 73 | * @param {Function} done 74 | * @api private 75 | */ 76 | 77 | function jadeCleaner(pkg, done) { 78 | if (!pkg.config.templates) return done(); 79 | 80 | var files = pkg.config.templates.filter(jadeFilter); 81 | 82 | files.forEach(remove); 83 | 84 | function remove(file) { 85 | pkg.removeFile('templates', file); 86 | } 87 | 88 | done(); 89 | } 90 | 91 | /** 92 | * Filter for templates array 93 | * looking for `.jade` templates 94 | * 95 | * @param {String} filename 96 | * @return {Function} filter func 97 | * @api private 98 | */ 99 | 100 | function jadeFilter(filename) { 101 | return '.jade' === extname(filename); 102 | } -------------------------------------------------------------------------------- /lib/bill-diff/view.js: -------------------------------------------------------------------------------- 1 | var View = require('view'); 2 | var template = require('./template'); 3 | var dom = require('dom'); 4 | var BillTransformer = require('./BillSection'); 5 | var TextSection = require('text-section'); 6 | var move = require('move'); 7 | 8 | function DiffView() { 9 | if (!this instanceof DiffView) { 10 | return new DiffView(); 11 | } 12 | 13 | View.call(this, template); 14 | this.sections = []; 15 | } 16 | 17 | module.exports = DiffView; 18 | 19 | View(DiffView); 20 | 21 | /** 22 | * Change bill content for the moment only when click in some step. 23 | * 24 | * @param {StepSchema} currentStep The Step Schema to show up in the view. 25 | * @param {StepSchema} previousStep Previous Step for calculateDiff difference. 26 | */ 27 | DiffView.prototype.changeBillContent = function (currentStep, previousStep) { 28 | //TODO: the only purpose of have 2 steps in the same view, is to be compare them. 29 | // Maybe this should be resolved outside of this view. 30 | //Here should append the current text, and anchor the differences, calculated. 31 | 32 | var section = new BillTransformer(); 33 | var currentParagraph = section.transformToSection(currentStep); 34 | if (previousStep) { 35 | var previousParagraph = section.transformToSection(previousStep); 36 | currentParagraph = section.calculateDiff(currentParagraph, previousParagraph); 37 | } 38 | 39 | this.showBillText(currentParagraph); 40 | }; 41 | 42 | /** 43 | * Move the main text to the left. 44 | */ 45 | DiffView.prototype.collapseLeftText = function () { 46 | move('#bill-diff .text') 47 | .to(-250, 0) 48 | .duration('1.0s') 49 | .end(); 50 | dom('#bill-diff .text p').addClass('low-point'); 51 | }; 52 | 53 | /** 54 | * Center the main text in the middle of the screen 55 | */ 56 | DiffView.prototype.centerText = function () { 57 | move('#bill-diff .text') 58 | .to(0, 0) 59 | .duration('1.0s') 60 | .end(); 61 | dom('#bill-diff .text p').removeClass('low-point'); 62 | }; 63 | 64 | /** 65 | * Convert the model called 'paragraphs to html for show on the template. 66 | * 67 | * @param paragraphs 68 | */ 69 | DiffView.prototype.showBillText = function (paragraphs) { 70 | var textContainer = dom('#bill-diff .text'); 71 | var self = this; 72 | textContainer.text(''); //Clean up the text container. 73 | 74 | paragraphs.forEach(function (paragraph) { 75 | var section = new TextSection(paragraph); 76 | section.on('section:side-view:hide', self.centerText.bind(self)); 77 | section.on('section:side-view:show', self.collapseLeftText.bind(self)); 78 | textContainer.append(section.el); 79 | }); 80 | }; 81 | -------------------------------------------------------------------------------- /lib/user-model/model.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var request = require('superagent'); 6 | var Emitter = require('emitter'); 7 | 8 | /** 9 | * Expose user model 10 | */ 11 | 12 | module.exports = User; 13 | 14 | /** 15 | * User 16 | * 17 | * @param {String} path user's load path 18 | * @return {User} `User` instance 19 | * @api public 20 | */ 21 | 22 | function User (path) { 23 | if (!(this instanceof User)) { 24 | return new User(path); 25 | }; 26 | 27 | this.$_path = path; 28 | this.$_ready = "unloaded"; 29 | } 30 | 31 | /** 32 | * Inherit from `Emitter` 33 | */ 34 | 35 | Emitter(User.prototype); 36 | 37 | /** 38 | * Loads user from path 39 | * 40 | * @param {String} path user's load path 41 | * @return {User} `User` instance. 42 | * @api public 43 | */ 44 | 45 | User.prototype.load = function(path) { 46 | var _this = this; 47 | this.$_path = path || this.$_path; 48 | this.$_ready = "loading"; 49 | 50 | request 51 | .get('/api/users/'.concat(this.$_path)) 52 | .set('Accept', 'application/json') 53 | .on('error', _handleRequestError.bind(this)) 54 | .end(function(res) { 55 | var u = res.body; 56 | 57 | if (!res.ok) { 58 | return _handleRequestError.bind(_this)(res.error); 59 | }; 60 | 61 | if (!(u.id || u._id)) { 62 | return _handleRequestError.bind(_this)('User not found'); 63 | }; 64 | 65 | for (var prop in u) { 66 | if (u.hasOwnProperty(prop)) { 67 | _this[prop] = u[prop] 68 | } 69 | } 70 | _this.$_ready = "loaded"; 71 | _this.emit('ready'); 72 | }); 73 | 74 | return this; 75 | } 76 | 77 | /** 78 | * Call `fn` once User is 79 | * ready from loading 80 | * 81 | * @param {Function} fn callback fired on ready 82 | * @return {User} `User` instance 83 | * @api public 84 | */ 85 | 86 | User.prototype.ready = function(fn) { 87 | var _this = this; 88 | 89 | function done() { 90 | if ("loaded" === _this.state()) { 91 | return fn(); 92 | } 93 | } 94 | 95 | if ("loaded" === this.state()) { 96 | setTimeout(done, 0); 97 | } else { 98 | this.once("ready", done); 99 | } 100 | 101 | return this; 102 | } 103 | 104 | /** 105 | * Get $_ready state 106 | * 107 | * @return {String} 108 | * @api public 109 | */ 110 | 111 | User.prototype.state = function() { 112 | return this.$_ready; 113 | } 114 | 115 | /** 116 | * Handle error from requests 117 | * 118 | * @param {Object} err from request 119 | * @api private 120 | */ 121 | 122 | function _handleRequestError (err) { 123 | this.$_ready = "unloaded"; 124 | this.emit('error', err); 125 | } -------------------------------------------------------------------------------- /lib/dashboard/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var template = require('./template'); 5 | var View = require('view'); 6 | var request = require('superagent'); 7 | var itemTemplate = require('./item'); 8 | var ripple = require('ripple'); 9 | var Proposer = require('proposer'); 10 | var t = require('t'); 11 | var query = require('query'); 12 | var clamp = require('clampjs'); 13 | 14 | /** 15 | * Create Dashboard List view container 16 | */ 17 | function Dashboard() { 18 | if (!(this instanceof Dashboard)) { 19 | return new Dashboard(); 20 | } 21 | 22 | // Define template 23 | View.call(this, template); 24 | 25 | 26 | /** 27 | * Model of each item on the list. 28 | * @type {Array} 29 | */ 30 | this.items = []; 31 | 32 | this.loadItems(); 33 | } 34 | 35 | //Extend from view. 36 | View(Dashboard); 37 | 38 | /** 39 | * Load the whole model for fill the sidebar. 40 | */ 41 | Dashboard.prototype.loadItems = function () { 42 | var billUrl = '/api/bill/small/all/'; 43 | var self = this; 44 | //TODO: remove the api call, and move to homeview. 45 | request.get(billUrl).end(function (res) { 46 | var listItems = res.body; 47 | listItems.forEach(function (bill, index) { 48 | bill.index = index; 49 | self.items.push(bill); 50 | self.createDashboardItem(bill); 51 | }); 52 | }); 53 | }; 54 | 55 | /** 56 | * From some model create the list element. 57 | * And bind click action 58 | * 59 | * @param {Object} item a model with the attributes to be created 60 | */ 61 | Dashboard.prototype.createDashboardItem = function (item) { 62 | var Template = ripple(itemTemplate()); 63 | var self = this; 64 | Template.directive('click-at', function (index, el, view) { 65 | el.addEventListener('click', function () { 66 | self.clickAt(index); 67 | }); 68 | }); 69 | 70 | item.enter = t('See-this'); 71 | item.image = 'imageUrl'; 72 | var itemView = new Template(item); 73 | this.el.append(itemView.el); 74 | 75 | //Add author in the dashboard items. 76 | item.authors.forEach(function (author) { 77 | author.authorTitle = t("proposed-by"); 78 | var proposerView = new Proposer(author); 79 | var authorWrapper = query('.author-wrapper', itemView.el); 80 | authorWrapper.appendChild(proposerView.el); 81 | }); 82 | clamp(query('.description', itemView.el), {clamp: '160px' }); 83 | }; 84 | 85 | /** 86 | * Trigger the event sidebar:click-at when the user click on 87 | * some element of the list. 88 | */ 89 | Dashboard.prototype.clickAt = function (index) { 90 | var reduceBill = this.items[index]; 91 | this.emit('dashboard:click-at', reduceBill.billId); 92 | }; 93 | 94 | /** 95 | * Expose Dashboard 96 | */ 97 | module.exports = Dashboard; 98 | -------------------------------------------------------------------------------- /lib/models/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var mongoose = require('mongoose'); 6 | var Schema = mongoose.Schema; 7 | var ObjectId = Schema.ObjectId; 8 | 9 | 10 | /** 11 | * Define `User` Schema 12 | */ 13 | 14 | var UserSchema = new Schema({ 15 | firstName: { type: String } 16 | , lastName: { type: String } 17 | , username: { type: String } 18 | , avatar: { type: String } 19 | , email: { type: String, lowercase: true, trim: true } // main email 20 | , profiles: { 21 | facebook: { type: Object } 22 | , twitter: { type: Object } 23 | } 24 | , createdAt: { type: Date, default: Date.now } 25 | , updatedAt: { type: Date } 26 | }); 27 | 28 | /** 29 | * Define Schema Indexes for MongoDB 30 | */ 31 | 32 | UserSchema.index({ firstName:1, lastName:1 }); 33 | 34 | /** 35 | * Define Schema toObject options 36 | */ 37 | 38 | UserSchema.set('toObject', { getters: true }); 39 | UserSchema.set('toJSON', { getters: true }); 40 | 41 | /** 42 | * -- Model's Plugin Extensions 43 | */ 44 | 45 | // Nothing here yet 46 | 47 | /** 48 | * -- Model's API Extension 49 | */ 50 | 51 | /** 52 | * Get `fullName` from `firstName` and `lastName` 53 | * 54 | * @return {String} fullName 55 | * @api public 56 | */ 57 | 58 | UserSchema.virtual('fullName').get(function() { 59 | return this.firstName + ' ' + this.lastName; 60 | }); 61 | 62 | /** 63 | * Set `fullName` from `String` param splitting 64 | * and calling firstName as first value and lastName 65 | * as the concatenation of the rest values 66 | * 67 | * @param {String} name 68 | * @return {User} 69 | * @api public 70 | */ 71 | 72 | UserSchema.virtual('fullName').set(function(name) { 73 | var split = name.split(' '); 74 | if(split.length) { 75 | this.firstName = split.shift(); 76 | this.lastName = split.join(' '); 77 | } 78 | 79 | return this; 80 | }); 81 | 82 | /** 83 | * Find `User` by its email 84 | * 85 | * @param {String} email 86 | * @return {Error} err 87 | * @return {User} user 88 | * @api public 89 | */ 90 | 91 | UserSchema.statics.findByEmail = function(email, cb) { 92 | return this.findOne({ email: email }) 93 | .exec(cb); 94 | } 95 | 96 | /** 97 | * Find `User` by social provider id 98 | * 99 | * @param {String|Number} id 100 | * @param {String} social 101 | * @return {Error} err 102 | * @return {User} user 103 | * @api public 104 | */ 105 | 106 | UserSchema.statics.findByProvider = function(profile, cb) { 107 | var path = 'profiles.'.concat(profile.provider).concat('.id'); 108 | var query = {}; 109 | query[path] = profile.id; 110 | return this.findOne(query) 111 | .exec(cb); 112 | } 113 | 114 | /** 115 | * Expose `User` Model 116 | */ 117 | 118 | module.exports = mongoose.model('User', UserSchema); -------------------------------------------------------------------------------- /lib/bill-diff/BillSection.js: -------------------------------------------------------------------------------- 1 | var jsdiff = require('jsdiff'); 2 | 3 | /** 4 | * Utility to transform from {StepSchema} to {TextSection}. 5 | * 6 | * Waiting for a better name. 7 | * As additional type, this constructor is also an Autobot. 8 | * (Desepticons not allowed) 9 | * 10 | * @returns {BillTransformer} 11 | * @constructor 12 | */ 13 | function BillTransformer() { 14 | if (!this instanceof BillTransformer) { 15 | return new BillTransformer(); 16 | } 17 | } 18 | 19 | /** 20 | * Model for each bill's section. 21 | * 22 | * @param current 23 | * @param diff 24 | * @constructor 25 | */ 26 | function TextSection(current, diff) { 27 | this.current = current || ''; 28 | this.diff = diff || { changes: [] }; 29 | this.sideChanges = []; 30 | this.mainChanges = []; 31 | } 32 | 33 | /** 34 | * Apply the transform from the {StepSchema} to {BillTransformer} 35 | * 36 | * @param {StepSchema} currentStep The current step. can't be null. 37 | * 38 | * @returns {TextSection[]} 39 | */ 40 | BillTransformer.prototype.transformToSection = function (currentStep) { 41 | var textSections = []; 42 | var paragraphClauses = []; 43 | textSections.push(new TextSection(currentStep.billText.mediaTitle)); 44 | textSections.push(new TextSection(currentStep.billText.summary)); 45 | 46 | //Transform each clause in to TextSection. 47 | paragraphClauses = currentStep.billText.clauses.map(function (clause) { 48 | return new TextSection(clause.clauseName + "\n" + clause.text); 49 | }); 50 | 51 | //Merge and return each section 52 | return textSections.concat(paragraphClauses); 53 | }; 54 | 55 | /** 56 | * 57 | * @param {BillTransformer} currentSection. can't be null. 58 | * @param {BillTransformer} previousSection. can't be null. 59 | * 60 | * @returns {BillTransformer} current step. 61 | */ 62 | BillTransformer.prototype.calculateDiff = function (currentSection, previousSection) { 63 | // if is the step 1, this shouldn't compare. 64 | var maxLength = currentSection.length; 65 | 66 | for (var i=0; i