Use this document as a way to quick start any new project.
All you get is this message and a barebones HTML document.
{{_i}}We've included a few basic examples as starting points for your work with Bootstrap. We encourage folks to iterate on these examples and not simply use them as an end result.{{/i}}
6 |{{_i}}Featuring a hero unit for a primary message and three supporting elements.{{/i}}
16 |{{_i}}Uses our new responsive, fluid grid system to create a seamless liquid layout.{{/i}}
23 |{{_i}}A barebones HTML document with all the Bootstrap CSS and javascript included.{{/i}}
30 | and elements
3 | // --------------------------------------------------------
4 |
5 | // Inline and block code styles
6 | code,
7 | pre {
8 | padding: 0 3px 2px;
9 | #font > #family > .monospace;
10 | font-size: @baseFontSize - 1;
11 | color: @grayDark;
12 | .border-radius(3px);
13 | }
14 |
15 | // Inline code
16 | code {
17 | padding: 2px 4px;
18 | color: #d14;
19 | background-color: #f7f7f9;
20 | border: 1px solid #e1e1e8;
21 | }
22 |
23 | // Blocks of code
24 | pre {
25 | display: block;
26 | padding: (@baseLineHeight - 1) / 2;
27 | margin: 0 0 @baseLineHeight / 2;
28 | font-size: @baseFontSize * .925; // 13px to 12px
29 | line-height: @baseLineHeight;
30 | word-break: break-all;
31 | word-wrap: break-word;
32 | white-space: pre;
33 | white-space: pre-wrap;
34 | background-color: #f5f5f5;
35 | border: 1px solid #ccc; // fallback for IE7-8
36 | border: 1px solid rgba(0,0,0,.15);
37 | .border-radius(4px);
38 |
39 | // Make prettyprint styles more spaced out for readability
40 | &.prettyprint {
41 | margin-bottom: @baseLineHeight;
42 | }
43 |
44 | // Account for some code outputs that place code tags in pre tags
45 | code {
46 | padding: 0;
47 | color: inherit;
48 | background-color: transparent;
49 | border: 0;
50 | }
51 | }
52 |
53 | // Enable scrollable blocks of code
54 | .pre-scrollable {
55 | max-height: 340px;
56 | overflow-y: scroll;
57 | }
--------------------------------------------------------------------------------
/bootstrap/src/less/component-animations.less:
--------------------------------------------------------------------------------
1 | // COMPONENT ANIMATIONS
2 | // --------------------
3 |
4 | .fade {
5 | opacity: 0;
6 | .transition(opacity .15s linear);
7 | &.in {
8 | opacity: 1;
9 | }
10 | }
11 |
12 | .collapse {
13 | position: relative;
14 | height: 0;
15 | overflow: hidden;
16 | .transition(height .35s ease);
17 | &.in {
18 | height: auto;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/bootstrap/src/less/dropdowns.less:
--------------------------------------------------------------------------------
1 | // DROPDOWN MENUS
2 | // --------------
3 |
4 | // Use the .menu class on any
element within the topbar or ul.tabs and you'll get some superfancy dropdowns
5 | .dropup,
6 | .dropdown {
7 | position: relative;
8 | }
9 | .dropdown-toggle {
10 | // The caret makes the toggle a bit too tall in IE7
11 | *margin-bottom: -3px;
12 | }
13 | .dropdown-toggle:active,
14 | .open .dropdown-toggle {
15 | outline: 0;
16 | }
17 |
18 | // Dropdown arrow/caret
19 | // --------------------
20 | .caret {
21 | display: inline-block;
22 | width: 0;
23 | height: 0;
24 | vertical-align: top;
25 | border-top: 4px solid @black;
26 | border-right: 4px solid transparent;
27 | border-left: 4px solid transparent;
28 | content: "";
29 | .opacity(30);
30 | }
31 |
32 | // Place the caret
33 | .dropdown .caret {
34 | margin-top: 8px;
35 | margin-left: 2px;
36 | }
37 | .dropdown:hover .caret,
38 | .open .caret {
39 | .opacity(100);
40 | }
41 |
42 | // The dropdown menu (ul)
43 | // ----------------------
44 | .dropdown-menu {
45 | position: absolute;
46 | top: 100%;
47 | left: 0;
48 | z-index: @zindexDropdown;
49 | display: none; // none by default, but block on "open" of the menu
50 | float: left;
51 | min-width: 160px;
52 | padding: 4px 0;
53 | margin: 1px 0 0; // override default ul
54 | list-style: none;
55 | background-color: @dropdownBackground;
56 | border: 1px solid #ccc;
57 | border: 1px solid rgba(0,0,0,.2);
58 | *border-right-width: 2px;
59 | *border-bottom-width: 2px;
60 | .border-radius(5px);
61 | .box-shadow(0 5px 10px rgba(0,0,0,.2));
62 | -webkit-background-clip: padding-box;
63 | -moz-background-clip: padding;
64 | background-clip: padding-box;
65 |
66 | // Aligns the dropdown menu to right
67 | &.pull-right {
68 | right: 0;
69 | left: auto;
70 | }
71 |
72 | // Dividers (basically an hr) within the dropdown
73 | .divider {
74 | .nav-divider(@dropdownDividerTop, @dropdownDividerBottom);
75 | }
76 |
77 | // Links within the dropdown menu
78 | a {
79 | display: block;
80 | padding: 3px 15px;
81 | clear: both;
82 | font-weight: normal;
83 | line-height: @baseLineHeight;
84 | color: @dropdownLinkColor;
85 | white-space: nowrap;
86 | }
87 | }
88 |
89 | // Hover state
90 | // -----------
91 | .dropdown-menu li > a:hover,
92 | .dropdown-menu .active > a,
93 | .dropdown-menu .active > a:hover {
94 | color: @dropdownLinkColorHover;
95 | text-decoration: none;
96 | background-color: @dropdownLinkBackgroundHover;
97 | }
98 |
99 | // Open state for the dropdown
100 | // ---------------------------
101 | .open {
102 | // IE7's z-index only goes to the nearest positioned ancestor, which would
103 | // make the menu appear below buttons that appeared later on the page
104 | *z-index: @zindexDropdown;
105 |
106 | & > .dropdown-menu {
107 | display: block;
108 | }
109 | }
110 |
111 | // Right aligned dropdowns
112 | // ---------------------------
113 | .pull-right > .dropdown-menu {
114 | right: 0;
115 | left: auto;
116 | }
117 |
118 | // Allow for dropdowns to go bottom up (aka, dropup-menu)
119 | // ------------------------------------------------------
120 | // Just add .dropup after the standard .dropdown class and you're set, bro.
121 | // TODO: abstract this so that the navbar fixed styles are not placed here?
122 | .dropup,
123 | .navbar-fixed-bottom .dropdown {
124 | // Reverse the caret
125 | .caret {
126 | border-top: 0;
127 | border-bottom: 4px solid @black;
128 | content: "\2191";
129 | }
130 | // Different positioning for bottom up menu
131 | .dropdown-menu {
132 | top: auto;
133 | bottom: 100%;
134 | margin-bottom: 1px;
135 | }
136 | }
137 |
138 | // Typeahead
139 | // ---------
140 | .typeahead {
141 | margin-top: 2px; // give it some space to breathe
142 | .border-radius(4px);
143 | }
144 |
--------------------------------------------------------------------------------
/bootstrap/src/less/grid.less:
--------------------------------------------------------------------------------
1 | // Fixed (940px)
2 | #grid > .core(@gridColumnWidth, @gridGutterWidth);
3 |
4 | // Fluid (940px)
5 | #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth);
--------------------------------------------------------------------------------
/bootstrap/src/less/hero-unit.less:
--------------------------------------------------------------------------------
1 | // HERO UNIT
2 | // ---------
3 |
4 | .hero-unit {
5 | padding: 60px;
6 | margin-bottom: 30px;
7 | background-color: @heroUnitBackground;
8 | .border-radius(6px);
9 | h1 {
10 | margin-bottom: 0;
11 | font-size: 60px;
12 | line-height: 1;
13 | color: @heroUnitHeadingColor;
14 | letter-spacing: -1px;
15 | }
16 | p {
17 | font-size: 18px;
18 | font-weight: 200;
19 | line-height: @baseLineHeight * 1.5;
20 | color: @heroUnitLeadColor;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/bootstrap/src/less/labels-badges.less:
--------------------------------------------------------------------------------
1 | // LABELS & BADGES
2 | // ---------------
3 |
4 | // Base classes
5 | .label,
6 | .badge {
7 | font-size: @baseFontSize * .846;
8 | font-weight: bold;
9 | line-height: 14px; // ensure proper line-height if floated
10 | color: @white;
11 | vertical-align: baseline;
12 | white-space: nowrap;
13 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
14 | background-color: @grayLight;
15 | }
16 | // Set unique padding and border-radii
17 | .label {
18 | padding: 1px 4px 2px;
19 | .border-radius(3px);
20 | }
21 | .badge {
22 | padding: 1px 9px 2px;
23 | .border-radius(9px);
24 | }
25 |
26 | // Hover state, but only for links
27 | a {
28 | &.label:hover,
29 | &.badge:hover {
30 | color: @white;
31 | text-decoration: none;
32 | cursor: pointer;
33 | }
34 | }
35 |
36 | // Colors
37 | // Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute)
38 | .label,
39 | .badge {
40 | // Important (red)
41 | &-important { background-color: @errorText; }
42 | &-important[href] { background-color: darken(@errorText, 10%); }
43 | // Warnings (orange)
44 | &-warning { background-color: @orange; }
45 | &-warning[href] { background-color: darken(@orange, 10%); }
46 | // Success (green)
47 | &-success { background-color: @successText; }
48 | &-success[href] { background-color: darken(@successText, 10%); }
49 | // Info (turquoise)
50 | &-info { background-color: @infoText; }
51 | &-info[href] { background-color: darken(@infoText, 10%); }
52 | // Inverse (black)
53 | &-inverse { background-color: @grayDark; }
54 | &-inverse[href] { background-color: darken(@grayDark, 10%); }
55 | }
56 |
--------------------------------------------------------------------------------
/bootstrap/src/less/layouts.less:
--------------------------------------------------------------------------------
1 | //
2 | // Layouts
3 | // Fixed-width and fluid (with sidebar) layouts
4 | // --------------------------------------------
5 |
6 |
7 | // Container (centered, fixed-width layouts)
8 | .container {
9 | .container-fixed();
10 | }
11 |
12 | // Fluid layouts (left aligned, with sidebar, min- & max-width content)
13 | .container-fluid {
14 | padding-right: @gridGutterWidth;
15 | padding-left: @gridGutterWidth;
16 | .clearfix();
17 | }
--------------------------------------------------------------------------------
/bootstrap/src/less/modals.less:
--------------------------------------------------------------------------------
1 | // MODALS
2 | // ------
3 |
4 | // Recalculate z-index where appropriate
5 | .modal-open {
6 | .dropdown-menu { z-index: @zindexDropdown + @zindexModal; }
7 | .dropdown.open { *z-index: @zindexDropdown + @zindexModal; }
8 | .popover { z-index: @zindexPopover + @zindexModal; }
9 | .tooltip { z-index: @zindexTooltip + @zindexModal; }
10 | }
11 |
12 | // Background
13 | .modal-backdrop {
14 | position: fixed;
15 | top: 0;
16 | right: 0;
17 | bottom: 0;
18 | left: 0;
19 | z-index: @zindexModalBackdrop;
20 | background-color: @black;
21 | // Fade for backdrop
22 | &.fade { opacity: 0; }
23 | }
24 |
25 | .modal-backdrop,
26 | .modal-backdrop.fade.in {
27 | .opacity(80);
28 | }
29 |
30 | // Base modal
31 | .modal {
32 | position: fixed;
33 | top: 50%;
34 | left: 50%;
35 | z-index: @zindexModal;
36 | overflow: auto;
37 | width: 560px;
38 | margin: -250px 0 0 -280px;
39 | background-color: @white;
40 | border: 1px solid #999;
41 | border: 1px solid rgba(0,0,0,.3);
42 | *border: 1px solid #999; /* IE6-7 */
43 | .border-radius(6px);
44 | .box-shadow(0 3px 7px rgba(0,0,0,0.3));
45 | .background-clip(padding-box);
46 | &.fade {
47 | .transition(e('opacity .3s linear, top .3s ease-out'));
48 | top: -25%;
49 | }
50 | &.fade.in { top: 50%; }
51 | }
52 | .modal-header {
53 | padding: 9px 15px;
54 | border-bottom: 1px solid #eee;
55 | // Close icon
56 | .close { margin-top: 2px; }
57 | }
58 |
59 | // Body (where all modal content resides)
60 | .modal-body {
61 | overflow-y: auto;
62 | max-height: 400px;
63 | padding: 15px;
64 | }
65 | // Remove bottom margin if need be
66 | .modal-form {
67 | margin-bottom: 0;
68 | }
69 |
70 | // Footer (for actions)
71 | .modal-footer {
72 | padding: 14px 15px 15px;
73 | margin-bottom: 0;
74 | text-align: right; // right align buttons
75 | background-color: #f5f5f5;
76 | border-top: 1px solid #ddd;
77 | .border-radius(0 0 6px 6px);
78 | .box-shadow(inset 0 1px 0 @white);
79 | .clearfix(); // clear it in case folks use .pull-* classes on buttons
80 |
81 | // Properly space out buttons
82 | .btn + .btn {
83 | margin-left: 5px;
84 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs
85 | }
86 | // but override that for button groups
87 | .btn-group .btn + .btn {
88 | margin-left: -1px;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/bootstrap/src/less/pager.less:
--------------------------------------------------------------------------------
1 | // PAGER
2 | // -----
3 |
4 | .pager {
5 | margin-left: 0;
6 | margin-bottom: @baseLineHeight;
7 | list-style: none;
8 | text-align: center;
9 | .clearfix();
10 | }
11 | .pager li {
12 | display: inline;
13 | }
14 | .pager a {
15 | display: inline-block;
16 | padding: 5px 14px;
17 | background-color: #fff;
18 | border: 1px solid #ddd;
19 | .border-radius(15px);
20 | }
21 | .pager a:hover {
22 | text-decoration: none;
23 | background-color: #f5f5f5;
24 | }
25 | .pager .next a {
26 | float: right;
27 | }
28 | .pager .previous a {
29 | float: left;
30 | }
31 | .pager .disabled a,
32 | .pager .disabled a:hover {
33 | color: @grayLight;
34 | background-color: #fff;
35 | cursor: default;
36 | }
--------------------------------------------------------------------------------
/bootstrap/src/less/pagination.less:
--------------------------------------------------------------------------------
1 | // PAGINATION
2 | // ----------
3 |
4 | .pagination {
5 | height: @baseLineHeight * 2;
6 | margin: @baseLineHeight 0;
7 | }
8 | .pagination ul {
9 | display: inline-block;
10 | .ie7-inline-block();
11 | margin-left: 0;
12 | margin-bottom: 0;
13 | .border-radius(3px);
14 | .box-shadow(0 1px 2px rgba(0,0,0,.05));
15 | }
16 | .pagination li {
17 | display: inline;
18 | }
19 | .pagination a {
20 | float: left;
21 | padding: 0 14px;
22 | line-height: (@baseLineHeight * 2) - 2;
23 | text-decoration: none;
24 | border: 1px solid #ddd;
25 | border-left-width: 0;
26 | }
27 | .pagination a:hover,
28 | .pagination .active a {
29 | background-color: #f5f5f5;
30 | }
31 | .pagination .active a {
32 | color: @grayLight;
33 | cursor: default;
34 | }
35 | .pagination .disabled span,
36 | .pagination .disabled a,
37 | .pagination .disabled a:hover {
38 | color: @grayLight;
39 | background-color: transparent;
40 | cursor: default;
41 | }
42 | .pagination li:first-child a {
43 | border-left-width: 1px;
44 | .border-radius(3px 0 0 3px);
45 | }
46 | .pagination li:last-child a {
47 | .border-radius(0 3px 3px 0);
48 | }
49 |
50 | // Centered
51 | .pagination-centered {
52 | text-align: center;
53 | }
54 | .pagination-right {
55 | text-align: right;
56 | }
57 |
--------------------------------------------------------------------------------
/bootstrap/src/less/popovers.less:
--------------------------------------------------------------------------------
1 | // POPOVERS
2 | // --------
3 |
4 | .popover {
5 | position: absolute;
6 | top: 0;
7 | left: 0;
8 | z-index: @zindexPopover;
9 | display: none;
10 | padding: 5px;
11 | &.top { margin-top: -5px; }
12 | &.right { margin-left: 5px; }
13 | &.bottom { margin-top: 5px; }
14 | &.left { margin-left: -5px; }
15 | &.top .arrow { #popoverArrow > .top(); }
16 | &.right .arrow { #popoverArrow > .right(); }
17 | &.bottom .arrow { #popoverArrow > .bottom(); }
18 | &.left .arrow { #popoverArrow > .left(); }
19 | .arrow {
20 | position: absolute;
21 | width: 0;
22 | height: 0;
23 | }
24 | }
25 | .popover-inner {
26 | padding: 3px;
27 | width: 280px;
28 | overflow: hidden;
29 | background: @black; // has to be full background declaration for IE fallback
30 | background: rgba(0,0,0,.8);
31 | .border-radius(6px);
32 | .box-shadow(0 3px 7px rgba(0,0,0,0.3));
33 | }
34 | .popover-title {
35 | padding: 9px 15px;
36 | line-height: 1;
37 | background-color: #f5f5f5;
38 | border-bottom:1px solid #eee;
39 | .border-radius(3px 3px 0 0);
40 | }
41 | .popover-content {
42 | padding: 14px;
43 | background-color: @white;
44 | .border-radius(0 0 3px 3px);
45 | .background-clip(padding-box);
46 | p, ul, ol {
47 | margin-bottom: 0;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/bootstrap/src/less/progress-bars.less:
--------------------------------------------------------------------------------
1 | // PROGRESS BARS
2 | // -------------
3 |
4 |
5 | // ANIMATIONS
6 | // ----------
7 |
8 | // Webkit
9 | @-webkit-keyframes progress-bar-stripes {
10 | from { background-position: 40px 0; }
11 | to { background-position: 0 0; }
12 | }
13 |
14 | // Firefox
15 | @-moz-keyframes progress-bar-stripes {
16 | from { background-position: 40px 0; }
17 | to { background-position: 0 0; }
18 | }
19 |
20 | // IE9
21 | @-ms-keyframes progress-bar-stripes {
22 | from { background-position: 40px 0; }
23 | to { background-position: 0 0; }
24 | }
25 |
26 | // Opera
27 | @-o-keyframes progress-bar-stripes {
28 | from { background-position: 0 0; }
29 | to { background-position: 40px 0; }
30 | }
31 |
32 | // Spec
33 | @keyframes progress-bar-stripes {
34 | from { background-position: 40px 0; }
35 | to { background-position: 0 0; }
36 | }
37 |
38 |
39 |
40 | // THE BARS
41 | // --------
42 |
43 | // Outer container
44 | .progress {
45 | overflow: hidden;
46 | height: 18px;
47 | margin-bottom: 18px;
48 | #gradient > .vertical(#f5f5f5, #f9f9f9);
49 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
50 | .border-radius(4px);
51 | }
52 |
53 | // Bar of progress
54 | .progress .bar {
55 | width: 0%;
56 | height: 18px;
57 | color: @white;
58 | font-size: 12px;
59 | text-align: center;
60 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
61 | #gradient > .vertical(#149bdf, #0480be);
62 | .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));
63 | .box-sizing(border-box);
64 | .transition(width .6s ease);
65 | }
66 |
67 | // Striped bars
68 | .progress-striped .bar {
69 | #gradient > .striped(#149bdf);
70 | .background-size(40px 40px);
71 | }
72 |
73 | // Call animation for the active one
74 | .progress.active .bar {
75 | -webkit-animation: progress-bar-stripes 2s linear infinite;
76 | -moz-animation: progress-bar-stripes 2s linear infinite;
77 | -ms-animation: progress-bar-stripes 2s linear infinite;
78 | -o-animation: progress-bar-stripes 2s linear infinite;
79 | animation: progress-bar-stripes 2s linear infinite;
80 | }
81 |
82 |
83 |
84 | // COLORS
85 | // ------
86 |
87 | // Danger (red)
88 | .progress-danger .bar {
89 | #gradient > .vertical(#ee5f5b, #c43c35);
90 | }
91 | .progress-danger.progress-striped .bar {
92 | #gradient > .striped(#ee5f5b);
93 | }
94 |
95 | // Success (green)
96 | .progress-success .bar {
97 | #gradient > .vertical(#62c462, #57a957);
98 | }
99 | .progress-success.progress-striped .bar {
100 | #gradient > .striped(#62c462);
101 | }
102 |
103 | // Info (teal)
104 | .progress-info .bar {
105 | #gradient > .vertical(#5bc0de, #339bb9);
106 | }
107 | .progress-info.progress-striped .bar {
108 | #gradient > .striped(#5bc0de);
109 | }
110 |
111 | // Warning (orange)
112 | .progress-warning .bar {
113 | #gradient > .vertical(lighten(@orange, 15%), @orange);
114 | }
115 | .progress-warning.progress-striped .bar {
116 | #gradient > .striped(lighten(@orange, 15%));
117 | }
118 |
--------------------------------------------------------------------------------
/bootstrap/src/less/reset.less:
--------------------------------------------------------------------------------
1 | // Reset.less
2 | // Adapted from Normalize.css http://github.com/necolas/normalize.css
3 | // ------------------------------------------------------------------------
4 |
5 | // Display in IE6-9 and FF3
6 | // -------------------------
7 |
8 | article,
9 | aside,
10 | details,
11 | figcaption,
12 | figure,
13 | footer,
14 | header,
15 | hgroup,
16 | nav,
17 | section {
18 | display: block;
19 | }
20 |
21 | // Display block in IE6-9 and FF3
22 | // -------------------------
23 |
24 | audio,
25 | canvas,
26 | video {
27 | display: inline-block;
28 | *display: inline;
29 | *zoom: 1;
30 | }
31 |
32 | // Prevents modern browsers from displaying 'audio' without controls
33 | // -------------------------
34 |
35 | audio:not([controls]) {
36 | display: none;
37 | }
38 |
39 | // Base settings
40 | // -------------------------
41 |
42 | html {
43 | font-size: 100%;
44 | -webkit-text-size-adjust: 100%;
45 | -ms-text-size-adjust: 100%;
46 | }
47 | // Focus states
48 | a:focus {
49 | .tab-focus();
50 | }
51 | // Hover & Active
52 | a:hover,
53 | a:active {
54 | outline: 0;
55 | }
56 |
57 | // Prevents sub and sup affecting line-height in all browsers
58 | // -------------------------
59 |
60 | sub,
61 | sup {
62 | position: relative;
63 | font-size: 75%;
64 | line-height: 0;
65 | vertical-align: baseline;
66 | }
67 | sup {
68 | top: -0.5em;
69 | }
70 | sub {
71 | bottom: -0.25em;
72 | }
73 |
74 | // Img border in a's and image quality
75 | // -------------------------
76 |
77 | img {
78 | max-width: 100%; // Make images inherently responsive
79 | vertical-align: middle;
80 | border: 0;
81 | -ms-interpolation-mode: bicubic;
82 | }
83 |
84 | // Prevent max-width from affecting Google Maps
85 | #map_canvas img {
86 | max-width: none;
87 | }
88 |
89 | // Forms
90 | // -------------------------
91 |
92 | // Font size in all browsers, margin changes, misc consistency
93 | button,
94 | input,
95 | select,
96 | textarea {
97 | margin: 0;
98 | font-size: 100%;
99 | vertical-align: middle;
100 | }
101 | button,
102 | input {
103 | *overflow: visible; // Inner spacing ie IE6/7
104 | line-height: normal; // FF3/4 have !important on line-height in UA stylesheet
105 | }
106 | button::-moz-focus-inner,
107 | input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
108 | padding: 0;
109 | border: 0;
110 | }
111 | button,
112 | input[type="button"],
113 | input[type="reset"],
114 | input[type="submit"] {
115 | cursor: pointer; // Cursors on all buttons applied consistently
116 | -webkit-appearance: button; // Style clickable inputs in iOS
117 | }
118 | input[type="search"] { // Appearance in Safari/Chrome
119 | -webkit-box-sizing: content-box;
120 | -moz-box-sizing: content-box;
121 | box-sizing: content-box;
122 | -webkit-appearance: textfield;
123 | }
124 | input[type="search"]::-webkit-search-decoration,
125 | input[type="search"]::-webkit-search-cancel-button {
126 | -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
127 | }
128 | textarea {
129 | overflow: auto; // Remove vertical scrollbar in IE6-9
130 | vertical-align: top; // Readability and alignment cross-browser
131 | }
132 |
--------------------------------------------------------------------------------
/bootstrap/src/less/responsive-1200px-min.less:
--------------------------------------------------------------------------------
1 | // LARGE DESKTOP & UP
2 | // ------------------
3 |
4 | @media (min-width: 1200px) {
5 |
6 | // Fixed grid
7 | #grid > .core(70px, 30px);
8 |
9 | // Fluid grid
10 | #grid > .fluid(5.982905983%, 2.564102564%);
11 |
12 | // Input grid
13 | #grid > .input(70px, 30px);
14 |
15 | // Thumbnails
16 | .thumbnails {
17 | margin-left: -30px;
18 | }
19 | .thumbnails > li {
20 | margin-left: 30px;
21 | }
22 | .row-fluid .thumbnails {
23 | margin-left: 0;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/bootstrap/src/less/responsive-767px-max.less:
--------------------------------------------------------------------------------
1 | // UP TO LANDSCAPE PHONE
2 | // ---------------------
3 |
4 | @media (max-width: 480px) {
5 |
6 | // Smooth out the collapsing/expanding nav
7 | .nav-collapse {
8 | -webkit-transform: translate3d(0, 0, 0); // activate the GPU
9 | }
10 |
11 | // Block level the page header small tag for readability
12 | .page-header h1 small {
13 | display: block;
14 | line-height: @baseLineHeight;
15 | }
16 |
17 | // Update checkboxes for iOS
18 | input[type="checkbox"],
19 | input[type="radio"] {
20 | border: 1px solid #ccc;
21 | }
22 |
23 | // Remove the horizontal form styles
24 | .form-horizontal .control-group > label {
25 | float: none;
26 | width: auto;
27 | padding-top: 0;
28 | text-align: left;
29 | }
30 | // Move over all input controls and content
31 | .form-horizontal .controls {
32 | margin-left: 0;
33 | }
34 | // Move the options list down to align with labels
35 | .form-horizontal .control-list {
36 | padding-top: 0; // has to be padding because margin collaspes
37 | }
38 | // Move over buttons in .form-actions to align with .controls
39 | .form-horizontal .form-actions {
40 | padding-left: 10px;
41 | padding-right: 10px;
42 | }
43 |
44 | // Modals
45 | .modal {
46 | position: absolute;
47 | top: 10px;
48 | left: 10px;
49 | right: 10px;
50 | width: auto;
51 | margin: 0;
52 | &.fade.in { top: auto; }
53 | }
54 | .modal-header .close {
55 | padding: 10px;
56 | margin: -10px;
57 | }
58 |
59 | // Carousel
60 | .carousel-caption {
61 | position: static;
62 | }
63 |
64 | }
65 |
66 |
67 |
68 | // LANDSCAPE PHONE TO SMALL DESKTOP & PORTRAIT TABLET
69 | // --------------------------------------------------
70 |
71 | @media (max-width: 767px) {
72 |
73 | // Padding to set content in a bit
74 | body {
75 | padding-left: 20px;
76 | padding-right: 20px;
77 | }
78 | // Negative indent the now static "fixed" navbar
79 | .navbar-fixed-top,
80 | .navbar-fixed-bottom {
81 | margin-left: -20px;
82 | margin-right: -20px;
83 | }
84 | // Remove padding on container given explicit padding set on body
85 | .container-fluid {
86 | padding: 0;
87 | }
88 |
89 | // TYPOGRAPHY
90 | // ----------
91 | // Reset horizontal dl
92 | .dl-horizontal {
93 | dt {
94 | float: none;
95 | clear: none;
96 | width: auto;
97 | text-align: left;
98 | }
99 | dd {
100 | margin-left: 0;
101 | }
102 | }
103 |
104 | // GRID & CONTAINERS
105 | // -----------------
106 | // Remove width from containers
107 | .container {
108 | width: auto;
109 | }
110 | // Fluid rows
111 | .row-fluid {
112 | width: 100%;
113 | }
114 | // Undo negative margin on rows and thumbnails
115 | .row,
116 | .thumbnails {
117 | margin-left: 0;
118 | }
119 | // Make all grid-sized elements block level again
120 | [class*="span"],
121 | .row-fluid [class*="span"] {
122 | float: none;
123 | display: block;
124 | width: auto;
125 | margin-left: 0;
126 | }
127 |
128 | // FORM FIELDS
129 | // -----------
130 | // Make span* classes full width
131 | .input-large,
132 | .input-xlarge,
133 | .input-xxlarge,
134 | input[class*="span"],
135 | select[class*="span"],
136 | textarea[class*="span"],
137 | .uneditable-input {
138 | .input-block-level();
139 | }
140 | // But don't let it screw up prepend/append inputs
141 | .input-prepend input,
142 | .input-append input,
143 | .input-prepend input[class*="span"],
144 | .input-append input[class*="span"] {
145 | display: inline-block; // redeclare so they don't wrap to new lines
146 | width: auto;
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/bootstrap/src/less/responsive-768px-979px.less:
--------------------------------------------------------------------------------
1 | // PORTRAIT TABLET TO DEFAULT DESKTOP
2 | // ----------------------------------
3 |
4 | @media (min-width: 768px) and (max-width: 979px) {
5 |
6 | // Fixed grid
7 | #grid > .core(42px, 20px);
8 |
9 | // Fluid grid
10 | #grid > .fluid(5.801104972%, 2.762430939%);
11 |
12 | // Input grid
13 | #grid > .input(42px, 20px);
14 |
15 | // No need to reset .thumbnails here since it's the same @gridGutterWidth
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/bootstrap/src/less/responsive-navbar.less:
--------------------------------------------------------------------------------
1 | // TABLETS AND BELOW
2 | // -----------------
3 | @media (max-width: 979px) {
4 |
5 | // UNFIX THE TOPBAR
6 | // ----------------
7 | // Remove any padding from the body
8 | body {
9 | padding-top: 0;
10 | }
11 | // Unfix the navbar
12 | .navbar-fixed-top,
13 | .navbar-fixed-bottom {
14 | position: static;
15 | }
16 | .navbar-fixed-top {
17 | margin-bottom: @baseLineHeight;
18 | }
19 | .navbar-fixed-bottom {
20 | margin-top: @baseLineHeight;
21 | }
22 | .navbar-fixed-top .navbar-inner,
23 | .navbar-fixed-bottom .navbar-inner {
24 | padding: 5px;
25 | }
26 | .navbar .container {
27 | width: auto;
28 | padding: 0;
29 | }
30 | // Account for brand name
31 | .navbar .brand {
32 | padding-left: 10px;
33 | padding-right: 10px;
34 | margin: 0 0 0 -5px;
35 | }
36 |
37 | // COLLAPSIBLE NAVBAR
38 | // ------------------
39 | // Nav collapse clears brand
40 | .nav-collapse {
41 | clear: both;
42 | }
43 | // Block-level the nav
44 | .nav-collapse .nav {
45 | float: none;
46 | margin: 0 0 (@baseLineHeight / 2);
47 | }
48 | .nav-collapse .nav > li {
49 | float: none;
50 | }
51 | .nav-collapse .nav > li > a {
52 | margin-bottom: 2px;
53 | }
54 | .nav-collapse .nav > .divider-vertical {
55 | display: none;
56 | }
57 | .nav-collapse .nav .nav-header {
58 | color: @navbarText;
59 | text-shadow: none;
60 | }
61 | // Nav and dropdown links in navbar
62 | .nav-collapse .nav > li > a,
63 | .nav-collapse .dropdown-menu a {
64 | padding: 6px 15px;
65 | font-weight: bold;
66 | color: @navbarLinkColor;
67 | .border-radius(3px);
68 | }
69 | // Buttons
70 | .nav-collapse .btn {
71 | padding: 4px 10px 4px;
72 | font-weight: normal;
73 | .border-radius(4px);
74 | }
75 | .nav-collapse .dropdown-menu li + li a {
76 | margin-bottom: 2px;
77 | }
78 | .nav-collapse .nav > li > a:hover,
79 | .nav-collapse .dropdown-menu a:hover {
80 | background-color: @navbarBackground;
81 | }
82 | // Buttons in the navbar
83 | .nav-collapse.in .btn-group {
84 | margin-top: 5px;
85 | padding: 0;
86 | }
87 | // Dropdowns in the navbar
88 | .nav-collapse .dropdown-menu {
89 | position: static;
90 | top: auto;
91 | left: auto;
92 | float: none;
93 | display: block;
94 | max-width: none;
95 | margin: 0 15px;
96 | padding: 0;
97 | background-color: transparent;
98 | border: none;
99 | .border-radius(0);
100 | .box-shadow(none);
101 | }
102 | .nav-collapse .dropdown-menu:before,
103 | .nav-collapse .dropdown-menu:after {
104 | display: none;
105 | }
106 | .nav-collapse .dropdown-menu .divider {
107 | display: none;
108 | }
109 | // Forms in navbar
110 | .nav-collapse .navbar-form,
111 | .nav-collapse .navbar-search {
112 | float: none;
113 | padding: (@baseLineHeight / 2) 15px;
114 | margin: (@baseLineHeight / 2) 0;
115 | border-top: 1px solid @navbarBackground;
116 | border-bottom: 1px solid @navbarBackground;
117 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)");
118 | }
119 | // Pull right (secondary) nav content
120 | .navbar .nav-collapse .nav.pull-right {
121 | float: none;
122 | margin-left: 0;
123 | }
124 | // Hide everything in the navbar save .brand and toggle button */
125 | .nav-collapse,
126 | .nav-collapse.collapse {
127 | overflow: hidden;
128 | height: 0;
129 | }
130 | // Navbar button
131 | .navbar .btn-navbar {
132 | display: block;
133 | }
134 |
135 | // STATIC NAVBAR
136 | // -------------
137 | .navbar-static .navbar-inner {
138 | padding-left: 10px;
139 | padding-right: 10px;
140 | }
141 | }
142 |
143 |
144 | // DEFAULT DESKTOP
145 | // ---------------
146 |
147 | // Required to make the collapsing navbar work on regular desktops
148 | @media (min-width: 980px) {
149 | .nav-collapse.collapse {
150 | height: auto !important;
151 | overflow: visible !important;
152 | }
153 | }
--------------------------------------------------------------------------------
/bootstrap/src/less/responsive-utilities.less:
--------------------------------------------------------------------------------
1 | // RESPONSIVE CLASSES
2 | // ------------------
3 |
4 | // Hide from screenreaders and browsers
5 | // Credit: HTML5 Boilerplate
6 | .hidden {
7 | display: none;
8 | visibility: hidden;
9 | }
10 |
11 | // Visibility utilities
12 |
13 | // For desktops
14 | .visible-phone { display: none !important; }
15 | .visible-tablet { display: none !important; }
16 | .visible-desktop { } // Don't set initially
17 | .hidden-phone { }
18 | .hidden-tablet { }
19 | .hidden-desktop { display: none !important; }
20 |
21 | // Phones only
22 | @media (max-width: 767px) {
23 | // Show
24 | .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior
25 | // Hide
26 | .hidden-phone { display: none !important; }
27 | // Hide everything else
28 | .hidden-desktop { display: inherit !important; }
29 | .visible-desktop { display: none !important; }
30 | }
31 |
32 | // Tablets & small desktops only
33 | @media (min-width: 768px) and (max-width: 979px) {
34 | // Show
35 | .visible-tablet { display: inherit !important; }
36 | // Hide
37 | .hidden-tablet { display: none !important; }
38 | // Hide everything else
39 | .hidden-desktop { display: inherit !important; }
40 | .visible-desktop { display: none !important ; }
41 | }
42 |
--------------------------------------------------------------------------------
/bootstrap/src/less/responsive.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.0.4
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 | */
10 |
11 |
12 | // Responsive.less
13 | // For phone and tablet devices
14 | // -------------------------------------------------------------
15 |
16 |
17 | // REPEAT VARIABLES & MIXINS
18 | // -------------------------
19 | // Required since we compile the responsive stuff separately
20 |
21 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc
22 | @import "mixins.less";
23 |
24 |
25 | // RESPONSIVE CLASSES
26 | // ------------------
27 |
28 | @import "responsive-utilities.less";
29 |
30 |
31 | // MEDIA QUERIES
32 | // ------------------
33 |
34 | // Phones to portrait tablets and narrow desktops
35 | @import "responsive-767px-max.less";
36 |
37 | // Tablets to regular desktops
38 | @import "responsive-768px-979px.less";
39 |
40 | // Large desktops
41 | @import "responsive-1200px-min.less";
42 |
43 |
44 | // RESPONSIVE NAVBAR
45 | // ------------------
46 |
47 | // From 979px and below, show a button to toggle navbar contents
48 | @import "responsive-navbar.less";
49 |
--------------------------------------------------------------------------------
/bootstrap/src/less/scaffolding.less:
--------------------------------------------------------------------------------
1 | // Scaffolding
2 | // Basic and global styles for generating a grid system, structural layout, and page templates
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 |
--------------------------------------------------------------------------------
/bootstrap/src/less/tests/css-tests.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap CSS Tests
3 | */
4 |
5 |
6 | /* Remove background image */
7 | body {
8 | background-image: none;
9 | }
10 |
11 | /* Space out subhead */
12 | .subhead {
13 | margin-bottom: 36px;
14 | }
15 | h4 {
16 | margin-bottom: 5px;
17 | }
18 |
19 |
20 | /* colgroup tests */
21 | .col1 {
22 | background-color: rgba(255,0,0,.1);
23 | }
24 | .col2 {
25 | background-color: rgba(0,255,0,.1);
26 | }
27 | .col3 {
28 | background-color: rgba(0,0,255,.1);
29 | }
30 |
31 |
32 | /* Fluid row inputs */
33 | #rowInputs .row > [class*=span],
34 | #fluidRowInputs .row-fluid > [class*=span] {
35 | background-color: rgba(255,0,0,.1);
36 | }
37 |
38 |
39 | /* Fluid grid */
40 | .fluid-grid {
41 | margin-bottom: 45px;
42 | }
43 | .fluid-grid .row {
44 | height: 40px;
45 | padding-top: 10px;
46 | margin-top: 10px;
47 | color: #ddd;
48 | text-align: center;
49 | }
50 | .fluid-grid .span1 {
51 | background-color: #999;
52 | }
53 |
--------------------------------------------------------------------------------
/bootstrap/src/less/tests/forms.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bootstrap, from Twitter
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/bootstrap/src/less/tests/navbar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bootstrap, from Twitter
6 |
7 |
8 |
9 |
10 |
11 |
12 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
55 |
56 |
57 |
58 |
59 |
81 | Navbar example
82 | This example is a quick exercise to illustrate how the default, static navbar and fixed to top navbar work. It includes the responsive CSS and HTML, so it also adapts to your viewport and device.
83 |
84 | View navbar docs »
85 |
86 |
87 |
88 |
89 |
90 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/bootstrap/src/less/thumbnails.less:
--------------------------------------------------------------------------------
1 | // THUMBNAILS
2 | // ----------
3 | // Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files
4 |
5 | // Make wrapper ul behave like the grid
6 | .thumbnails {
7 | margin-left: -@gridGutterWidth;
8 | list-style: none;
9 | .clearfix();
10 | }
11 | // Fluid rows have no left margin
12 | .row-fluid .thumbnails {
13 | margin-left: 0;
14 | }
15 |
16 | // Float li to make thumbnails appear in a row
17 | .thumbnails > li {
18 | float: left; // Explicity set the float since we don't require .span* classes
19 | margin-bottom: @baseLineHeight;
20 | margin-left: @gridGutterWidth;
21 | }
22 |
23 | // The actual thumbnail (can be `a` or `div`)
24 | .thumbnail {
25 | display: block;
26 | padding: 4px;
27 | line-height: 1;
28 | border: 1px solid #ddd;
29 | .border-radius(4px);
30 | .box-shadow(0 1px 1px rgba(0,0,0,.075));
31 | }
32 | // Add a hover state for linked versions only
33 | a.thumbnail:hover {
34 | border-color: @linkColor;
35 | .box-shadow(0 1px 4px rgba(0,105,214,.25));
36 | }
37 |
38 | // Images and captions
39 | .thumbnail > img {
40 | display: block;
41 | max-width: 100%;
42 | margin-left: auto;
43 | margin-right: auto;
44 | }
45 | .thumbnail .caption {
46 | padding: 9px;
47 | }
48 |
--------------------------------------------------------------------------------
/bootstrap/src/less/tooltip.less:
--------------------------------------------------------------------------------
1 | // TOOLTIP
2 | // ------=
3 |
4 | .tooltip {
5 | position: absolute;
6 | z-index: @zindexTooltip;
7 | display: block;
8 | visibility: visible;
9 | padding: 5px;
10 | font-size: 11px;
11 | .opacity(0);
12 | &.in { .opacity(80); }
13 | &.top { margin-top: -2px; }
14 | &.right { margin-left: 2px; }
15 | &.bottom { margin-top: 2px; }
16 | &.left { margin-left: -2px; }
17 | &.top .tooltip-arrow { #popoverArrow > .top(); }
18 | &.left .tooltip-arrow { #popoverArrow > .left(); }
19 | &.bottom .tooltip-arrow { #popoverArrow > .bottom(); }
20 | &.right .tooltip-arrow { #popoverArrow > .right(); }
21 | }
22 | .tooltip-inner {
23 | max-width: 200px;
24 | padding: 3px 8px;
25 | color: @white;
26 | text-align: center;
27 | text-decoration: none;
28 | background-color: @black;
29 | .border-radius(4px);
30 | }
31 | .tooltip-arrow {
32 | position: absolute;
33 | width: 0;
34 | height: 0;
35 | }
36 |
--------------------------------------------------------------------------------
/bootstrap/src/less/type.less:
--------------------------------------------------------------------------------
1 | // Typography.less
2 | // Headings, body text, lists, code, and more for a versatile and durable typography system
3 | // ----------------------------------------------------------------------------------------
4 |
5 |
6 | // BODY TEXT
7 | // ---------
8 |
9 | p {
10 | margin: 0 0 @baseLineHeight / 2;
11 | small {
12 | font-size: @baseFontSize - 2;
13 | color: @grayLight;
14 | }
15 | }
16 | .lead {
17 | margin-bottom: @baseLineHeight;
18 | font-size: 20px;
19 | font-weight: 200;
20 | line-height: @baseLineHeight * 1.5;
21 | }
22 |
23 | // HEADINGS
24 | // --------
25 |
26 | h1, h2, h3, h4, h5, h6 {
27 | margin: 0;
28 | font-family: @headingsFontFamily;
29 | font-weight: @headingsFontWeight;
30 | color: @headingsColor;
31 | text-rendering: optimizelegibility; // Fix the character spacing for headings
32 | small {
33 | font-weight: normal;
34 | color: @grayLight;
35 | }
36 | }
37 | h1 {
38 | font-size: 30px;
39 | line-height: @baseLineHeight * 2;
40 | small {
41 | font-size: 18px;
42 | }
43 | }
44 | h2 {
45 | font-size: 24px;
46 | line-height: @baseLineHeight * 2;
47 | small {
48 | font-size: 18px;
49 | }
50 | }
51 | h3 {
52 | font-size: 18px;
53 | line-height: @baseLineHeight * 1.5;
54 | small {
55 | font-size: 14px;
56 | }
57 | }
58 | h4, h5, h6 {
59 | line-height: @baseLineHeight;
60 | }
61 | h4 {
62 | font-size: 14px;
63 | small {
64 | font-size: 12px;
65 | }
66 | }
67 | h5 {
68 | font-size: 12px;
69 | }
70 | h6 {
71 | font-size: 11px;
72 | color: @grayLight;
73 | text-transform: uppercase;
74 | }
75 |
76 | // Page header
77 | .page-header {
78 | padding-bottom: @baseLineHeight - 1;
79 | margin: @baseLineHeight 0;
80 | border-bottom: 1px solid @grayLighter;
81 | }
82 | .page-header h1 {
83 | line-height: 1;
84 | }
85 |
86 |
87 |
88 | // LISTS
89 | // -----
90 |
91 | // Unordered and Ordered lists
92 | ul, ol {
93 | padding: 0;
94 | margin: 0 0 @baseLineHeight / 2 25px;
95 | }
96 | ul ul,
97 | ul ol,
98 | ol ol,
99 | ol ul {
100 | margin-bottom: 0;
101 | }
102 | ul {
103 | list-style: disc;
104 | }
105 | ol {
106 | list-style: decimal;
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 | line-height: @baseLineHeight - 1; // fix jank Helvetica Neue font bug
128 | }
129 | dd {
130 | margin-left: @baseLineHeight / 2;
131 | }
132 | // Horizontal layout (like forms)
133 | .dl-horizontal {
134 | dt {
135 | float: left;
136 | width: 120px;
137 | clear: left;
138 | text-align: right;
139 | .text-overflow();
140 | }
141 | dd {
142 | margin-left: 130px;
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 | // Emphasis
158 | strong {
159 | font-weight: bold;
160 | }
161 | em {
162 | font-style: italic;
163 | }
164 | .muted {
165 | color: @grayLight;
166 | }
167 |
168 | // Abbreviations and acronyms
169 | abbr[title] {
170 | cursor: help;
171 | border-bottom: 1px dotted @grayLight;
172 | }
173 | abbr.initialism {
174 | font-size: 90%;
175 | text-transform: uppercase;
176 | }
177 |
178 | // Blockquotes
179 | blockquote {
180 | padding: 0 0 0 15px;
181 | margin: 0 0 @baseLineHeight;
182 | border-left: 5px solid @grayLighter;
183 | p {
184 | margin-bottom: 0;
185 | #font > .shorthand(16px,300,@baseLineHeight * 1.25);
186 | }
187 | small {
188 | display: block;
189 | line-height: @baseLineHeight;
190 | color: @grayLight;
191 | &:before {
192 | content: '\2014 \00A0';
193 | }
194 | }
195 |
196 | // Float right with text-align: right
197 | &.pull-right {
198 | float: right;
199 | padding-right: 15px;
200 | padding-left: 0;
201 | border-right: 5px solid @grayLighter;
202 | border-left: 0;
203 | p,
204 | small {
205 | text-align: right;
206 | }
207 | }
208 | }
209 |
210 | // Quotes
211 | q:before,
212 | q:after,
213 | blockquote:before,
214 | blockquote:after {
215 | content: "";
216 | }
217 |
218 | // Addresses
219 | address {
220 | display: block;
221 | margin-bottom: @baseLineHeight;
222 | font-style: normal;
223 | line-height: @baseLineHeight;
224 | }
225 |
226 | // Misc
227 | small {
228 | font-size: 100%;
229 | }
230 | cite {
231 | font-style: normal;
232 | }
233 |
--------------------------------------------------------------------------------
/bootstrap/src/less/utilities.less:
--------------------------------------------------------------------------------
1 | // UTILITY CLASSES
2 | // ---------------
3 |
4 | // Quick floats
5 | .pull-right {
6 | float: right;
7 | }
8 | .pull-left {
9 | float: left;
10 | }
11 |
12 | // Toggling content
13 | .hide {
14 | display: none;
15 | }
16 | .show {
17 | display: block;
18 | }
19 |
20 | // Visibility
21 | .invisible {
22 | visibility: hidden;
23 | }
24 |
--------------------------------------------------------------------------------
/bootstrap/src/less/wells.less:
--------------------------------------------------------------------------------
1 | // WELLS
2 | // -----
3 |
4 | .well {
5 | min-height: 20px;
6 | padding: 19px;
7 | margin-bottom: 20px;
8 | background-color: #f5f5f5;
9 | border: 1px solid #eee;
10 | border: 1px solid rgba(0,0,0,.05);
11 | .border-radius(4px);
12 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));
13 | blockquote {
14 | border-color: #ddd;
15 | border-color: rgba(0,0,0,.15);
16 | }
17 | }
18 |
19 | // Sizes
20 | .well-large {
21 | padding: 24px;
22 | .border-radius(6px);
23 | }
24 | .well-small {
25 | padding: 9px;
26 | .border-radius(3px);
27 | }
28 |
--------------------------------------------------------------------------------
/bootstrap/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bootstrap"
3 | , "description": "HTML, CSS, and JS toolkit from Twitter."
4 | , "version": "2.0.4"
5 | , "keywords": ["bootstrap", "css"]
6 | , "homepage": "http://twitter.github.com/bootstrap/"
7 | , "author": "Twitter Inc."
8 | , "scripts": { "test": "make test" }
9 | , "repository": {
10 | "type": "git"
11 | , "url": "https://github.com/twitter/bootstrap.git"
12 | }
13 | , "licenses": [
14 | {
15 | "type": "Apache-2.0"
16 | , "url": "http://www.apache.org/licenses/LICENSE-2.0"
17 | }
18 | ]
19 | , "devDependencies": {
20 | "uglify-js": "1.2.6"
21 | , "jshint": "0.6.1"
22 | , "recess": "1.0.3"
23 | , "connect": "2.1.3"
24 | }
25 | }
--------------------------------------------------------------------------------
/chrome-extension/background.js:
--------------------------------------------------------------------------------
1 | chrome.browserAction.onClicked.addListener(function(tab) {
2 | var action_url = "javascript:(function(){var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),l=d.location,e=encodeURIComponent,p=((e(s))?e(s):e(document.title))+' '+e(l.href);window.open('http://circular.io/?p='+p);})();";
3 | chrome.tabs.update(tab.id, {url: action_url});
4 | });
5 |
--------------------------------------------------------------------------------
/chrome-extension/icon_100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/chrome-extension/icon_100.png
--------------------------------------------------------------------------------
/chrome-extension/icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/chrome-extension/icon_128.png
--------------------------------------------------------------------------------
/chrome-extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Circular",
3 | "description": "The free tweet scheduling app.",
4 | "version": "1.0.3",
5 |
6 | "background": {
7 | "scripts": [
8 | "background.js"
9 | ]
10 | },
11 | "icons": {
12 | "128": "icon_128.png"
13 | },
14 | "browser_action": {
15 | "default_title": "Add to Circular.io",
16 | "default_icon": "icon_100.png"
17 | },
18 | "permissions": [
19 | "tabs",
20 | "*://*/*"
21 | ],
22 | "manifest_version": 2
23 | }
--------------------------------------------------------------------------------
/chrome/icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/chrome/icon_128.png
--------------------------------------------------------------------------------
/chrome/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Circular",
3 | "description": "The free tweet scheduling app.",
4 | "version": "1.0.2",
5 | "app": {
6 | "urls": [
7 | "*://circular.io/"
8 | ],
9 | "launch": {
10 | "web_url": "http://circular.io/"
11 | }
12 | },
13 | "icons": {
14 | "128": "icon_128.png"
15 | },
16 | "permissions": [
17 | ],
18 | "manifest_version": 2
19 | }
--------------------------------------------------------------------------------
/images/add_account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/add_account.png
--------------------------------------------------------------------------------
/images/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/background.jpg
--------------------------------------------------------------------------------
/images/circular-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/circular-header.png
--------------------------------------------------------------------------------
/images/circular-large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/circular-large.png
--------------------------------------------------------------------------------
/images/circular-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/circular-logo.png
--------------------------------------------------------------------------------
/images/circular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/circular.png
--------------------------------------------------------------------------------
/images/empty-timeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/empty-timeline.png
--------------------------------------------------------------------------------
/images/github-ribbon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/github-ribbon.png
--------------------------------------------------------------------------------
/images/network-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/network-icons.png
--------------------------------------------------------------------------------
/images/screenshot1-new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/screenshot1-new.png
--------------------------------------------------------------------------------
/images/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/screenshot1.png
--------------------------------------------------------------------------------
/images/screenshot2-new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/screenshot2-new.png
--------------------------------------------------------------------------------
/images/screenshot3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/screenshot3.png
--------------------------------------------------------------------------------
/images/twitter-bird.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julien-c/Circular/10baba5552703eccf2a035133d39bd3540682744/images/twitter-bird.png
--------------------------------------------------------------------------------
/js/bookmarklet-src.js:
--------------------------------------------------------------------------------
1 | var d = document,
2 | w = window,
3 | e = w.getSelection,
4 | k = d.getSelection,
5 | x = d.selection,
6 | s = (e ? e() : (k) ? k() : (x ? x.createRange().text : 0)),
7 | l = d.location,
8 | e = encodeURIComponent,
9 | p = ((e(s)) ? e(s) : e(document.title)) + ' ' + e(l.href);
10 |
11 | window.open('http://circular.io/?p=' + p);
12 |
13 |
14 | /*
15 |
16 | Output from http://ted.mielczarek.org/code/mozilla/bookmarklet.html :
17 |
18 |
78 |
79 |
80 | Add to Circular
19 |
20 | */
--------------------------------------------------------------------------------
/js/grunt.js:
--------------------------------------------------------------------------------
1 | /*global module:false*/
2 | module.exports = function(grunt) {
3 |
4 | // Project configuration.
5 | grunt.initConfig({
6 | meta: {
7 | version: '1.1',
8 | banner: '/*! Circular - v<%= meta.version %> - ' +
9 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
10 | ' * http://circular.io/\n' +
11 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> ' +
12 | 'Julien Chaumond; Licensed MIT \n */\n\n'
13 | },
14 | concat: {
15 | dist: {
16 | src: [
17 | 'vendor/jquery-1.7.2.min.js',
18 | '../bootstrap/js/bootstrap.js',
19 | 'vendor/jquery-ui-1.8.22.custom.min.js',
20 | 'vendor/jquery.hotkeys.js',
21 | 'vendor/jquery.filedrop.js',
22 | 'vendor/spin.min.js',
23 | 'vendor/jstz.min.js',
24 | 'vendor/mustache.js',
25 | 'vendor/underscore.js',
26 | 'vendor/backbone.js',
27 | 'vendor/twitter-text-1.11.0.min.js',
28 | 'src/bootstrap.js',
29 | 'src/utils.js',
30 | 'src/models/*.js',
31 | 'src/collections/*.js',
32 | 'src/views/*.js',
33 | 'src/app.js',
34 | 'src/start.js',
35 | ],
36 | dest: 'circular.js'
37 | }
38 | },
39 | min: {
40 | dist: {
41 | src: ['', ''],
42 | dest: 'circular.min.js'
43 | }
44 | },
45 | watch: {
46 | files: '',
47 | tasks: 'concat min'
48 | },
49 | uglify: {}
50 | });
51 |
52 | // Default task.
53 | grunt.registerTask('default', 'concat min');
54 |
55 | };
56 |
--------------------------------------------------------------------------------
/js/src/app.js:
--------------------------------------------------------------------------------
1 | Circular.App = {
2 | /* This is simple app-level jQuery stuff for which Backbone seems overkill */
3 | initialize: function() {
4 |
5 | var spinner = new Spinner({width: 3, color: '#222', speed: 1, trail: 60, hwaccel: true}).spin($('#spinner').get(0));
6 |
7 | /* Sign in with Twitter */
8 |
9 | $.get("api/oauth.php", null, function(data){
10 | spinner.stop();
11 | if (data && data.id) {
12 | // We have a signed-in account
13 | $(".not-logged-in").hide();
14 | $(".logged-in").show();
15 | // Store logged in data:
16 | Circular.account = data.id;
17 | Circular.users = data.users;
18 | // Select the first user by default:
19 | // Too bad there's no _.first() function that works on an object-type collection.
20 | var i = 1;
21 | _.each(Circular.users, function(user){
22 | if (i == 1) {
23 | user.selected = 'selected';
24 | }
25 | i++;
26 | });
27 | // Trigger "logged in" event:
28 | Circular.events.trigger('loggedin');
29 | }
30 | else {
31 | // Else, we'll just update the posts counter:
32 | Circular.App.updateCounter();
33 | }
34 | });
35 |
36 | $(".signin").click(function(){
37 | $.ajax({
38 | url: "api/oauth.php?start=1",
39 | success: function(data){
40 | if (data && data.authurl) {
41 | // Start the OAuth dance:
42 | window.location = data.authurl;
43 | }
44 | },
45 | error: function(data){
46 | new Circular.Views.Alert({type: "alert-error", content: "Unknown Twitter API error"});
47 | }
48 | });
49 | });
50 |
51 | $(".logout").click(function(e){
52 | e.preventDefault();
53 | $.ajax({
54 | url: "api/oauth.php?wipe=1",
55 | success: function(){
56 | window.location.reload();
57 | }
58 | });
59 | });
60 |
61 | /* Main Navigation */
62 |
63 | $(".link-to-settings").live('click', function(){
64 | $(".composer, .posts").hide();
65 | $(".settings").show();
66 | });
67 |
68 | $(".link-to-dashboard").live('click', function(){
69 | $(".composer, .posts").show();
70 | $(".settings").hide();
71 | });
72 |
73 | /* Twitter Bootstrap JS */
74 |
75 | $("body").tooltip({
76 | selector: '[rel=tooltip]',
77 | animation: false
78 | });
79 |
80 | Circular.events.on('button:setstate', function(btn, state){
81 | btn.button(state);
82 | });
83 |
84 |
85 | /* jQuery Hotkeys */
86 |
87 | $("#textarea").bind('keydown', 'meta+return', function(){
88 | $("#addtoposts").click();
89 | });
90 |
91 | /* Event tracking */
92 | Circular.events.on('track:post', function(){
93 | Circular.App.trackEvent('Posts', 'add');
94 | });
95 | },
96 | trackEvent: function(category, action, label, value, noninteraction){
97 | // Wrapper to Google Analytics' event tracking, if present on the page:
98 | if (typeof _gaq !== 'undefined') {
99 | _gaq.push(['_trackEvent', category, action]);
100 | }
101 | },
102 | updateCounter: function(){
103 | $.getJSON("api/counter", function(data){
104 | $('.counter').text(data.count);
105 | });
106 | }
107 | }
108 |
109 |
--------------------------------------------------------------------------------
/js/src/bootstrap.js:
--------------------------------------------------------------------------------
1 | window.Circular = {
2 | Models: {},
3 | Collections: {},
4 | Views: {},
5 | events: _.clone(Backbone.Events),
6 | account: null,
7 | users: null
8 | };
9 |
10 |
--------------------------------------------------------------------------------
/js/src/collections/posts.js:
--------------------------------------------------------------------------------
1 | Circular.Collections.Posts = Backbone.Collection.extend({
2 | url: "api/posts",
3 | model: Circular.Models.Post,
4 | initialize: function(){
5 | Circular.events.on('ui:posts:sort', this.refreshPostsOrder, this);
6 | },
7 | refreshPostsOrder: function(order){
8 | // Refresh sorting order after jQuery UI sorting:
9 | // (Too bad Underscore doesn't have a function to order an array of objects by an array of one of their attributes)
10 | // @see https://github.com/documentcloud/underscore/issues/692
11 | this.models = this.sortBy(function(post){
12 | return _.indexOf(order, post.id);
13 | });
14 | Circular.events.trigger('posts:sort', order);
15 | },
16 | groupByUser: function(){
17 | var users = {};
18 | _.each(Circular.users, function(value, key){
19 | users[key] = [];
20 | });
21 | var out = this.groupBy(function(post){
22 | return post.get('user');
23 | });
24 | out = _.extend(users, out);
25 | return out;
26 | }
27 | });
28 |
29 |
--------------------------------------------------------------------------------
/js/src/models/post.js:
--------------------------------------------------------------------------------
1 | Circular.Models.Post = Backbone.Model.extend({
2 | initialize: function(attributes){
3 | if (this.get('status') == "") {
4 | this.set('status', this.randomQuote());
5 | }
6 | // We don't set the time now as it will be set after refreshing posting times
7 | },
8 | randomQuote: function(){
9 | var quotes = [
10 | "All wrong-doing arises because of mind. If mind is transformed can wrong-doing remain?",
11 | "Ambition is like love, impatient both of delays and rivals.",
12 | "An idea that is developed and put into action is more important than an idea that exists only as an idea.",
13 | "Thousands of candles can be lit from a single candle, and the life of the candle will not be shortened.",
14 | "Do not dwell in the past, do not dream of the future, concentrate the mind on the present moment.",
15 | "Do not overrate what you have received, nor envy others. He who envies others does not obtain peace of mind.",
16 | "The mind is everything. What you think you become.",
17 | "No one saves us but ourselves. No one can and no one may. We ourselves must walk the path.",
18 | "Peace comes from within. Do not seek it without.",
19 | "The only real failure in life is not to be true to the best one knows.",
20 | "The way is not in the sky. The way is in the heart.",
21 | "There are only two mistakes one can make along the road to truth; not going all the way, and not starting.",
22 | "There has to be evil so that good can prove its purity above it."
23 | ];
24 |
25 | quotes = _.map(quotes, function(quote){
26 | quote += " ~ via @CircularIO";
27 | return quote;
28 | });
29 |
30 | return quotes[Math.floor(Math.random()*quotes.length)];
31 | }
32 | });
33 |
34 |
--------------------------------------------------------------------------------
/js/src/models/poststimes.js:
--------------------------------------------------------------------------------
1 | Circular.Models.PostsTimes = Backbone.Model.extend({
2 | urlRoot: "api/times",
3 | initialize: function(options){
4 | this.posts = options.posts;
5 | this.settings = options.settings;
6 |
7 | Circular.events.on('posts:sort', this.computePostsTimes, this);
8 | Circular.events.on('settings:saved', this.computePostsTimes, this);
9 | this.posts.on('add', this.computePostsTimes, this);
10 | this.posts.on('remove', this.computePostsTimes, this);
11 | },
12 | computePostsTimes: function(){
13 | _.each(this.posts.groupByUser(), function(posts, user){
14 | this.computePostsTimesForUser(posts, user);
15 | }, this);
16 |
17 | this.posts.trigger('poststimes:refresh');
18 | this.save();
19 | },
20 | computePostsTimesForUser: function(posts, user){
21 | var date = new Date();
22 | var secondsUpToNowToday = Circular.Utils.Time.secondsFrom24HourTime({
23 | hour: date.getHours(),
24 | minute: date.getMinutes()
25 | });
26 |
27 | var times = this.settings.get('times');
28 | // Let's find which scheduled time is the next one:
29 | var i = _.sortedIndex(_.map(times, Circular.Utils.Time.secondsFrom12HourTime), secondsUpToNowToday);
30 | // So times[i] is the next scheduled time.
31 | // More precisely: times[i % times.length]
32 |
33 | var day = 0;
34 |
35 | _.each(posts, function(post){
36 | if ((i % times.length == 0) && (i > 0)){
37 | day++;
38 | }
39 | // This post must be scheduled to be sent in `day` days, at time `times[i % times.length]`:
40 |
41 | // Now compute the UNIX timestamp for this time:
42 | var then = new Date(date.getFullYear(), date.getMonth(), date.getDate() + day, 0, 0, Circular.Utils.Time.secondsFrom12HourTime(times[i % times.length]));
43 | // We use the fact that this method "expands" parameters.
44 | // (ie: you can specify 32 days or 5000 seconds, parameters will overflow)
45 | // @todo: Check that this is documented and standard.
46 | var timestamp = Circular.Utils.Time.generateUnixTimestamp(then);
47 |
48 | post.set({time: timestamp});
49 |
50 | i++;
51 | });
52 | },
53 | save: function(){
54 | // Only keep id and time from the Posts collection:
55 | var posts = [];
56 | this.posts.each(function(post){
57 | posts.push({
58 | id: post.id,
59 | time: post.get('time')
60 | });
61 | });
62 |
63 | // Now save to server:
64 | $.ajax({
65 | url: this.urlRoot,
66 | type: 'POST',
67 | data: JSON.stringify({posts: posts}),
68 | contentType: 'application/json',
69 | dataType: 'json',
70 | error: function(){
71 | new Circular.Views.Alert({type: "alert-error", content: "Something went wrong while updating your posts..."});
72 | }
73 | });
74 | }
75 | });
76 |
77 |
--------------------------------------------------------------------------------
/js/src/models/settings.js:
--------------------------------------------------------------------------------
1 | Circular.Models.Settings = Backbone.Model.extend({
2 | urlRoot: "api/settings",
3 | initialize: function(){
4 | this.fetch();
5 |
6 | // Defaults:
7 | if (!this.has('timezone')) {
8 | // Notice: The timezone is not actually used right now.
9 | this.set('timezone', this.defaultTimezone()).saveLocalSettings();
10 | }
11 | if (!this.has('times')) {
12 | this.set('times', this.defaultTimes()).saveLocalSettings();
13 | }
14 | },
15 | defaultTimezone: function(){
16 | // Automatic Timezone Detection
17 | var timezone = jstz.determine();
18 | return timezone.name();
19 | },
20 | defaultTimes: function(){
21 | // Like Buffer, we create 4 random times, 2 in the AM, 2 in the PM:
22 | var times = [
23 | {hour:9, minute:Math.floor(Math.random()*60), ampm:"am"},
24 | {hour:11, minute:Math.floor(Math.random()*60), ampm:"am"},
25 | {hour:3, minute:Math.floor(Math.random()*60), ampm:"pm"},
26 | {hour:5, minute:Math.floor(Math.random()*60), ampm:"pm"}
27 | ];
28 | return times;
29 | },
30 | // @ see http://stackoverflow.com/questions/11817015/is-it-good-practice-to-override-fetch-and-save-directly-from-the-model
31 | fetch: function(){
32 | this.set('timezone', localStorage['timezone']);
33 | if (localStorage['times']) {
34 | this.set('times', JSON.parse(localStorage['times']));
35 | }
36 | $.ajax({
37 | url: this.urlRoot,
38 | type: 'GET',
39 | context: this,
40 | success: function(data){
41 | this.set('email', data.email);
42 | Circular.events.trigger('settings:fetched');
43 | }
44 | });
45 | },
46 | save: function(){
47 | this.saveLocalSettings();
48 | this.saveServerSettings();
49 | },
50 | saveLocalSettings: function(){
51 | localStorage['timezone'] = this.get('timezone');
52 | localStorage['times'] = JSON.stringify(this.get('times'));
53 | },
54 | saveServerSettings: function(){
55 | Circular.Utils.postJSON(this.urlRoot, {email: this.get('email')});
56 | }
57 | });
58 |
59 |
--------------------------------------------------------------------------------
/js/src/start.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function(){
2 |
3 | // Initialize App
4 | Circular.App.initialize();
5 |
6 |
7 | Circular.events.on('loggedin', function(){
8 |
9 | // Initialize Settings
10 | var settings = new Circular.Models.Settings();
11 | new Circular.Views.Settings({model: settings});
12 |
13 | // Initialize Composer and Posts
14 | var posts = new Circular.Collections.Posts();
15 | posts.fetch();
16 | new Circular.Views.Composer({collection: posts});
17 | new Circular.Views.Posts({collection: posts});
18 |
19 | // Initialize PostsTimes
20 | var poststimes = new Circular.Models.PostsTimes({posts: posts, settings: settings});
21 | });
22 |
23 | });
24 |
25 |
--------------------------------------------------------------------------------
/js/src/utils.js:
--------------------------------------------------------------------------------
1 | Circular.Utils = {
2 | getParameterByName: function(name) {
3 | // @see http://stackoverflow.com/a/901144/593036
4 | name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); var regexS = "[\\?&]" + name + "=([^]*)"; var regex = new RegExp(regexS); var results = regex.exec(window.location.search); if(results == null) return ""; else return decodeURIComponent(results[1].replace(/\+/g, " "));
5 | },
6 | postJSON: function(url, data){
7 | return $.ajax({
8 | url: url,
9 | type: 'POST',
10 | data: JSON.stringify(data),
11 | contentType: 'application/json',
12 | dataType: 'json'
13 | });
14 | }
15 | };
16 |
17 | Circular.Utils.Time = {
18 | generateUnixTimestamp: function(then){
19 | return Math.floor(then.getTime() / 1000);
20 | },
21 | secondsFrom12HourTime: function(time){
22 | // 12am = Midnight, 1am, ..., 12pm = Noon, 1pm, ...
23 | // @see http://en.wikipedia.org/wiki/12-hour_clock
24 | var hour = time.hour % 12;
25 | var seconds = time.minute*60 + hour*60*60;
26 | if (time.ampm == "pm") {
27 | seconds += 12*60*60;
28 | }
29 | return seconds;
30 | },
31 | secondsFrom24HourTime: function(time){
32 | return time.minute*60 + time.hour*60*60;
33 | },
34 | get12HourTimeFrom24HourTime: function(hour, minute){
35 | var _12HourTime = {
36 | hour: hour % 12,
37 | minute: minute,
38 | ampm: (hour < 12) ? "am" : "pm"
39 | };
40 | if (_12HourTime.hour == 0) {
41 | _12HourTime.hour = 12;
42 | }
43 | return _12HourTime;
44 | },
45 | format12HourTime: function(time){
46 | var minute = time.minute;
47 | if (minute < 10) {
48 | minute = '0' + minute;
49 | }
50 | return time.hour + ":" + minute + " " + time.ampm;
51 | },
52 | formatDay: function(day){
53 | // `day` is the offset in days from today (local time)
54 | var heading;
55 | if (day == 0){
56 | heading = "Today";
57 | }
58 | else if (day == 1){
59 | heading = "Tomorrow";
60 | }
61 | else {
62 | var date = new Date(+new Date + 1000*60*60*24*day);
63 | var DaysOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
64 | var DaysOfMonth = ["","1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th","11th","12th","13th","14th","15th","16th","17th","18th","19th","20th","21st","22nd","23rd","24th","25th","26th","27th","28th","29th","30th","31st"];
65 | var Months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
66 | heading = DaysOfWeek[date.getDay()] + " " + DaysOfMonth[date.getDate()] + " " + Months[date.getMonth()];
67 | }
68 | return heading;
69 | },
70 | local12HourTimeAndDayFromTimeStamp: function(timestamp){
71 | var date = new Date(timestamp * 1000);
72 | var now = new Date();
73 | var local12HourTime = this.get12HourTimeFrom24HourTime(date.getHours(), date.getMinutes());
74 |
75 | // Now let's compute the day offset (local time):
76 | var localMidnightToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
77 | var localMidnightTodayTimestamp = this.generateUnixTimestamp(localMidnightToday);
78 | var day = Math.floor((timestamp - localMidnightTodayTimestamp) / (24*60*60));
79 |
80 | local12HourTime.day = day;
81 |
82 | return local12HourTime;
83 | }
84 | };
85 |
86 |
--------------------------------------------------------------------------------
/js/src/views/alert.js:
--------------------------------------------------------------------------------
1 | Circular.Views.Alert = Backbone.View.extend({
2 | el: "#alerts",
3 | template: $("#tpl-alert").html(),
4 | initialize: function(options){
5 | this.type = options.type;
6 | this.content = options.content;
7 | this.render();
8 | },
9 | render: function(){
10 | var output = Mustache.render(
11 | this.template,
12 | {type: this.type, content: this.content}
13 | );
14 | $(output).prependTo(this.$el).delay(2000).fadeOut();
15 | return this;
16 | }
17 | });
18 |
19 |
--------------------------------------------------------------------------------
/js/src/views/settings.js:
--------------------------------------------------------------------------------
1 | Circular.Views.Settings = Backbone.View.extend({
2 | el: $(".settings"),
3 | templateSettingsTime: $("#tpl-time").html(),
4 | events: {
5 | "click #addtime": "addtime",
6 | "click .settings-time button.close": "removetime",
7 | "click #savesettings": "saveSettings"
8 | },
9 | initialize: function(){
10 | Circular.events.on('settings:fetched', this.render, this);
11 | },
12 | addtime: function(e){
13 | e.preventDefault();
14 | var output = Mustache.render(this.templateSettingsTime, {});
15 | this.$(".times").append(output);
16 | },
17 | removetime: function(e){
18 | e.preventDefault();
19 | $(e.target).closest(".settings-time").fadeOut('fast', function(){
20 | $(this).remove();
21 | });
22 | },
23 | render: function(){
24 | // Set frontend to current values from model:
25 | this.renderTimezone();
26 | this.renderTimes();
27 | this.renderEmail();
28 | },
29 | renderTimezone: function(){
30 | this.$("select.timezone").val(this.model.get('timezone'));
31 | },
32 | renderTimes: function(){
33 | // First clear displayed times (as we use this function when refreshing times after saving, to render them ordered):
34 | this.$(".times").empty();
35 |
36 | _.each(this.model.get('times'), function(time){
37 | var output = Mustache.render(this.templateSettingsTime, {});
38 | var out = $(output);
39 | $("select.hour", out).val(time.hour);
40 | $("select.minute", out).val(time.minute);
41 | $("select.ampm", out).val(time.ampm);
42 | this.$(".times").append(out);
43 | }, this);
44 | },
45 | renderEmail: function(){
46 | this.$('input.email').val(this.model.get('email'));
47 | },
48 | saveSettings: function(e){
49 | var btn = $(e.target);
50 | Circular.events.trigger('button:setstate', btn, 'loading');
51 | setTimeout(function(){
52 | Circular.events.trigger('button:setstate', btn, 'reset');
53 | }, 500);
54 |
55 | this.model.set('timezone', this.$("select.timezone").val());
56 |
57 | var times = [];
58 | this.$("p.settings-time").each(function(){
59 | times.push({
60 | hour: $(this).find("select.hour").val(),
61 | minute: $(this).find("select.minute").val(),
62 | ampm: $(this).find("select.ampm").val(),
63 | });
64 | });
65 | // Sort times by chronological order:
66 | times = _.sortBy(times, Circular.Utils.Time.secondsFrom12HourTime);
67 | this.model.set('times', times);
68 |
69 | this.model.set('email', this.$('input.email').val());
70 |
71 | // Actual saving:
72 | this.model.save();
73 | // Refresh displayed times in Settings:
74 | this.renderTimes();
75 | // Trigger event so that posting times in the Timeline view can be refreshed:
76 | Circular.events.trigger('settings:saved');
77 | }
78 | });
79 |
80 |
--------------------------------------------------------------------------------
/js/vendor/jquery.hotkeys.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery Hotkeys Plugin
3 | * Copyright 2010, John Resig
4 | * Dual licensed under the MIT or GPL Version 2 licenses.
5 | *
6 | * Based upon the plugin by Tzury Bar Yochay:
7 | * http://github.com/tzuryby/hotkeys
8 | *
9 | * Original idea by:
10 | * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
11 | */
12 |
13 | (function(jQuery){
14 |
15 | jQuery.hotkeys = {
16 | version: "0.8",
17 |
18 | specialKeys: {
19 | 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
20 | 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
21 | 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
22 | 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
23 | 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
24 | 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
25 | 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
26 | },
27 |
28 | shiftNums: {
29 | "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
30 | "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
31 | ".": ">", "/": "?", "\\": "|"
32 | }
33 | };
34 |
35 | function keyHandler( handleObj ) {
36 | // Only care when a possible input has been specified
37 | if ( typeof handleObj.data !== "string" ) {
38 | return;
39 | }
40 |
41 | var origHandler = handleObj.handler,
42 | keys = handleObj.data.toLowerCase().split(" ");
43 |
44 | handleObj.handler = function( event ) {
45 | // Don't fire in text-accepting inputs that we didn't directly bind to
46 | if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
47 | event.target.type === "text") ) {
48 | return;
49 | }
50 |
51 | // Keypress represents characters, not special keys
52 | var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
53 | character = String.fromCharCode( event.which ).toLowerCase(),
54 | key, modif = "", possible = {};
55 |
56 | // check combinations (alt|ctrl|shift+anything)
57 | if ( event.altKey && special !== "alt" ) {
58 | modif += "alt+";
59 | }
60 |
61 | if ( event.ctrlKey && special !== "ctrl" ) {
62 | modif += "ctrl+";
63 | }
64 |
65 | // TODO: Need to make sure this works consistently across platforms
66 | if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
67 | modif += "meta+";
68 | }
69 |
70 | if ( event.shiftKey && special !== "shift" ) {
71 | modif += "shift+";
72 | }
73 |
74 | if ( special ) {
75 | possible[ modif + special ] = true;
76 |
77 | } else {
78 | possible[ modif + character ] = true;
79 | possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
80 |
81 | // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
82 | if ( modif === "shift+" ) {
83 | possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
84 | }
85 | }
86 |
87 | for ( var i = 0, l = keys.length; i < l; i++ ) {
88 | if ( possible[ keys[i] ] ) {
89 | return origHandler.apply( this, arguments );
90 | }
91 | }
92 | };
93 | }
94 |
95 | jQuery.each([ "keydown", "keyup", "keypress" ], function() {
96 | jQuery.event.special[ this ] = { add: keyHandler };
97 | });
98 |
99 | })( jQuery );
--------------------------------------------------------------------------------
/js/vendor/jstz.min.js:
--------------------------------------------------------------------------------
1 | var jstz=function(){var b=function(a){a=-a.getTimezoneOffset();return null!==a?a:0},c=function(){return b(new Date(2010,0,1,0,0,0,0))},f=function(){return b(new Date(2010,5,1,0,0,0,0))},e=function(){var a=c(),d=f(),b=c()-f();return new jstz.TimeZone(jstz.olson.timezones[0>b?a+",1":0>1):c.left+e)+"px",top:(c.top=="auto"?i.y-h.y+(a.offsetHeight>>1):c.top+e)+"px"})),d.setAttribute("aria-role","progressbar"),b.lines(d,b.opts);if(!f){var j=0,k=c.fps,m=k/c.speed,o=(1-c.opacity)/(m*c.trail/100),p=m/c.lines;!function q(){j++;for(var a=c.lines;a;a--){var e=Math.max(1-(j+a*p)%m*o,c.opacity);b.opacity(d,c.lines-a,e,c)}b.timeout=b.el&&setTimeout(q,~~(1e3/k))}()}return b},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=c),this},lines:function(a,b){function e(a,d){return l(g(),{position:"absolute",width:b.length+b.width+"px",height:b.width+"px",background:a,boxShadow:d,transformOrigin:"left",transform:"rotate("+~~(360/b.lines*c+b.rotate)+"deg) translate("+b.radius+"px"+",0)",borderRadius:(b.width>>1)+"px"})}var c=0,d;for(;c',b)}var b=l(g("group"),{behavior:"url(#default#VML)"});!k(b,"transform")&&b.adj?(i.addRule(".spin-vml","behavior:url(#default#VML)"),p.prototype.lines=function(b,c){function f(){return l(a("group",{coordsize:e+" "+e,coordorigin:-d+" "+ -d}),{width:e,height:e})}function k(b,e,g){h(i,h(l(f(),{rotation:360/c.lines*b+"deg",left:~~e}),h(l(a("roundrect",{arcsize:1}),{width:d,height:c.width,left:c.radius,top:-c.width>>1,filter:g}),a("fill",{color:c.color,opacity:c.opacity}),a("stroke",{opacity:0}))))}var d=c.length+c.width,e=2*d,g=-(c.width+c.length)*2+"px",i=l(f(),{position:"absolute",top:g,left:g}),j;if(c.shadow)for(j=1;j<=c.lines;j++)k(j,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(j=1;j<=c.lines;j++)k(j);return h(b,i)},p.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d