├── app ├── assets │ ├── img │ │ ├── .gitignore │ │ ├── g0v.png │ │ ├── logo.png │ │ ├── 300x200.gif │ │ ├── favicon.ico │ │ ├── htc-one.jpg │ │ ├── glyphicons.png │ │ ├── glyphicons-halflings.png │ │ └── glyphicons-halflings-white.png │ └── font │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff ├── partials │ ├── profile.jade │ ├── tags.jade │ ├── debtclock.jade │ ├── partial1.jade │ ├── partial2.jade │ ├── vote.jade │ ├── partial3.jade │ ├── login.jade │ ├── partial4.jade │ └── nav.jade ├── init.ls ├── styles │ ├── vote.sass │ ├── treemap.css │ ├── tags.sass │ ├── themes │ │ └── custom │ │ │ ├── _overrides.less │ │ │ └── _variables.less │ ├── heading.sass │ ├── debtclock.sass │ ├── tooltip.css │ ├── styles.sass │ ├── bubbles.sass │ ├── treemap.sass │ └── app.scss ├── views │ └── widgets │ │ └── googlenews.jade ├── tax.tw.ls ├── spending.ls ├── app │ ├── directives.ls │ ├── services.ls │ ├── filters.ls │ └── controllers.ls ├── currency.ls ├── app.ls ├── info.ls ├── dailybread.js ├── CustomTooltip.js ├── unitmapper.ls ├── index.static.jade ├── testd3.ls └── dailybread.ls ├── worker └── item-site-crawler │ └── .gitignore ├── scripts ├── compile-tests.sh ├── init.sh └── test.sh ├── thumbnail.png ├── .gitignore ├── .gitmodules ├── environment.json ├── vendor ├── styles │ ├── themes │ │ ├── default │ │ │ ├── _overrides.less │ │ │ └── _variables.less │ │ ├── sapling │ │ │ ├── _overrides.less │ │ │ └── _variables.less │ │ └── smokey │ │ │ ├── _overrides.less │ │ │ └── _variables.less │ ├── sapling │ │ ├── _account.less │ │ ├── _angular.less │ │ ├── _sticky-footer.less │ │ ├── _breadcrumbs.less │ │ └── _auth.less │ └── bootstrap │ │ ├── _layouts.scss │ │ ├── _component-animations.scss │ │ ├── _utilities.scss │ │ ├── _grid.scss │ │ ├── _breadcrumbs.scss │ │ ├── _hero-unit.scss │ │ ├── _responsive-768px-979px.scss │ │ ├── _wells.scss │ │ ├── _responsive-1200px-min.scss │ │ ├── _accordion.scss │ │ ├── _close.scss │ │ ├── _pager.scss │ │ ├── _scaffolding.scss │ │ ├── _responsive-utilities.scss │ │ ├── _alerts.scss │ │ ├── _thumbnails.scss │ │ ├── _code.scss │ │ ├── _pagination.scss │ │ ├── _tooltip.scss │ │ ├── _labels-badges.scss │ │ ├── _modals.scss │ │ ├── _carousel.scss │ │ ├── _reset.scss │ │ ├── _progress-bars.scss │ │ ├── _popovers.scss │ │ ├── _responsive-767px-max.scss │ │ ├── _responsive-navbar.scss │ │ ├── _type.scss │ │ ├── _dropdowns.scss │ │ ├── _buttons.scss │ │ ├── _tables.scss │ │ └── _button-groups.scss └── scripts │ ├── console-helper.js │ ├── parse.js │ └── angular │ └── http-auth-interceptor.js ├── NEWS.md ├── NOTES ├── test ├── unit │ └── app │ │ ├── services.spec.coffee │ │ ├── contoller.spec.js │ │ ├── services.spec.js │ │ ├── contoller.spec.coffee │ │ ├── filter.spec.coffee │ │ ├── filter.spec.js │ │ ├── directive.spec.coffee │ │ └── directive.spec.js ├── e2e │ └── app │ │ └── scenario.js └── testacular_conf.js ├── CONVENTIONS.md ├── TODO.md ├── deploy ├── AUTHORS.md ├── Makefile ├── g0v.ls ├── CONTRIBUTORS.md ├── insert.ls ├── lib ├── schema.ls └── user.ls ├── server ├── app.ls ├── opengraph.ls ├── auth.ls └── main.ls ├── package.ls ├── config.ls ├── g0v.json ├── package.json ├── README.md └── LICENSE /app/assets/img/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /worker/item-site-crawler/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/compile-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | coffee -cw -b test 4 | -------------------------------------------------------------------------------- /scripts/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf node_modules 4 | npm install 5 | -------------------------------------------------------------------------------- /thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/thumbnail.png -------------------------------------------------------------------------------- /app/partials/profile.jade: -------------------------------------------------------------------------------- 1 | div(ng-controller="Profile") 2 | h1 Hi, {{name}} 3 | -------------------------------------------------------------------------------- /app/assets/img/g0v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/img/g0v.png -------------------------------------------------------------------------------- /app/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/img/logo.png -------------------------------------------------------------------------------- /app/init.ls: -------------------------------------------------------------------------------- 1 | 2 | angular.element(document).ready -> 3 | angular.bootstrap(document, [\app]) 4 | -------------------------------------------------------------------------------- /app/assets/img/300x200.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/img/300x200.gif -------------------------------------------------------------------------------- /app/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/img/favicon.ico -------------------------------------------------------------------------------- /app/assets/img/htc-one.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/img/htc-one.jpg -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node_modules/testacular/bin/testacular test/testacular_conf.js 4 | -------------------------------------------------------------------------------- /app/assets/img/glyphicons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/img/glyphicons.png -------------------------------------------------------------------------------- /app/assets/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /app/assets/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/assets/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /app/assets/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | _public 3 | app/assets/*.html 4 | app/assets/partials/app/*.html 5 | server/*.js 6 | lib/*.js 7 | *.swp 8 | -------------------------------------------------------------------------------- /app/assets/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g0v/twbudget/HEAD/app/assets/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /app/styles/vote.sass: -------------------------------------------------------------------------------- 1 | #budget-vote-label 2 | text-align: center 3 | #BudgetItem 4 | margin-left: 0px 5 | .vote-btn 6 | font-size: 12px 7 | 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "app/assets/openspendingjs"] 2 | path = app/assets/openspendingjs 3 | url = https://github.com/openspending/openspendingjs.git 4 | -------------------------------------------------------------------------------- /app/styles/treemap.css: -------------------------------------------------------------------------------- 1 | .cell { 2 | border: solid 1px white; 3 | font: 10px sans-serif; 4 | line-height: 12px; 5 | overflow: hidden; 6 | position: absolute; 7 | text-indent: 2px; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /app/styles/tags.sass: -------------------------------------------------------------------------------- 1 | #budget-tagger .input-append 2 | margin-left: 23px 3 | #budget-tags 4 | height: 105px 5 | overflow-x: hidden 6 | .budget-tag 7 | display: block 8 | float: left 9 | margin: 1px 10 | 11 | -------------------------------------------------------------------------------- /environment.json: -------------------------------------------------------------------------------- 1 | {"authproviders": { 2 | "facebook": { 3 | "client_id": "278354422273748", 4 | "secret": "60cd3abadc8135f3eb5d1101b95becef" 5 | } 6 | }, 7 | "base_uri": "http://local.host:8000/" 8 | 9 | } 10 | 11 | -------------------------------------------------------------------------------- /vendor/styles/themes/default/_overrides.less: -------------------------------------------------------------------------------- 1 | 2 | // Footer 3 | .footer { 4 | background-color: @grayDark; 5 | padding: 30px 0; 6 | text-shadow: none; 7 | border-top: 1px solid #e5e5e5; 8 | .box-shadow(inset 0 5px 15px rgba(0, 0, 0, .025)); 9 | } 10 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | ###### Notice 2 | 3 | *This file documents the changes in **Sapling** versions that are listed below.* 4 | 5 | *Items should be added to this file as:* 6 | 7 | ### YYYY-MM-DD Release 8 | 9 | + Additional changes. 10 | 11 | + More changes. 12 | 13 | * * * 14 | -------------------------------------------------------------------------------- /app/partials/tags.jade: -------------------------------------------------------------------------------- 1 | #budget-tagger 2 | .input-append 3 | input.span2(ng-model="tagname",size="13",type="text",placeholder="例如: 林益世") 4 | button.btn(type="button",ng-click="addtag()") 標記相關人士 5 | #budget-tags 6 | span.badge.budget-tag(ng-repeat="tag in tags") 7 | {{tag}} 8 | -------------------------------------------------------------------------------- /NOTES: -------------------------------------------------------------------------------- 1 | -- 2 | product model: 3 | name 4 | image 5 | 6 | brand 7 | excerpt 8 | description 9 | variant 10 | meta: { material, dimension, ... } 11 | tags: [....] # category, etc 12 | 13 | 14 | item model: 15 | merchant 16 | price 17 | url / merchat-id 18 | 19 | 20 | -------------------------------------------------------------------------------- /vendor/styles/sapling/_account.less: -------------------------------------------------------------------------------- 1 | // The nav icons break into seperate lines at widths between 768px and 980px 2 | // To prevent this hide the icons at those widths 3 | @media (min-width: 768px) and (max-width: 980px) { 4 | #account-nav [class^="icon-"] { 5 | display: none; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/app/services.spec.coffee: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | # jasmine specs for services go here 4 | 5 | describe "service", -> 6 | 7 | beforeEach(module "app.services") 8 | 9 | describe "version", -> 10 | it "should return current version", inject((version) -> 11 | expect(version).toEqual "0.1" 12 | ) 13 | -------------------------------------------------------------------------------- /CONVENTIONS.md: -------------------------------------------------------------------------------- 1 | ## CONVENTIONS 2 | ### *Coding best practices for Sapling code.* 3 | 4 | *** 5 | 6 | * Use Coffeescript for scripts 7 | * Use Less for styles 8 | * Use Jade for templates 9 | * Alias `$scope` to `s` within controllers. 10 | * Capitalize variable and method names to indicate that they are public, i.e exported. 11 | -------------------------------------------------------------------------------- /app/partials/debtclock.jade: -------------------------------------------------------------------------------- 1 | .treemap-root(ng-controller="DebtClock") 2 | #debtclock-dashboard 3 | #debtclock-total-number 4 | 現有國債: 5 | span#debtclock_total {{ total.debt | unitconvert }} 6 | #debtclock-total-person 7 | (平均每人負擔: 8 | span#debtclock_perperson {{ total.avg | number:0 }} 9 | 元) 10 | 11 | -------------------------------------------------------------------------------- /app/views/widgets/googlenews.jade: -------------------------------------------------------------------------------- 1 | //{{keywords}} are queries seperate by comma, ex &q=交通及運輸設,總統府, which should be valid url encoding 2 | iframe(frameborder='0', width='300', height='250', marginwidth='0', marginheight='0', src='http://www.google.com/uds/modules/elements/newsshow/iframe.html?ned=tw&hl=zh-TW&rsz=large&format=300x250&q={{keywords}}') 3 | 4 | -------------------------------------------------------------------------------- /test/unit/app/contoller.spec.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 'use strict'; 3 | 4 | describe("controllers", function() { 5 | beforeEach(module("app.controllers")); 6 | describe("MyCtrl1", function() { 7 | return it("should...'"); 8 | }); 9 | return describe("MyCtrl2", function() { 10 | return it("should...'"); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## TODO 2 | 3 | 1. Removed dependencies to Bootstrap, and jQuery. 4 | 2. Automatically compile Jade to HTML. 5 | 3. Improve testing suite. Ideas: 6 | - Packages test with packages 7 | mypackage/ 8 | controllers.coffee 9 | controllers_test.coffee 10 | - Adapt testacular to read coffeescript directly 11 | - Integrate with Brunch's test suite. 12 | -------------------------------------------------------------------------------- /test/unit/app/services.spec.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 'use strict'; 3 | 4 | describe("service", function() { 5 | beforeEach(module("app.services")); 6 | return describe("version", function() { 7 | return it("should return current version", inject(function(version) { 8 | return expect(version).toEqual("0.1"); 9 | })); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/unit/app/contoller.spec.coffee: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | # jasmine specs for controllers go here 4 | 5 | # TODO figure out how to test Controllers that use modules 6 | describe "controllers", -> 7 | 8 | beforeEach(module "app.controllers") 9 | 10 | describe "MyCtrl1", -> 11 | 12 | it "should...'" 13 | 14 | describe "MyCtrl2", -> 15 | 16 | it "should...'" 17 | -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | branch=$1 3 | echo $branch 4 | killall brunch 5 | rm -rf _public 6 | git checkout $branch 7 | git merge $branch/master 8 | git merge master 9 | rm -rf _public 10 | BUILD=git-`git describe --always` ./node_modules/.bin/brunch b -o 11 | make clean-server 12 | make server 13 | git add -f -A _public server lib 14 | git commit -m 'regen' 15 | git push $branch $branch:master 16 | -------------------------------------------------------------------------------- /vendor/styles/sapling/_angular.less: -------------------------------------------------------------------------------- 1 | input.ng-dirty.ng-valid { 2 | /*.formFieldState(@successText, @successText, @successBackground);*/ 3 | border-color: @successText; 4 | } 5 | 6 | input.ng-dirty.ng-invalid { 7 | /*.formFieldState(@errorText, @errorText, @errorBackground);*/ 8 | border-color: @errorText; 9 | } 10 | 11 | input.ng-pristine { 12 | 13 | } 14 | 15 | input.ng-dirty { 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /vendor/styles/sapling/_sticky-footer.less: -------------------------------------------------------------------------------- 1 | 2 | // Sticky Footer 3 | 4 | @footerHeight: 88px; 5 | 6 | html, body { 7 | height: 100%; 8 | } 9 | 10 | .wrapper { 11 | min-height: 100%; 12 | height: auto !important; 13 | height: 100%; 14 | margin: 0 auto -@footerHeight; 15 | } 16 | 17 | .push { 18 | height: @footerHeight; 19 | } 20 | 21 | .wrapper > .container { 22 | padding-top: 24px; 23 | } 24 | -------------------------------------------------------------------------------- /app/styles/themes/custom/_overrides.less: -------------------------------------------------------------------------------- 1 | /* 2 | Place custom styles here. Thes styles are ment to be loaded after 3 | bootstrap, so the may override it's behavior. 4 | */ 5 | 6 | body { 7 | #gradient > .striped(@blue); 8 | } 9 | 10 | // Footer 11 | .footer { 12 | background-color: #ffffff; 13 | padding: 30px 0; 14 | text-shadow: none; 15 | .box-shadow(inset 0 5px 15px rgba(0, 0, 0, .025)); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | ###### Notice 2 | 3 | *This is the official list of **Sapling** authors for copyright purposes.* 4 | 5 | *This file is distinct from the CONTRIBUTORS file. See the latter for an 6 | explanation.* 7 | 8 | *Names should be added to this file as:* 9 | 10 | `Organization` or `Name ` 11 | 12 | *Please keep the list sorted.* 13 | 14 | * * * 15 | 16 | Kyle Finley 17 | 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | JS_FILES=server/app.js server/main.js server/auth.js server/opengraph.js lib/user.js lib/schema.js 2 | 3 | .ls.js: 4 | env PATH="$$PATH:./node_modules/LiveScript/bin" livescript -c $< 5 | 6 | server :: $(JS_FILES) 7 | clean-server: 8 | rm -f $(JS_FILES) 9 | 10 | client :: 11 | env PATH="$$PATH:./node_modules/brunch/bin" brunch b 12 | 13 | run :: server 14 | node server/app.js 15 | 16 | .SUFFIXES: .jade .html .ls .js 17 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_layouts.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Layouts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container (centered, fixed-width layouts) 7 | .container { 8 | @include container-fixed(); 9 | } 10 | 11 | // Fluid layouts (left aligned, with sidebar, min- & max-width content) 12 | .container-fluid { 13 | padding-right: $gridGutterWidth; 14 | padding-left: $gridGutterWidth; 15 | @include clearfix(); 16 | } -------------------------------------------------------------------------------- /app/tax.tw.ls: -------------------------------------------------------------------------------- 1 | taiwanPersonalTax = (salary) -> 2 | baseTaxFree = 82000 + 76000 + 104000 # 免稅額, 標準扣除額, 薪資所得特別扣除額 3 | salary = if (salary > baseTaxFree) then salary - baseTaxFree else 0 4 | rawTax = | (salary > 4230000) => salary * 0.4 - 774400 5 | | (salary > 2260000) => salary * 0.3 - 351400 6 | | (salary > 1130000) => salary * 0.2 - 125400 7 | | (salary > 500000) => salary * 0.12 - 35000 8 | | otherwise => salary * 0.05 9 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_component-animations.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | 6 | .fade { 7 | opacity: 0; 8 | @include transition(opacity .15s linear); 9 | &.in { 10 | opacity: 1; 11 | } 12 | } 13 | 14 | .collapse { 15 | position: relative; 16 | height: 0; 17 | overflow: hidden; 18 | @include transition(height .35s ease); 19 | &.in { 20 | height: auto; 21 | } 22 | } -------------------------------------------------------------------------------- /test/unit/app/filter.spec.coffee: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | # jasmine specs for filters go here 4 | describe "filter", -> 5 | beforeEach(module "app.filters") 6 | 7 | describe "interpolate", -> 8 | 9 | beforeEach(module(($provide) -> 10 | $provide.value "version", "TEST_VER" 11 | return 12 | )) 13 | 14 | it "should replace VERSION", inject((interpolateFilter) -> 15 | expect(interpolateFilter("before %VERSION% after")).toEqual "before TEST_VER after" 16 | ) 17 | -------------------------------------------------------------------------------- /vendor/scripts/console-helper.js: -------------------------------------------------------------------------------- 1 | // Make it safe to do console.log() always. 2 | (function (con) { 3 | var method; 4 | var dummy = function() {}; 5 | var methods = ('assert,count,debug,dir,dirxml,error,exception,group,' + 6 | 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,' + 7 | 'time,timeEnd,trace,warn').split(','); 8 | while (method = methods.pop()) { 9 | con[method] = con[method] || dummy; 10 | } 11 | })(window.console = window.console || {}); 12 | -------------------------------------------------------------------------------- /app/styles/heading.sass: -------------------------------------------------------------------------------- 1 | .site-heading 2 | display: block 3 | margin-bottom: 10px 4 | .site-heading .navbar-inner 5 | width: 1004px 6 | margin: -3px auto 0 auto 7 | padding: 0 8 | .navbar .brand 9 | margin-left: -13px 10 | font-family: 'Cousine', sans-serif 11 | font-weight: 400 12 | font-style: normal 13 | line-height: 16px 14 | img 15 | vertical-align: bottom 16 | width: 48px 17 | height: 20px 18 | span 19 | font-size: 13px 20 | .nav li 21 | font-size: 13px 22 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_utilities.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Quick floats 7 | .pull-right { 8 | float: right; 9 | } 10 | .pull-left { 11 | float: left; 12 | } 13 | 14 | // Toggling content 15 | .hide { 16 | display: none; 17 | } 18 | .show { 19 | display: block; 20 | } 21 | 22 | // Visibility 23 | .invisible { 24 | visibility: hidden; 25 | } 26 | 27 | // For Affix plugin 28 | .affix { 29 | position: fixed; 30 | } -------------------------------------------------------------------------------- /vendor/styles/sapling/_breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // BREADCRUMBS 2 | // ----------- 3 | 4 | .breadcrumb { 5 | padding: 7px 14px; 6 | margin: 0 0 @baseLineHeight; 7 | #gradient > .vertical(#f5f5f5, @white); 8 | /*border: 1px solid #ddd;*/ 9 | .border-radius(3px); 10 | .box-shadow(inset 0 1px 0 @white); 11 | li { 12 | display: inline-block; 13 | text-shadow: 0 1px 0 @white; 14 | } 15 | .divider { 16 | padding: 0 5px; 17 | color: @grayLight; 18 | } 19 | .active a { 20 | color: @grayDark; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/unit/app/filter.spec.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 'use strict'; 3 | 4 | describe("filter", function() { 5 | beforeEach(module("app.filters")); 6 | return describe("interpolate", function() { 7 | beforeEach(module(function($provide) { 8 | $provide.value("version", "TEST_VER"); 9 | })); 10 | return it("should replace VERSION", inject(function(interpolateFilter) { 11 | return expect(interpolateFilter("before %VERSION% after")).toEqual("before TEST_VER after"); 12 | })); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /app/spending.ls: -------------------------------------------------------------------------------- 1 | blah = -> 2 | OpenSpending.scriptRoot = \/openspendingjs 3 | OpenSpending.localeGroupSeparator = \, 4 | OpenSpending.localeDecimalSeparator = \. 5 | showspending 1 6 | showspending = (type) -> 7 | <- jQuery.ajax do 8 | url: \/openspendingjs/widgets/treemap/main.js 9 | cache: true 10 | dataType: "script" 11 | .done 12 | dfd = new OpenSpending.Treemap $(\#bubbletree), do 13 | dataset: \twbudget 14 | siteUrl: \http://openspending.org 15 | , do 16 | drilldowns: <[cat depname]> 17 | <- dfd.done 18 | -------------------------------------------------------------------------------- /app/styles/debtclock.sass: -------------------------------------------------------------------------------- 1 | @import 'vendor//styles/bootstrap/_mixins.scss' 2 | 3 | #debtclock-dashboard 4 | border: 1px solid black 5 | +border-radius(4px) 6 | box-shadow: 1px 3px 4px 1px #888 7 | background: #eee 8 | font-size: 20px 9 | z-index: -1 10 | 11 | #debtclock-total-number 12 | margin: 10px 13 | font-size: 2em 14 | line-height: 2em 15 | text-align: center 16 | 17 | #debtclock-total-person 18 | margin: 10px 19 | font-size: 1.3em 20 | line-height: 1.3em 21 | text-align: center 22 | 23 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_grid.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | // Fixed (940px) 6 | @include grid-core($gridColumnWidth, $gridGutterWidth); 7 | 8 | // Fluid (940px) 9 | @include grid-fluid($fluidGridColumnWidth, $fluidGridGutterWidth); 10 | 11 | // Reset utility classes due to specificity 12 | [class*="span"].hide, 13 | .row-fluid [class*="span"].hide { 14 | display: none; 15 | } 16 | 17 | [class*="span"].pull-right, 18 | .row-fluid [class*="span"].pull-right { 19 | float: right; 20 | } -------------------------------------------------------------------------------- /test/unit/app/directive.spec.coffee: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | # jasmine specs for directives go here 4 | describe "directives", -> 5 | 6 | beforeEach(module "app.directives") 7 | 8 | describe "app-version", -> 9 | 10 | it "should print current version", -> 11 | module ($provide) -> 12 | $provide.value "version", "TEST_VER" 13 | return 14 | 15 | inject ($compile, $rootScope) -> 16 | element = $compile("")($rootScope) 17 | expect(element.text()).toEqual "TEST_VER" 18 | 19 | 20 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: 8px 15px; 8 | margin: 0 0 $baseLineHeight; 9 | list-style: none; 10 | background-color: #f5f5f5; 11 | @include border-radius(4px); 12 | li { 13 | display: inline-block; 14 | @include ie7-inline-block(); 15 | text-shadow: 0 1px 0 $white; 16 | } 17 | .divider { 18 | padding: 0 5px; 19 | color: #ccc; 20 | } 21 | .active { 22 | color: $grayLight; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/partials/partial1.jade: -------------------------------------------------------------------------------- 1 | div(ng-controller="MyCtrl1") 2 | div.categories(id="categories",when-h-scrolled='updateblah()') 3 | div.category(style="left:{{i*300+30}}px",ng-repeat="(i,result) in results") 4 | button.btn(style="text-align:center;") {{ result.name }} 5 | ul.thumbnails.products(when-scrolled='moreProducts(i)',ng-class='"product"+blah(i)') 6 | li.span3.product(ng-repeat="product in result.products") 7 | img(src="img/htc-one.jpg") 8 | h3 {{ product.name }} 9 | p {{ product.description }} qwertyuaisdfghjzkxcvbj 10 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_hero-unit.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Hero unit 3 | // -------------------------------------------------- 4 | 5 | 6 | .hero-unit { 7 | padding: 60px; 8 | margin-bottom: 30px; 9 | background-color: $heroUnitBackground; 10 | @include border-radius(6px); 11 | h1 { 12 | margin-bottom: 0; 13 | font-size: 60px; 14 | line-height: 1; 15 | color: $heroUnitHeadingColor; 16 | letter-spacing: -1px; 17 | } 18 | p { 19 | font-size: 18px; 20 | font-weight: 200; 21 | line-height: $baseLineHeight * 1.5; 22 | color: $heroUnitLeadColor; 23 | } 24 | } -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_responsive-768px-979px.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Tablet to desktop 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 768px) and (max-width: 979px) { 7 | 8 | // Fixed grid 9 | @include grid-core($gridColumnWidth768, $gridGutterWidth768); 10 | 11 | // Fluid grid 12 | @include grid-fluid($fluidGridColumnWidth768, $fluidGridGutterWidth768); 13 | 14 | // Input grid 15 | @include grid-input($gridColumnWidth768, $gridGutterWidth768); 16 | 17 | // No need to reset .thumbnails here since it's the same $gridGutterWidth 18 | 19 | } -------------------------------------------------------------------------------- /g0v.ls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lsc -cj 2 | author: 'clkao' 3 | contributors: <[zbryikt CA jeffhung ipa]> 4 | status: 'alpha' 5 | name: 'budget' 6 | name_zh: '中央政府總預算' 7 | description: 'Taiwan Central Government Budget' 8 | description_zh: '中央政府總預算視覺化' 9 | homepage: 'http://budget.g0v.tw/' 10 | repository: 11 | type: 'git' 12 | url: 'https://github.com/g0v/twbudget' 13 | licenses: [ 14 | "type": "MIT" 15 | ] 16 | keywords: <[angularjs livescript sass bootstrap]> 17 | audience: <[public]> 18 | products: <[website]> 19 | projects: <[budget]> 20 | thumbnail: 'https://raw.github.com/g0v/twbudget/master/thumbnail.png' 21 | needs: <[designer programmer writer]> 22 | -------------------------------------------------------------------------------- /test/unit/app/directive.spec.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 'use strict'; 3 | 4 | describe("directives", function() { 5 | beforeEach(module("app.directives")); 6 | return describe("app-version", function() { 7 | return it("should print current version", function() { 8 | module(function($provide) { 9 | $provide.value("version", "TEST_VER"); 10 | }); 11 | return inject(function($compile, $rootScope) { 12 | var element; 13 | element = $compile("")($rootScope); 14 | return expect(element.text()).toEqual("TEST_VER"); 15 | }); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /app/styles/tooltip.css: -------------------------------------------------------------------------------- 1 | .tooltip { 2 | position: absolute; 3 | top: 100px; 4 | left: 100px; 5 | -moz-border-radius:5px; 6 | border-radius: 5px; 7 | border: 2px solid #000; 8 | /* background: #222222; */ 9 | background: #fff; 10 | opacity: .9; 11 | /* color: #eeeeee; */ 12 | color: black; 13 | padding: 10px; 14 | width: 400px; 15 | font-size: 12px; 16 | z-index: 10; 17 | } 18 | 19 | .tooltip .title { 20 | font-size: 13px; 21 | } 22 | 23 | .tooltip .name { 24 | font-weight:bold; 25 | } 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | ###### Notice 2 | 3 | *This is the official list of people who can contribute (and typically have 4 | contributed) code to the **Sapling** repository.* 5 | 6 | *The AUTHORS file lists the copyright holders; this file lists people. For 7 | example, the employees of an organization are listed here but not in AUTHORS, 8 | because the organization holds the copyright.* 9 | 10 | *Names should be added to this file like so:* 11 | 12 | Name 13 | 14 | *Please keep the list sorted.* 15 | 16 | * * * 17 | 18 | ### Initial author(s) 19 | 20 | Kyle Finley 21 | 22 | ### Maintainer 23 | 24 | 25 | 26 | ### Other authors 27 | 28 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_wells.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: $wellBackground; 12 | border: 1px solid darken($wellBackground, 7%); 13 | @include border-radius(4px); 14 | @include box-shadow(#{inset 0 1px 1px rgba(0,0,0,.05)}); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-large { 23 | padding: 24px; 24 | @include border-radius(6px); 25 | } 26 | .well-small { 27 | padding: 9px; 28 | @include border-radius(3px); 29 | } -------------------------------------------------------------------------------- /insert.ls: -------------------------------------------------------------------------------- 1 | Q = require \q 2 | {Product} = require \./lib/schema 3 | json = try JSON.parse do 4 | require \fs .readFileSync \environment.json \utf8 5 | mongoose = require \mongoose 6 | mongoose.connect json?MONGOLAB_URI ? process.env?MONGOLAB_URI ? \mongodb://localhost/ydh 7 | 8 | p = new Product do 9 | name: "ONE X" 10 | brand: "HTC" 11 | image: "XXX" 12 | excerpt: 'lalala lal lala' 13 | description: 'llfdsf kldsfk lfsad ' 14 | meta: {} 15 | tags: [] 16 | 17 | defer = Q.defer! 18 | defers = [] 19 | defers.push defer.promise 20 | 21 | do 22 | err <- p.save 23 | defer.resolve! 24 | 25 | <- Q.allResolved defers 26 | .then 27 | console.log \alldone 28 | process.exit 0 29 | -------------------------------------------------------------------------------- /app/partials/partial2.jade: -------------------------------------------------------------------------------- 1 | .container 2 | #dailybread(ng-controller="DailyBread") 3 | h2.page-header 每日支出 4 | small 納稅人每日支付些什麼? 5 | #preloader(style='text-align: center; padding: 100px;') 6 | span.txt loading 7 | #content-wrap(style='display: none;') 8 | #db-topbar.layout-row.layout-3col-15-70-15 9 | form.form-inline 10 | label 每年繳納稅金 : 11 | .input-prepend.input-append 12 | span.add-on $ 13 | input.input-small(type="text",id="db-tax",name="db-tax",ng-model="tax") 14 | a.btn.btn-primary(ng-click="tax= tax + 5000") + 15 | a.btn.btn-danger(ng-click="tax= tax - 5000") - 16 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_responsive-1200px-min.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Large desktop and up 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 1200px) { 7 | 8 | // Fixed grid 9 | @include grid-core($gridColumnWidth1200, $gridGutterWidth1200); 10 | 11 | // Fluid grid 12 | @include grid-fluid($fluidGridColumnWidth1200, $fluidGridGutterWidth1200); 13 | 14 | // Input grid 15 | @include grid-input($gridColumnWidth1200, $gridGutterWidth1200); 16 | 17 | // Thumbnails 18 | .thumbnails { 19 | margin-left: -$gridGutterWidth1200; 20 | } 21 | .thumbnails > li { 22 | margin-left: $gridGutterWidth1200; 23 | } 24 | .row-fluid .thumbnails { 25 | margin-left: 0; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /app/app/directives.ls: -------------------------------------------------------------------------------- 1 | # Directive 2 | 3 | # Create an object to hold the module. 4 | mod = {} 5 | 6 | mod.appVersion = [ 7 | 'version' 8 | 9 | (version) -> 10 | 11 | (scope, elm, attrs) -> 12 | elm.text(version) 13 | ] 14 | 15 | mod.whenScrolled = -> (scope, elm, attr) -> 16 | raw = elm[0] 17 | elm.bind 'scroll', -> 18 | if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) 19 | scope.$apply(attr.whenScrolled) 20 | 21 | mod.whenHScrolled = -> (scope, elm, attr) -> 22 | raw = elm[0] 23 | elm.bind 'scroll', -> 24 | scope.$apply(attr.whenHScrolled) 25 | 26 | # register the module with Angular 27 | angular.module('app.directives', [ 28 | # require the 'app.service' module 29 | 'app.services' 30 | ]).directive(mod) 31 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_accordion.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Accordion 3 | // -------------------------------------------------- 4 | 5 | 6 | // Parent container 7 | .accordion { 8 | margin-bottom: $baseLineHeight; 9 | } 10 | 11 | // Group == heading + body 12 | .accordion-group { 13 | margin-bottom: 2px; 14 | border: 1px solid #e5e5e5; 15 | @include border-radius(4px); 16 | } 17 | .accordion-heading { 18 | border-bottom: 0; 19 | } 20 | .accordion-heading .accordion-toggle { 21 | display: block; 22 | padding: 8px 15px; 23 | } 24 | 25 | // General toggle styles 26 | .accordion-toggle { 27 | cursor: pointer; 28 | } 29 | 30 | // Inner needs the styles because you can't animate properly with any styles on the element 31 | .accordion-inner { 32 | padding: 9px 15px; 33 | border-top: 1px solid #e5e5e5; 34 | } -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_close.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: 20px; 9 | font-weight: bold; 10 | line-height: $baseLineHeight; 11 | color: $black; 12 | text-shadow: 0 1px 0 rgba(255,255,255,1); 13 | @include opacity(0.2); 14 | &:hover { 15 | color: $black; 16 | text-decoration: none; 17 | cursor: pointer; 18 | @include opacity(0.4); 19 | } 20 | } 21 | 22 | // Additional properties for button version 23 | // iOS requires the button element instead of an anchor tag. 24 | // If you want the anchor version, it requires `href="#"`. 25 | button.close { 26 | padding: 0; 27 | cursor: pointer; 28 | background: transparent; 29 | border: 0; 30 | -webkit-appearance: none; 31 | } -------------------------------------------------------------------------------- /lib/schema.ls: -------------------------------------------------------------------------------- 1 | mongoose = require \mongoose 2 | Schema = mongoose.Schema 3 | 4 | s = 5 | ProductSchema: Schema do 6 | name: String 7 | brand: String 8 | image: String 9 | excerpt: String 10 | description: String 11 | variant: [] 12 | meta: {} 13 | tags: [] 14 | 15 | s.ItemSchema = Schema do 16 | product: String 17 | price: Number 18 | merchant: String 19 | 20 | s.BudgetItemSchema = Schema do 21 | key: String 22 | nlikes: Number 23 | nconfuses: Number 24 | nhates: Number 25 | ncuts: Number 26 | likes: [] 27 | confuses: [] 28 | hates: [] 29 | cuts: [] 30 | tags: [] 31 | 32 | module.exports = { [name, mongoose.model name, s[name + 'Schema']] for name in 33 | <[ Product BudgetItem ]> 34 | } 35 | -------------------------------------------------------------------------------- /app/partials/vote.jade: -------------------------------------------------------------------------------- 1 | #budget-vote 2 | #budget-vote-label 3 | span.label 透過按一下表達你對於此預算的看法! 4 | #BudgetItem.btn-toolbar 5 | .btn-group 6 | button.btn.vote-btn(ng-click="like()") 7 | span#vote1 {{nlikes || 0}} 人 8 | br 9 | i.icon-big-plus 10 | br 11 | 希望追加 12 | button.btn.vote-btn(ng-click="confuse()") 13 | span#vote2 {{nconfuses || 0}} 人 14 | br 15 | i.icon-big-question-sign 16 | br 17 | 表示不懂 18 | button.btn.vote-btn(ng-click="hate()") 19 | span#vote3 {{nhates || 0}} 人 20 | br 21 | i.icon-big-minus 22 | br 23 | 建議減少 24 | button.btn.vote-btn.btn-danger(ng-click="cut()") 25 | span#vote4 {{ncuts || 0}} 人 26 | br 27 | i.icon-big-ban-circle 28 | br 29 | 要求刪除 30 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_pager.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | margin: $baseLineHeight 0; 8 | list-style: none; 9 | text-align: center; 10 | @include clearfix(); 11 | } 12 | .pager li { 13 | display: inline; 14 | } 15 | .pager a, 16 | .pager span { 17 | display: inline-block; 18 | padding: 5px 14px; 19 | background-color: #fff; 20 | border: 1px solid #ddd; 21 | @include border-radius(15px); 22 | } 23 | .pager a:hover { 24 | text-decoration: none; 25 | background-color: #f5f5f5; 26 | } 27 | .pager .next a, 28 | .pager .next span { 29 | float: right; 30 | } 31 | .pager .previous a { 32 | float: left; 33 | } 34 | .pager .disabled a, 35 | .pager .disabled a:hover, 36 | .pager .disabled span { 37 | color: $grayLight; 38 | background-color: #fff; 39 | cursor: default; 40 | } -------------------------------------------------------------------------------- /server/app.ls: -------------------------------------------------------------------------------- 1 | argv = try require \optimist .argv 2 | json = try JSON.parse do 3 | require \fs .readFileSync \environment.json \utf8 4 | default_port = if process.env.NODE_ENV is \production => 80 else 8000 5 | port = Number(argv?port or json?PORT_NODEJS or process.env.PORT or process.env.VCAP_APP_PORT) or default_port 6 | host = argv?host or process.env.VCAP_APP_HOST or \0.0.0.0 7 | basepath = (argv?basepath or "") - /\/$/ 8 | 9 | console.log "Please connect to: http://#{ 10 | if host is \0.0.0.0 then require \os .hostname! else host 11 | }:#port/" 12 | 13 | <- (require \zappajs) port, host 14 | @BASEPATH = basepath 15 | 16 | @mongoose = require \mongoose 17 | @mongoose.connect json?MONGOLAB_URI ? process.env?MONGOLAB_URI ? \mongodb://localhost/ydh 18 | @config = json ? {} 19 | @config.cookieSecret ?= 'its-secret' 20 | @config.authproviders ?= {} 21 | 22 | @include \main 23 | 24 | -------------------------------------------------------------------------------- /app/partials/partial3.jade: -------------------------------------------------------------------------------- 1 | #treemap-root 2 | #treemap-root-inner 3 | #debitmask 2,500億 4 | button#treemap-backbtn.btn 5 | i.icon-big-back 6 | #budget-detail 7 | #budget-detail-inner 8 | #budget-detail-info 9 | h3 10 | i.icon-big-info-sign(style="margin-top:2px") 11 | span#budget-detail-depname-field 教育部 12 | #budget-detail-amount 13 | #budget-detail-amount-field1 14 | span#budget-detail-amount-field1-value 2,100億 15 | span#budget-detail-amount-field1-unit 元 16 | (即為 17 | span#budget-detail-amount-field2 2,100億元 18 | ) 19 | #budget-detail-category 20 | 屬於 21 | span#budget-detail-category-field 教育支出 22 | 項目 23 | hr 24 | include tags 25 | hr 26 | include vote 27 | script. 28 | setTimeout(function() { 29 | initTreeMap(); 30 | }, 500); 31 | -------------------------------------------------------------------------------- /app/app/services.ls: -------------------------------------------------------------------------------- 1 | # Services 2 | 3 | # Create an object to hold the module. 4 | mod = {} 5 | 6 | mod.version = -> "0.1" 7 | 8 | mod.ProductSearch = <[ $http ]> ++ ($http) -> 9 | currentQuery = '' 10 | results = {} 11 | 12 | search: (query, cb) -> 13 | currentQuery = query 14 | $http.get('/1/products/' + currentQuery)success cb 15 | 16 | getResults: -> results 17 | moreResults: (which) -> 18 | console.log \more 19 | results[which]products.push name: 'newly added' 20 | 21 | mod.BudgetItem = <[ $http ]> ++ ($http) -> 22 | get: (key, cb) -> 23 | $http.get("/1/budgetitems/#key")success cb 24 | update: (key, verb, cb) -> 25 | console.log \updating, key, verb 26 | $http.post("/1/budgetitems/#key/#verb")success cb 27 | addtag: (key, tag, cb) -> 28 | $http.post("/1/budgetitems/#key/tags/#tag")success cb 29 | 30 | 31 | angular.module('app.services', []).factory(mod) 32 | -------------------------------------------------------------------------------- /app/app/filters.ls: -------------------------------------------------------------------------------- 1 | # Filters 2 | 3 | angular.module('app.filters', []). 4 | filter \interpolate, [\version, (version) -> 5 | (text) -> String(text).replace(/\%VERSION\%/mg, version) ] 6 | 7 | angular.module('app.filters', []). 8 | filter \unitconvert, [\$filter, ($filter) -> 9 | (v) -> 10 | idx = UnitMapper.get! 11 | c = CurrencyData[idx] 12 | v = parseInt(10000 * v / c[2]) / 10000 13 | v = parseInt(10 * v) / 10 if v > 1 and v < 1000 14 | v = $filter('number')(v, 0) 15 | v + c[0] + c[1] 16 | ] 17 | 18 | 19 | # TODO Convert to following syntax. 20 | # # Create an object to hold the module. 21 | # mod = {} 22 | # 23 | # mod.interpolate = [ 24 | # 'version' 25 | # 26 | # (version) -> 27 | # 28 | # (text) -> 29 | # String(text).replace(/\%VERSION\%/mg, version) 30 | # ] 31 | # 32 | # # register the module with Angular 33 | # angular.module('app.filters', [ 34 | # # require the 'app.service' module 35 | # ]).filter(mod) 36 | -------------------------------------------------------------------------------- /app/currency.ls: -------------------------------------------------------------------------------- 1 | CurrencyData = [ 2 | ["", "元", 1], 3 | ["份","營養午餐",25], 4 | ["份","營養午餐(回扣)",30], 5 | ["人的","年薪",308000], 6 | ["座","釣魚台",80000000], 7 | ["分鐘","太空旅遊",1000000], 8 | ["碗","鬍鬚張魯肉飯",68], 9 | ["個","便當",50], 10 | ["杯","珍奶",30], 11 | ["份","雞排加珍奶",60], 12 | ["個","晨水匾",700000000], 13 | ["個","夢想家",200000000], 14 | ["個","林益世(粗估)",83000000], 15 | ["座","冰島",2000080000000], 16 | ["坪","帝寶",2500000], 17 | ["支","iPhone5",25900], 18 | ["座","硬兔的小島",2000080000000] 19 | ]; 20 | 21 | CurrencyConvert = (v, idx, full) -> 22 | idx ?= 0 23 | c = CurrencyData[idx] 24 | v = parseInt(10000 * v / c[2]) / 10000 25 | v = parseInt(10 * v) / 10 if v > 1 and v < 1000 26 | if v >= 1000 and v < 10000 27 | v = parseInt(v / 1000) + "千" 28 | else if v >= 10000 and v < 100000000 29 | v = parseInt(v / 10000) + "萬" 30 | else if v >= 100000000 and v < 1000000000000 31 | v = parseInt(v / 100000000) + "億" 32 | else v = parseInt(v / 1000000000000) + "兆" if v >= 1000000000000 33 | v + (if full then c[0] + c[1] else "") 34 | -------------------------------------------------------------------------------- /test/e2e/app/scenario.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* http://docs.angularjs.org/guide/dev_guide.e2e-testing */ 4 | 5 | describe('my app', function() { 6 | 7 | beforeEach(function() { 8 | browser().navigateTo('http://localhost:3333/'); 9 | }); 10 | 11 | 12 | it('should automatically redirect to /view1 when location hash/fragment is empty', function() { 13 | expect(browser().location().url()).toBe("/view1"); 14 | }); 15 | 16 | 17 | describe('view1', function() { 18 | 19 | beforeEach(function() { 20 | browser().navigateTo('#/view1'); 21 | }); 22 | 23 | 24 | it('should render view1 when user navigates to /view1', function() { 25 | expect(element('[ng-view] p:first').text()). 26 | toMatch(/partial for view 1/); 27 | }); 28 | 29 | }); 30 | 31 | 32 | describe('view2', function() { 33 | 34 | beforeEach(function() { 35 | browser().navigateTo('#/view2'); 36 | }); 37 | 38 | 39 | it('should render view2 when user navigates to /view2', function() { 40 | expect(element('[ng-view] p:first').text()). 41 | toMatch(/partial for view 2/); 42 | }); 43 | 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_scaffolding.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Scaffolding 3 | // -------------------------------------------------- 4 | 5 | 6 | // Body reset 7 | // ------------------------- 8 | 9 | body { 10 | margin: 0; 11 | font-family: $baseFontFamily; 12 | font-size: $baseFontSize; 13 | line-height: $baseLineHeight; 14 | color: $textColor; 15 | background-color: $bodyBackground; 16 | } 17 | 18 | 19 | // Links 20 | // ------------------------- 21 | 22 | a { 23 | color: $linkColor; 24 | text-decoration: none; 25 | } 26 | a:hover { 27 | color: $linkColorHover; 28 | text-decoration: underline; 29 | } 30 | 31 | 32 | // Images 33 | // ------------------------- 34 | 35 | // Rounded corners 36 | .img-rounded { 37 | @include border-radius(6px); 38 | } 39 | 40 | // Add polaroid-esque trim 41 | .img-polaroid { 42 | padding: 4px; 43 | background-color: #fff; 44 | border: 1px solid #ccc; 45 | border: 1px solid rgba(0,0,0,.2); 46 | @include box-shadow(#{0 1px 3px rgba(0,0,0,.1)}); 47 | } 48 | 49 | // Perfect circle 50 | .img-circle { 51 | @include border-radius(500px); // crank the border-radius so it works with most reasonably sized images 52 | } 53 | -------------------------------------------------------------------------------- /package.ls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lsc -cj 2 | author: 'g0v.tw' 3 | name: 'g0v.tw' 4 | description: 'g0v.tw' 5 | version: '0.0.1' 6 | homepage: 'https://github.com/g0v/twbudget' 7 | repository: 8 | type: 'git' 9 | url: 'https://github.com/g0v/twbudget' 10 | engines: 11 | node: '4.4.x' 12 | npm: '2.15.x' 13 | scripts: 14 | prepublish: 'lsc -cj package.ls' 15 | start: 'node server/app.js' 16 | dependencies: 17 | zappajs: '0.6.x' 18 | mongoose: '3.2.x' 19 | passport: '0.1.x' 20 | bcrypt: '*' 21 | 'cookie-sessions': 'git://github.com/clkao/cookie-sessions.git' 22 | 'passport-local': '0.1.x' 23 | 'passport-facebook': '0.1.x' 24 | 'passport-github': '0.1.x' 25 | 'passport-twitter': '0.1.x' 26 | jade: '0.27.x' 27 | devDependencies: 28 | LiveScript: '1.1.1' 29 | q: '*' 30 | brunch: '1.6.x' 31 | 'javascript-brunch': '1.5.x' 32 | 'LiveScript-brunch': '1.5.x' 33 | 'css-brunch': '1.5.x' 34 | 'sass-brunch': '1.5.x' 35 | 'jade-brunch': '1.5.x' 36 | 'static-jade-brunch': '>= 1.4.8 < 1.5' 37 | 'auto-reload-brunch': '1.6.x' 38 | 'uglify-js-brunch': '1.5.x' 39 | 'clean-css-brunch': '1.5.x' 40 | 'jade-angularjs-brunch': '0.0.5' 41 | 'jsenv-brunch': '1.4.2' 42 | -------------------------------------------------------------------------------- /config.ls: -------------------------------------------------------------------------------- 1 | exports.config = 2 | # See docs at http://brunch.readthedocs.org/en/latest/config.html. 3 | modules: 4 | definition: false 5 | wrapper: false 6 | paths: 7 | public: '_public' 8 | files: 9 | javascripts: 10 | joinTo: 11 | 'js/app.js': /^app/ 12 | 'js/vendor.js': /^vendor/ 13 | order: 14 | before: 15 | 'vendor/scripts/console-helper.js' 16 | 'vendor/scripts/jquery-1.8.2.js' 17 | 'vendor/scripts/angular/angular.js' 18 | 'vendor/scripts/angular/angular-resource.js' 19 | 'vendor/scripts/angular/angular-cookies.js' 20 | 21 | stylesheets: 22 | joinTo: 23 | 'css/app.css': /^(app|vendor)/ 24 | templates: 25 | joinTo: 26 | # this name is required for jade_angular plugin to work 27 | 'js/dontUseMe': /^app/ 28 | 29 | # Enable or disable minifying of result js / css files. 30 | # minify: true 31 | plugins: 32 | jade: 33 | options: 34 | pretty: yes 35 | locals: {} 36 | static_jade: 37 | extension: '.static.jade' 38 | path: [ /^app/ ] 39 | jade_angular: 40 | modules_folder: \partials 41 | locals: {} 42 | -------------------------------------------------------------------------------- /vendor/styles/sapling/_auth.less: -------------------------------------------------------------------------------- 1 | // Hover state 2 | //.btn:hover { 3 | // text-decoration: none; 4 | // background-position: 0; 5 | // border-color: rgba(14, 130, 180, 0.4); 6 | // .box-shadow(0 0 5px rgba(14, 130, 180, 0.55)); 7 | // 8 | // // transition is only when going to hover, otherwise the background 9 | // // behind the gradient (there for IE<=9 fallback) gets mismatched 10 | // .transition(box-shadow .1s linear); 11 | //} 12 | 13 | //.btn:hover { 14 | // background-position: 0; 15 | // border-color: rgba(14, 130, 180, 0.4); 16 | // .box-shadow(0 0 5px rgba(14, 130, 180, 0.55)); 17 | //} 18 | 19 | //.btn.active, .btn:active { 20 | // @shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); 21 | // .box-shadow(@shadow); 22 | //} 23 | 24 | .provider-btn { 25 | display: inline-block; 26 | list-style: none; 27 | padding: 5px; 28 | } 29 | .provider-btn .btn img { 30 | width: 90px; 31 | height: 30px; 32 | } 33 | 34 | .password-form { 35 | padding-top: 20px; 36 | border-left: 1px solid #e5e5e5; 37 | } 38 | 39 | .password-form .form-actions { 40 | background-color: transparent; 41 | border: none; 42 | } 43 | 44 | .aeauth-form h4 { 45 | text-align: center; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /app/partials/login.jade: -------------------------------------------------------------------------------- 1 | #login-holder.modal.fade(ng-model="loginShown", ng-controller="LoginController",ui-modal) 2 | a.btn.close(data-dismiss="modal") × 3 | div.modal-header 4 | h1 Login 5 | div.modal-body 6 | .row-fluid 7 | .span6 8 | form(ng-submit="submit()") 9 | label email 10 | input.email.login-inp(type="text",ng-model="email") 11 | label Password 12 | input.password.login-inp(type="password",ng-model="password") 13 | div.clear 14 | input.btn.btn-primary.submit-login(type="submit",value="Login") 15 | .span6 16 | p Login with: 17 | a.btn.btn-primary.login-github(onclick="window.open('/auth/github', 'auth', 'height=400,width=400,modal=yes,alwaysRaised=yes')") Github 18 | a.btn.btn-primary.login-twitter(onclick="window.open('/auth/twitter', 'auth', 'height=400,width=400,modal=yes,alwaysRaised=yes')") Twitter 19 | a.btn.btn-primary.login-facebook(onclick="window.open('/auth/facebook', 'auth', 'height=400,width=400,modal=yes,alwaysRaised=yes')") facebook 20 | div.clearfix 21 | div.alert.alert-error(ng-show="message",ng-model="message") {{ message }} 22 | 23 | div.modal-footer 24 | -------------------------------------------------------------------------------- /lib/user.ls: -------------------------------------------------------------------------------- 1 | bcrypt = require \bcrypt 2 | mongoose = require \mongoose 3 | Schema = mongoose.Schema 4 | 5 | s = {} 6 | User = s.UserSchema = new Schema do 7 | name: String 8 | email: { type: String, +sparse, +unique } 9 | salt: { type: String } 10 | hash: { type: String } 11 | votepref: String 12 | votearea: String 13 | accounts: [] 14 | 15 | User.virtual('password') 16 | .get -> @_password 17 | .set (password) -> 18 | @_password = password 19 | salt = @salt = bcrypt.genSaltSync 10 20 | @hash = bcrypt.hashSync password, salt 21 | 22 | User.method 'verifyPassword' (password, callback) -> bcrypt.compare password, @hash, callback 23 | 24 | User.static 'authenticate' (email, password, callback) -> 25 | console.log \auth, email 26 | err, user <- @findOne email: email 27 | if err => return callback err 28 | unless user => return callback null null message: "Unknown User" 29 | user.verifyPassword password, (err, passwordCorrect) -> 30 | | err => callback err 31 | | !passwordCorrect => callback null false message: "Invalid Password" 32 | | otherwise => callback null user 33 | 34 | module.exports = { [name, mongoose.model name, s[name + 'Schema']] for name in 35 | <[ User ]> 36 | } 37 | -------------------------------------------------------------------------------- /vendor/styles/themes/sapling/_overrides.less: -------------------------------------------------------------------------------- 1 | // Overrides 2 | 3 | h1, h2, h3, h4, h5, h6 { 4 | font-weight: normal; 5 | /*font-family: 'Crete Round', "Helvetica Neue", Helvetica, Arial, sans-serif;*/ 6 | padding-bottom: .3em; 7 | color: #1F444B; 8 | } 9 | .nav .brand { 10 | padding: 0; 11 | } 12 | 13 | .clickable { 14 | cursor: pointer; 15 | } 16 | 17 | // Forms 18 | .form-actions { 19 | background-color: transparent; 20 | border: none; 21 | } 22 | 23 | // Navbar 24 | .navbar-inner { 25 | padding-top: 6px; 26 | border-bottom: 1px solid #e5e5e5; 27 | .box-shadow(inset 0 -2px 5px rgba(0, 0, 0, .025)); 28 | } 29 | 30 | .navbar .nav > li > a { 31 | text-shadow: none; 32 | } 33 | 34 | .navbar .brand a:hover { 35 | text-decoration: none; 36 | } 37 | 38 | .nav-list .active > a, .nav-list .active > a:hover { 39 | @shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); 40 | .box-shadow(@shadow); 41 | text-shadow: 0 1px 0 rgba(0, 0, 0, .2); 42 | } 43 | .nav-list > li > a { 44 | line-height: 24px; 45 | padding: 3px 10px; 46 | } 47 | // Sections 48 | section header { 49 | padding-bottom: 12px; 50 | } 51 | 52 | // Footer 53 | .footer { 54 | background-color: @tan; 55 | padding: 30px 0; 56 | text-shadow: none; 57 | border-top: 1px solid #e5e5e5; 58 | .box-shadow(inset 0 5px 15px rgba(0, 0, 0, .025)); 59 | } 60 | -------------------------------------------------------------------------------- /vendor/styles/themes/smokey/_overrides.less: -------------------------------------------------------------------------------- 1 | // Overrides 2 | 3 | h1, h2, h3, h4, h5, h6 { 4 | font-weight: normal; 5 | /*font-family: 'Crete Round', "Helvetica Neue", Helvetica, Arial, sans-serif;*/ 6 | padding-bottom: .3em; 7 | color: #1F444B; 8 | } 9 | .nav .brand { 10 | padding: 0; 11 | } 12 | 13 | .clickable { 14 | cursor: pointer; 15 | } 16 | 17 | // Forms 18 | .form-actions { 19 | background-color: transparent; 20 | border: none; 21 | } 22 | 23 | // Navbar 24 | .navbar-inner { 25 | padding-top: 6px; 26 | border-bottom: 1px solid #e5e5e5; 27 | .box-shadow(inset 0 -2px 5px rgba(0, 0, 0, .025)); 28 | } 29 | 30 | .navbar .nav > li > a { 31 | text-shadow: none; 32 | } 33 | 34 | .navbar .brand a:hover { 35 | text-decoration: none; 36 | } 37 | 38 | .nav-list .active > a, .nav-list .active > a:hover { 39 | @shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); 40 | .box-shadow(@shadow); 41 | text-shadow: 0 1px 0 rgba(0, 0, 0, .2); 42 | } 43 | .nav-list > li > a { 44 | line-height: 24px; 45 | padding: 3px 10px; 46 | } 47 | // Sections 48 | section header { 49 | padding-bottom: 12px; 50 | } 51 | 52 | // Footer 53 | .footer { 54 | background-color: @smoke; 55 | padding: 30px 0; 56 | text-shadow: none; 57 | border-top: 1px solid #e5e5e5; 58 | .box-shadow(inset 0 5px 15px rgba(0, 0, 0, .025)); 59 | } 60 | -------------------------------------------------------------------------------- /vendor/scripts/parse.js: -------------------------------------------------------------------------------- 1 | var ret = {}; 2 | var category = {}; 3 | var depname = {}; 4 | var ret = {}; 5 | var dep, cat, val; 6 | function add(root, list, idx, value) { 7 | if(!root["childhash"]) root.childhash = {}; 8 | if(!root.childhash[list[idx]]) root.childhash[list[idx]] = {}; 9 | if(idx"+node.name+" "+(node.children?"":node.value)+""); 26 | for(i in node.children) { 27 | dump(node.children[i], level+1); 28 | } 29 | } 30 | 31 | function parse(data) { 32 | var retval = {"name": "root"}; 33 | for(i in data.drilldown) { 34 | cat = data.drilldown[i].cat; 35 | dep = data.drilldown[i].depname; 36 | val = data.drilldown[i].amount; 37 | add(ret, [cat,dep], 0, val); 38 | } 39 | reorg(retval, ret.childhash); 40 | //dump(retval, 1); 41 | return retval; 42 | } 43 | -------------------------------------------------------------------------------- /g0v.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twbudget", 3 | "name_zh": "中央政府總預算", 4 | "status": "Alpha", 5 | "tags": [ 6 | "angularjs", 7 | "livescript", 8 | "sass", 9 | "bootstrap" 10 | ], 11 | "licenses": "MIT", 12 | "author": "clkao", 13 | "contributors": [ 14 | "zbryikt", 15 | "CA", 16 | "jeffhung", 17 | "ipa" 18 | ], 19 | "description": "Taiwan Central Government Budget project supercharges citizens with the power of visualization and social network to supervise government spending. Provide different perspectives of budget data like historical trend, cross-department comparison, public opinion and component breakdown of tax by government spending with interactive treemap, bubble chart and feedback mechanism.", 20 | "description_zh": "中央政府總預算視覺化專案利用資料視覺化與社群網路的力量提升公民對政府預算規畫的監督能力。此專案利用各種互動式的視覺化技>巧 ( 包含樹狀圖 ( treemap ) 、泡泡圖 (Bubble Chart ) 搭配民眾反饋機制來展示預算數字的各種面向,例如歷史趨勢、跨部門比較、公眾意見與稅金的拆解。", 21 | "homepage": "http://budget.g0v.tw/", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/g0v/twbudget" 25 | }, 26 | "audience": [ 27 | "public" 28 | ], 29 | "products": [ 30 | "website" 31 | ], 32 | "projects": [ 33 | "budget" 34 | ], 35 | "thumbnail": "https://raw.github.com/g0v/twbudget/master/thumbnail.png", 36 | "needs": [ 37 | "designer", 38 | "programmer", 39 | "writer" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "g0v.tw", 3 | "name": "g0v.tw", 4 | "description": "g0v.tw", 5 | "version": "0.0.1", 6 | "homepage": "https://github.com/g0v/twbudget", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/g0v/twbudget" 10 | }, 11 | "engines": { 12 | "node": "4.4.x", 13 | "npm": "2.15.x" 14 | }, 15 | "scripts": { 16 | "prepublish": "lsc -cj package.ls", 17 | "start": "node server/app.js" 18 | }, 19 | "dependencies": { 20 | "zappajs": "0.6.x", 21 | "mongoose": "3.2.x", 22 | "passport": "0.1.x", 23 | "bcrypt": "*", 24 | "cookie-sessions": "git://github.com/clkao/cookie-sessions.git", 25 | "passport-local": "0.1.x", 26 | "passport-facebook": "0.1.x", 27 | "passport-github": "0.1.x", 28 | "passport-twitter": "0.1.x", 29 | "jade": "0.27.x" 30 | }, 31 | "devDependencies": { 32 | "LiveScript": "1.1.1", 33 | "q": "*", 34 | "brunch": "1.6.x", 35 | "javascript-brunch": "1.5.x", 36 | "LiveScript-brunch": "1.5.x", 37 | "css-brunch": "1.5.x", 38 | "sass-brunch": "1.5.x", 39 | "jade-brunch": "1.5.x", 40 | "static-jade-brunch": ">= 1.4.8 < 1.5", 41 | "auto-reload-brunch": "1.6.x", 42 | "uglify-js-brunch": "1.5.x", 43 | "clean-css-brunch": "1.5.x", 44 | "jade-angularjs-brunch": "0.0.5", 45 | "jsenv-brunch": "1.4.2" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_responsive-utilities.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Hide from screenreaders and browsers 7 | // Credit: HTML5 Boilerplate 8 | .hidden { 9 | display: none; 10 | visibility: hidden; 11 | } 12 | 13 | // Visibility utilities 14 | 15 | // For desktops 16 | .visible-phone { display: none !important; } 17 | .visible-tablet { display: none !important; } 18 | .hidden-phone { } 19 | .hidden-tablet { } 20 | .hidden-desktop { display: none !important; } 21 | .visible-desktop { display: inherit !important; } 22 | 23 | // Tablets & small desktops only 24 | @media (min-width: 768px) and (max-width: 979px) { 25 | // Hide everything else 26 | .hidden-desktop { display: inherit !important; } 27 | .visible-desktop { display: none !important ; } 28 | // Show 29 | .visible-tablet { display: inherit !important; } 30 | // Hide 31 | .hidden-tablet { display: none !important; } 32 | } 33 | 34 | // Phones only 35 | @media (max-width: 767px) { 36 | // Hide everything else 37 | .hidden-desktop { display: inherit !important; } 38 | .visible-desktop { display: none !important; } 39 | // Show 40 | .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior 41 | // Hide 42 | .hidden-phone { display: none !important; } 43 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # twbudget 2 | ### visualizing taiwan central government spending 3 | 4 | ![twbudget](https://raw.github.com/g0v/twbudget/master/thumbnail.png "twbudget") 5 | 6 | ## Prerequisites 7 | 8 | 9 | ### Windows 7(32-bit): 10 | 11 | 1.Install the correct Windows SDK from [here](http://go.microsoft.com/?linkid=7729279) for the Node modules requiring rebuild for installation such as bcrypt. 12 | 13 | 2.Install Visual Studio 2008 Redistributables from [here](http://www.microsoft.com/downloads/details.aspx?familyid=9B2DA534-3E03-4391-8A4D-074B9F2BC1BF) for OpenSLL, which is for bcrypt. 14 | 15 | 3.Install OpenSSL from [here](http://slproweb.com/download/Win32OpenSSL-1_0_1e.exe) for bcrypt. 16 | 17 | 4.Same as below except "brew" is not available on Windows so please install below modules separately. 18 | 19 | ### Mac OS X and [Homebrew](http://mxcl.github.io/homebrew/): 20 | 21 | $ brew install node # Install nodejs and npm 22 | $ brew install brew-gem # Install sass 23 | $ gem install sass 24 | $ brew install mongodb # Install mongodb 25 | $ mongod # Run mongodb in foreground 26 | 27 | ## build 28 | 29 | * `npm i` to install node packages 30 | 31 | ### Running the app during development 32 | 33 | * `brunch w &` 34 | * `make run` 35 | 36 | Then navigate your browser to [http://localhost:8000](http://localhost:8000) 37 | 38 | ## License 39 | 40 | MIT http://g0v.mit-license.org/ 41 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_alerts.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: 8px 35px 8px 14px; 11 | margin-bottom: $baseLineHeight; 12 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 13 | background-color: $warningBackground; 14 | border: 1px solid $warningBorder; 15 | @include border-radius(4px); 16 | color: $warningText; 17 | } 18 | .alert h4 { 19 | margin: 0; 20 | } 21 | 22 | // Adjust close link position 23 | .alert .close { 24 | position: relative; 25 | top: -2px; 26 | right: -21px; 27 | line-height: $baseLineHeight; 28 | } 29 | 30 | 31 | // Alternate styles 32 | // ------------------------- 33 | 34 | .alert-success { 35 | background-color: $successBackground; 36 | border-color: $successBorder; 37 | color: $successText; 38 | } 39 | .alert-danger, 40 | .alert-error { 41 | background-color: $errorBackground; 42 | border-color: $errorBorder; 43 | color: $errorText; 44 | } 45 | .alert-info { 46 | background-color: $infoBackground; 47 | border-color: $infoBorder; 48 | color: $infoText; 49 | } 50 | 51 | 52 | // Block alerts 53 | // ------------------------- 54 | 55 | .alert-block { 56 | padding-top: 14px; 57 | padding-bottom: 14px; 58 | } 59 | .alert-block > p, 60 | .alert-block > ul { 61 | margin-bottom: 0; 62 | } 63 | .alert-block p + p { 64 | margin-top: 5px; 65 | } -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_thumbnails.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files 7 | 8 | // Make wrapper ul behave like the grid 9 | .thumbnails { 10 | margin-left: -$gridGutterWidth; 11 | list-style: none; 12 | @include clearfix(); 13 | } 14 | // Fluid rows have no left margin 15 | .row-fluid .thumbnails { 16 | margin-left: 0; 17 | } 18 | 19 | // Float li to make thumbnails appear in a row 20 | .thumbnails > li { 21 | float: left; // Explicity set the float since we don't require .span* classes 22 | margin-bottom: $baseLineHeight; 23 | margin-left: $gridGutterWidth; 24 | } 25 | 26 | // The actual thumbnail (can be `a` or `div`) 27 | .thumbnail { 28 | display: block; 29 | padding: 4px; 30 | line-height: $baseLineHeight; 31 | border: 1px solid #ddd; 32 | @include border-radius(4px); 33 | @include box-shadow(#{0 1px 3px rgba(0,0,0,.055)}); 34 | @include transition(all .2s ease-in-out); 35 | } 36 | // Add a hover state for linked versions only 37 | a.thumbnail:hover { 38 | border-color: $linkColor; 39 | @include box-shadow(#{0 1px 4px rgba(0,105,214,.25)}); 40 | } 41 | 42 | // Images and captions 43 | .thumbnail > img { 44 | display: block; 45 | max-width: 100%; 46 | margin-left: auto; 47 | margin-right: auto; 48 | } 49 | .thumbnail .caption { 50 | padding: 9px; 51 | color: $gray; 52 | } -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_code.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and blocK) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | pre { 9 | padding: 0 3px 2px; 10 | @include font-family-monospace(); 11 | font-size: $baseFontSize - 2; 12 | color: $grayDark; 13 | @include border-radius(3px); 14 | } 15 | 16 | // Inline code 17 | code { 18 | padding: 2px 4px; 19 | color: #d14; 20 | background-color: #f7f7f9; 21 | border: 1px solid #e1e1e8; 22 | } 23 | 24 | // Blocks of code 25 | pre { 26 | display: block; 27 | padding: ($baseLineHeight - 1) / 2; 28 | margin: 0 0 $baseLineHeight / 2; 29 | font-size: $baseFontSize - 1; // 14px to 13px 30 | line-height: $baseLineHeight; 31 | word-break: break-all; 32 | word-wrap: break-word; 33 | white-space: pre; 34 | white-space: pre-wrap; 35 | background-color: #f5f5f5; 36 | border: 1px solid #ccc; // fallback for IE7-8 37 | border: 1px solid rgba(0,0,0,.15); 38 | @include border-radius(4px); 39 | 40 | // Make prettyprint styles more spaced out for readability 41 | &.prettyprint { 42 | margin-bottom: $baseLineHeight; 43 | } 44 | 45 | // Account for some code outputs that place code tags in pre tags 46 | code { 47 | padding: 0; 48 | color: inherit; 49 | background-color: transparent; 50 | border: 0; 51 | } 52 | } 53 | 54 | // Enable scrollable blocks of code 55 | .pre-scrollable { 56 | max-height: 340px; 57 | overflow-y: scroll; 58 | } -------------------------------------------------------------------------------- /app/app.ls: -------------------------------------------------------------------------------- 1 | # Declare app level module which depends on filters, and services 2 | angular.module \app, <[ partials ngResource app.controllers app.directives app.filters app.services ui.directives ui.state ]> 3 | 4 | .config <[$stateProvider $urlRouterProvider $locationProvider]> ++ ($stateProvider, $urlRouterProvider, $locationProvider) -> 5 | $stateProvider 6 | .state 'view2' do 7 | url: '/view2' 8 | templateUrl: '/partials/partial2.html' 9 | .state 'view3' do 10 | url: '/view3' 11 | templateUrl: '/partials/partial3.html' 12 | controller: \BudgetItem 13 | .state 'budget' do 14 | url: '/budget' 15 | templateUrl: '/partials/partial4.html' 16 | controller: \BudgetItem 17 | .state 'budget.detail' do 18 | url: '/{code}' 19 | templateUrl: '/partials/partial4.html' 20 | .state 'debtclock' do 21 | url: '/debtclock' 22 | templateUrl: '/partials/debtclock.html' 23 | .state 'profile' do 24 | url: '/profile' 25 | templateUrl: '/partials/profile.html' 26 | 27 | $urlRouterProvider 28 | .otherwise('/budget') 29 | 30 | $locationProvider.html5Mode true 31 | 32 | .run <[$rootScope $state $stateParams $location]> ++ ($rootScope, $state, $stateParams, $location) -> 33 | $rootScope.$state = $state 34 | $rootScope.$stateParam = $stateParams 35 | $rootScope.go = -> $location.path it 36 | #$rootScope._build = window.global.config.BUILD 37 | $rootScope.$on \$stateChangeSuccess (e, {url,name}) -> 38 | window?ga? 'send' 'pageview' page: url, title: name 39 | -------------------------------------------------------------------------------- /app/styles/styles.sass: -------------------------------------------------------------------------------- 1 | body 2 | overflow: scroll 3 | div.categories 4 | padding: 10px 5 | position: relative 6 | overflow-y: hidden 7 | overflow-x: scroll 8 | height: 400px 9 | text-align: center 10 | div.category 11 | position: absolute 12 | width: 250px 13 | height: 50px 14 | top: 10px 15 | ul.products 16 | overflow: scroll 17 | height: 400px 18 | margin-top: 20px 19 | ul.product1 20 | color: #f00 21 | ul.product2 22 | color: #0f0 23 | ul.product3 24 | color: #00f 25 | li.product 26 | list-style: none 27 | margin: 5px 28 | text-align: left 29 | 30 | .main-content 31 | width: 1004px 32 | 33 | #treemap, #bubbletree, #aggregate 34 | margin: 10px 35 | height: 450px 36 | width: 700px 37 | border: 1px solid #eee 38 | box-shadow: 1px 3px 4px 1px #888 39 | 40 | #mytab 41 | width: 800px 42 | height: 550px 43 | 44 | .icon-big-back, .icon-big-plus, .icon-big-minus, .icon-big-info-sign, .icon-big-question-sign, .icon-big-ban-circle, .icon-big-close 45 | width: 32px 46 | height: 32px 47 | background-image: url(/img/glyphicons.png) 48 | background-repeat: no-repeat 49 | .icon-big-back 50 | background-position: -48px -1056px 51 | .icon-big-plus 52 | background-position: -0px -912px 53 | .icon-big-minus 54 | background-position: -48px -912px 55 | .icon-big-info-sign 56 | background-position: -240px -912px 57 | .icon-big-question-sign 58 | background-position: -192px -912px 59 | .icon-big-ban-circle 60 | background-position: -432px -912px 61 | .icon-big-close 62 | background-position: -96px -912px 63 | -------------------------------------------------------------------------------- /app/partials/partial4.jade: -------------------------------------------------------------------------------- 1 | .treemap-root 2 | .btn-group(data-toggle="buttons-radio") 3 | a.btn.default.active Default 4 | a.btn.bycat 政事別 5 | a.btn.bytop 主管單位 6 | #bubble-circle-size.btn 圖例 7 | #bubble-info 8 | #bubble-info-inner 9 | i#bubble-info-close.icon-big-close(style="margin-top:-5px") 10 | #bubble-info-left 11 | #bubble-detail-info 12 | #bubble-detail-names 13 | i.icon-big-info-sign(style="margin-top:-9px") 14 | span#bubble-detail-name ??? 15 | |( 16 | span#bubble-detail-depname ??? 17 | ) 18 | #bubble-detail-amount 19 | span#bubble-detail-amount-value ??? 20 | span#bubble-detail-amount-quantifier 21 | span#bubble-detail-amount-unit 元 22 | |( 23 | span#bubble-detail-amount-change ??? 24 | | ) 25 | br 26 | |( 即為 27 | span#bubble-detail-amount-alt ??? 28 | | ) 29 | br 30 | #bubble-detail-amount-link 31 | span 連結到此項 : 32 | a#bubble-detail-link(href="view4") http://g0v.tw/view4 33 | hr 34 | #bubble-detail-change-bar 35 | hr 36 | include tags 37 | hr 38 | include vote 39 | #bubble-info-right 40 | #bubble-chart 41 | script 42 | test_bubble(); 43 | svg 44 | g 45 | defs 46 | radialGradient(id="MyGradient",gradientUnits="userSpaceOnUse",cx="500",cy="350",r="500",fx="400",fy="200") 47 | stop(offset="0%",stop-color="red") 48 | stop(offset="50%",stop-color="blue") 49 | stop(offset="100%",stop-color="red") 50 | -------------------------------------------------------------------------------- /test/testacular_conf.js: -------------------------------------------------------------------------------- 1 | // Sample Testacular configuration file, that contain pretty much all the available options 2 | // It's used for running client tests on Travis (http://travis-ci.org/#!/vojtajina/testacular) 3 | // Most of the options can be overriden by cli arguments (see testacular --help) 4 | 5 | 6 | 7 | // base path, that will be used to resolve files and exclude 8 | basePath = '../' 9 | 10 | // list of files / patterns to load in the browser 11 | files = [ 12 | JASMINE, 13 | JASMINE_ADAPTER, 14 | '_public/js/vendor.js', 15 | '_public/js/app.js', 16 | 'test/vendor/angular/angular-mocks.js', 17 | 'test/unit/app/*.js' 18 | ]; 19 | 20 | // list of files to exclude 21 | exclude = []; 22 | 23 | // use dots reporter, as travis terminal does not support escaping sequences 24 | // possible values: 'dots' || 'progress' 25 | reporter = 'progress'; 26 | 27 | // web server port 28 | port = 3334; 29 | 30 | // cli runner port 31 | runnerPort = 3335; 32 | 33 | // enable / disable colors in the output (reporters and logs) 34 | colors: true; 35 | 36 | // level of logging 37 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 38 | logLevel = LOG_INFO; 39 | //logLevel = LOG_DEBUG; 40 | 41 | // enable / disable watching file and executing tests whenever any file changes 42 | autoWatch = true; 43 | 44 | // polling interval in ms (ignored on OS that support inotify) 45 | autoWatchInterval: 0; 46 | 47 | // Start these browsers, currently available: 48 | // - Chrome 49 | // - ChromeCanary 50 | // - Firefox 51 | // - Opera 52 | // - Safari 53 | // - PhantomJS 54 | browsers = ['Chrome']; 55 | 56 | // Auto run tests on start (when browsers are captured) and exit 57 | singleRun = false; 58 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_pagination.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination (multiple pages) 3 | // -------------------------------------------------- 4 | 5 | 6 | .pagination { 7 | height: $baseLineHeight * 2; 8 | margin: $baseLineHeight 0; 9 | } 10 | .pagination ul { 11 | display: inline-block; 12 | @include ie7-inline-block(); 13 | margin-left: 0; 14 | margin-bottom: 0; 15 | @include border-radius(3px); 16 | @include box-shadow(#{0 1px 2px rgba(0,0,0,.05)}); 17 | } 18 | .pagination ul > li { 19 | display: inline; 20 | } 21 | .pagination ul > li > a, 22 | .pagination ul > li > span { 23 | float: left; 24 | padding: 0 14px; 25 | line-height: ($baseLineHeight * 2) - 2; 26 | text-decoration: none; 27 | background-color: $paginationBackground; 28 | border: 1px solid $paginationBorder; 29 | border-left-width: 0; 30 | } 31 | .pagination ul > li > a:hover, 32 | .pagination ul > .active > a, 33 | .pagination ul > .active > span { 34 | background-color: #f5f5f5; 35 | } 36 | .pagination ul > .active > a, 37 | .pagination ul > .active > span { 38 | color: $grayLight; 39 | cursor: default; 40 | } 41 | .pagination ul > .disabled > span, 42 | .pagination ul > .disabled > a, 43 | .pagination ul > .disabled > a:hover { 44 | color: $grayLight; 45 | background-color: transparent; 46 | cursor: default; 47 | } 48 | .pagination ul > li:first-child > a, 49 | .pagination ul > li:first-child > span { 50 | border-left-width: 1px; 51 | @include border-radius(3px 0 0 3px); 52 | } 53 | .pagination ul > li:last-child > a, 54 | .pagination ul > li:last-child > span { 55 | @include border-radius(0 3px 3px 0); 56 | } 57 | 58 | // Centered 59 | .pagination-centered { 60 | text-align: center; 61 | } 62 | .pagination-right { 63 | text-align: right; 64 | } 65 | -------------------------------------------------------------------------------- /app/styles/bubbles.sass: -------------------------------------------------------------------------------- 1 | @import 'vendor//styles/bootstrap/_mixins.scss' 2 | text 3 | font-size: 13px 4 | #details 5 | position: absolute 6 | height: 250px 7 | width: 420px 8 | top: 380px 9 | border: 1px solid black 10 | background: white 11 | opacity: 0.7 12 | +border-radius(4px) 13 | box-shadow: 1px 3px 4px 1px #888 14 | 15 | #bubble-circle-size 16 | position: absolute 17 | top: 0px 18 | left: 560px 19 | 20 | #bubble-info-close 21 | position: absolute 22 | cursor: pointer 23 | opacity: 0.0 24 | right: 10px 25 | 26 | #bubble-info 27 | position: absolute 28 | top: 55px 29 | left: 5px 30 | width: 360px 31 | height: 535px 32 | z-index: -1 33 | background: #eee 34 | box-shadow: 0px 0px 5px 35 | 36 | #bubble-info-inner 37 | margin: 20px 10px 10px 10px 38 | #budget-tags 39 | height: 65px 40 | hr 41 | margin: 5px 42 | 43 | #bubble-info-left 44 | width: 360px 45 | float: left 46 | 47 | #bubble-info-right 48 | width: 360px 49 | float: left 50 | margin-left: 40px 51 | 52 | #bubble-detail-info 53 | height: 115px 54 | .bubbletext 55 | font-size: 13px 56 | 57 | #bubble-chart 58 | margin-top: -30px 59 | #year-chart 60 | height: 150px 61 | rect 62 | fill: steelblue 63 | 64 | #bubble-detail-amount 65 | margin-top: 10px 66 | text-align: center 67 | 68 | #bubble-detail-amount-value 69 | font-size: 24px 70 | 71 | #bubble-detail-names 72 | height: 36px 73 | width: 330px 74 | line-height: 11px 75 | 76 | #bubble-detail-name 77 | font-size: 19px 78 | 79 | #bubble-detail-change-bar,#bubble-detail-change-bar2 80 | height: 144px 81 | rect 82 | fill: steelblue 83 | 84 | #bubble-detail-amount-link 85 | text-align: right 86 | margin: 10px 20px 87 | font-size: 10px 88 | color: #999 89 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_tooltip.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Tooltips 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .tooltip { 8 | position: absolute; 9 | z-index: $zindexTooltip; 10 | display: block; 11 | visibility: visible; 12 | padding: 5px; 13 | font-size: 11px; 14 | @include opacity(0); 15 | &.in { @include opacity(0.8); } 16 | &.top { margin-top: -3px; } 17 | &.right { margin-left: 3px; } 18 | &.bottom { margin-top: 3px; } 19 | &.left { margin-left: -3px; } 20 | } 21 | 22 | // Wrapper for the tooltip content 23 | .tooltip-inner { 24 | max-width: 200px; 25 | padding: 3px 8px; 26 | color: $tooltipColor; 27 | text-align: center; 28 | text-decoration: none; 29 | background-color: $tooltipBackground; 30 | @include border-radius(4px); 31 | } 32 | 33 | // Arrows 34 | .tooltip-arrow { 35 | position: absolute; 36 | width: 0; 37 | height: 0; 38 | border-color: transparent; 39 | border-style: solid; 40 | } 41 | .tooltip { 42 | &.top .tooltip-arrow { 43 | bottom: 0; 44 | left: 50%; 45 | margin-left: -$tooltipArrowWidth; 46 | border-width: $tooltipArrowWidth $tooltipArrowWidth 0; 47 | border-top-color: $tooltipArrowColor; 48 | } 49 | &.right .tooltip-arrow { 50 | top: 50%; 51 | left: 0; 52 | margin-top: -$tooltipArrowWidth; 53 | border-width: $tooltipArrowWidth $tooltipArrowWidth $tooltipArrowWidth 0; 54 | border-right-color: $tooltipArrowColor; 55 | } 56 | &.left .tooltip-arrow { 57 | top: 50%; 58 | right: 0; 59 | margin-top: -$tooltipArrowWidth; 60 | border-width: $tooltipArrowWidth 0 $tooltipArrowWidth $tooltipArrowWidth; 61 | border-left-color: $tooltipArrowColor; 62 | } 63 | &.bottom .tooltip-arrow { 64 | top: 0; 65 | left: 50%; 66 | margin-left: -$tooltipArrowWidth; 67 | border-width: 0 $tooltipArrowWidth $tooltipArrowWidth; 68 | border-bottom-color: $tooltipArrowColor; 69 | } 70 | } -------------------------------------------------------------------------------- /app/info.ls: -------------------------------------------------------------------------------- 1 | InfoPanel = 2 | setState: (s) -> 3 | @state = s 4 | if s==1 then 5 | d3.select \#bubble-info-right .transition! .duration 750 .style \opacity 0.0 6 | d3.select \#bubble-info-right .transition! .delay 750 .style \display \none 7 | d3.select \#bubble-info .transition! .duration 750 .style \width \360px .style \opacity 1.0 .style \margin-right \-100px 8 | d3.select \#bubble-info .transition! .ease -> 1 9 | .delay 750 .style \position \absolute .style \left \5px .style \margin-left \0 .style \top \55px .style \z-index -1 10 | d3.select \#bubble-info-close .transition! .duration 750 .style \opacity 0.0 11 | if s==2 then 12 | d3.select \#bubble-info .style \z-index -1 .transition! .duration 475 .style \opacity 0.2 13 | d3.select \#bubble-info-right .style \display \block 14 | d3.select \#bubble-info-right .transition! .duration 750 .style \opacity 1.0 15 | d3.select \#bubble-info .transition! .duration 750 .style \width \994px .style \opacity 0.2 16 | d3.select \#bubble-info-close .transition! .duration 750 .style \opacity 1.0 .style \margin-right \0px 17 | d3.select \#bubble-info .transition! .ease -> 1 18 | .delay 750 .style \position \fixed .style \left \50% .style \margin-left \-497px .style \top \107px .style \z-index -1 19 | if s==3 then 20 | d3.select \#bubble-info .style \z-index,100 .transition! .duration 475 .style \opacity 0.9 21 | d3.select \#bubble-info-right .style \display \block 22 | d3.select \#bubble-info-right .transition! .duration 750 .style \opacity 1.0 23 | d3.select \#bubble-info .transition! .duration 750 .style \width \994px .style \opacity 0.9 24 | d3.select \#bubble-info-close .transition! .duration 750 .style \opacity 1.0 .style \margin-right \0px 25 | #d3.select \#bubble-info .transition! .ease -> 1 26 | # .delay 750 .style \position \fixed .style \left \50% .style \margin-left \-497px .style \top \107px .style \z-index -1 27 | -------------------------------------------------------------------------------- /app/styles/treemap.sass: -------------------------------------------------------------------------------- 1 | .treemap-root 2 | position: relative 3 | overflow: hidden 4 | 5 | #treemap-root 6 | position: relative 7 | overflow: hidden 8 | #treemap-root-inner 9 | position: absolute 10 | margin-top: 0px 11 | #treemap-svg 12 | overflow: hidden 13 | width: 630px 14 | height: 560px 15 | #budget-addunit-result 16 | margin: 20px 17 | #addunit-value-group .alert 18 | margin: 10px 19 | display: none 20 | #addunit-value-group.error .alert 21 | display: block!important 22 | #budget-detail 23 | float: right 24 | width: 360px 25 | height: 560px 26 | background: #eee 27 | box-shadow: 1px 5px 5px #888 28 | #budget-detail-inner 29 | margin: 10px 30 | height: 540px 31 | background: #eee 32 | #budget-detail-info 33 | position: relative 34 | height: 200px 35 | #budget-detail-amount 36 | position: absolute 37 | top: 0 38 | left: 0 39 | margin-top: 85px 40 | width: 100% 41 | text-align: center 42 | #budget-detail-amount-field1 43 | display: block 44 | font-size: 36px 45 | line-height: 30px 46 | text-shadow: #999 3px 3px 2px 47 | #budget-detail-amount-field1-unit 48 | font-size: 24px 49 | #budget-detail-amount-field2 50 | font-size: 13px 51 | line-height: 38px 52 | #budget-detail-category 53 | position: absolute 54 | top: 0 55 | left: 0 56 | width: 100% 57 | margin-top: 195px 58 | text-align: right 59 | 60 | .budget-treemap 61 | display: block 62 | box-shadow: 1px 5px 5px #888 63 | -moz-user-select: none 64 | -khtml-user-select: none 65 | -webkit-user-select: none 66 | user-select: none 67 | cursor: pointer 68 | text 69 | font-size: 11px 70 | rect 71 | fill: none 72 | stroke-width: 5px 73 | #treemap-backbtn 74 | position: absolute 75 | top: 510px 76 | left: 563px 77 | z-index: 10 78 | #debitmask 79 | background: #f99 80 | position: absolute 81 | top: 0px 82 | left: 20px 83 | z-index: 12 84 | font-size: 100px 85 | width: 600px 86 | height: 520px 87 | line-height: 520px 88 | text-align: center 89 | opacity: 0.8 90 | display: none 91 | 92 | -------------------------------------------------------------------------------- /app/dailybread.js: -------------------------------------------------------------------------------- 1 | dailybread = function () { 2 | var os_path = '/openspendingjs'; 3 | 4 | var db_load_data = function load_data(db, data) { 5 | $('#content-wrap').show(); 6 | $('#preloader').remove(); 7 | 8 | db.setDataFromAggregator(data, ['unknown']); 9 | db.setIconLookup(function(name) { 10 | var style = OpenSpending.Styles.Cofog[name]; 11 | if (style != undefined) { 12 | return style['icon']; 13 | } 14 | return os_path+'/app/dailybread/icons/unknown.svg'; 15 | }); 16 | db.draw(); 17 | } 18 | 19 | var db_init = function db_init() { 20 | $('#preloader .txt').html('loading data'); 21 | 22 | var db = new OpenSpending.DailyBread($('#dailybread')); 23 | window.__db = db; 24 | 25 | new OpenSpending.Aggregator({ 26 | apiUrl: 'http://openspending.org/api', 27 | dataset: 'twbudget', 28 | drilldowns: ['cat', 'depcat', 'name'], 29 | cuts: ['year:2013'], 30 | rootNodeLabel: 'Total', 31 | breakdown: 'topname', 32 | callback: function (data) { db_load_data(db, data); } 33 | }); 34 | } 35 | 36 | yepnope({ 37 | load: [ 38 | // 'http://wheredoesmymoneygo.org/wp-content/themes/wdmmg/wdmmg.css', 39 | //'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.6/themes/ui-lightness/jquery-ui.css', 40 | //'http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js', 41 | //'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js', 42 | os_path + '/lib/vendor/base64.js', 43 | os_path + '/lib/vendor/underscore.js', 44 | os_path + '/lib/vendor/raphael-min.js', 45 | os_path + '/lib/aggregator.js', 46 | os_path + '/app/dailybread/css/dailybread.css', 47 | os_path + '/app/dailybread/js/cofog.js' 48 | ], 49 | complete: function () {dailybread.loaded=1; jQuery(function ($) { db_init() } ); } 50 | }); 51 | if(dailybread.loaded==1) db_init(); // db_init if re-enter partial2 52 | } 53 | 54 | 55 | dailybread.loaded = 0; 56 | -------------------------------------------------------------------------------- /app/CustomTooltip.js: -------------------------------------------------------------------------------- 1 | function CustomTooltip(tooltipId, width){ 2 | var tooltipId = tooltipId; 3 | var positionFixed = false; 4 | $("body").append("
"); 5 | 6 | if(width){ 7 | $("#"+tooltipId).css("width", width); 8 | } 9 | 10 | hideTooltip(); 11 | 12 | function showTooltip(content, event){ 13 | $("#"+tooltipId).html(content); 14 | $("#"+tooltipId).show(); 15 | 16 | if(event) updatePosition(event); 17 | } 18 | 19 | function hideTooltip(){ 20 | if(!positionFixed) $("#"+tooltipId).hide(); 21 | } 22 | 23 | function updatePosition(event){ 24 | var ttid = "#"+tooltipId; 25 | var xOffset = 20; 26 | var yOffset = 10; 27 | 28 | var ttw = $(ttid).width(); 29 | var tth = $(ttid).height(); 30 | var wscrY = $(window).scrollTop(); 31 | var wscrX = $(window).scrollLeft(); 32 | var curX = (document.all) ? event.clientX + wscrX : event.pageX; 33 | var curY = (document.all) ? event.clientY + wscrY : event.pageY; 34 | var ttleft = ((curX - wscrX + xOffset*2 + ttw) > $(window).width()) ? curX - ttw - xOffset*2 : curX + xOffset; 35 | if (ttleft < wscrX + xOffset){ 36 | ttleft = wscrX + xOffset; 37 | } 38 | var tttop = ((curY - wscrY + yOffset*2 + tth) > $(window).height()) ? curY - tth - yOffset*2 : curY + yOffset; 39 | if (tttop < wscrY + yOffset){ 40 | tttop = curY + yOffset; 41 | } 42 | if(!positionFixed) $(ttid).css('top', tttop + 'px').css('left', ttleft + 'px'); 43 | } 44 | 45 | function fixPosition(fixed,parent) { 46 | positionFixed = fixed; 47 | var node = $("#"+tooltipId); 48 | node.remove(); 49 | if(fixed) { 50 | node.css({top:"0px",left:"0px",width:"100%",height:"100%"}); 51 | parent.append(node); 52 | showTooltip(); 53 | } else { 54 | node.css({top:"0px",left:"0px",width:"auto",height:"auto"}); 55 | $(document.body).append(node); 56 | hideTooltip(); 57 | } 58 | } 59 | 60 | return { 61 | showTooltip: showTooltip, 62 | hideTooltip: hideTooltip, 63 | updatePosition: updatePosition, 64 | fixPosition: fixPosition 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /server/opengraph.ls: -------------------------------------------------------------------------------- 1 | @include = -> 2 | fs = require \fs 3 | 4 | CurrencyData = [ 5 | ["", "元", 1], 6 | ["份","營養午餐",25], 7 | ["份","營養午餐(回扣)",30], 8 | ["人的","年薪",308000], 9 | ["座","釣魚台",80000000], 10 | ["分鐘","太空旅遊",1000000], 11 | ["碗","鬍鬚張魯肉飯",68], 12 | ["個","便當",50], 13 | ["杯","珍奶",30], 14 | ["份","雞排加珍奶",60], 15 | ["個","晨水匾",700000000], 16 | ["個","夢想家",200000000], 17 | ["個","林益世(粗估)",83000000], 18 | ["座","冰島",2000080000000], 19 | ["坪","帝寶",2500000], 20 | ["支","iPhone5",25900], 21 | ["座","硬兔的小島",2000080000000] 22 | ]; 23 | 24 | CurrencyConvert = (v, idx, full) -> 25 | idx ?= 0 26 | c = CurrencyData[idx] 27 | v = parseInt(10000 * v / c[2]) / 10000 28 | v = parseInt(10 * v) / 10 if v > 1 and v < 1000 29 | if v >= 1000 and v < 10000 30 | v = parseInt(v / 1000) + "千" 31 | else if v >= 10000 and v < 100000000 32 | v = parseInt(v / 10000) + "萬" 33 | else if v >= 100000000 and v < 1000000000000 34 | v = parseInt(v / 100000000) + "億" 35 | else v = parseInt(v / 1000000000000) + "兆" if v >= 1000000000000 36 | v + (if full then c[0] + c[1] else "") 37 | 38 | @loadCsv = (fn, cb) -> 39 | fs.readFile fn, \utf8, (err,data) -> 40 | hash = {} 41 | if err then return console.log err 42 | arr = data.split '\n' .filter (e,i,a) -> 43 | e.length>0 44 | for i from arr.length-1 to 0 by -1 45 | arr[i] = arr[i].split \, 46 | hash[arr[i][1]] = arr[i] 47 | cb(hash) 48 | 49 | @getOpenGraph = (hash, code) -> 50 | item = hash[code] 51 | ret = 52 | og_title: "" 53 | og_url: "" 54 | og_description: "" 55 | if !item then return ret 56 | ret.og_title = " : 預算項目「" + item[3] + "」(屬於" + item[4] + " > " + item[5] + " > " + item[6] + ")" 57 | ret.og_url = "budget/"+code 58 | ret.og_description = "【"+item[4]+" > " + item[5] + " > " + item[6] + " > " + item[3] + "】的年度預算為" + CurrencyConvert(item[2],0,true) + ", 相當於" + (CurrencyConvert item[2],parseInt(Math.random!*CurrencyData.length),true) + ", 也等於" + (CurrencyConvert item[2],parseInt(Math.random!*CurrencyData.length),true) 59 | return ret; 60 | 61 | -------------------------------------------------------------------------------- /app/partials/nav.jade: -------------------------------------------------------------------------------- 1 | ul.nav 2 | li(ng-class="getClass('/view2')") 3 | a(ng-href='/view2') 每日支出 4 | li(ng-class="getClass('/view3')") 5 | a(ng-href='/view3') 鳥瞰圖 6 | li(ng-class="getClass('/view4')") 7 | a(ng-href='/budget') 變化圖 8 | li(ng-class="getClass('/debtclock')") 9 | a(ng-href='/debtclock') 國債鐘 10 | li.dropdown 11 | a.dropdown-toggle(id="dLabel",role="button",data-toggle="dropdown",data-target="#") 12 | 單位換算 13 | b.caret 14 | ul.dropdown-menu(id="unit-selector",ng-controller="UnitMapper",role="menu",aria-labelledby="dLabel") 15 | li(ng-repeat="item in units") 16 | a(onclick="UnitMapper.update({{$index}})") 17 | {{item[1]}} 18 | li.divider 19 | li 20 | a(onclick="UnitMapper.update(-1)") 隨便來一個 21 | li.disabled 22 | a(data-toggle="modal",onclick='$("#addunit-value-group").removeClass("error");$("#addunit-modal").modal()') 我要加單位 23 | .modal#addunit-modal.fade(role="dialog",tabindex="-1",aria-labelledby="budget-addunit-label",aria-hidden="true") 24 | .modal-header 25 | button.close(type="button",data-dismiss="modal",aria-hidden="true") x 26 | h3#budget-addunit-label 我要加單位 27 | .modal-body(style="text-align:center") 28 | .input-append 29 | span.add-on 一 30 | input.input-mini(title="量詞欄位不可空白",ng-model="addunit_quantity",type="text",placeholder="量詞,如:個 或 塊") 31 | input.input-medium(title="單位欄位不可空白",ng-model="addunit_unit",type="text",placeholder="單位,如:蘋果 或 金條") 32 | span.add-on 等於 33 | input.span2(title="價錢欄位需填數字",ng-model="addunit_value",type="text",placeholder="價錢,如:50 或 2000 (全數字)") 34 | span.add-on 元 35 | #budget-addunit-result 36 | 預覽: 一{{addunit_quantity}}{{addunit_unit}} 等於 {{addunit_value}} 元 37 | .modal-footer 38 | button.btn(data-dismiss="modal",aria-hidden="true") 取消 39 | button.btn.btn-primary(ng-click="addunit()") 送出 40 | 41 | ul.nav.pull-right 42 | li(ng-class="getClass('/profile')") 43 | a(ng-href='/profile') 我的帳號 44 | li 45 | a 46 | .fb-like(data-send="false",data-href="https://facebook.com/g0v.tw",data-width="120",data-layout="button_count",data-show-faces="false",data-font="verdana") 47 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_labels-badges.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Labels and badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base classes 7 | .label, 8 | .badge { 9 | font-size: $baseFontSize * .846; 10 | font-weight: bold; 11 | line-height: 14px; // ensure proper line-height if floated 12 | color: $white; 13 | vertical-align: baseline; 14 | white-space: nowrap; 15 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 16 | background-color: $grayLight; 17 | } 18 | // Set unique padding and border-radii 19 | .label { 20 | padding: 1px 4px 2px; 21 | @include border-radius(3px); 22 | } 23 | .badge { 24 | padding: 1px 9px 2px; 25 | @include border-radius(9px); 26 | } 27 | 28 | // Hover state, but only for links 29 | a { 30 | &.label:hover, 31 | &.badge:hover { 32 | color: $white; 33 | text-decoration: none; 34 | cursor: pointer; 35 | } 36 | } 37 | 38 | // Colors 39 | // Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute) 40 | 41 | // Important (red) 42 | .label-important, .badge-important { background-color: $errorText; } 43 | .label-important[href], .badge-important[href] { background-color: darken($errorText, 10%); } 44 | // Warnings (orange) 45 | .label-warning, .badge-warning { background-color: $orange; } 46 | .label-warning[href], .badge-warning[href] { background-color: darken($orange, 10%); } 47 | // Success (green) 48 | .label-success, .badge-success { background-color: $successText; } 49 | .label-success[href], .badge-success[href] { background-color: darken($successText, 10%); } 50 | // Info (turquoise) 51 | .label-info, .badge-info { background-color: $infoText; } 52 | .label-info[href], .badge-info[href] { background-color: darken($infoText, 10%); } 53 | // Inverse (black) 54 | .label-inverse, .badge-inverse { background-color: $grayDark; } 55 | .label-inverse[href], .badge-inverse[href] { background-color: darken($grayDark, 10%); } 56 | 57 | // Quick fix for labels/badges in buttons 58 | .btn { 59 | .label, 60 | .badge { 61 | position: relative; 62 | top: -1px; 63 | } 64 | } 65 | .btn-mini { 66 | .label, 67 | .badge { 68 | top: 0; 69 | } 70 | } -------------------------------------------------------------------------------- /app/styles/app.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Bootstrap v2.1.0 3 | // 4 | // Copyright 2012 Twitter, Inc 5 | // Licensed under the Apache License v2.0 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | // Converted to Sass by @thomasmcdonald_, and distributed as bootstrap-sass 10 | 11 | // Core variables and mixins 12 | @import "vendor/styles/bootstrap/variables"; // Modify this for custom colors, font-sizes, etc 13 | @import "vendor/styles/bootstrap/mixins"; 14 | 15 | // CSS Reset 16 | @import "vendor/styles/bootstrap/reset"; 17 | 18 | // Grid system and page structure 19 | @import "vendor/styles/bootstrap/scaffolding"; 20 | @import "vendor/styles/bootstrap/grid"; 21 | @import "vendor/styles/bootstrap/layouts"; 22 | 23 | // Base CSS 24 | @import "vendor/styles/bootstrap/type"; 25 | @import "vendor/styles/bootstrap/code"; 26 | @import "vendor/styles/bootstrap/forms"; 27 | @import "vendor/styles/bootstrap/tables"; 28 | 29 | // Components: common 30 | @import "vendor/styles/bootstrap/sprites"; 31 | @import "vendor/styles/bootstrap/dropdowns"; 32 | @import "vendor/styles/bootstrap/wells"; 33 | @import "vendor/styles/bootstrap/component-animations"; 34 | @import "vendor/styles/bootstrap/close"; 35 | 36 | // Components: Buttons & Alerts 37 | @import "vendor/styles/bootstrap/buttons"; 38 | @import "vendor/styles/bootstrap/button-groups"; 39 | @import "vendor/styles/bootstrap/alerts"; // Note: alerts share common CSS with buttons and thus have styles in _buttons.scss 40 | 41 | // Components: Nav 42 | @import "vendor/styles/bootstrap/navs"; 43 | @import "vendor/styles/bootstrap/navbar"; 44 | @import "vendor/styles/bootstrap/breadcrumbs"; 45 | @import "vendor/styles/bootstrap/pagination"; 46 | @import "vendor/styles/bootstrap/pager"; 47 | 48 | // Components: Popovers 49 | @import "vendor/styles/bootstrap/modals"; 50 | @import "vendor/styles/bootstrap/tooltip"; 51 | @import "vendor/styles/bootstrap/popovers"; 52 | 53 | // Components: Misc 54 | @import "vendor/styles/bootstrap/thumbnails"; 55 | @import "vendor/styles/bootstrap/labels-badges"; 56 | @import "vendor/styles/bootstrap/progress-bars"; 57 | @import "vendor/styles/bootstrap/accordion"; 58 | @import "vendor/styles/bootstrap/carousel"; 59 | @import "vendor/styles/bootstrap/hero-unit"; 60 | 61 | // Utility classes 62 | @import "vendor/styles/bootstrap/utilities"; // Has to be last to override when necessary 63 | -------------------------------------------------------------------------------- /app/unitmapper.ls: -------------------------------------------------------------------------------- 1 | 2 | UnitMapper = 3 | unit: 0 4 | callbacks: [] 5 | random: -> 6 | this.unit=parseInt(Math.random()*this.table.length) 7 | get: -> return this.unit 8 | getUnit: (des_unit) -> 9 | des_unit ?= this.unit 10 | return this.table[des_unit][1] 11 | 12 | getQuantifier: (des_unit) -> 13 | des_unit ?= this.unit 14 | return this.table[des_unit][0] 15 | 16 | convert: (value, des_unit, full_desc) -> 17 | if des_unit==-1 then des_unit=parseInt Math.random()*this.table.length 18 | des_unit ?= this.unit 19 | unitdata=this.table[des_unit] 20 | value=parseInt(10000*value/unitdata[2])/10000 21 | value = if value>=1000000000000 then parseInt(value/1000000000000)+"兆" 22 | else if value>=100000000 then parseInt(value/100000000)+"億" 23 | else if value>=10000 then parseInt(value/10000)+"萬" 24 | else if value>=1000 then parseInt(value/1000)+"千" 25 | else if value>=1 then parseInt(10*value)/10 26 | else value 27 | return value + if full_desc then unitdata[0]+unitdata[1] else "" 28 | 29 | onchange: (func) -> 30 | this.callbacks.push(func) 31 | 32 | update: (idx) -> 33 | if this.unit>=0 34 | #$('#unit-selector li:eq('+this.unit+') a i').css {"visibility":"hidden"} 35 | $('#unit-selector li:eq('+this.unit+') ').removeClass \active 36 | this.unit = if idx==-1 then parseInt Math.random()*this.table.length 37 | else if idx==void then 0 38 | else idx 39 | 40 | #$('#unit-selector li:eq('+this.unit+') a i').css visibility:\visible 41 | $('#unit-selector li:eq('+this.unit+')').addClass \active 42 | 43 | d3.selectAll(\text.amount).text (d) -> 44 | UnitMapper.convert d.size || d.value.sum, UnitMapper.unit, true 45 | jQuery.each $(".unit-convert"), -> 46 | $(this).text UnitMapper.convert $(this).attr("cc-value"), UnitMapper.unit, true 47 | jQuery.each this.callbacks, (x)-> this() 48 | 49 | init: -> 50 | return 51 | 52 | table: 53 | ["" \元 1] 54 | <[份 營養午餐 25]> 55 | <[份 營養午餐(回扣) 30]> 56 | <[人的 年薪 308000]> 57 | <[座 釣魚台 80000000]> 58 | <[分鐘 太空旅遊 1000000]> 59 | <[碗 鬍鬚張魯肉飯 68]> 60 | <[個 便當 50]> 61 | <[杯 珍奶 30]> 62 | <[份 雞排加珍奶 60]> 63 | <[個 晨水匾 700000000]> 64 | <[個 夢想家 200000000]> 65 | <[個 林益世(粗估) 83000000]> 66 | <[座 冰島 2000080000000]> 67 | <[坪 帝寶 2500000]> 68 | <[支 iPhone5 25900]> 69 | <[座 硬兔的小島 2000080000000]> 70 | -------------------------------------------------------------------------------- /vendor/scripts/angular/http-auth-interceptor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license HTTP Auth Interceptor Module for AngularJS 3 | * (c) 2012 Witold Szczerba 4 | * License: MIT 5 | */ 6 | angular.module('http-auth-interceptor', []) 7 | 8 | .provider('authService', function() { 9 | /** 10 | * Holds all the requests which failed due to 401 response, 11 | * so they can be re-requested in future, once login is completed. 12 | */ 13 | var buffer = []; 14 | 15 | /** 16 | * Required by HTTP interceptor. 17 | * Function is attached to provider to be invisible for regular users of this service. 18 | */ 19 | this.pushToBuffer = function(config, deferred) { 20 | buffer.push({ 21 | config: config, 22 | deferred: deferred 23 | }); 24 | } 25 | 26 | this.$get = ['$rootScope','$injector', function($rootScope, $injector) { 27 | var $http; //initialized later because of circular dependency problem 28 | function retry(config, deferred) { 29 | $http = $http || $injector.get('$http'); 30 | $http(config).then(function(response) { 31 | deferred.resolve(response); 32 | }); 33 | } 34 | function retryAll() { 35 | for (var i = 0; i < buffer.length; ++i) { 36 | retry(buffer[i].config, buffer[i].deferred); 37 | } 38 | buffer = []; 39 | } 40 | 41 | return { 42 | loginConfirmed: function() { 43 | $rootScope.$broadcast('event:auth-loginConfirmed'); 44 | retryAll(); 45 | } 46 | } 47 | }] 48 | }) 49 | 50 | /** 51 | * $http interceptor. 52 | * On 401 response - it stores the request and broadcasts 'event:angular-auth-loginRequired'. 53 | */ 54 | .config(['$httpProvider', 'authServiceProvider', function($httpProvider, authServiceProvider) { 55 | 56 | var interceptor = ['$rootScope', '$q', function($rootScope, $q) { 57 | function success(response) { 58 | return response; 59 | } 60 | 61 | function error(response) { 62 | if (response.status === 401) { 63 | var deferred = $q.defer(); 64 | authServiceProvider.pushToBuffer(response.config, deferred); 65 | $rootScope.$broadcast('event:auth-loginRequired'); 66 | return deferred.promise; 67 | } 68 | // otherwise 69 | return $q.reject(response); 70 | } 71 | 72 | return function(promise) { 73 | return promise.then(success, error); 74 | } 75 | 76 | }]; 77 | $httpProvider.responseInterceptors.push(interceptor); 78 | }]); 79 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_modals.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Modals 3 | // -------------------------------------------------- 4 | 5 | 6 | // Recalculate z-index where appropriate, 7 | // but only apply to elements within modal 8 | .modal-open modal { 9 | .dropdown-menu { z-index: $zindexDropdown + $zindexModal; } 10 | .dropdown.open { *z-index: $zindexDropdown + $zindexModal; } 11 | .popover { z-index: $zindexPopover + $zindexModal; } 12 | .tooltip { z-index: $zindexTooltip + $zindexModal; } 13 | } 14 | 15 | // Background 16 | .modal-backdrop { 17 | position: fixed; 18 | top: 0; 19 | right: 0; 20 | bottom: 0; 21 | left: 0; 22 | z-index: $zindexModalBackdrop; 23 | background-color: $black; 24 | // Fade for backdrop 25 | &.fade { opacity: 0; } 26 | } 27 | 28 | .modal-backdrop, 29 | .modal-backdrop.fade.in { 30 | @include opacity(0.8); 31 | } 32 | 33 | // Base modal 34 | .modal { 35 | position: fixed; 36 | top: 50%; 37 | left: 50%; 38 | z-index: $zindexModal; 39 | overflow: auto; 40 | width: 560px; 41 | margin: -250px 0 0 -280px; 42 | background-color: $white; 43 | border: 1px solid #999; 44 | border: 1px solid rgba(0,0,0,.3); 45 | *border: 1px solid #999; /* IE6-7 */ 46 | @include border-radius(6px); 47 | @include box-shadow(#{0 3px 7px rgba(0,0,0,0.3)}); 48 | @include background-clip(padding-box); 49 | &.fade { 50 | @include transition(#{opacity .3s linear, top .3s ease-out}); 51 | top: -25%; 52 | } 53 | &.fade.in { top: 50%; } 54 | } 55 | .modal-header { 56 | padding: 9px 15px; 57 | border-bottom: 1px solid #eee; 58 | // Close icon 59 | .close { margin-top: 2px; } 60 | // Heading 61 | h3 { 62 | margin: 0; 63 | line-height: 30px; 64 | } 65 | } 66 | 67 | // Body (where all modal content resides) 68 | .modal-body { 69 | overflow-y: auto; 70 | max-height: 400px; 71 | padding: 15px; 72 | } 73 | // Remove bottom margin if need be 74 | .modal-form { 75 | margin-bottom: 0; 76 | } 77 | 78 | // Footer (for actions) 79 | .modal-footer { 80 | padding: 14px 15px 15px; 81 | margin-bottom: 0; 82 | text-align: right; // right align buttons 83 | background-color: #f5f5f5; 84 | border-top: 1px solid #ddd; 85 | @include border-radius(0 0 6px 6px); 86 | @include box-shadow(#{inset 0 1px 0 $white}); 87 | @include clearfix(); // clear it in case folks use .pull-* classes on buttons 88 | 89 | // Properly space out buttons 90 | .btn + .btn { 91 | margin-left: 5px; 92 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs 93 | } 94 | // but override that for button groups 95 | .btn-group .btn + .btn { 96 | margin-left: -1px; 97 | } 98 | } -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_carousel.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Carousel 3 | // -------------------------------------------------- 4 | 5 | 6 | .carousel { 7 | position: relative; 8 | margin-bottom: $baseLineHeight; 9 | line-height: 1; 10 | } 11 | 12 | .carousel-inner { 13 | overflow: hidden; 14 | width: 100%; 15 | position: relative; 16 | } 17 | 18 | .carousel { 19 | 20 | .item { 21 | display: none; 22 | position: relative; 23 | @include transition(.6s ease-in-out left); 24 | } 25 | 26 | // Account for jankitude on images 27 | .item > img { 28 | display: block; 29 | line-height: 1; 30 | } 31 | 32 | .active, 33 | .next, 34 | .prev { display: block; } 35 | 36 | .active { 37 | left: 0; 38 | } 39 | 40 | .next, 41 | .prev { 42 | position: absolute; 43 | top: 0; 44 | width: 100%; 45 | } 46 | 47 | .next { 48 | left: 100%; 49 | } 50 | .prev { 51 | left: -100%; 52 | } 53 | .next.left, 54 | .prev.right { 55 | left: 0; 56 | } 57 | 58 | .active.left { 59 | left: -100%; 60 | } 61 | .active.right { 62 | left: 100%; 63 | } 64 | 65 | } 66 | 67 | // Left/right controls for nav 68 | // --------------------------- 69 | 70 | .carousel-control { 71 | position: absolute; 72 | top: 40%; 73 | left: 15px; 74 | width: 40px; 75 | height: 40px; 76 | margin-top: -20px; 77 | font-size: 60px; 78 | font-weight: 100; 79 | line-height: 30px; 80 | color: $white; 81 | text-align: center; 82 | background: $grayDarker; 83 | border: 3px solid $white; 84 | @include border-radius(23px); 85 | @include opacity(0.5); 86 | 87 | // we can't have this transition here 88 | // because webkit cancels the carousel 89 | // animation if you trip this while 90 | // in the middle of another animation 91 | // ;_; 92 | // @include transition(opacity .2s linear); 93 | 94 | // Reposition the right one 95 | &.right { 96 | left: auto; 97 | right: 15px; 98 | } 99 | 100 | // Hover state 101 | &:hover { 102 | color: $white; 103 | text-decoration: none; 104 | @include opacity(0.9); 105 | } 106 | } 107 | 108 | 109 | // Caption for text below images 110 | // ----------------------------- 111 | 112 | .carousel-caption { 113 | position: absolute; 114 | left: 0; 115 | right: 0; 116 | bottom: 0; 117 | padding: 15px; 118 | background: $grayDark; 119 | background: rgba(0,0,0,.75); 120 | } 121 | .carousel-caption h4, 122 | .carousel-caption p { 123 | color: $white; 124 | line-height: $baseLineHeight; 125 | } 126 | .carousel-caption h4 { 127 | margin: 0 0 5px; 128 | } 129 | .carousel-caption p { 130 | margin-bottom: 0; 131 | } -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_reset.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Modals 3 | // Adapted from http://github.com/necolas/normalize.css 4 | // -------------------------------------------------- 5 | 6 | 7 | // Display in IE6-9 and FF3 8 | // ------------------------- 9 | 10 | article, 11 | aside, 12 | details, 13 | figcaption, 14 | figure, 15 | footer, 16 | header, 17 | hgroup, 18 | nav, 19 | section { 20 | display: block; 21 | } 22 | 23 | // Display block in IE6-9 and FF3 24 | // ------------------------- 25 | 26 | audio, 27 | canvas, 28 | video { 29 | display: inline-block; 30 | *display: inline; 31 | *zoom: 1; 32 | } 33 | 34 | // Prevents modern browsers from displaying 'audio' without controls 35 | // ------------------------- 36 | 37 | audio:not([controls]) { 38 | display: none; 39 | } 40 | 41 | // Base settings 42 | // ------------------------- 43 | 44 | html { 45 | font-size: 100%; 46 | -webkit-text-size-adjust: 100%; 47 | -ms-text-size-adjust: 100%; 48 | } 49 | // Focus states 50 | a:focus { 51 | @include tab-focus(); 52 | } 53 | // Hover & Active 54 | a:hover, 55 | a:active { 56 | outline: 0; 57 | } 58 | 59 | // Prevents sub and sup affecting line-height in all browsers 60 | // ------------------------- 61 | 62 | sub, 63 | sup { 64 | position: relative; 65 | font-size: 75%; 66 | line-height: 0; 67 | vertical-align: baseline; 68 | } 69 | sup { 70 | top: -0.5em; 71 | } 72 | sub { 73 | bottom: -0.25em; 74 | } 75 | 76 | // Img border in a's and image quality 77 | // ------------------------- 78 | 79 | img { 80 | /* Responsive images (ensure images don't scale beyond their parents) */ 81 | max-width: 100%; /* Part 1: Set a maxium relative to the parent */ 82 | width: auto\9; /* IE7-8 need help adjusting responsive images */ 83 | height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */ 84 | vertical-align: middle; 85 | border: 0; 86 | -ms-interpolation-mode: bicubic; 87 | } 88 | 89 | // Prevent max-width from affecting Google Maps 90 | #map_canvas img { 91 | max-width: none; 92 | } 93 | 94 | // Forms 95 | // ------------------------- 96 | 97 | // Font size in all browsers, margin changes, misc consistency 98 | button, 99 | input, 100 | select, 101 | textarea { 102 | margin: 0; 103 | font-size: 100%; 104 | vertical-align: middle; 105 | } 106 | button, 107 | input { 108 | *overflow: visible; // Inner spacing ie IE6/7 109 | line-height: normal; // FF3/4 have !important on line-height in UA stylesheet 110 | } 111 | button::-moz-focus-inner, 112 | input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 113 | padding: 0; 114 | border: 0; 115 | } 116 | button, 117 | input[type="button"], 118 | input[type="reset"], 119 | input[type="submit"] { 120 | cursor: pointer; // Cursors on all buttons applied consistently 121 | -webkit-appearance: button; // Style clickable inputs in iOS 122 | } 123 | input[type="search"] { // Appearance in Safari/Chrome 124 | -webkit-box-sizing: content-box; 125 | -moz-box-sizing: content-box; 126 | box-sizing: content-box; 127 | -webkit-appearance: textfield; 128 | } 129 | input[type="search"]::-webkit-search-decoration, 130 | input[type="search"]::-webkit-search-cancel-button { 131 | -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 132 | } 133 | textarea { 134 | overflow: auto; // Remove vertical scrollbar in IE6-9 135 | vertical-align: top; // Readability and alignment cross-browser 136 | } -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_progress-bars.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Progress bars 3 | // -------------------------------------------------- 4 | 5 | 6 | // ANIMATIONS 7 | // ---------- 8 | 9 | // Webkit 10 | @-webkit-keyframes progress-bar-stripes { 11 | from { background-position: 40px 0; } 12 | to { background-position: 0 0; } 13 | } 14 | 15 | // Firefox 16 | @-moz-keyframes progress-bar-stripes { 17 | from { background-position: 40px 0; } 18 | to { background-position: 0 0; } 19 | } 20 | 21 | // IE9 22 | @-ms-keyframes progress-bar-stripes { 23 | from { background-position: 40px 0; } 24 | to { background-position: 0 0; } 25 | } 26 | 27 | // Opera 28 | @-o-keyframes progress-bar-stripes { 29 | from { background-position: 0 0; } 30 | to { background-position: 40px 0; } 31 | } 32 | 33 | // Spec 34 | @keyframes progress-bar-stripes { 35 | from { background-position: 40px 0; } 36 | to { background-position: 0 0; } 37 | } 38 | 39 | 40 | 41 | // THE BARS 42 | // -------- 43 | 44 | // Outer container 45 | .progress { 46 | overflow: hidden; 47 | height: $baseLineHeight; 48 | margin-bottom: $baseLineHeight; 49 | @include gradient-vertical(#f5f5f5, #f9f9f9); 50 | @include box-shadow(#{inset 0 1px 2px rgba(0,0,0,.1)}); 51 | @include border-radius(4px); 52 | } 53 | 54 | // Bar of progress 55 | .progress .bar { 56 | width: 0%; 57 | height: 100%; 58 | color: $white; 59 | float: left; 60 | font-size: 12px; 61 | text-align: center; 62 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 63 | @include gradient-vertical(#149bdf, #0480be); 64 | @include box-shadow(#{inset 0 -1px 0 rgba(0,0,0,.15)}); 65 | @include box-sizing(border-box); 66 | @include transition(width .6s ease); 67 | } 68 | .progress .bar + .bar { 69 | @include box-shadow(#{inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)}); 70 | } 71 | 72 | // Striped bars 73 | .progress-striped .bar { 74 | @include gradient-striped(#149bdf); 75 | @include background-size(40px 40px); 76 | } 77 | 78 | // Call animation for the active one 79 | .progress.active .bar { 80 | -webkit-animation: progress-bar-stripes 2s linear infinite; 81 | -moz-animation: progress-bar-stripes 2s linear infinite; 82 | -ms-animation: progress-bar-stripes 2s linear infinite; 83 | -o-animation: progress-bar-stripes 2s linear infinite; 84 | animation: progress-bar-stripes 2s linear infinite; 85 | } 86 | 87 | 88 | 89 | // COLORS 90 | // ------ 91 | 92 | // Danger (red) 93 | .progress-danger .bar, .progress .bar-danger { 94 | @include gradient-vertical(#ee5f5b, #c43c35); 95 | } 96 | .progress-danger.progress-striped .bar, .progress-striped .bar-danger { 97 | @include gradient-striped(#ee5f5b); 98 | } 99 | 100 | // Success (green) 101 | .progress-success .bar, .progress .bar-success { 102 | @include gradient-vertical(#62c462, #57a957); 103 | } 104 | .progress-success.progress-striped .bar, .progress-striped .bar-success { 105 | @include gradient-striped(#62c462); 106 | } 107 | 108 | // Info (teal) 109 | .progress-info .bar, .progress .bar-info { 110 | @include gradient-vertical(#5bc0de, #339bb9); 111 | } 112 | .progress-info.progress-striped .bar, .progress-striped .bar-info { 113 | @include gradient-striped(#5bc0de); 114 | } 115 | 116 | // Warning (orange) 117 | .progress-warning .bar, .progress .bar-warning { 118 | @include gradient-vertical(lighten($orange, 15%), $orange); 119 | } 120 | .progress-warning.progress-striped .bar, .progress-striped .bar-warning { 121 | @include gradient-striped(lighten($orange, 15%)); 122 | } -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_popovers.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Popovers 3 | // -------------------------------------------------- 4 | 5 | 6 | .popover { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | z-index: $zindexPopover; 11 | display: none; 12 | width: 236px; 13 | padding: 1px; 14 | background-color: $popoverBackground; 15 | -webkit-background-clip: padding-box; 16 | -moz-background-clip: padding; 17 | background-clip: padding-box; 18 | border: 1px solid #ccc; 19 | border: 1px solid rgba(0,0,0,.2); 20 | @include border-radius(6px); 21 | @include box-shadow(#{0 5px 10px rgba(0,0,0,.2)}); 22 | 23 | // Offset the popover to account for the popover arrow 24 | &.top { margin-bottom: 10px; } 25 | &.right { margin-left: 10px; } 26 | &.bottom { margin-top: 10px; } 27 | &.left { margin-right: 10px; } 28 | 29 | } 30 | 31 | .popover-title { 32 | margin: 0; // reset heading margin 33 | padding: 8px 14px; 34 | font-size: 14px; 35 | font-weight: normal; 36 | line-height: 18px; 37 | background-color: $popoverTitleBackground; 38 | border-bottom: 1px solid darken($popoverTitleBackground, 5%); 39 | @include border-radius(5px 5px 0 0); 40 | } 41 | 42 | .popover-content { 43 | padding: 9px 14px; 44 | p, ul, ol { 45 | margin-bottom: 0; 46 | } 47 | } 48 | 49 | // Arrows 50 | .popover .arrow, 51 | .popover .arrow:after { 52 | position: absolute; 53 | display: inline-block; 54 | width: 0; 55 | height: 0; 56 | border-color: transparent; 57 | border-style: solid; 58 | } 59 | .popover .arrow:after { 60 | content: ""; 61 | z-index: -1; 62 | } 63 | 64 | .popover { 65 | &.top .arrow { 66 | bottom: -$popoverArrowWidth; 67 | left: 50%; 68 | margin-left: -$popoverArrowWidth; 69 | border-width: $popoverArrowWidth $popoverArrowWidth 0; 70 | border-top-color: $popoverArrowColor; 71 | &:after { 72 | border-width: $popoverArrowOuterWidth $popoverArrowOuterWidth 0; 73 | border-top-color: $popoverArrowOuterColor; 74 | bottom: -1px; 75 | left: -$popoverArrowOuterWidth; 76 | } 77 | } 78 | &.right .arrow { 79 | top: 50%; 80 | left: -$popoverArrowWidth; 81 | margin-top: -$popoverArrowWidth; 82 | border-width: $popoverArrowWidth $popoverArrowWidth $popoverArrowWidth 0; 83 | border-right-color: $popoverArrowColor; 84 | &:after { 85 | border-width: $popoverArrowOuterWidth $popoverArrowOuterWidth $popoverArrowOuterWidth 0; 86 | border-right-color: $popoverArrowOuterColor; 87 | bottom: -$popoverArrowOuterWidth; 88 | left: -1px; 89 | } 90 | } 91 | &.bottom .arrow { 92 | top: -$popoverArrowWidth; 93 | left: 50%; 94 | margin-left: -$popoverArrowWidth; 95 | border-width: 0 $popoverArrowWidth $popoverArrowWidth; 96 | border-bottom-color: $popoverArrowColor; 97 | &:after { 98 | border-width: 0 $popoverArrowOuterWidth $popoverArrowOuterWidth; 99 | border-bottom-color: $popoverArrowOuterColor; 100 | top: -1px; 101 | left: -$popoverArrowOuterWidth; 102 | } 103 | } 104 | &.left .arrow { 105 | top: 50%; 106 | right: -$popoverArrowWidth; 107 | margin-top: -$popoverArrowWidth; 108 | border-width: $popoverArrowWidth 0 $popoverArrowWidth $popoverArrowWidth; 109 | border-left-color: $popoverArrowColor; 110 | &:after { 111 | border-width: $popoverArrowOuterWidth 0 $popoverArrowOuterWidth $popoverArrowOuterWidth; 112 | border-left-color: $popoverArrowOuterColor; 113 | bottom: -$popoverArrowOuterWidth; 114 | right: -1px; 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /app/index.static.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html(lang='en',prefix="og: http://ogp.me/ns#") 3 | head 4 | meta(charset='utf-8') 5 | meta(http-equiv="X-UA-Compatible", content="IE=edge,chrome=1") 6 | meta(name='viewport', content='width=device-width') 7 | meta(name='description', content='') 8 | meta(name='author', content='') 9 | base(href="/") 10 | 11 | if locals.og_title!=undefined 12 | meta(property='og:title',content='g0v.tw : 中央政府總預算 #{locals.og_title}') 13 | meta(property='og:type',content='website') 14 | meta(property='og:url',content='http://g0v.tw/#{locals.og_url}') 15 | meta(property='og:image',content='http://g0v.tw/img/g0v.png') 16 | meta(property='og:description',content='#{locals.og_description}') 17 | title g0v.tw: 中央政府總預算 #{locals.og_title} 18 | else 19 | meta(property='og:title',content='g0v.tw : 中央政府總預算') 20 | meta(property='og:type',content='website') 21 | meta(property='og:url',content='http://g0v.tw/') 22 | meta(property='og:image',content='http://g0v.tw/img/g0v.png') 23 | meta(property='og:description',content='零時政府 g0v.tw : 中央政府總預算 Visualization') 24 | title g0v.tw: 中央政府總預算 25 | link(rel='shortcut icon',href='/img/favicon.ico') 26 | link(rel='stylesheet', href='/css/app.css') 27 | link(rel='stylesheet', href='http://fonts.googleapis.com/css?family=Cousine:700italic',type='text/css') 28 | link(rel='stylesheet', href='http://fonts.googleapis.com/css?family=Orbitron:400,500',type='text/css') 29 | 30 | //-script(src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js') 31 | //if lte IE 7 32 | script(src='http://cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js') 33 | //if lte IE 8 34 | script(src='//html5shiv.googlecode.com/svn/trunk/html5.js') 35 | script(src='//cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js') 36 | script(src='//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js') 37 | script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js') 38 | script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular-resource.min.js') 39 | script(src='//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.0.1/angular-ui-router.min.js') 40 | script(src='//cdnjs.cloudflare.com/ajax/libs/angular-ui/0.4.0/angular-ui.min.js') 41 | script(src='//cdnjs.cloudflare.com/ajax/libs/d3/3.1.6/d3.min.js') 42 | script(src='/js/vendor.js') 43 | script(src='/js/partials.js') 44 | script(src='/js/app.js') 45 | 46 | body(ng-controller='AppCtrl',ng-cloak) 47 | #fb-root 48 | script 49 | (function(d, s, id) { 50 | var js, fjs = d.getElementsByTagName(s)[0]; 51 | if (d.getElementById(id)) return; 52 | js = d.createElement(s); js.id = id; 53 | js.src = "//connect.facebook.net/zh_TW/all.js#xfbml=1"; 54 | fjs.parentNode.insertBefore(js, fjs); 55 | }(document, 'script', 'facebook-jssdk')); 56 | .wrapper 57 | .navbar.site-heading 58 | .navbar-inner 59 | .container 60 | .brand 61 | img(src="/img/logo.png",alt="Logo: G0V.tw") 62 | span / 中央政府總預算 63 | .nav-collapse 64 | div(ng-include="'/partials/nav.html'") 65 | .container.main-content 66 | div(ui-view) 67 | div(ng-include="'/partials/login.html'") 68 | .push 69 | script. 70 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 71 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 72 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 73 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 74 | 75 | ga('create', 'UA-41326468-1', 'g0v.tw'); 76 | -------------------------------------------------------------------------------- /server/auth.ls: -------------------------------------------------------------------------------- 1 | global <<< require \../lib/user 2 | Strategy = {} 3 | LocalStrategy = require('passport-local').Strategy 4 | Strategy.GitHub = require('passport-github').Strategy 5 | Strategy.Twitter = require('passport-twitter').Strategy 6 | Strategy.Facebook = require('passport-facebook').Strategy 7 | 8 | findOrCreateUser = (profile, provider, done) -> 9 | err, user <- User.findOne 'accounts.id': profile.id, 'accounts.provider': provider 10 | return done(null, user) if user?id 11 | 12 | console.log profile 13 | # XXX check email for existing user 14 | user = new User do 15 | accounts: [ provider: provider, id: profile.id ] 16 | email: profile.emails?0?value 17 | name: profile.displayName 18 | err <- user.save 19 | return done(err, null) if err 20 | console.log \newuser, user 21 | done(null, user) 22 | 23 | @include = -> 24 | passport = @passport 25 | passport.serializeUser (user, done) -> done(null, user.id) 26 | 27 | passport.deserializeUser (id, done) ~> 28 | User.findById id, null, null, (err, user) -> 29 | if err 30 | console.log err 31 | return done null, null 32 | done null, user 33 | 34 | mount_auth = (provider, args = {}) ~> 35 | @app.get "/auth/#{provider}", passport.authenticate(provider, args), -> 36 | @get "/auth/#{provider}/callback": -> 37 | auth = passport.authenticate provider, (err,user,info) ~> 38 | done = (err, user, info) ~> 39 | result = if user => auth: 1, user: user else authFailed: 1, challenges: info 40 | @render 'authdone.jade': profile: JSON.stringify(result), layout: no 41 | if user 42 | err <- @request.logIn user 43 | if (err) => return done(err) 44 | return done(null, user) 45 | done(err, user, info) 46 | auth @request, @response, -> 47 | 48 | for _provider, strategy_class of Strategy => let strategy_class, provider = _provider.toLowerCase! 49 | config = @config.authproviders[provider] 50 | return unless config 51 | strategy = do 52 | params = { callbackURL: @config.base_uri + "auth/#provider/callback" } <<< if config.client_id => do 53 | clientID: config.client_id 54 | clientSecret: config.secret 55 | else do 56 | consumerKey: config.consumer_key 57 | consumerSecret: config.consumer_secret 58 | # some way to hook user code here 59 | (accessToken, refreshToken, profile, done) <- new strategy_class params 60 | <- process.nextTick 61 | findOrCreateUser profile, provider, done 62 | passport.use strategy 63 | mount_auth provider, config.options 64 | 65 | @view 'authdone.jade': ''' 66 | !!! 5 67 | html 68 | head 69 | title done 70 | //script(src='/js/vendor.js') 71 | script(type='text/javascript') 72 | window.opener.postMessage(!{profile}, window.location); 73 | window.close(); 74 | body 75 | ''' 76 | 77 | passport.use new LocalStrategy usernameField: \email, (...args) -> 78 | User.authenticate(...args) 79 | 80 | @post '/auth/login': -> 81 | auth = passport.authenticate 'local', (err, user, info) ~> 82 | if err => return next(err) 83 | if (!user) 84 | return @response.send info, 403 85 | @request.logIn user, (err) ~> 86 | if (err) => return next(err) 87 | @response.send user 88 | auth @request, @response, -> 89 | 90 | @app.get '/auth/logout', (req, res) -> 91 | req.logout() 92 | res.send 'ok' 93 | -------------------------------------------------------------------------------- /server/main.ls: -------------------------------------------------------------------------------- 1 | {Product,BudgetItem} = require \../lib/schema 2 | 3 | @include = -> 4 | @passport = require \passport 5 | CookieStore = require \cookie-sessions 6 | @use @express.static __dirname + \/../_public 7 | @use \bodyParser 8 | @use CookieStore secret: @config.cookieSecret, onError: -> 9 | return {} 10 | @app.use @passport.initialize! 11 | @app.use @passport.session! 12 | @use @app.router 13 | @app.set("trust proxy", true); 14 | @app.set("views", "#{__dirname}/../app") 15 | 16 | RealBin = require \path .dirname do 17 | require \fs .realpathSync __filename 18 | RealBin -= /\/server/ 19 | 20 | sendFile = (file) -> -> 21 | @response.contentType \text/html 22 | @response.sendfile "#RealBin/_public/#file" 23 | 24 | JsonType = { \Content-Type : 'application/json; charset=utf-8' } 25 | 26 | @helper ensureAuthenticated: (next) -> 27 | if @request?isAuthenticated! => return next! 28 | @response.send 401 29 | 30 | @get '/1/products/:query': -> 31 | err, res <~ Product.find! 32 | .exec 33 | results = for i in [1 to 10] 34 | name: \ONE, categoryKey: \htc:one, products: res 35 | @response.send results 36 | 37 | @get '/1/profile': -> 38 | <~ @ensureAuthenticated 39 | @response.send @request.user 40 | 41 | @get '/1/budgetitems': -> 42 | err, item <~ BudgetItem.find {}, 'key nhates nconfuses nlikes ncuts' 43 | .exec 44 | @response.send item 45 | 46 | @get '/1/budgetitems/:key': -> 47 | err, item <~ BudgetItem.findOne {key: @params.key}, 'key nhates nconfuses nlikes ncuts tags' 48 | .exec 49 | console.log @params.key, item 50 | @response.send item 51 | 52 | @post '/1/budgetitems/:key/tags/:tag': -> 53 | key = @params.key 54 | tag = @params.tag 55 | 56 | done = (err, item) ~> 57 | errr, updated <~ item.update $addToSet: tags: tag 58 | console.log errr, updated 59 | err, item <~ BudgetItem.findOne 'key': key 60 | @response.send item 61 | 62 | err, item <- BudgetItem.findOne 'key': key 63 | return done(null, item) if item?id 64 | item = new BudgetItem do 65 | key: key 66 | err <- item.save 67 | return done(err, null) if err 68 | done(null, item) 69 | @post '/1/budgetitems/:key/:what': -> 70 | key = @params.key 71 | done = (err, item) ~> 72 | console.log item 73 | if @params.what in <[likes confuses hates cuts]> 74 | console.log item._id 75 | user_id = @request.user?_id ? @request.ip 76 | 77 | errr, updated <~ BudgetItem.update {_id: item._id, "#{@params.what}": {$ne: user_id}}, do 78 | $inc: "n#{@params.what}": 1 79 | $push: { "#{@params.what}": user_id } 80 | console.log errr, updated 81 | err, item <~ BudgetItem.findOne 'key': key 82 | @response.send item 83 | else 84 | @response.send item 85 | 86 | err, item <- BudgetItem.findOne 'key': key 87 | return done(null, item) if item?id 88 | item = new BudgetItem do 89 | key: key 90 | err <- item.save 91 | return done(err, null) if err 92 | done(null, item) 93 | 94 | @include \auth 95 | @include \opengraph 96 | @csv2012 = null 97 | @loadCsv \app/assets/data/tw2012ap.csv, (hash) ~> 98 | @csv2012 = hash 99 | getOpenGraph = (code) ~> @getOpenGraph @csv2012,code 100 | @get '/:what': sendFile \index.html 101 | @get '/budget/:code': -> 102 | code = (@request.path.match /\/budget\/(\S+)/)[1] 103 | @render 'index.static.jade': getOpenGraph code 104 | #sendFile \index.html 105 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_responsive-767px-max.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Landscape phone to desktop/tablet 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (max-width: 767px) { 7 | 8 | // Padding to set content in a bit 9 | body { 10 | padding-left: 20px; 11 | padding-right: 20px; 12 | } 13 | // Negative indent the now static "fixed" navbar 14 | .navbar-fixed-top, 15 | .navbar-fixed-bottom, 16 | .navbar-static-top { 17 | margin-left: -20px; 18 | margin-right: -20px; 19 | } 20 | // Remove padding on container given explicit padding set on body 21 | .container-fluid { 22 | padding: 0; 23 | } 24 | 25 | // TYPOGRAPHY 26 | // ---------- 27 | // Reset horizontal dl 28 | .dl-horizontal { 29 | dt { 30 | float: none; 31 | clear: none; 32 | width: auto; 33 | text-align: left; 34 | } 35 | dd { 36 | margin-left: 0; 37 | } 38 | } 39 | 40 | // GRID & CONTAINERS 41 | // ----------------- 42 | // Remove width from containers 43 | .container { 44 | width: auto; 45 | } 46 | // Fluid rows 47 | .row-fluid { 48 | width: 100%; 49 | } 50 | // Undo negative margin on rows and thumbnails 51 | .row, 52 | .thumbnails { 53 | margin-left: 0; 54 | } 55 | .thumbnails > li { 56 | float: none; 57 | margin-left: 0; // Reset the default margin for all li elements when no .span* classes are present 58 | } 59 | // Make all grid-sized elements block level again 60 | [class*="span"], 61 | .row-fluid [class*="span"] { 62 | float: none; 63 | display: block; 64 | width: 100%; 65 | margin-left: 0; 66 | @include box-sizing(border-box); 67 | } 68 | .span12, 69 | .row-fluid .span12 { 70 | width: 100%; 71 | @include box-sizing(border-box); 72 | } 73 | 74 | // FORM FIELDS 75 | // ----------- 76 | // Make span* classes full width 77 | .input-large, 78 | .input-xlarge, 79 | .input-xxlarge, 80 | input[class*="span"], 81 | select[class*="span"], 82 | textarea[class*="span"], 83 | .uneditable-input { 84 | @include input-block-level(); 85 | } 86 | // But don't let it screw up prepend/append inputs 87 | .input-prepend input, 88 | .input-append input, 89 | .input-prepend input[class*="span"], 90 | .input-append input[class*="span"] { 91 | display: inline-block; // redeclare so they don't wrap to new lines 92 | width: auto; 93 | } 94 | .controls-row [class*="span"] + [class*="span"] { 95 | margin-left: 0; 96 | } 97 | 98 | // Modals 99 | .modal { 100 | position: fixed; 101 | top: 20px; 102 | left: 20px; 103 | right: 20px; 104 | width: auto; 105 | margin: 0; 106 | &.fade.in { top: auto; } 107 | } 108 | 109 | } 110 | 111 | 112 | 113 | // UP TO LANDSCAPE PHONE 114 | // --------------------- 115 | 116 | @media (max-width: 480px) { 117 | 118 | // Smooth out the collapsing/expanding nav 119 | .nav-collapse { 120 | -webkit-transform: translate3d(0, 0, 0); // activate the GPU 121 | } 122 | 123 | // Block level the page header small tag for readability 124 | .page-header h1 small { 125 | display: block; 126 | line-height: $baseLineHeight; 127 | } 128 | 129 | // Update checkboxes for iOS 130 | input[type="checkbox"], 131 | input[type="radio"] { 132 | border: 1px solid #ccc; 133 | } 134 | 135 | // Remove the horizontal form styles 136 | .form-horizontal { 137 | .control-label { 138 | float: none; 139 | width: auto; 140 | padding-top: 0; 141 | text-align: left; 142 | } 143 | // Move over all input controls and content 144 | .controls { 145 | margin-left: 0; 146 | } 147 | // Move the options list down to align with labels 148 | .control-list { 149 | padding-top: 0; // has to be padding because margin collaspes 150 | } 151 | // Move over buttons in .form-actions to align with .controls 152 | .form-actions { 153 | padding-left: 10px; 154 | padding-right: 10px; 155 | } 156 | } 157 | 158 | // Modals 159 | .modal { 160 | top: 10px; 161 | left: 10px; 162 | right: 10px; 163 | } 164 | .modal-header .close { 165 | padding: 10px; 166 | margin: -10px; 167 | } 168 | 169 | // Carousel 170 | .carousel-caption { 171 | position: static; 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_responsive-navbar.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Navbar 3 | // -------------------------------------------------- 4 | 5 | 6 | // TABLETS AND BELOW 7 | // ----------------- 8 | @media (max-width: 979px) { 9 | 10 | // UNFIX THE TOPBAR 11 | // ---------------- 12 | // Remove any padding from the body 13 | body { 14 | padding-top: 0; 15 | } 16 | // Unfix the navbars 17 | .navbar-fixed-top, 18 | .navbar-fixed-bottom { 19 | position: static; 20 | } 21 | .navbar-fixed-top { 22 | margin-bottom: $baseLineHeight; 23 | } 24 | .navbar-fixed-bottom { 25 | margin-top: $baseLineHeight; 26 | } 27 | .navbar-fixed-top .navbar-inner, 28 | .navbar-fixed-bottom .navbar-inner { 29 | padding: 5px; 30 | } 31 | .navbar .container { 32 | width: auto; 33 | padding: 0; 34 | } 35 | // Account for brand name 36 | .navbar .brand { 37 | padding-left: 10px; 38 | padding-right: 10px; 39 | margin: 0 0 0 -5px; 40 | } 41 | 42 | // COLLAPSIBLE NAVBAR 43 | // ------------------ 44 | // Nav collapse clears brand 45 | .nav-collapse { 46 | clear: both; 47 | } 48 | // Block-level the nav 49 | .nav-collapse .nav { 50 | float: none; 51 | margin: 0 0 ($baseLineHeight / 2); 52 | } 53 | .nav-collapse .nav > li { 54 | float: none; 55 | } 56 | .nav-collapse .nav > li > a { 57 | margin-bottom: 2px; 58 | } 59 | .nav-collapse .nav > .divider-vertical { 60 | display: none; 61 | } 62 | .nav-collapse .nav .nav-header { 63 | color: $navbarText; 64 | text-shadow: none; 65 | } 66 | // Nav and dropdown links in navbar 67 | .nav-collapse .nav > li > a, 68 | .nav-collapse .dropdown-menu a { 69 | padding: 9px 15px; 70 | font-weight: bold; 71 | color: $navbarLinkColor; 72 | @include border-radius(3px); 73 | } 74 | // Buttons 75 | .nav-collapse .btn { 76 | padding: 4px 10px 4px; 77 | font-weight: normal; 78 | @include border-radius(4px); 79 | } 80 | .nav-collapse .dropdown-menu li + li a { 81 | margin-bottom: 2px; 82 | } 83 | .nav-collapse .nav > li > a:hover, 84 | .nav-collapse .dropdown-menu a:hover { 85 | background-color: $navbarBackground; 86 | } 87 | .navbar-inverse .nav-collapse .nav > li > a:hover, 88 | .navbar-inverse .nav-collapse .dropdown-menu a:hover { 89 | background-color: $navbarInverseBackground; 90 | } 91 | // Buttons in the navbar 92 | .nav-collapse.in .btn-group { 93 | margin-top: 5px; 94 | padding: 0; 95 | } 96 | // Dropdowns in the navbar 97 | .nav-collapse .dropdown-menu { 98 | position: static; 99 | top: auto; 100 | left: auto; 101 | float: none; 102 | display: block; 103 | max-width: none; 104 | margin: 0 15px; 105 | padding: 0; 106 | background-color: transparent; 107 | border: none; 108 | @include border-radius(0); 109 | @include box-shadow(none); 110 | } 111 | .nav-collapse .dropdown-menu:before, 112 | .nav-collapse .dropdown-menu:after { 113 | display: none; 114 | } 115 | .nav-collapse .dropdown-menu .divider { 116 | display: none; 117 | } 118 | .nav-collapse .nav > li > .dropdown-menu { 119 | &:before, 120 | &:after { 121 | display: none; 122 | } 123 | } 124 | // Forms in navbar 125 | .nav-collapse .navbar-form, 126 | .nav-collapse .navbar-search { 127 | float: none; 128 | padding: ($baseLineHeight / 2) 15px; 129 | margin: ($baseLineHeight / 2) 0; 130 | border-top: 1px solid $navbarBackground; 131 | border-bottom: 1px solid $navbarBackground; 132 | @include box-shadow(#{inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)}); 133 | } 134 | .navbar-inverse .nav-collapse .navbar-form, 135 | .navbar-inverse .nav-collapse .navbar-search { 136 | border-top-color: $navbarInverseBackground; 137 | border-bottom-color: $navbarInverseBackground; 138 | } 139 | // Pull right (secondary) nav content 140 | .navbar .nav-collapse .nav.pull-right { 141 | float: none; 142 | margin-left: 0; 143 | } 144 | // Hide everything in the navbar save .brand and toggle button */ 145 | .nav-collapse, 146 | .nav-collapse.collapse { 147 | overflow: hidden; 148 | height: 0; 149 | } 150 | // Navbar button 151 | .navbar .btn-navbar { 152 | display: block; 153 | } 154 | 155 | // STATIC NAVBAR 156 | // ------------- 157 | .navbar-static .navbar-inner { 158 | padding-left: 10px; 159 | padding-right: 10px; 160 | } 161 | 162 | 163 | } 164 | 165 | 166 | // DEFAULT DESKTOP 167 | // --------------- 168 | 169 | @media (min-width: 980px) { 170 | 171 | // Required to make the collapsing navbar work on regular desktops 172 | .nav-collapse.collapse { 173 | height: auto !important; 174 | overflow: visible !important; 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_type.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Typography 3 | // -------------------------------------------------- 4 | 5 | 6 | // Body text 7 | // ------------------------- 8 | 9 | p { 10 | margin: 0 0 $baseLineHeight / 2; 11 | } 12 | .lead { 13 | margin-bottom: $baseLineHeight; 14 | font-size: $baseFontSize * 1.5; 15 | font-weight: 200; 16 | line-height: $baseLineHeight * 1.5; 17 | } 18 | 19 | 20 | // Emphasis & misc 21 | // ------------------------- 22 | 23 | small { 24 | font-size: 85%; // Ex: 14px base font * 85% = about 12px 25 | } 26 | strong { 27 | font-weight: bold; 28 | } 29 | em { 30 | font-style: italic; 31 | } 32 | cite { 33 | font-style: normal; 34 | } 35 | 36 | // Utility classes 37 | .muted { 38 | color: $grayLight; 39 | } 40 | .text-warning { 41 | color: $warningText; 42 | } 43 | .text-error { 44 | color: $errorText; 45 | } 46 | .text-info { 47 | color: $infoText; 48 | } 49 | .text-success { 50 | color: $successText; 51 | } 52 | 53 | 54 | // Headings 55 | // ------------------------- 56 | 57 | h1, h2, h3, h4, h5, h6 { 58 | margin: ($baseLineHeight / 2) 0; 59 | font-family: $headingsFontFamily; 60 | font-weight: $headingsFontWeight; 61 | line-height: 1; 62 | color: $headingsColor; 63 | text-rendering: optimizelegibility; // Fix the character spacing for headings 64 | small { 65 | font-weight: normal; 66 | line-height: 1; 67 | color: $grayLight; 68 | } 69 | } 70 | h1 { font-size: 36px; line-height: 40px; } 71 | h2 { font-size: 30px; line-height: 40px; } 72 | h3 { font-size: 24px; line-height: 40px; } 73 | h4 { font-size: 18px; line-height: 20px; } 74 | h5 { font-size: 14px; line-height: 20px; } 75 | h6 { font-size: 12px; line-height: 20px; } 76 | 77 | h1 small { font-size: 24px; } 78 | h2 small { font-size: 18px; } 79 | h3 small { font-size: 14px; } 80 | h4 small { font-size: 14px; } 81 | 82 | 83 | // Page header 84 | // ------------------------- 85 | 86 | .page-header { 87 | padding-bottom: ($baseLineHeight / 2) - 1; 88 | margin: $baseLineHeight 0 ($baseLineHeight * 1.5); 89 | border-bottom: 1px solid $grayLighter; 90 | } 91 | 92 | 93 | 94 | // Lists 95 | // -------------------------------------------------- 96 | 97 | // Unordered and Ordered lists 98 | ul, ol { 99 | padding: 0; 100 | margin: 0 0 $baseLineHeight / 2 25px; 101 | } 102 | ul ul, 103 | ul ol, 104 | ol ol, 105 | ol ul { 106 | margin-bottom: 0; 107 | } 108 | li { 109 | line-height: $baseLineHeight; 110 | } 111 | ul.unstyled, 112 | ol.unstyled { 113 | margin-left: 0; 114 | list-style: none; 115 | } 116 | 117 | // Description Lists 118 | dl { 119 | margin-bottom: $baseLineHeight; 120 | } 121 | dt, 122 | dd { 123 | line-height: $baseLineHeight; 124 | } 125 | dt { 126 | font-weight: bold; 127 | } 128 | dd { 129 | margin-left: $baseLineHeight / 2; 130 | } 131 | // Horizontal layout (like forms) 132 | .dl-horizontal { 133 | @include clearfix(); // Ensure dl clears floats if empty dd elements present 134 | dt { 135 | float: left; 136 | width: $horizontalComponentOffset - 20; 137 | clear: left; 138 | text-align: right; 139 | @include text-overflow(); 140 | } 141 | dd { 142 | margin-left: $horizontalComponentOffset; 143 | } 144 | } 145 | 146 | // MISC 147 | // ---- 148 | 149 | // Horizontal rules 150 | hr { 151 | margin: $baseLineHeight 0; 152 | border: 0; 153 | border-top: 1px solid $hrBorder; 154 | border-bottom: 1px solid $white; 155 | } 156 | 157 | // Abbreviations and acronyms 158 | abbr[title] { 159 | cursor: help; 160 | border-bottom: 1px dotted $grayLight; 161 | } 162 | abbr.initialism { 163 | font-size: 90%; 164 | text-transform: uppercase; 165 | } 166 | 167 | // Blockquotes 168 | blockquote { 169 | padding: 0 0 0 15px; 170 | margin: 0 0 $baseLineHeight; 171 | border-left: 5px solid $grayLighter; 172 | p { 173 | margin-bottom: 0; 174 | @include font-shorthand(16px,300,$baseLineHeight * 1.25); 175 | } 176 | small { 177 | display: block; 178 | line-height: $baseLineHeight; 179 | color: $grayLight; 180 | &:before { 181 | content: '\2014 \00A0'; 182 | } 183 | } 184 | 185 | // Float right with text-align: right 186 | &.pull-right { 187 | float: right; 188 | padding-right: 15px; 189 | padding-left: 0; 190 | border-right: 5px solid $grayLighter; 191 | border-left: 0; 192 | p, 193 | small { 194 | text-align: right; 195 | } 196 | small { 197 | &:before { 198 | content: ''; 199 | } 200 | &:after { 201 | content: '\00A0 \2014'; 202 | } 203 | } 204 | } 205 | } 206 | 207 | // Quotes 208 | q:before, 209 | q:after, 210 | blockquote:before, 211 | blockquote:after { 212 | content: ""; 213 | } 214 | 215 | // Addresses 216 | address { 217 | display: block; 218 | margin-bottom: $baseLineHeight; 219 | font-style: normal; 220 | line-height: $baseLineHeight; 221 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SAPLING 2 | ======= 3 | Copyright (c) 2012 The Sapling Authors. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the 14 | distribution. 15 | * Neither the name of Google Inc. nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | 32 | ANGULARJS 33 | ========= 34 | The MIT License 35 | 36 | Copyright (c) 2010-2012 Google, Inc. http://angularjs.org 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in 46 | all copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 54 | THE SOFTWARE. 55 | 56 | 57 | BRUNCH 58 | ====== 59 | The MIT License 60 | 61 | Copyright (c) 2011 Allan Berger, Jan Monschke, Martin Schürrer, 62 | Thomas Schranz, Nik Graf, Paul Miller 63 | 64 | Permission is hereby granted, free of charge, to any person obtaining a copy 65 | of this software and associated documentation files (the "Software"), to deal 66 | in the Software without restriction, including without limitation the rights 67 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 68 | copies of the Software, and to permit persons to whom the Software is 69 | furnished to do so, subject to the following conditions: 70 | 71 | The above copyright notice and this permission notice shall be included in 72 | all copies or substantial portions of the Software. 73 | 74 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 75 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 76 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 77 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 78 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 79 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 80 | THE SOFTWARE. 81 | 82 | 83 | TWITTER BOOTSTRAP 84 | ================= 85 | Copyright 2011 Twitter, Inc. 86 | All rights reserved. 87 | 88 | Licensed under the Apache License, Version 2.0 (the "License"); 89 | you may not use this file except in compliance with the License. 90 | You may obtain a copy of the License at 91 | 92 | http://www.apache.org/licenses/LICENSE-2.0 93 | 94 | Unless required by applicable law or agreed to in writing, software 95 | distributed under the License is distributed on an "AS IS" BASIS, 96 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 97 | See the License for the specific language governing permissions and 98 | limitations under the License. 99 | 100 | 101 | FONT AWESOME 102 | ============ 103 | Copyright (c) 2012 Dave Gandy http://fortawesome.github.com/Font-Awesome/ 104 | 105 | Licensed under CC BY 3.0: 106 | 107 | http://creativecommons.org/licenses/by/3.0/ 108 | 109 | A mention of 'Font Awesome - http://fortawesome.github.com/Font-Awesome' 110 | in human-readable source code is considered acceptable attribution 111 | (most common on the web). If human readable source code is not available 112 | to the end user, a mention in an 'About' or 'Credits' screen is considered 113 | acceptable (most common in desktop or mobile software). 114 | 115 | -------------------------------------------------------------------------------- /app/testd3.ls: -------------------------------------------------------------------------------- 1 | mapforyear = (year, cb) -> 2 | json <- d3.csv "/data/tw#{year}ap.csv" 3 | cb {[code, entry] for {code}:entry in json} 4 | 5 | dataforyear = (year, cb) -> 6 | json <- d3.csv "/data/tw#{year}ap.csv" 7 | json = d3.nest! 8 | .key -> it.cat 9 | .key -> it.depname 10 | .map json 11 | cb {key: \root, values: json} 12 | 13 | dataOverYears = (y2018, y2019) -> 14 | for code, entry of y2019 15 | entry.byYear = { 2019: +entry.amount, 2018: +y2018[code]?amount } 16 | entry.change = (entry.byYear.2019 - entry.byYear.2018) / entry.byYear.2018 if entry.byYear.2018 17 | entry.amount = 0 if entry.amount is \NaN 18 | entry 19 | 20 | by_year = null 21 | init_year_data = (cb) -> 22 | return cb by_year if by_year 23 | 24 | by_year := {} 25 | by_year.2007 <- mapforyear 2007 26 | by_year.2008 <- mapforyear 2008 27 | by_year.2009 <- mapforyear 2009 28 | by_year.2010 <- mapforyear 2010 29 | by_year.2011 <- mapforyear 2011 30 | by_year.2012 <- mapforyear 2012 31 | by_year.2013 <- mapforyear 2013 32 | by_year.2014 <- mapforyear 2014 33 | by_year.2015 <- mapforyear 2015 34 | by_year.2016 <- mapforyear 2016 35 | by_year.2017 <- mapforyear 2017 36 | by_year.2018 <- mapforyear 2018 37 | by_year.2019 <- mapforyear 2019 38 | 39 | cb by_year 40 | 41 | bar_chart = (id,mode) -> 42 | by_year <- init_year_data! 43 | 44 | data = [{year, amount: +((by_year[year] && by_year[year][id])?amount ? 0)} for year in [2007 to 2019]] 45 | margin = {top: 10, right: 30, bottom: 20, left: 90} 46 | width = 360 - margin.left - margin.right 47 | height = 140 - margin.top - margin.bottom 48 | 49 | x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1) 50 | 51 | y = d3.scale.linear().range([height, 0]) 52 | 53 | xAxis = d3.svg.axis().scale(x).orient("bottom") 54 | 55 | yAxis = d3.svg.axis().scale(y).orient("left") 56 | 57 | 58 | svg = d3.select('#bubble-detail-change-bar'+if mode=='default' then '' else '2').html('')append("svg") 59 | .attr("width", width + margin.left + margin.right) 60 | .attr("height", height + margin.top + margin.bottom) 61 | .append("g") 62 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 63 | 64 | x.domain data.map -> ('0' + (it.year % 100)).slice(-2) 65 | y.domain [0, d3.max(data, -> it.amount/1000000)] 66 | 67 | svg.append("g") 68 | .attr("class", "x axis") 69 | .attr("transform", "translate(0," + height + ")") 70 | .call(xAxis); 71 | 72 | svg.append("g") 73 | .attr("class", "y axis") 74 | .call(yAxis) 75 | .append("text") 76 | .attr("transform", "rotate(-90)") 77 | .attr("y", -86) 78 | .attr("dy", ".71em") 79 | .style("text-anchor", "end") 80 | .text("金額(百萬元)"); 81 | 82 | svg.selectAll(\.bar)data(data) 83 | .enter!append \rect 84 | .attr \class \bar 85 | .attr \x -> x it.year 86 | .attr \width x.rangeBand! 87 | .attr \y -> y it.amount/1000000 88 | .attr \height -> height - y(it.amount/1000000) 89 | 90 | test_bubble = -> 91 | chart = null 92 | 93 | render_vis = (data) -> 94 | chart := new BubbleChart {data} 95 | ..do_show_details = (data, mode) -> 96 | bar_chart data.id, mode 97 | ..start! 98 | ..display_group_all! 99 | 100 | y2018 <- mapforyear 2018 101 | y2019 <- mapforyear 2019 102 | data = dataOverYears y2018, y2019 103 | data .= sort (a, b) -> b.amount - a.amount 104 | #data .= slice 0, 600 105 | render_vis data 106 | $('.btn.bycat')click -> chart.display_by_attr \cat 107 | $('.btn.bytop')click -> chart.display_by_attr \topname 108 | $('.btn.default')click -> chart.display_group_all! 109 | 110 | testd3 = -> 111 | cell = -> 112 | @style \left -> it.x + \px 113 | .style \top -> it.y + \px 114 | .style \width -> Math.max(0, it.dx - 1) + \px 115 | .style \height -> Math.max(0, it.dy - 1) + \px 116 | 117 | width = 960 118 | height = 500 119 | 120 | color = d3.scale.category20c! 121 | 122 | treemap = d3.layout.treemap!children -> it.values 123 | .size [ width, height ] 124 | .sticky true 125 | .value -> it.change 126 | 127 | div = d3.select(\#bubble-chart).append("div") 128 | .style \position \relative 129 | .style \width width + \px 130 | .style \height height + \px 131 | 132 | y2018 <- mapforyear 2018 133 | y2019 <- mapforyear 2019 134 | data = dataOverYears y2018, y2019 135 | json = d3.nest! 136 | .key -> it.cat 137 | .key -> it.depname 138 | .entries data 139 | json = {key: \root, values: json} 140 | 141 | div.data([ json ]).selectAll("div") 142 | .data treemap.nodes 143 | .enter!append("div")attr \class \cell 144 | .style \background -> 145 | if it.values then null else color(it.cat) 146 | .call cell 147 | .text -> if it.values then null else it.name 148 | 149 | d3.select(\#y2019).on \click -> 150 | div.selectAll("div") 151 | .data treemap.value -> it.byYear?2019 152 | .transition() 153 | .duration(1500) 154 | .call(cell) 155 | d3.select(\#y2018).on \click -> 156 | div.selectAll("div") 157 | .data treemap.value -> it.byYear?2018 158 | .transition() 159 | .duration(1500) 160 | .call(cell) 161 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_dropdowns.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Dropdown menus 3 | // -------------------------------------------------- 4 | 5 | 6 | // Use the .menu class on any
  • element within the topbar or ul.tabs and you'll get some superfancy dropdowns 7 | .dropup, 8 | .dropdown { 9 | position: relative; 10 | } 11 | .dropdown-toggle { 12 | // The caret makes the toggle a bit too tall in IE7 13 | *margin-bottom: -3px; 14 | } 15 | .dropdown-toggle:active, 16 | .open .dropdown-toggle { 17 | outline: 0; 18 | } 19 | 20 | // Dropdown arrow/caret 21 | // -------------------- 22 | .caret { 23 | display: inline-block; 24 | width: 0; 25 | height: 0; 26 | vertical-align: top; 27 | border-top: 4px solid $black; 28 | border-right: 4px solid transparent; 29 | border-left: 4px solid transparent; 30 | content: ""; 31 | } 32 | 33 | // Place the caret 34 | .dropdown .caret { 35 | margin-top: 8px; 36 | margin-left: 2px; 37 | } 38 | 39 | // The dropdown menu (ul) 40 | // ---------------------- 41 | .dropdown-menu { 42 | position: absolute; 43 | top: 100%; 44 | left: 0; 45 | z-index: $zindexDropdown; 46 | display: none; // none by default, but block on "open" of the menu 47 | float: left; 48 | min-width: 160px; 49 | padding: 5px 0; 50 | margin: 2px 0 0; // override default ul 51 | list-style: none; 52 | background-color: $dropdownBackground; 53 | border: 1px solid #ccc; // Fallback for IE7-8 54 | border: 1px solid $dropdownBorder; 55 | *border-right-width: 2px; 56 | *border-bottom-width: 2px; 57 | @include border-radius(6px); 58 | @include box-shadow(#{0 5px 10px rgba(0,0,0,.2)}); 59 | -webkit-background-clip: padding-box; 60 | -moz-background-clip: padding; 61 | background-clip: padding-box; 62 | 63 | // Aligns the dropdown menu to right 64 | &.pull-right { 65 | right: 0; 66 | left: auto; 67 | } 68 | 69 | // Dividers (basically an hr) within the dropdown 70 | .divider { 71 | @include nav-divider($dropdownDividerTop, $dropdownDividerBottom); 72 | } 73 | 74 | // Links within the dropdown menu 75 | a { 76 | display: block; 77 | padding: 3px 20px; 78 | clear: both; 79 | font-weight: normal; 80 | line-height: $baseLineHeight; 81 | color: $dropdownLinkColor; 82 | white-space: nowrap; 83 | } 84 | } 85 | 86 | // Hover state 87 | // ----------- 88 | .dropdown-menu li > a:hover, 89 | .dropdown-menu li > a:focus, 90 | .dropdown-submenu:hover > a { 91 | text-decoration: none; 92 | color: $dropdownLinkColorHover; 93 | background-color: $dropdownLinkBackgroundHover; 94 | @include gradient-vertical($dropdownLinkBackgroundHover, darken($dropdownLinkBackgroundHover, 5%)); 95 | } 96 | 97 | // Active state 98 | // ------------ 99 | .dropdown-menu .active > a, 100 | .dropdown-menu .active > a:hover { 101 | color: $dropdownLinkColorHover; 102 | text-decoration: none; 103 | outline: 0; 104 | background-color: $dropdownLinkBackgroundActive; 105 | @include gradient-vertical($dropdownLinkBackgroundActive, darken($dropdownLinkBackgroundActive, 5%)); 106 | } 107 | 108 | // Disabled state 109 | // -------------- 110 | // Gray out text and ensure the hover state remains gray 111 | .dropdown-menu .disabled > a, 112 | .dropdown-menu .disabled > a:hover { 113 | color: $grayLight; 114 | } 115 | // Nuke hover effects 116 | .dropdown-menu .disabled > a:hover { 117 | text-decoration: none; 118 | background-color: transparent; 119 | cursor: default; 120 | } 121 | 122 | // Open state for the dropdown 123 | // --------------------------- 124 | .open { 125 | // IE7's z-index only goes to the nearest positioned ancestor, which would 126 | // make the menu appear below buttons that appeared later on the page 127 | *z-index: $zindexDropdown; 128 | 129 | & > .dropdown-menu { 130 | display: block; 131 | } 132 | } 133 | 134 | // Right aligned dropdowns 135 | // --------------------------- 136 | .pull-right > .dropdown-menu { 137 | right: 0; 138 | left: auto; 139 | } 140 | 141 | // Allow for dropdowns to go bottom up (aka, dropup-menu) 142 | // ------------------------------------------------------ 143 | // Just add .dropup after the standard .dropdown class and you're set, bro. 144 | // TODO: abstract this so that the navbar fixed styles are not placed here? 145 | .dropup, 146 | .navbar-fixed-bottom .dropdown { 147 | // Reverse the caret 148 | .caret { 149 | border-top: 0; 150 | border-bottom: 4px solid $black; 151 | content: ""; 152 | } 153 | // Different positioning for bottom up menu 154 | .dropdown-menu { 155 | top: auto; 156 | bottom: 100%; 157 | margin-bottom: 1px; 158 | } 159 | } 160 | 161 | // Sub menus 162 | // --------------------------- 163 | .dropdown-submenu { 164 | position: relative; 165 | } 166 | .dropdown-submenu > .dropdown-menu { 167 | top: 0; 168 | left: 100%; 169 | margin-top: -6px; 170 | margin-left: -1px; 171 | -webkit-border-radius: 0 6px 6px 6px; 172 | -moz-border-radius: 0 6px 6px 6px; 173 | border-radius: 0 6px 6px 6px; 174 | } 175 | .dropdown-submenu:hover .dropdown-menu { 176 | display: block; 177 | } 178 | 179 | .dropdown-submenu > a:after { 180 | display: block; 181 | content: " "; 182 | float: right; 183 | width: 0; 184 | height: 0; 185 | border-color: transparent; 186 | border-style: solid; 187 | border-width: 5px 0 5px 5px; 188 | border-left-color: darken($dropdownBackground, 20%); 189 | margin-top: 5px; 190 | margin-right: -10px; 191 | } 192 | .dropdown-submenu:hover > a:after { 193 | border-left-color: $dropdownLinkColorHover; 194 | } 195 | 196 | 197 | // Tweak nav headers 198 | // ----------------- 199 | // Increase padding from 15px to 20px on sides 200 | .dropdown .dropdown-menu .nav-header { 201 | padding-left: 20px; 202 | padding-right: 20px; 203 | } 204 | 205 | // Typeahead 206 | // --------- 207 | .typeahead { 208 | margin-top: 2px; // give it some space to breathe 209 | @include border-radius(4px); 210 | } 211 | -------------------------------------------------------------------------------- /app/app/controllers.ls: -------------------------------------------------------------------------------- 1 | mod = {} 2 | 3 | mod.AppCtrl = [ 4 | '$scope' 5 | '$location' 6 | '$resource' 7 | '$rootScope' 8 | 9 | (s, $location, $resource, $rootScope) -> 10 | 11 | # Uses the url to determine if the selected 12 | # menu item should have the class active. 13 | s.$location = $location 14 | s.$watch('$location.path()', (path) -> 15 | s.activeNavId = path || '/' 16 | ) 17 | 18 | # getClass compares the current url with the id. 19 | # If the current url starts with the id it returns 'active' 20 | # otherwise it will return '' an empty string. E.g. 21 | # 22 | # # current url = '/products/1' 23 | # getClass('/products') # returns 'active' 24 | # getClass('/orders') # returns '' 25 | # 26 | s.getClass = (id) -> 27 | if s.activeNavId.substring(0, id.length) == id 28 | return 'active' 29 | else 30 | return '' 31 | ] 32 | 33 | mod.LoginController = <[ $scope $http authService ]> ++ ($scope, $http, authService) -> 34 | $scope.$on 'event:auth-loginRequired' -> 35 | $scope.loginShown = true 36 | $scope.$on 'event:auth-loginConfirmed' -> 37 | $scope.loginShown = false 38 | 39 | window.addEventListener 'message' ({data}) -> 40 | <- $scope.$apply 41 | if data.auth 42 | $scope.message = '' 43 | authService.loginConfirmed! 44 | if data.authFailed 45 | $scope.message = data.message || 'login failed' 46 | 47 | $scope.message = '' 48 | $scope.submit = -> 49 | $http.post 'auth/login' $scope{email, password} 50 | .success -> 51 | $scope.message = '' 52 | authService.loginConfirmed! 53 | .error -> 54 | $scope.message = if typeof it is \object => it.message else it 55 | 56 | 57 | mod.Profile = <[ $scope $http ]> ++ ($scope, $http) -> 58 | $scope.name = 'Guest'; 59 | $http.get('/1/profile')success ({name}:res) -> 60 | $scope.name = name 61 | 62 | mod.MyCtrl1 = <[ $scope ProductSearch ]> ++ ($scope, productSearch) -> 63 | $scope.updateblah = (which) -> 64 | i = parseInt $('#categories')scrollLeft!/150 65 | if $scope.category_index_old!=i then 66 | $scope.category_index_old = i 67 | $scope.blah = (which) -> 68 | i = 1 + Math.abs which - parseInt $('#categories')scrollLeft!/150 69 | if i>=3 then i=3 70 | i 71 | 72 | $scope.moreProducts = productSearch.moreResults 73 | $scope.search = 'HTC' 74 | $scope.cc = 1 75 | $scope.results <- productSearch.search("htc") 76 | 77 | mod.BudgetItem = <[ $scope $state BudgetItem ]> ++ ($scope, $state, BudgetItem) -> 78 | $scope.$watch '$state.params.code' (code) -> 79 | $scope.code = code 80 | 81 | update_from_item = (res) -> 82 | $scope <<< res{nlikes,nhates,ncuts,nconfuses,tags} 83 | 84 | $scope.$watch \key -> 85 | res <- BudgetItem.get $scope.key 86 | update_from_item res 87 | $scope <<< do 88 | nlikes: '???' 89 | nconfuses: '???' 90 | nhates: '???' 91 | ncuts: '???' 92 | like: -> BudgetItem.update $scope.key, \likes, update_from_item 93 | hate: -> BudgetItem.update $scope.key, \hates, update_from_item 94 | confuse: -> BudgetItem.update $scope.key, \confuses, update_from_item 95 | cut: -> BudgetItem.update $scope.key, \cuts, update_from_item 96 | addtag: -> 97 | if $scope.tagname then BudgetItem.addtag $scope.key, $scope.tagname, update_from_item 98 | addunit: -> 99 | if !$scope.addunit_quantity then return $('#addunit-modal input:eq(0)') .tooltip("show") 100 | if !$scope.addunit_unit then return $('#addunit-modal input:eq(1)') .tooltip("show") 101 | if !jQuery.isNumeric $scope.addunit_value then return $('#addunit-modal input:eq(2)') .tooltip("show") 102 | # console.log "add-unit: [quantifier: ",$scope.addunit_quantity, ",unit: ",$scope.addunit_unit,",value: ",$scope.addunit_value,"]" 103 | $ \#addunit-modal .modal \hide 104 | units: [ 105 | ["" \元 \1 ] 106 | <[份 營養午餐 25]> 107 | <[份 營養午餐(回扣) 30]> 108 | <[人 的一年薪水 308000]> 109 | <[座 釣魚台 80000000]> 110 | <[秒 太空旅遊 16666]> 111 | <[碗 鬍鬚張魯肉飯 68]> 112 | <[個 便當 50]> 113 | <[杯 珍奶 30]> 114 | <[份 雞排加珍奶 60]> 115 | <[個 晨水匾 700000000]> 116 | <[個 夢想家 200000000]> 117 | <[個 林益世(粗估) 83000000]> 118 | <[座 冰島 2000080000000]> 119 | <[坪 帝寶 2500000]> 120 | <[支 iPhone5 25900]> 121 | <[座 硬兔的小島 2000080000000]> 122 | ] 123 | 124 | mod.DebtClock = <[ $scope $timeout ]> ++ ($scope, $timeout) -> 125 | # Source: http://www.dgbas.gov.tw/ct.asp?xItem=33906&CtNode=5736&mp=1 總說明 P13 126 | national-debt = 59412yi * 10000wan * 10000ntd # 依國際貨幣基金定義 127 | national-payable = 109703yi * 10000wan * 10000ntd # 潛藏負債, 不含地方政府部份 128 | 129 | $scope.data = { yr2012: { base: national-debt + national-payable, interest: 7389 } } 130 | #console.log($scope.data.yr2008.base) 131 | #console.log($scope.data.yr2008.interest) 132 | $scope.refreshDebtClock = -> 133 | now = new Date() 134 | spday = new Date(2013, 1-1, 1); 135 | message = '' 136 | a = ((now.getTime() - spday.getTime()) / (1000) * $scope.data.yr2012.interest) + $scope.data.yr2012.base; 137 | a = Math.ceil a 138 | $scope.total = { debt: a, avg: Math.round(a / 23367320) } 139 | $scope.scheduleDebtClockRefresh = -> 140 | timeoutId = $timeout !-> 141 | $scope.refreshDebtClock! 142 | $scope.scheduleDebtClockRefresh! 143 | , 1000 144 | $scope.scheduleDebtClockRefresh! 145 | 146 | 147 | mod.DailyBread = <[ $scope $http ]> ++ ($scope, $http) -> 148 | $scope.tax = 80000 149 | $scope.$watch 'tax' -> 150 | window.__db?setTax $scope.tax 151 | dailybread! 152 | 153 | mod.UnitMapper = <[ $scope ]> ++ ($scope) -> 154 | $scope.units=UnitMapper.table 155 | 156 | mod.MyCtrl2 = [ 157 | '$scope' 158 | (s) -> 159 | s.Title = "MyCtrl2" 160 | ] 161 | 162 | angular.module('app.controllers', ['http-auth-interceptor']).controller(mod) 163 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_buttons.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Buttons 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // -------------------------------------------------- 8 | 9 | // Core 10 | .btn { 11 | display: inline-block; 12 | @include ie7-inline-block(); 13 | padding: 4px 14px; 14 | margin-bottom: 0; // For input.btn 15 | font-size: $baseFontSize; 16 | line-height: $baseLineHeight; 17 | *line-height: $baseLineHeight; 18 | text-align: center; 19 | vertical-align: middle; 20 | cursor: pointer; 21 | @include buttonBackground($btnBackground, $btnBackgroundHighlight, $grayDark, 0 1px 1px rgba(255,255,255,.75)); 22 | border: 1px solid $btnBorder; 23 | *border: 0; // Remove the border to prevent IE7's black border on input:focus 24 | border-bottom-color: darken($btnBorder, 10%); 25 | @include border-radius(4px); 26 | @include ie7-restore-left-whitespace(); // Give IE7 some love 27 | @include box-shadow(#{inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)}); 28 | 29 | // Hover state 30 | &:hover { 31 | color: $grayDark; 32 | text-decoration: none; 33 | background-color: darken($white, 10%); 34 | *background-color: darken($white, 15%); /* Buttons in IE7 don't get borders, so darken on hover */ 35 | background-position: 0 -15px; 36 | 37 | // transition is only when going to hover, otherwise the background 38 | // behind the gradient (there for IE<=9 fallback) gets mismatched 39 | @include transition(background-position .1s linear); 40 | } 41 | 42 | // Focus state for keyboard and accessibility 43 | &:focus { 44 | @include tab-focus(); 45 | } 46 | 47 | // Active state 48 | &.active, 49 | &:active { 50 | background-color: darken($white, 10%); 51 | background-color: darken($white, 15%) \9; 52 | background-image: none; 53 | outline: 0; 54 | @include box-shadow(#{inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)}); 55 | } 56 | 57 | // Disabled state 58 | &.disabled, 59 | &[disabled] { 60 | cursor: default; 61 | background-color: darken($white, 10%); 62 | background-image: none; 63 | @include opacity(0.65); 64 | @include box-shadow(none); 65 | } 66 | 67 | } 68 | 69 | 70 | 71 | // Button Sizes 72 | // -------------------------------------------------- 73 | 74 | // Large 75 | .btn-large { 76 | padding: 9px 14px; 77 | font-size: $baseFontSize + 2px; 78 | line-height: normal; 79 | @include border-radius(5px); 80 | } 81 | .btn-large [class^="icon-"] { 82 | margin-top: 2px; 83 | } 84 | 85 | // Small 86 | .btn-small { 87 | padding: 3px 9px; 88 | font-size: $baseFontSize - 2px; 89 | line-height: $baseLineHeight - 2px; 90 | } 91 | .btn-small [class^="icon-"] { 92 | margin-top: 0; 93 | } 94 | 95 | // Mini 96 | .btn-mini { 97 | padding: 2px 6px; 98 | font-size: $baseFontSize - 3px; 99 | line-height: $baseLineHeight - 3px; 100 | } 101 | 102 | 103 | // Block button 104 | // ------------------------- 105 | 106 | .btn-block { 107 | display: block; 108 | width: 100%; 109 | padding-left: 0; 110 | padding-right: 0; 111 | @include box-sizing(border-box); 112 | } 113 | 114 | // Vertically space out multiple block buttons 115 | .btn-block + .btn-block { 116 | margin-top: 5px; 117 | } 118 | 119 | // Specificity overrides 120 | input[type="submit"], 121 | input[type="reset"], 122 | input[type="button"] { 123 | &.btn-block { 124 | width: 100%; 125 | } 126 | } 127 | 128 | 129 | 130 | // Alternate buttons 131 | // -------------------------------------------------- 132 | 133 | // Provide *some* extra contrast for those who can get it 134 | .btn-primary.active, 135 | .btn-warning.active, 136 | .btn-danger.active, 137 | .btn-success.active, 138 | .btn-info.active, 139 | .btn-inverse.active { 140 | color: rgba(255,255,255,.75); 141 | } 142 | 143 | // Set the backgrounds 144 | // ------------------------- 145 | .btn { 146 | // reset here as of 2.0.3 due to Recess property order 147 | border-color: #c5c5c5; 148 | border-color: rgba(0,0,0,.15) rgba(0,0,0,.15) rgba(0,0,0,.25); 149 | } 150 | .btn-primary { 151 | @include buttonBackground($btnPrimaryBackground, $btnPrimaryBackgroundHighlight); 152 | } 153 | // Warning appears are orange 154 | .btn-warning { 155 | @include buttonBackground($btnWarningBackground, $btnWarningBackgroundHighlight); 156 | } 157 | // Danger and error appear as red 158 | .btn-danger { 159 | @include buttonBackground($btnDangerBackground, $btnDangerBackgroundHighlight); 160 | } 161 | // Success appears as green 162 | .btn-success { 163 | @include buttonBackground($btnSuccessBackground, $btnSuccessBackgroundHighlight); 164 | } 165 | // Info appears as a neutral blue 166 | .btn-info { 167 | @include buttonBackground($btnInfoBackground, $btnInfoBackgroundHighlight); 168 | } 169 | // Inverse appears as dark gray 170 | .btn-inverse { 171 | @include buttonBackground($btnInverseBackground, $btnInverseBackgroundHighlight); 172 | } 173 | 174 | 175 | // Cross-browser Jank 176 | // -------------------------------------------------- 177 | 178 | button.btn, 179 | input[type="submit"].btn { 180 | 181 | // Firefox 3.6 only I believe 182 | &::-moz-focus-inner { 183 | padding: 0; 184 | border: 0; 185 | } 186 | 187 | // IE7 has some default padding on button controls 188 | *padding-top: 3px; 189 | *padding-bottom: 3px; 190 | 191 | &.btn-large { 192 | *padding-top: 7px; 193 | *padding-bottom: 7px; 194 | } 195 | &.btn-small { 196 | *padding-top: 3px; 197 | *padding-bottom: 3px; 198 | } 199 | &.btn-mini { 200 | *padding-top: 1px; 201 | *padding-bottom: 1px; 202 | } 203 | } 204 | 205 | 206 | // Link buttons 207 | // -------------------------------------------------- 208 | 209 | // Make a button look and behave like a link 210 | .btn-link, 211 | .btn-link:active, 212 | .btn-link[disabled] { 213 | background-color: transparent; 214 | background-image: none; 215 | @include box-shadow(none); 216 | } 217 | .btn-link { 218 | border-color: transparent; 219 | cursor: pointer; 220 | color: $linkColor; 221 | @include border-radius(0); 222 | } 223 | .btn-link:hover { 224 | color: $linkColorHover; 225 | text-decoration: underline; 226 | background-color: transparent; 227 | } 228 | .btn-link[disabled]:hover { 229 | color: $grayDark; 230 | text-decoration: none; 231 | } 232 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_tables.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Tables 3 | // -------------------------------------------------- 4 | 5 | 6 | // BASE TABLES 7 | // ----------------- 8 | 9 | table { 10 | max-width: 100%; 11 | background-color: $tableBackground; 12 | border-collapse: collapse; 13 | border-spacing: 0; 14 | } 15 | 16 | // BASELINE STYLES 17 | // --------------- 18 | 19 | .table { 20 | width: 100%; 21 | margin-bottom: $baseLineHeight; 22 | // Cells 23 | th, 24 | td { 25 | padding: 8px; 26 | line-height: $baseLineHeight; 27 | text-align: left; 28 | vertical-align: top; 29 | border-top: 1px solid $tableBorder; 30 | } 31 | th { 32 | font-weight: bold; 33 | } 34 | // Bottom align for column headings 35 | thead th { 36 | vertical-align: bottom; 37 | } 38 | // Remove top border from thead by default 39 | caption + thead tr:first-child th, 40 | caption + thead tr:first-child td, 41 | colgroup + thead tr:first-child th, 42 | colgroup + thead tr:first-child td, 43 | thead:first-child tr:first-child th, 44 | thead:first-child tr:first-child td { 45 | border-top: 0; 46 | } 47 | // Account for multiple tbody instances 48 | tbody + tbody { 49 | border-top: 2px solid $tableBorder; 50 | } 51 | } 52 | 53 | 54 | 55 | // CONDENSED TABLE W/ HALF PADDING 56 | // ------------------------------- 57 | 58 | .table-condensed { 59 | th, 60 | td { 61 | padding: 4px 5px; 62 | } 63 | } 64 | 65 | 66 | // BORDERED VERSION 67 | // ---------------- 68 | 69 | .table-bordered { 70 | border: 1px solid $tableBorder; 71 | border-collapse: separate; // Done so we can round those corners! 72 | *border-collapse: collapse; // IE7 can't round corners anyway 73 | border-left: 0; 74 | @include border-radius(4px); 75 | th, 76 | td { 77 | border-left: 1px solid $tableBorder; 78 | } 79 | // Prevent a double border 80 | caption + thead tr:first-child th, 81 | caption + tbody tr:first-child th, 82 | caption + tbody tr:first-child td, 83 | colgroup + thead tr:first-child th, 84 | colgroup + tbody tr:first-child th, 85 | colgroup + tbody tr:first-child td, 86 | thead:first-child tr:first-child th, 87 | tbody:first-child tr:first-child th, 88 | tbody:first-child tr:first-child td { 89 | border-top: 0; 90 | } 91 | // For first th or td in the first row in the first thead or tbody 92 | thead:first-child tr:first-child th:first-child, 93 | tbody:first-child tr:first-child td:first-child { 94 | -webkit-border-top-left-radius: 4px; 95 | border-top-left-radius: 4px; 96 | -moz-border-radius-topleft: 4px; 97 | } 98 | thead:first-child tr:first-child th:last-child, 99 | tbody:first-child tr:first-child td:last-child { 100 | -webkit-border-top-right-radius: 4px; 101 | border-top-right-radius: 4px; 102 | -moz-border-radius-topright: 4px; 103 | } 104 | // For first th or td in the first row in the first thead or tbody 105 | thead:last-child tr:last-child th:first-child, 106 | tbody:last-child tr:last-child td:first-child, 107 | tfoot:last-child tr:last-child td:first-child { 108 | @include border-radius(0 0 0 4px); 109 | -webkit-border-bottom-left-radius: 4px; 110 | border-bottom-left-radius: 4px; 111 | -moz-border-radius-bottomleft: 4px; 112 | } 113 | thead:last-child tr:last-child th:last-child, 114 | tbody:last-child tr:last-child td:last-child, 115 | tfoot:last-child tr:last-child td:last-child { 116 | -webkit-border-bottom-right-radius: 4px; 117 | border-bottom-right-radius: 4px; 118 | -moz-border-radius-bottomright: 4px; 119 | } 120 | 121 | // Special fixes to round the left border on the first td/th 122 | caption + thead tr:first-child th:first-child, 123 | caption + tbody tr:first-child td:first-child, 124 | colgroup + thead tr:first-child th:first-child, 125 | colgroup + tbody tr:first-child td:first-child { 126 | -webkit-border-top-left-radius: 4px; 127 | border-top-left-radius: 4px; 128 | -moz-border-radius-topleft: 4px; 129 | } 130 | caption + thead tr:first-child th:last-child, 131 | caption + tbody tr:first-child td:last-child, 132 | colgroup + thead tr:first-child th:last-child, 133 | colgroup + tbody tr:first-child td:last-child { 134 | -webkit-border-top-right-radius: 4px; 135 | border-top-right-radius: 4px; 136 | -moz-border-radius-topleft: 4px; 137 | } 138 | 139 | } 140 | 141 | 142 | 143 | 144 | // ZEBRA-STRIPING 145 | // -------------- 146 | 147 | // Default zebra-stripe styles (alternating gray and transparent backgrounds) 148 | .table-striped { 149 | tbody { 150 | tr:nth-child(odd) td, 151 | tr:nth-child(odd) th { 152 | background-color: $tableBackgroundAccent; 153 | } 154 | } 155 | } 156 | 157 | 158 | // HOVER EFFECT 159 | // ------------ 160 | // Placed here since it has to come after the potential zebra striping 161 | .table-hover { 162 | tbody { 163 | tr:hover td, 164 | tr:hover th { 165 | background-color: $tableBackgroundHover; 166 | } 167 | } 168 | } 169 | 170 | 171 | // TABLE CELL SIZING 172 | // ----------------- 173 | 174 | // Reset default grid behavior 175 | table [class*=span], 176 | .row-fluid table [class*=span] { 177 | display: table-cell; 178 | float: none; // undo default grid column styles 179 | margin-left: 0; // undo default grid column styles 180 | } 181 | 182 | // Change the column widths to account for td/th padding 183 | table { 184 | @for $i from 1 through $gridColumns { 185 | .span#{$i} { @include tableColumns($i); } 186 | } 187 | } 188 | 189 | 190 | 191 | // TABLE BACKGROUNDS 192 | // ----------------- 193 | // Exact selectors below required to override .table-striped 194 | 195 | .table tbody tr { 196 | &.success td { 197 | background-color: $successBackground; 198 | } 199 | &.error td { 200 | background-color: $errorBackground; 201 | } 202 | &.warning td { 203 | background-color: $warningBackground; 204 | } 205 | &.info td { 206 | background-color: $infoBackground; 207 | } 208 | tbody tr.warning td { 209 | background-color: $warningBackground; 210 | } 211 | } 212 | 213 | // Hover states for .table-hover 214 | .table-hover tbody tr { 215 | &.success:hover td { 216 | background-color: darken($successBackground, 5%); 217 | } 218 | &.error:hover td { 219 | background-color: darken($errorBackground, 5%); 220 | } 221 | &.warning:hover td { 222 | background-color: darken($warningBackground, 5%); 223 | } 224 | &.info:hover td { 225 | background-color: darken($infoBackground, 5%); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /app/styles/themes/custom/_variables.less: -------------------------------------------------------------------------------- 1 | // Variables.less 2 | // Variables to customize the look and feel of Bootstrap 3 | // ----------------------------------------------------- 4 | 5 | 6 | 7 | // GLOBAL VALUES 8 | // -------------------------------------------------- 9 | 10 | 11 | // Grays 12 | // ------------------------- 13 | @black: #000; 14 | @grayDarker: #222; 15 | @grayDark: #333; 16 | @gray: #555; 17 | @grayLight: #999; 18 | @grayLighter: #eee; 19 | @white: #fff; 20 | 21 | 22 | // Accent colors 23 | // ------------------------- 24 | @blue: #049cdb; 25 | @blueDark: #0064cd; 26 | @green: #46a546; 27 | @red: #9d261d; 28 | @yellow: #ffc40d; 29 | @orange: #f89406; 30 | @pink: #c3325f; 31 | @purple: #7a43b6; 32 | 33 | 34 | // Scaffolding 35 | // ------------------------- 36 | @bodyBackground: @white; 37 | @textColor: @grayDark; 38 | 39 | 40 | // Links 41 | // ------------------------- 42 | @linkColor: #08c; 43 | @linkColorHover: darken(@linkColor, 15%); 44 | 45 | 46 | // Typography 47 | // ------------------------- 48 | @sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; 49 | @serifFontFamily: Georgia, "Times New Roman", Times, serif; 50 | @monoFontFamily: Menlo, Monaco, Consolas, "Courier New", monospace; 51 | 52 | @baseFontSize: 13px; 53 | @baseFontFamily: @sansFontFamily; 54 | @baseLineHeight: 18px; 55 | @altFontFamily: @serifFontFamily; 56 | 57 | @headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily 58 | @headingsFontWeight: bold; // instead of browser default, bold 59 | @headingsColor: inherit; // empty to use BS default, @textColor 60 | 61 | 62 | // Tables 63 | // ------------------------- 64 | @tableBackground: transparent; // overall background-color 65 | @tableBackgroundAccent: #f9f9f9; // for striping 66 | @tableBackgroundHover: #f5f5f5; // for hover 67 | @tableBorder: #ddd; // table and cell border 68 | 69 | 70 | // Buttons 71 | // ------------------------- 72 | @btnBackground: @white; 73 | @btnBackgroundHighlight: darken(@white, 10%); 74 | @btnBorder: #ccc; 75 | 76 | @btnPrimaryBackground: @linkColor; 77 | @btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 15%); 78 | 79 | @btnInfoBackground: #5bc0de; 80 | @btnInfoBackgroundHighlight: #2f96b4; 81 | 82 | @btnSuccessBackground: #62c462; 83 | @btnSuccessBackgroundHighlight: #51a351; 84 | 85 | @btnWarningBackground: lighten(@orange, 15%); 86 | @btnWarningBackgroundHighlight: @orange; 87 | 88 | @btnDangerBackground: #ee5f5b; 89 | @btnDangerBackgroundHighlight: #bd362f; 90 | 91 | @btnInverseBackground: @gray; 92 | @btnInverseBackgroundHighlight: @grayDarker; 93 | 94 | 95 | // Forms 96 | // ------------------------- 97 | @inputBackground: @white; 98 | @inputBorder: #ccc; 99 | @inputBorderRadius: 3px; 100 | @inputDisabledBackground: @grayLighter; 101 | @formActionsBackground: #f5f5f5; 102 | 103 | // Dropdowns 104 | // ------------------------- 105 | @dropdownBackground: @white; 106 | @dropdownBorder: rgba(0,0,0,.2); 107 | @dropdownLinkColor: @grayDark; 108 | @dropdownLinkColorHover: @white; 109 | @dropdownLinkBackgroundHover: @linkColor; 110 | 111 | 112 | 113 | 114 | // COMPONENT VARIABLES 115 | // -------------------------------------------------- 116 | 117 | // Z-index master list 118 | // ------------------------- 119 | // Used for a bird's eye view of components dependent on the z-axis 120 | // Try to avoid customizing these :) 121 | @zindexDropdown: 1000; 122 | @zindexPopover: 1010; 123 | @zindexTooltip: 1020; 124 | @zindexFixedNavbar: 1030; 125 | @zindexModalBackdrop: 1040; 126 | @zindexModal: 1050; 127 | 128 | 129 | // Sprite icons path 130 | // ------------------------- 131 | @iconSpritePath: "../img/glyphicons-halflings.png"; 132 | @iconWhiteSpritePath: "../img/glyphicons-halflings-white.png"; 133 | 134 | 135 | // Input placeholder text color 136 | // ------------------------- 137 | @placeholderText: @grayLight; 138 | 139 | 140 | // Hr border color 141 | // ------------------------- 142 | @hrBorder: @grayLighter; 143 | 144 | 145 | // Navbar 146 | // ------------------------- 147 | @navbarHeight: 40px; 148 | @navbarBackground: @grayDarker; 149 | @navbarBackgroundHighlight: @grayDark; 150 | 151 | @navbarText: @grayLight; 152 | @navbarLinkColor: @grayLight; 153 | @navbarLinkColorHover: @white; 154 | @navbarLinkColorActive: @navbarLinkColorHover; 155 | @navbarLinkBackgroundHover: transparent; 156 | @navbarLinkBackgroundActive: @navbarBackground; 157 | 158 | @navbarSearchBackground: lighten(@navbarBackground, 25%); 159 | @navbarSearchBackgroundFocus: @white; 160 | @navbarSearchBorder: darken(@navbarSearchBackground, 30%); 161 | @navbarSearchPlaceholderColor: #ccc; 162 | @navbarBrandColor: @navbarLinkColor; 163 | 164 | 165 | // Hero unit 166 | // ------------------------- 167 | @heroUnitBackground: @grayLighter; 168 | @heroUnitHeadingColor: inherit; 169 | @heroUnitLeadColor: inherit; 170 | 171 | 172 | // Form states and alerts 173 | // ------------------------- 174 | @warningText: #c09853; 175 | @warningBackground: #fcf8e3; 176 | @warningBorder: darken(spin(@warningBackground, -10), 3%); 177 | 178 | @errorText: #b94a48; 179 | @errorBackground: #f2dede; 180 | @errorBorder: darken(spin(@errorBackground, -10), 3%); 181 | 182 | @successText: #468847; 183 | @successBackground: #dff0d8; 184 | @successBorder: darken(spin(@successBackground, -10), 5%); 185 | 186 | @infoText: #3a87ad; 187 | @infoBackground: #d9edf7; 188 | @infoBorder: darken(spin(@infoBackground, -10), 7%); 189 | 190 | 191 | 192 | // GRID 193 | // -------------------------------------------------- 194 | 195 | // Default 940px grid 196 | // ------------------------- 197 | @gridColumns: 12; 198 | @gridColumnWidth: 60px; 199 | @gridGutterWidth: 20px; 200 | @gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); 201 | 202 | // Fluid grid 203 | // ------------------------- 204 | @fluidGridColumnWidth: 6.382978723%; 205 | @fluidGridGutterWidth: 2.127659574%; 206 | -------------------------------------------------------------------------------- /vendor/styles/themes/default/_variables.less: -------------------------------------------------------------------------------- 1 | // Variables.less 2 | // Variables to customize the look and feel of Bootstrap 3 | // ----------------------------------------------------- 4 | 5 | 6 | 7 | // GLOBAL VALUES 8 | // -------------------------------------------------- 9 | 10 | 11 | // Grays 12 | // ------------------------- 13 | @black: #000; 14 | @grayDarker: #222; 15 | @grayDark: #333; 16 | @gray: #555; 17 | @grayLight: #999; 18 | @grayLighter: #eee; 19 | @white: #fff; 20 | 21 | 22 | // Accent colors 23 | // ------------------------- 24 | @blue: #049cdb; 25 | @blueDark: #0064cd; 26 | @green: #46a546; 27 | @red: #9d261d; 28 | @yellow: #ffc40d; 29 | @orange: #f89406; 30 | @pink: #c3325f; 31 | @purple: #7a43b6; 32 | 33 | 34 | // Scaffolding 35 | // ------------------------- 36 | @bodyBackground: @white; 37 | @textColor: @grayDark; 38 | 39 | 40 | // Links 41 | // ------------------------- 42 | @linkColor: #08c; 43 | @linkColorHover: darken(@linkColor, 15%); 44 | 45 | 46 | // Typography 47 | // ------------------------- 48 | @sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; 49 | @serifFontFamily: Georgia, "Times New Roman", Times, serif; 50 | @monoFontFamily: Menlo, Monaco, Consolas, "Courier New", monospace; 51 | 52 | @baseFontSize: 13px; 53 | @baseFontFamily: @sansFontFamily; 54 | @baseLineHeight: 18px; 55 | @altFontFamily: @serifFontFamily; 56 | 57 | @headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily 58 | @headingsFontWeight: bold; // instead of browser default, bold 59 | @headingsColor: inherit; // empty to use BS default, @textColor 60 | 61 | 62 | // Tables 63 | // ------------------------- 64 | @tableBackground: transparent; // overall background-color 65 | @tableBackgroundAccent: #f9f9f9; // for striping 66 | @tableBackgroundHover: #f5f5f5; // for hover 67 | @tableBorder: #ddd; // table and cell border 68 | 69 | 70 | // Buttons 71 | // ------------------------- 72 | @btnBackground: @white; 73 | @btnBackgroundHighlight: darken(@white, 10%); 74 | @btnBorder: #ccc; 75 | 76 | @btnPrimaryBackground: @linkColor; 77 | @btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 15%); 78 | 79 | @btnInfoBackground: #5bc0de; 80 | @btnInfoBackgroundHighlight: #2f96b4; 81 | 82 | @btnSuccessBackground: #62c462; 83 | @btnSuccessBackgroundHighlight: #51a351; 84 | 85 | @btnWarningBackground: lighten(@orange, 15%); 86 | @btnWarningBackgroundHighlight: @orange; 87 | 88 | @btnDangerBackground: #ee5f5b; 89 | @btnDangerBackgroundHighlight: #bd362f; 90 | 91 | @btnInverseBackground: @gray; 92 | @btnInverseBackgroundHighlight: @grayDarker; 93 | 94 | 95 | // Forms 96 | // ------------------------- 97 | @inputBackground: @white; 98 | @inputBorder: #ccc; 99 | @inputBorderRadius: 3px; 100 | @inputDisabledBackground: @grayLighter; 101 | @formActionsBackground: #f5f5f5; 102 | 103 | // Dropdowns 104 | // ------------------------- 105 | @dropdownBackground: @white; 106 | @dropdownBorder: rgba(0,0,0,.2); 107 | @dropdownLinkColor: @grayDark; 108 | @dropdownLinkColorHover: @white; 109 | @dropdownLinkBackgroundHover: @linkColor; 110 | 111 | 112 | 113 | 114 | // COMPONENT VARIABLES 115 | // -------------------------------------------------- 116 | 117 | // Z-index master list 118 | // ------------------------- 119 | // Used for a bird's eye view of components dependent on the z-axis 120 | // Try to avoid customizing these :) 121 | @zindexDropdown: 1000; 122 | @zindexPopover: 1010; 123 | @zindexTooltip: 1020; 124 | @zindexFixedNavbar: 1030; 125 | @zindexModalBackdrop: 1040; 126 | @zindexModal: 1050; 127 | 128 | 129 | // Sprite icons path 130 | // ------------------------- 131 | @iconSpritePath: "../img/glyphicons-halflings.png"; 132 | @iconWhiteSpritePath: "../img/glyphicons-halflings-white.png"; 133 | 134 | 135 | // Input placeholder text color 136 | // ------------------------- 137 | @placeholderText: @grayLight; 138 | 139 | 140 | // Hr border color 141 | // ------------------------- 142 | @hrBorder: @grayLighter; 143 | 144 | 145 | // Navbar 146 | // ------------------------- 147 | @navbarHeight: 40px; 148 | @navbarBackground: @grayDarker; 149 | @navbarBackgroundHighlight: @grayDark; 150 | 151 | @navbarText: @grayLight; 152 | @navbarLinkColor: @grayLight; 153 | @navbarLinkColorHover: @white; 154 | @navbarLinkColorActive: @navbarLinkColorHover; 155 | @navbarLinkBackgroundHover: transparent; 156 | @navbarLinkBackgroundActive: @navbarBackground; 157 | 158 | @navbarSearchBackground: lighten(@navbarBackground, 25%); 159 | @navbarSearchBackgroundFocus: @white; 160 | @navbarSearchBorder: darken(@navbarSearchBackground, 30%); 161 | @navbarSearchPlaceholderColor: #ccc; 162 | @navbarBrandColor: @navbarLinkColor; 163 | 164 | 165 | // Hero unit 166 | // ------------------------- 167 | @heroUnitBackground: @grayLighter; 168 | @heroUnitHeadingColor: inherit; 169 | @heroUnitLeadColor: inherit; 170 | 171 | 172 | // Form states and alerts 173 | // ------------------------- 174 | @warningText: #c09853; 175 | @warningBackground: #fcf8e3; 176 | @warningBorder: darken(spin(@warningBackground, -10), 3%); 177 | 178 | @errorText: #b94a48; 179 | @errorBackground: #f2dede; 180 | @errorBorder: darken(spin(@errorBackground, -10), 3%); 181 | 182 | @successText: #468847; 183 | @successBackground: #dff0d8; 184 | @successBorder: darken(spin(@successBackground, -10), 5%); 185 | 186 | @infoText: #3a87ad; 187 | @infoBackground: #d9edf7; 188 | @infoBorder: darken(spin(@infoBackground, -10), 7%); 189 | 190 | 191 | 192 | // GRID 193 | // -------------------------------------------------- 194 | 195 | // Default 940px grid 196 | // ------------------------- 197 | @gridColumns: 12; 198 | @gridColumnWidth: 60px; 199 | @gridGutterWidth: 20px; 200 | @gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); 201 | 202 | // Fluid grid 203 | // ------------------------- 204 | @fluidGridColumnWidth: 6.382978723%; 205 | @fluidGridGutterWidth: 2.127659574%; 206 | -------------------------------------------------------------------------------- /vendor/styles/themes/sapling/_variables.less: -------------------------------------------------------------------------------- 1 | // Variables.less 2 | // Variables to customize the look and feel of Bootstrap 3 | // ----------------------------------------------------- 4 | 5 | 6 | 7 | // GLOBAL VALUES 8 | // -------------------------------------------------- 9 | 10 | 11 | // Grays 12 | // ------------------------- 13 | @black: #000; 14 | @grayDarker: #222; 15 | @grayDark: #333; 16 | @gray: #555; 17 | @grayLight: #999; 18 | @grayLighter: #eee; 19 | @white: #fff; 20 | 21 | 22 | // Accent colors 23 | // ------------------------- 24 | @blue: #049cdb; 25 | @blueDark: #1F444B; 26 | @green: #46a546; 27 | @red: #9d261d; 28 | @yellow: #ffc40d; 29 | @orange: #f89406; 30 | @pink: #c3325f; 31 | @purple: #7a43b6; 32 | @tan: #EDECE6; 33 | 34 | 35 | // Scaffolding 36 | // ------------------------- 37 | @bodyBackground: @white; 38 | @textColor: @blueDark; 39 | 40 | 41 | // Links 42 | // ------------------------- 43 | @linkColor: #6E8F4A; 44 | @linkColorHover: #F26A1E; 45 | 46 | 47 | // Typography 48 | // ------------------------- 49 | @sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; 50 | @serifFontFamily: Georgia, "Times New Roman", Times, serif; 51 | @monoFontFamily: Menlo, Monaco, Consolas, "Courier New", monospace; 52 | 53 | @baseFontSize: 13px; 54 | @baseFontFamily: @sansFontFamily; 55 | @baseLineHeight: 18px; 56 | @altFontFamily: @serifFontFamily; 57 | 58 | @headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily 59 | @headingsFontWeight: bold; // instead of browser default, bold 60 | @headingsColor: inherit; // empty to use BS default, @textColor 61 | 62 | 63 | // Tables 64 | // ------------------------- 65 | @tableBackground: transparent; // overall background-color 66 | @tableBackgroundAccent: #f9f9f9; // for striping 67 | @tableBackgroundHover: #f5f5f5; // for hover 68 | @tableBorder: #ddd; // table and cell border 69 | 70 | 71 | // Buttons 72 | // ------------------------- 73 | @btnBackground: @white; 74 | @btnBackgroundHighlight: darken(@white, 10%); 75 | @btnBorder: #ccc; 76 | 77 | @btnPrimaryBackground: @linkColor; 78 | @btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 15%); 79 | 80 | @btnInfoBackground: #5bc0de; 81 | @btnInfoBackgroundHighlight: #2f96b4; 82 | 83 | @btnSuccessBackground: #62c462; 84 | @btnSuccessBackgroundHighlight: #51a351; 85 | 86 | @btnWarningBackground: lighten(@orange, 15%); 87 | @btnWarningBackgroundHighlight: @orange; 88 | 89 | @btnDangerBackground: #ee5f5b; 90 | @btnDangerBackgroundHighlight: #bd362f; 91 | 92 | @btnInverseBackground: @gray; 93 | @btnInverseBackgroundHighlight: @grayDarker; 94 | 95 | 96 | // Forms 97 | // ------------------------- 98 | @inputBackground: @white; 99 | @inputBorder: #ccc; 100 | @inputBorderRadius: 3px; 101 | @inputDisabledBackground: @grayLighter; 102 | @formActionsBackground: #f5f5f5; 103 | 104 | // Dropdowns 105 | // ------------------------- 106 | @dropdownBackground: @white; 107 | @dropdownBorder: rgba(0,0,0,.2); 108 | @dropdownLinkColor: @grayDark; 109 | @dropdownLinkColorHover: @white; 110 | @dropdownLinkBackgroundHover: @linkColor; 111 | 112 | 113 | 114 | 115 | // COMPONENT VARIABLES 116 | // -------------------------------------------------- 117 | 118 | // Z-index master list 119 | // ------------------------- 120 | // Used for a bird's eye view of components dependent on the z-axis 121 | // Try to avoid customizing these :) 122 | @zindexDropdown: 1000; 123 | @zindexPopover: 1010; 124 | @zindexTooltip: 1020; 125 | @zindexFixedNavbar: 1030; 126 | @zindexModalBackdrop: 1040; 127 | @zindexModal: 1050; 128 | 129 | 130 | // Sprite icons path 131 | // ------------------------- 132 | @iconSpritePath: "../img/glyphicons-halflings.png"; 133 | @iconWhiteSpritePath: "../img/glyphicons-halflings-white.png"; 134 | 135 | 136 | // Input placeholder text color 137 | // ------------------------- 138 | @placeholderText: @grayLight; 139 | 140 | 141 | // Hr border color 142 | // ------------------------- 143 | @hrBorder: @grayLighter; 144 | 145 | 146 | // Navbar 147 | // ------------------------- 148 | @navbarHeight: 70px; 149 | @navbarBackground: @tan; 150 | @navbarBackgroundHighlight: @tan; 151 | 152 | @navbarText: @blueDark; 153 | @navbarLinkColor: @blueDark; 154 | @navbarLinkColorHover: @linkColorHover; 155 | @navbarLinkColorActive: @navbarLinkColorHover; 156 | @navbarLinkBackgroundHover: transparent; 157 | @navbarLinkBackgroundActive: @navbarBackground; 158 | 159 | @navbarSearchBackground: lighten(@navbarBackground, 25%); 160 | @navbarSearchBackgroundFocus: @white; 161 | @navbarSearchBorder: darken(@navbarSearchBackground, 30%); 162 | @navbarSearchPlaceholderColor: #ccc; 163 | @navbarBrandColor: @navbarLinkColor; 164 | 165 | 166 | // Hero unit 167 | // ------------------------- 168 | @heroUnitBackground: @grayLighter; 169 | @heroUnitHeadingColor: inherit; 170 | @heroUnitLeadColor: inherit; 171 | 172 | 173 | // Form states and alerts 174 | // ------------------------- 175 | @warningText: #c09853; 176 | @warningBackground: #fcf8e3; 177 | @warningBorder: darken(spin(@warningBackground, -10), 3%); 178 | 179 | @errorText: #b94a48; 180 | @errorBackground: #f2dede; 181 | @errorBorder: darken(spin(@errorBackground, -10), 3%); 182 | 183 | @successText: #468847; 184 | @successBackground: #dff0d8; 185 | @successBorder: darken(spin(@successBackground, -10), 5%); 186 | 187 | @infoText: #3a87ad; 188 | @infoBackground: #d9edf7; 189 | @infoBorder: darken(spin(@infoBackground, -10), 7%); 190 | 191 | 192 | 193 | // GRID 194 | // -------------------------------------------------- 195 | 196 | // Default 940px grid 197 | // ------------------------- 198 | @gridColumns: 12; 199 | @gridColumnWidth: 60px; 200 | @gridGutterWidth: 20px; 201 | @gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); 202 | 203 | // Fluid grid 204 | // ------------------------- 205 | @fluidGridColumnWidth: 6.382978723%; 206 | @fluidGridGutterWidth: 2.127659574%; 207 | -------------------------------------------------------------------------------- /vendor/styles/themes/smokey/_variables.less: -------------------------------------------------------------------------------- 1 | // Variables.less 2 | // Variables to customize the look and feel of Bootstrap 3 | // ----------------------------------------------------- 4 | 5 | 6 | 7 | // GLOBAL VALUES 8 | // -------------------------------------------------- 9 | 10 | 11 | // Grays 12 | // ------------------------- 13 | @black: #000; 14 | @grayDarker: #222; 15 | @grayDark: #333; 16 | @gray: #555; 17 | @grayLight: #999; 18 | @grayLighter: #eee; 19 | @white: #fff; 20 | 21 | 22 | // Accent colors 23 | // ------------------------- 24 | @blue: #049cdb; 25 | @blueDark: #9FB3BB; 26 | @green: #46a546; 27 | @red: #9d261d; 28 | @yellow: #ffc40d; 29 | @orange: #f26a1e; 30 | @pink: #c3325f; 31 | @purple: #7a43b6; 32 | 33 | 34 | // Custom Colors 35 | // ------------------------- 36 | @smoke: #f5f5f5; 37 | @brownDark: #3C2415; 38 | @blueLight: #D4EEF9; 39 | @greenLight: #76A53A; 40 | 41 | 42 | // Scaffolding 43 | // ------------------------- 44 | @bodyBackground: @white; 45 | @textColor: @grayDark; 46 | 47 | 48 | // Links 49 | // ------------------------- 50 | @linkColor: #2299BB; 51 | @linkColorHover: #F26A1E; 52 | 53 | // Typography 54 | // ------------------------- 55 | @sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; 56 | @serifFontFamily: Georgia, "Times New Roman", Times, serif; 57 | @monoFontFamily: Menlo, Monaco, Consolas, "Courier New", monospace; 58 | 59 | @baseFontSize: 13px; 60 | @baseFontFamily: @sansFontFamily; 61 | @baseLineHeight: 18px; 62 | @altFontFamily: @serifFontFamily; 63 | 64 | @headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily 65 | @headingsFontWeight: bold; // instead of browser default, bold 66 | @headingsColor: inherit; // empty to use BS default, @textColor 67 | 68 | 69 | // Tables 70 | // ------------------------- 71 | @tableBackground: transparent; // overall background-color 72 | @tableBackgroundAccent: #f9f9f9; // for striping 73 | @tableBackgroundHover: #f5f5f5; // for hover 74 | @tableBorder: #ddd; // table and cell border 75 | 76 | 77 | // Buttons 78 | // ------------------------- 79 | @btnBackground: @white; 80 | @btnBackgroundHighlight: darken(@white, 10%); 81 | @btnBorder: #ccc; 82 | 83 | @btnPrimaryBackground: @linkColor; 84 | @btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 15%); 85 | 86 | @btnInfoBackground: #5bc0de; 87 | @btnInfoBackgroundHighlight: #2f96b4; 88 | 89 | @btnSuccessBackground: #62c462; 90 | @btnSuccessBackgroundHighlight: #51a351; 91 | 92 | @btnWarningBackground: lighten(@orange, 15%); 93 | @btnWarningBackgroundHighlight: @orange; 94 | 95 | @btnDangerBackground: #ee5f5b; 96 | @btnDangerBackgroundHighlight: #bd362f; 97 | 98 | @btnInverseBackground: @gray; 99 | @btnInverseBackgroundHighlight: @grayDarker; 100 | 101 | 102 | // Forms 103 | // ------------------------- 104 | @inputBackground: @white; 105 | @inputBorder: #ccc; 106 | @inputBorderRadius: 3px; 107 | @inputDisabledBackground: @grayLighter; 108 | @formActionsBackground: #f5f5f5; 109 | 110 | // Dropdowns 111 | // ------------------------- 112 | @dropdownBackground: @white; 113 | @dropdownBorder: rgba(0,0,0,.2); 114 | @dropdownLinkColor: @grayDark; 115 | @dropdownLinkColorHover: @white; 116 | @dropdownLinkBackgroundHover: @linkColor; 117 | 118 | 119 | 120 | 121 | // COMPONENT VARIABLES 122 | // -------------------------------------------------- 123 | 124 | // Z-index master list 125 | // ------------------------- 126 | // Used for a bird's eye view of components dependent on the z-axis 127 | // Try to avoid customizing these :) 128 | @zindexDropdown: 1000; 129 | @zindexPopover: 1010; 130 | @zindexTooltip: 1020; 131 | @zindexFixedNavbar: 1030; 132 | @zindexModalBackdrop: 1040; 133 | @zindexModal: 1050; 134 | 135 | 136 | // Sprite icons path 137 | // ------------------------- 138 | @iconSpritePath: "../img/glyphicons-halflings.png"; 139 | @iconWhiteSpritePath: "../img/glyphicons-halflings-white.png"; 140 | 141 | 142 | // Input placeholder text color 143 | // ------------------------- 144 | @placeholderText: @grayLight; 145 | 146 | 147 | // Hr border color 148 | // ------------------------- 149 | @hrBorder: @grayLighter; 150 | 151 | 152 | // Navbar 153 | // ------------------------- 154 | @navbarHeight: 70px; 155 | @navbarBackground: @smoke; 156 | @navbarBackgroundHighlight: @smoke; 157 | 158 | @navbarText: @brownDark; 159 | @navbarLinkColor: @brownDark; 160 | @navbarLinkColorHover: @linkColorHover; 161 | @navbarLinkColorActive: @navbarLinkColorHover; 162 | @navbarLinkBackgroundHover: transparent; 163 | @navbarLinkBackgroundActive: @navbarBackground; 164 | 165 | @navbarSearchBackground: lighten(@navbarBackground, 25%); 166 | @navbarSearchBackgroundFocus: @white; 167 | @navbarSearchBorder: darken(@navbarSearchBackground, 30%); 168 | @navbarSearchPlaceholderColor: #ccc; 169 | @navbarBrandColor: @navbarLinkColor; 170 | 171 | 172 | // Hero unit 173 | // ------------------------- 174 | @heroUnitBackground: @grayLighter; 175 | @heroUnitHeadingColor: inherit; 176 | @heroUnitLeadColor: inherit; 177 | 178 | 179 | // Form states and alerts 180 | // ------------------------- 181 | @warningText: #c09853; 182 | @warningBackground: #fcf8e3; 183 | @warningBorder: darken(spin(@warningBackground, -10), 3%); 184 | 185 | @errorText: #b94a48; 186 | @errorBackground: #f2dede; 187 | @errorBorder: darken(spin(@errorBackground, -10), 3%); 188 | 189 | @successText: #468847; 190 | @successBackground: #dff0d8; 191 | @successBorder: darken(spin(@successBackground, -10), 5%); 192 | 193 | @infoText: #3a87ad; 194 | @infoBackground: #d9edf7; 195 | @infoBorder: darken(spin(@infoBackground, -10), 7%); 196 | 197 | 198 | 199 | // GRID 200 | // -------------------------------------------------- 201 | 202 | // Default 940px grid 203 | // ------------------------- 204 | @gridColumns: 12; 205 | @gridColumnWidth: 60px; 206 | @gridGutterWidth: 20px; 207 | @gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); 208 | 209 | // Fluid grid 210 | // ------------------------- 211 | @fluidGridColumnWidth: 6.382978723%; 212 | @fluidGridGutterWidth: 2.127659574%; 213 | -------------------------------------------------------------------------------- /app/dailybread.ls: -------------------------------------------------------------------------------- 1 | OpenSpending ?= {} 2 | <- $ 3 | TAXMAN_URL = "http://taxman.openspending.org" 4 | OpenSpending.DailyBread = (elem) -> 5 | self = this 6 | @$e = $(elem) 7 | @$e.data "wdmmg.dailybread", this 8 | @tiers = [] 9 | @areas = [] 10 | @iconLookup = (name) -> 11 | 12 | @divby = 365 13 | @init = -> 14 | @setSalary 22000 # default starting salary 15 | /* TODO remove after confirming that this is redundent 16 | @$e.find(".wdmmg-slider").slider do 17 | value: @salaryVal 18 | min: 10000 19 | max: 200000 20 | step: 10 21 | animate: true 22 | slide: -> 23 | self.sliderSlide.apply self, arguments_ 24 | change: -> 25 | self.sliderChange.apply self, arguments_ 26 | */ 27 | @$e.delegate ".db-area-col", "click", self.handleClick 28 | 29 | @formatCurrency = (val, prec, sym, dec, sep) -> 30 | val /= @divby 31 | prec = (if prec? then 2 else prec) 32 | sym = sym or "$" 33 | dec = dec or "." 34 | sep = sep or "," 35 | str = void 36 | valAry = val.toFixed(prec).split(".") 37 | sepAry = [] 38 | i = valAry[0].length 39 | 40 | while i > 2 41 | sepAry.unshift valAry[0].slice(i - 3, i) 42 | i -= 3 43 | sepAry.unshift valAry[0].slice(0, i) if i isnt 0 44 | str = sym + sepAry.join(sep) 45 | str += dec + valAry[1] if prec > 0 46 | str 47 | 48 | 49 | @setTax = (tax) -> 50 | @taxVal = parseFloat(tax) 51 | self.draw! 52 | 53 | @sliderSlide = (evt, sld) -> 54 | self.setSalary sld.value 55 | self.drawTotals() 56 | 57 | @sliderChange = (evt, sld) -> 58 | self.setSalary sld.value 59 | self.draw true 60 | 61 | @handleClick = -> 62 | tier = $(this).closest(".db-tier") 63 | tierId = parseInt(tier.attr("data-db-tier"), 10) 64 | areaId = parseInt($(this).attr("data-db-area"), 10) 65 | 66 | # Update current selected area 67 | self.areas[tierId] = areaId 68 | 69 | # Slice off more specific selections 70 | self.areas = self.areas.slice(0, tierId + 1) 71 | tier.find(".db-area-col").css({"opacity":"1"}).end().find("[data-db-area=" + areaId + "]").css {"opacity":"0.5"} 72 | self.drawTier tierId + 1 73 | 74 | # Hide old tiers 75 | self.$e.find(".db-tier").each -> 76 | $(this).hide() if $(this).attr("data-db-tier") > tierId + 1 77 | 78 | 79 | # Simulate a click so that auto resize can happen on 80 | # wheredoesmymoneygo.com. Sadly custom events won't work here, and only 81 | # click appears to do the trick. 82 | $(self.$e).click() 83 | 84 | @setData = (data) -> 85 | self.data = data 86 | 87 | @setDataFromAggregator = (data, skip) -> 88 | handleChildren = (node, absolute) -> 89 | _.map _.filter(node.children, (child) -> 90 | _.indexOf skip, child.name 91 | ), (child) -> 92 | daily = (child.amount / node.amount) 93 | #daily = daily / 365.0 if absolute 94 | [child.name, child.label, daily, handleChildren(child, false)] 95 | 96 | 97 | self.setData handleChildren(data, true) 98 | 99 | @setIconLookup = (lookup) -> 100 | self.iconLookup = lookup 101 | 102 | @setSalary = (salary) -> 103 | self.salaryVal = salary 104 | 105 | @getTaxVal = -> 106 | @taxVal = 80000 107 | 108 | @draw = (sliderUpdate) -> 109 | _draw = -> 110 | self.drawTotals() 111 | if self.tiers.length is 0 112 | self.drawTier 0, sliderUpdate 113 | else 114 | i = 0 115 | tot = self.tiers.length 116 | 117 | while i < tot 118 | self.drawTier i, sliderUpdate 119 | i += 1 120 | 121 | taxUndef = (typeof self.taxVal is "undefined" or not self.taxVal?) 122 | if taxUndef => self.getTaxVal() 123 | _draw() 124 | 125 | @drawTotals = -> 126 | $('#db-salary p').text @formatCurrency(self.salaryVal, 0) 127 | $('#db-tax p').text @formatCurrency(self.taxVal, 0) 128 | 129 | @drawTier = (tierId, sliderUpdate) -> 130 | tdAry = self.taxAndDataForTier(tierId) 131 | return unless tdAry # No child tier for selected area. 132 | tax = tdAry[0] 133 | data = tdAry[1] 134 | t = self.tiers[tierId] = self.tiers[tierId] or $("
    ").appendTo(self.$e) 135 | data = data.sort (a,b) -> b.2 - a.2 136 | .slice(0, 10) 137 | n = data.length 138 | w = 100.0 / n 139 | icons = _.map(data, (d) -> 140 | self.iconLookup d[0] 141 | ) 142 | unless sliderUpdate 143 | tpl = "
    " + "<% _.each(areas, function(area, idx) { %>" + "
    " + "

    <%= area[1] %>

    " + "
    " + "<% }); %>" + "
    " + "
    " + "<% _.each(areas, function(area, idx) { %>" + "
    " + "
    " + "
    " + "
    " + "<% }); %>" + "
    " 144 | t.html _.template(tpl, 145 | activeArea: self.areas[tierId] 146 | areas: data 147 | width: w 148 | icons: icons 149 | ) 150 | self.drawIcons t 151 | 152 | # Update values 153 | valEls = t.find(".db-area-value") 154 | _.each data, (area, idx) ~> 155 | valEls.eq(idx).text @formatCurrency(tax * area[2], 2) 156 | 157 | t.show() 158 | 159 | @taxAndDataForTier = (tierId) -> 160 | data = self.data 161 | tax = self.taxVal 162 | areaId = void 163 | i = 0 164 | tot = tierId 165 | 166 | while i < tierId 167 | areaId = self.areas[i] 168 | if data[areaId] 169 | tax = tax * data[areaId][2] 170 | data = data[areaId][3] 171 | else 172 | return null 173 | i += 1 174 | [tax, data] 175 | 176 | @drawIcons = (t) -> 177 | iconRad = 35 178 | $(".db-area-icon svg", t).remove() 179 | $(".db-area-icon", t).each (i, e) -> 180 | iconUrl = void 181 | paper = void 182 | iconUrl = $(e).data("svg-url") 183 | paper = Raphael(e, iconRad + iconRad, iconRad + iconRad + 5) 184 | paper.circle(iconRad, iconRad, iconRad).attr do 185 | fill: '#830242' 186 | stroke: "none" 187 | 188 | paper.circle(iconRad, iconRad, iconRad - 2).attr do 189 | fill: "none" 190 | stroke: '#eee' 191 | opacity: 0.8 192 | "stroke-dasharray": "- " 193 | 194 | $.get iconUrl, (svg) -> 195 | if typeof (svg) is "string" 196 | svg = $(svg) 197 | svg = svg[svg.length - 1] 198 | return unless svg.getElementsByTagName 199 | j = void 200 | icon = void 201 | joined = "" 202 | paths = svg.getElementsByTagName("path") 203 | j = 0 204 | while j < paths.length 205 | joined += paths[j].getAttribute("d") + " " 206 | j++ 207 | icon = paper.path(joined) 208 | icon.attr do 209 | fill: "white" 210 | stroke: "none" 211 | 212 | icon.scale iconRad / 50, iconRad / 50, 0, 0 213 | 214 | 215 | 216 | @init() 217 | this 218 | -------------------------------------------------------------------------------- /vendor/styles/bootstrap/_button-groups.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Button groups 3 | // -------------------------------------------------- 4 | 5 | 6 | // Make the div behave like a button 7 | .btn-group { 8 | position: relative; 9 | font-size: 0; // remove as part 1 of font-size inline-block hack 10 | vertical-align: middle; // match .btn alignment given font-size hack above 11 | white-space: nowrap; // prevent buttons from wrapping when in tight spaces (e.g., the table on the tests page) 12 | @include ie7-restore-left-whitespace(); 13 | } 14 | 15 | // Space out series of button groups 16 | .btn-group + .btn-group { 17 | margin-left: 5px; 18 | } 19 | 20 | // Optional: Group multiple button groups together for a toolbar 21 | .btn-toolbar { 22 | font-size: 0; // Hack to remove whitespace that results from using inline-block 23 | margin-top: $baseLineHeight / 2; 24 | margin-bottom: $baseLineHeight / 2; 25 | .btn-group { 26 | display: inline-block; 27 | @include ie7-inline-block(); 28 | } 29 | .btn + .btn, 30 | .btn-group + .btn, 31 | .btn + .btn-group { 32 | margin-left: 5px; 33 | } 34 | } 35 | 36 | // Float them, remove border radius, then re-add to first and last elements 37 | .btn-group > .btn { 38 | position: relative; 39 | @include border-radius(0); 40 | } 41 | .btn-group > .btn + .btn { 42 | margin-left: -1px; 43 | } 44 | .btn-group > .btn, 45 | .btn-group > .dropdown-menu { 46 | font-size: $baseFontSize; // redeclare as part 2 of font-size inline-block hack 47 | } 48 | 49 | // Reset fonts for other sizes 50 | .btn-group > .btn-mini { 51 | font-size: 11px; 52 | } 53 | .btn-group > .btn-small { 54 | font-size: 12px; 55 | } 56 | .btn-group > .btn-large { 57 | font-size: 16px; 58 | } 59 | 60 | // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match 61 | .btn-group > .btn:first-child { 62 | margin-left: 0; 63 | -webkit-border-top-left-radius: 4px; 64 | -moz-border-radius-topleft: 4px; 65 | border-top-left-radius: 4px; 66 | -webkit-border-bottom-left-radius: 4px; 67 | -moz-border-radius-bottomleft: 4px; 68 | border-bottom-left-radius: 4px; 69 | } 70 | // Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it 71 | .btn-group > .btn:last-child, 72 | .btn-group > .dropdown-toggle { 73 | -webkit-border-top-right-radius: 4px; 74 | -moz-border-radius-topright: 4px; 75 | border-top-right-radius: 4px; 76 | -webkit-border-bottom-right-radius: 4px; 77 | -moz-border-radius-bottomright: 4px; 78 | border-bottom-right-radius: 4px; 79 | } 80 | // Reset corners for large buttons 81 | .btn-group > .btn.large:first-child { 82 | margin-left: 0; 83 | -webkit-border-top-left-radius: 6px; 84 | -moz-border-radius-topleft: 6px; 85 | border-top-left-radius: 6px; 86 | -webkit-border-bottom-left-radius: 6px; 87 | -moz-border-radius-bottomleft: 6px; 88 | border-bottom-left-radius: 6px; 89 | } 90 | .btn-group > .btn.large:last-child, 91 | .btn-group > .large.dropdown-toggle { 92 | -webkit-border-top-right-radius: 6px; 93 | -moz-border-radius-topright: 6px; 94 | border-top-right-radius: 6px; 95 | -webkit-border-bottom-right-radius: 6px; 96 | -moz-border-radius-bottomright: 6px; 97 | border-bottom-right-radius: 6px; 98 | } 99 | 100 | // On hover/focus/active, bring the proper btn to front 101 | .btn-group > .btn:hover, 102 | .btn-group > .btn:focus, 103 | .btn-group > .btn:active, 104 | .btn-group > .btn.active { 105 | z-index: 2; 106 | } 107 | 108 | // On active and open, don't show outline 109 | .btn-group .dropdown-toggle:active, 110 | .btn-group.open .dropdown-toggle { 111 | outline: 0; 112 | } 113 | 114 | 115 | 116 | // Split button dropdowns 117 | // ---------------------- 118 | 119 | // Give the line between buttons some depth 120 | .btn-group > .btn + .dropdown-toggle { 121 | padding-left: 8px; 122 | padding-right: 8px; 123 | @include box-shadow(#{inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)}); 124 | *padding-top: 5px; 125 | *padding-bottom: 5px; 126 | } 127 | .btn-group > .btn-mini + .dropdown-toggle { 128 | padding-left: 5px; 129 | padding-right: 5px; 130 | *padding-top: 2px; 131 | *padding-bottom: 2px; 132 | } 133 | .btn-group > .btn-small + .dropdown-toggle { 134 | *padding-top: 5px; 135 | *padding-bottom: 4px; 136 | } 137 | .btn-group > .btn-large + .dropdown-toggle { 138 | padding-left: 12px; 139 | padding-right: 12px; 140 | *padding-top: 7px; 141 | *padding-bottom: 7px; 142 | } 143 | 144 | .btn-group.open { 145 | 146 | // The clickable button for toggling the menu 147 | // Remove the gradient and set the same inset shadow as the :active state 148 | .dropdown-toggle { 149 | background-image: none; 150 | @include box-shadow(#{inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)}); 151 | } 152 | 153 | // Keep the hover's background when dropdown is open 154 | .btn.dropdown-toggle { 155 | background-color: $btnBackgroundHighlight; 156 | } 157 | .btn-primary.dropdown-toggle { 158 | background-color: $btnPrimaryBackgroundHighlight; 159 | } 160 | .btn-warning.dropdown-toggle { 161 | background-color: $btnWarningBackgroundHighlight; 162 | } 163 | .btn-danger.dropdown-toggle { 164 | background-color: $btnDangerBackgroundHighlight; 165 | } 166 | .btn-success.dropdown-toggle { 167 | background-color: $btnSuccessBackgroundHighlight; 168 | } 169 | .btn-info.dropdown-toggle { 170 | background-color: $btnInfoBackgroundHighlight; 171 | } 172 | .btn-inverse.dropdown-toggle { 173 | background-color: $btnInverseBackgroundHighlight; 174 | } 175 | } 176 | 177 | 178 | // Reposition the caret 179 | .btn .caret { 180 | margin-top: 8px; 181 | margin-left: 0; 182 | } 183 | // Carets in other button sizes 184 | .btn-mini .caret, 185 | .btn-small .caret, 186 | .btn-large .caret { 187 | margin-top: 6px; 188 | } 189 | .btn-large .caret { 190 | border-left-width: 5px; 191 | border-right-width: 5px; 192 | border-top-width: 5px; 193 | } 194 | // Upside down carets for .dropup 195 | .dropup .btn-large .caret { 196 | border-bottom: 5px solid $black; 197 | border-top: 0; 198 | } 199 | 200 | 201 | 202 | // Account for other colors 203 | .btn-primary, 204 | .btn-warning, 205 | .btn-danger, 206 | .btn-info, 207 | .btn-success, 208 | .btn-inverse { 209 | .caret { 210 | border-top-color: $white; 211 | border-bottom-color: $white; 212 | } 213 | } 214 | 215 | 216 | 217 | // Vertical button groups 218 | // ---------------------- 219 | 220 | .btn-group-vertical { 221 | display: inline-block; // makes buttons only take up the width they need 222 | @include ie7-inline-block(); 223 | } 224 | .btn-group-vertical .btn { 225 | display: block; 226 | float: none; 227 | width: 100%; 228 | @include border-radius(0); 229 | } 230 | .btn-group-vertical .btn + .btn { 231 | margin-left: 0; 232 | margin-top: -1px; 233 | } 234 | .btn-group-vertical .btn:first-child { 235 | @include border-radius(4px 4px 0 0); 236 | } 237 | .btn-group-vertical .btn:last-child { 238 | @include border-radius(0 0 4px 4px); 239 | } 240 | .btn-group-vertical .btn-large:first-child { 241 | @include border-radius(6px 6px 0 0); 242 | } 243 | .btn-group-vertical .btn-large:last-child { 244 | @include border-radius(0 0 6px 6px); 245 | } 246 | --------------------------------------------------------------------------------