twigg-app 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/data/quips.yml +45 -0
- data/lib/twigg-app/app/quips.rb +13 -0
- data/lib/twigg-app/app/routes.rb +43 -0
- data/lib/twigg-app/app/server.rb +146 -0
- data/lib/twigg-app/app/version.rb +5 -0
- data/lib/twigg-app/app.rb +14 -0
- data/lib/twigg-app.rb +5 -0
- data/public/application.js +106 -0
- data/public/favicon.ico +0 -0
- data/public/favicon.png +0 -0
- data/public/vendor/bootstrap/CNAME +1 -0
- data/public/vendor/bootstrap/CONTRIBUTING.md +66 -0
- data/public/vendor/bootstrap/Gruntfile.js +195 -0
- data/public/vendor/bootstrap/LICENSE +176 -0
- data/public/vendor/bootstrap/README.md +139 -0
- data/public/vendor/bootstrap/_config.yml +28 -0
- data/public/vendor/bootstrap/_includes/ads.html +1 -0
- data/public/vendor/bootstrap/_includes/footer.html +33 -0
- data/public/vendor/bootstrap/_includes/header.html +43 -0
- data/public/vendor/bootstrap/_includes/nav-components.html +135 -0
- data/public/vendor/bootstrap/_includes/nav-css.html +77 -0
- data/public/vendor/bootstrap/_includes/nav-customize.html +40 -0
- data/public/vendor/bootstrap/_includes/nav-getting-started.html +28 -0
- data/public/vendor/bootstrap/_includes/nav-javascript.html +88 -0
- data/public/vendor/bootstrap/_includes/nav-main.html +32 -0
- data/public/vendor/bootstrap/_includes/old-bs-docs.html +8 -0
- data/public/vendor/bootstrap/_includes/social-buttons.html +16 -0
- data/public/vendor/bootstrap/_layouts/customize.html +52 -0
- data/public/vendor/bootstrap/_layouts/default.html +72 -0
- data/public/vendor/bootstrap/_layouts/home.html +43 -0
- data/public/vendor/bootstrap/assets/css/docs.css +896 -0
- data/public/vendor/bootstrap/assets/css/pygments-manni.css +66 -0
- data/public/vendor/bootstrap/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
- data/public/vendor/bootstrap/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
- data/public/vendor/bootstrap/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
- data/public/vendor/bootstrap/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
- data/public/vendor/bootstrap/assets/ico/favicon.png +0 -0
- data/public/vendor/bootstrap/assets/js/application.js +82 -0
- data/public/vendor/bootstrap/assets/js/customizer.js +175 -0
- data/public/vendor/bootstrap/assets/js/holder.js +419 -0
- data/public/vendor/bootstrap/assets/js/html5shiv.js +8 -0
- data/public/vendor/bootstrap/assets/js/jquery.bbq.min.js +1287 -0
- data/public/vendor/bootstrap/assets/js/jquery.js +5 -0
- data/public/vendor/bootstrap/assets/js/jszip.js +1425 -0
- data/public/vendor/bootstrap/assets/js/less.js +9 -0
- data/public/vendor/bootstrap/assets/js/respond.min.js +6 -0
- data/public/vendor/bootstrap/assets/js/uglify.js +14 -0
- data/public/vendor/bootstrap/bower.json +11 -0
- data/public/vendor/bootstrap/browserstack.json +37 -0
- data/public/vendor/bootstrap/components.html +2555 -0
- data/public/vendor/bootstrap/composer.json +20 -0
- data/public/vendor/bootstrap/css.html +2276 -0
- data/public/vendor/bootstrap/customize.html +1480 -0
- data/public/vendor/bootstrap/dist/css/bootstrap.css +5579 -0
- data/public/vendor/bootstrap/dist/css/bootstrap.min.css +9 -0
- data/public/vendor/bootstrap/dist/js/bootstrap.js +1993 -0
- data/public/vendor/bootstrap/dist/js/bootstrap.min.js +6 -0
- data/public/vendor/bootstrap/getting-started.html +375 -0
- data/public/vendor/bootstrap/index.html +16 -0
- data/public/vendor/bootstrap/javascript.html +1904 -0
- data/public/vendor/bootstrap/js/affix.js +126 -0
- data/public/vendor/bootstrap/js/alert.js +98 -0
- data/public/vendor/bootstrap/js/button.js +109 -0
- data/public/vendor/bootstrap/js/carousel.js +217 -0
- data/public/vendor/bootstrap/js/collapse.js +179 -0
- data/public/vendor/bootstrap/js/dropdown.js +154 -0
- data/public/vendor/bootstrap/js/modal.js +244 -0
- data/public/vendor/bootstrap/js/popover.js +117 -0
- data/public/vendor/bootstrap/js/scrollspy.js +158 -0
- data/public/vendor/bootstrap/js/tab.js +135 -0
- data/public/vendor/bootstrap/js/tests/index.html +52 -0
- data/public/vendor/bootstrap/js/tests/phantom.js +63 -0
- data/public/vendor/bootstrap/js/tests/server.js +14 -0
- data/public/vendor/bootstrap/js/tests/unit/affix.js +25 -0
- data/public/vendor/bootstrap/js/tests/unit/alert.js +62 -0
- data/public/vendor/bootstrap/js/tests/unit/button.js +116 -0
- data/public/vendor/bootstrap/js/tests/unit/carousel.js +87 -0
- data/public/vendor/bootstrap/js/tests/unit/collapse.js +164 -0
- data/public/vendor/bootstrap/js/tests/unit/dropdown.js +219 -0
- data/public/vendor/bootstrap/js/tests/unit/modal.js +177 -0
- data/public/vendor/bootstrap/js/tests/unit/phantom.js +69 -0
- data/public/vendor/bootstrap/js/tests/unit/popover.js +133 -0
- data/public/vendor/bootstrap/js/tests/unit/scrollspy.js +37 -0
- data/public/vendor/bootstrap/js/tests/unit/tab.js +86 -0
- data/public/vendor/bootstrap/js/tests/unit/tooltip.js +437 -0
- data/public/vendor/bootstrap/js/tests/unit/transition.js +13 -0
- data/public/vendor/bootstrap/js/tests/vendor/jquery.js +5 -0
- data/public/vendor/bootstrap/js/tests/vendor/qunit.css +232 -0
- data/public/vendor/bootstrap/js/tests/vendor/qunit.js +1510 -0
- data/public/vendor/bootstrap/js/tooltip.js +382 -0
- data/public/vendor/bootstrap/js/transition.js +56 -0
- data/public/vendor/bootstrap/less/alerts.less +71 -0
- data/public/vendor/bootstrap/less/badges.less +51 -0
- data/public/vendor/bootstrap/less/bootstrap.less +63 -0
- data/public/vendor/bootstrap/less/breadcrumbs.less +23 -0
- data/public/vendor/bootstrap/less/button-groups.less +244 -0
- data/public/vendor/bootstrap/less/buttons.less +159 -0
- data/public/vendor/bootstrap/less/carousel.less +204 -0
- data/public/vendor/bootstrap/less/close.less +33 -0
- data/public/vendor/bootstrap/less/code.less +56 -0
- data/public/vendor/bootstrap/less/component-animations.less +29 -0
- data/public/vendor/bootstrap/less/dropdowns.less +176 -0
- data/public/vendor/bootstrap/less/forms.less +332 -0
- data/public/vendor/bootstrap/less/grid.less +340 -0
- data/public/vendor/bootstrap/less/input-groups.less +127 -0
- data/public/vendor/bootstrap/less/jumbotron.less +29 -0
- data/public/vendor/bootstrap/less/labels.less +54 -0
- data/public/vendor/bootstrap/less/list-group.less +88 -0
- data/public/vendor/bootstrap/less/media.less +56 -0
- data/public/vendor/bootstrap/less/mixins.less +693 -0
- data/public/vendor/bootstrap/less/modals.less +133 -0
- data/public/vendor/bootstrap/less/navbar.less +559 -0
- data/public/vendor/bootstrap/less/navs.less +228 -0
- data/public/vendor/bootstrap/less/normalize.less +396 -0
- data/public/vendor/bootstrap/less/pager.less +55 -0
- data/public/vendor/bootstrap/less/pagination.less +72 -0
- data/public/vendor/bootstrap/less/panels.less +128 -0
- data/public/vendor/bootstrap/less/popovers.less +133 -0
- data/public/vendor/bootstrap/less/print.less +100 -0
- data/public/vendor/bootstrap/less/progress-bars.less +99 -0
- data/public/vendor/bootstrap/less/responsive-utilities.less +149 -0
- data/public/vendor/bootstrap/less/scaffolding.less +111 -0
- data/public/vendor/bootstrap/less/tables.less +211 -0
- data/public/vendor/bootstrap/less/thumbnails.less +42 -0
- data/public/vendor/bootstrap/less/tooltip.less +95 -0
- data/public/vendor/bootstrap/less/type.less +238 -0
- data/public/vendor/bootstrap/less/utilities.less +42 -0
- data/public/vendor/bootstrap/less/variables.less +607 -0
- data/public/vendor/bootstrap/less/wells.less +29 -0
- data/public/vendor/bootstrap/package.json +33 -0
- data/public/vendor/bootstrap-glyphicons/CHANGELOG.md +3 -0
- data/public/vendor/bootstrap-glyphicons/CNAME +1 -0
- data/public/vendor/bootstrap-glyphicons/CONTRIBUTING.md +54 -0
- data/public/vendor/bootstrap-glyphicons/LICENSE +19 -0
- data/public/vendor/bootstrap-glyphicons/README.md +61 -0
- data/public/vendor/bootstrap-glyphicons/_config.yml +12 -0
- data/public/vendor/bootstrap-glyphicons/composer.json +9 -0
- data/public/vendor/bootstrap-glyphicons/css/bootstrap-glyphicons.css +2 -0
- data/public/vendor/bootstrap-glyphicons/css/bootstrap.css +9 -0
- data/public/vendor/bootstrap-glyphicons/css/docs.css +160 -0
- data/public/vendor/bootstrap-glyphicons/fonts/glyphiconshalflings-regular.eot +0 -0
- data/public/vendor/bootstrap-glyphicons/fonts/glyphiconshalflings-regular.otf +0 -0
- data/public/vendor/bootstrap-glyphicons/fonts/glyphiconshalflings-regular.svg +175 -0
- data/public/vendor/bootstrap-glyphicons/fonts/glyphiconshalflings-regular.ttf +0 -0
- data/public/vendor/bootstrap-glyphicons/fonts/glyphiconshalflings-regular.woff +0 -0
- data/public/vendor/bootstrap-glyphicons/index.html +255 -0
- data/public/vendor/bootstrap-glyphicons/less/bootstrap-glyphicons.less +201 -0
- data/public/vendor/bootstrap-glyphicons/package.json +18 -0
- data/public/vendor/d3/LICENSE +26 -0
- data/public/vendor/d3/README.md +7 -0
- data/public/vendor/d3/bower.json +25 -0
- data/public/vendor/d3/d3.js +8810 -0
- data/public/vendor/d3/d3.min.js +5 -0
- data/public/vendor/jquery/README.md +11 -0
- data/public/vendor/jquery/bower.json +11 -0
- data/public/vendor/jquery/component.json +15 -0
- data/public/vendor/jquery/composer.json +35 -0
- data/public/vendor/jquery/jquery-migrate.js +511 -0
- data/public/vendor/jquery/jquery-migrate.min.js +3 -0
- data/public/vendor/jquery/jquery.js +8829 -0
- data/public/vendor/jquery/jquery.min.js +6 -0
- data/public/vendor/jquery/jquery.min.map +1 -0
- data/public/vendor/jquery/package.json +7 -0
- data/public/vendor/replacejs/CHANGELOG.md +3 -0
- data/public/vendor/replacejs/MIT-LICENSE.txt +20 -0
- data/public/vendor/replacejs/README.md +123 -0
- data/public/vendor/replacejs/bower.json +24 -0
- data/public/vendor/replacejs/primer.js +17 -0
- data/public/vendor/replacejs/primer.min.js +1 -0
- data/public/vendor/replacejs/replace.js +182 -0
- data/public/vendor/stupidtable.js +158 -0
- data/views/dashboard.haml +17 -0
- data/views/layout.haml +60 -0
- metadata +330 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
# Replace.js
|
2
|
+
|
3
|
+
Tool for adding simple AJAX functionality to a site. See the [demo] for example
|
4
|
+
use cases.
|
5
|
+
|
6
|
+
[demo]: http://causes.github.io/replacejs/demo/
|
7
|
+
|
8
|
+
## Background
|
9
|
+
|
10
|
+
After doing web development for a while, we noticed that most interactions we
|
11
|
+
added to the site involved catching a click event, performing an AJAX request,
|
12
|
+
and updating part of the page based on the result. This typically required a
|
13
|
+
lot of boilerplate JavaScript to set up click handlers. All that JavaScript
|
14
|
+
slowed down the initial page load, and until the page finished loading, things
|
15
|
+
didn't work right. It also required duplicating business and rendering logic on
|
16
|
+
the server and the client. The site was slow and our JS code was brittle and
|
17
|
+
difficult to test. We realized that one small library could implement this
|
18
|
+
pattern and replace most of our JS code base.
|
19
|
+
|
20
|
+
We weren't the first to reach a similar conclusion -- see Makinde Adeagbo's
|
21
|
+
excellent presentation on [Primer].
|
22
|
+
|
23
|
+
[Primer]: http://blip.tv/jsconf/makinde-adeagbo-primer-facebook-s-2k-of-javascript-to-power-almost-all-interactions-3858673
|
24
|
+
|
25
|
+
## Requirements
|
26
|
+
|
27
|
+
Any server-side language should do, though we use Rails. jQuery must be
|
28
|
+
present.
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
Put the contents of `primer.min.js` in a <script> tag in the document
|
33
|
+
header. Link to `replace.js` at the bottom of the document body, below jQuery.
|
34
|
+
|
35
|
+
```html
|
36
|
+
<script src="/path/to/replace.js"></script>
|
37
|
+
```
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
You AJAX-ify a link by giving it a `data-replace` attribute containing a jQuery
|
42
|
+
selector. When the link is clicked, its closest ancestor matching the selector
|
43
|
+
will be replaced by the result of an AJAX request to the link's `href`.
|
44
|
+
|
45
|
+
```html
|
46
|
+
<div class="container">
|
47
|
+
<a href="/foo" data-replace=".container">Foo</a>
|
48
|
+
</div>
|
49
|
+
```
|
50
|
+
|
51
|
+
The link can replace itself. A convenient selector for doing so is '*'.
|
52
|
+
|
53
|
+
```html
|
54
|
+
<a href="/foo" data-replace="*">Foo</a>
|
55
|
+
```
|
56
|
+
|
57
|
+
It is up to you to ensure that the server returns an appropriate partial in
|
58
|
+
response to that AJAX request. If the URL also represents a full page that
|
59
|
+
might be visited normally (e.g. linked externally), add the `data-pushstate`
|
60
|
+
attribute to have the window's location updated using the `history.pushState()`
|
61
|
+
API. The server can check the `X-Requested-With` header to determine whether
|
62
|
+
to return a partial or a full page.
|
63
|
+
|
64
|
+
```html
|
65
|
+
<a href="/foo" data-replace="*" data-pushstate>Foo</a>
|
66
|
+
```
|
67
|
+
|
68
|
+
The `data-replace` attribute also works on forms, catching the `submit` event.
|
69
|
+
|
70
|
+
```html
|
71
|
+
<form action="/foo" data-replace="*">
|
72
|
+
<input type="submit">
|
73
|
+
</form>
|
74
|
+
```
|
75
|
+
|
76
|
+
## Styling
|
77
|
+
|
78
|
+
While the AJAX operation is in progress, the matched container will have the
|
79
|
+
CSS class `.replace-active`. Clicks/submits within that container will be
|
80
|
+
ignored until the current operation completes, so you don't have to worry
|
81
|
+
about forms being submitted twice when a user double-clicks the button.
|
82
|
+
|
83
|
+
The `.replace-active` class can be useful for styling the container in order
|
84
|
+
to give the user feedback about when the operation begins and ends. For
|
85
|
+
instance, the following CSS style will cause elements to be faded when they
|
86
|
+
are in the process of being replaced:
|
87
|
+
|
88
|
+
```css
|
89
|
+
.replace-active {
|
90
|
+
opacity: 0.5;
|
91
|
+
}
|
92
|
+
```
|
93
|
+
|
94
|
+
## Events
|
95
|
+
|
96
|
+
When a replace operation occurs, a `replace:done` event is triggered on each
|
97
|
+
top-level element of the inserted partial. For example, given the following
|
98
|
+
listener:
|
99
|
+
|
100
|
+
```javascript
|
101
|
+
$(document).on('replace:done', 'p', function() {
|
102
|
+
console.log(this.innerHTML);
|
103
|
+
});
|
104
|
+
```
|
105
|
+
|
106
|
+
If the server returns the following partial:
|
107
|
+
|
108
|
+
```html
|
109
|
+
<p>First.</p>
|
110
|
+
<p><em>Second.</em></p>
|
111
|
+
<div><p>Third.</p></div>
|
112
|
+
```
|
113
|
+
|
114
|
+
The console will print:
|
115
|
+
|
116
|
+
```
|
117
|
+
First.
|
118
|
+
<em>Second.</em>
|
119
|
+
```
|
120
|
+
|
121
|
+
## License
|
122
|
+
|
123
|
+
This project is released under the MIT license.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
{
|
2
|
+
"name": "replacejs",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"main": [
|
5
|
+
"replace.js",
|
6
|
+
"primer.js",
|
7
|
+
"primer.min.js"
|
8
|
+
],
|
9
|
+
"ignore": [
|
10
|
+
"**/.*",
|
11
|
+
"demo",
|
12
|
+
"index.html",
|
13
|
+
"node_modules",
|
14
|
+
"bower_components",
|
15
|
+
"test",
|
16
|
+
"tests"
|
17
|
+
],
|
18
|
+
"dependencies": {
|
19
|
+
"jquery": ">=1.10"
|
20
|
+
},
|
21
|
+
"devDependencies": {
|
22
|
+
"jquery-waypoints": "~2.0.2"
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
releaseClicks = (function(document) {
|
2
|
+
if (document.addEventListener) {
|
3
|
+
var events = [];
|
4
|
+
function handler(event) {
|
5
|
+
var data = event.target.dataset;
|
6
|
+
if (data && data.replace) {
|
7
|
+
events.push(event);
|
8
|
+
event.preventDefault();
|
9
|
+
}
|
10
|
+
}
|
11
|
+
document.addEventListener('click', handler);
|
12
|
+
return function() {
|
13
|
+
document.removeEventListener('click', handler);
|
14
|
+
return events;
|
15
|
+
}
|
16
|
+
}
|
17
|
+
})(document);
|
@@ -0,0 +1 @@
|
|
1
|
+
releaseClicks=function(e){if(e.addEventListener){var t=[];function n(e){var n=e.target.dataset;if(n&&n.replace){t.push(e);e.preventDefault()}}e.addEventListener("click",n);return function(){e.removeEventListener("click",n);return t}}}(document)
|
@@ -0,0 +1,182 @@
|
|
1
|
+
// Allow links to replace part of the page, rather than the entire thing.
|
2
|
+
//
|
3
|
+
// <div class="container">
|
4
|
+
// <a href="/foo" data-replace=".container">Foo</a>
|
5
|
+
// </div>
|
6
|
+
//
|
7
|
+
// When you click the link "Foo", it will:
|
8
|
+
// - Find an ancestor element matching the selector ".container".
|
9
|
+
// - Perform a GET request to /foo.
|
10
|
+
// - If the request is successful, replace the ancestor with the response body.
|
11
|
+
//
|
12
|
+
// It also works for forms:
|
13
|
+
//
|
14
|
+
// <div class="container">
|
15
|
+
// <form action="/bar" method="POST" data-replace=".container">
|
16
|
+
// <input type="submit">
|
17
|
+
// </form>
|
18
|
+
// </div>
|
19
|
+
//
|
20
|
+
// Notes:
|
21
|
+
// - It is possible to replace the same element that was clicked/submitted. A
|
22
|
+
// convenient selector for doing so is "*".
|
23
|
+
// - If a link has the attribute "data-pushstate" and the browser supports it,
|
24
|
+
// the address bar will be updated on success using history.pushState.
|
25
|
+
// - The container will have the class ".replace-active" while a replace
|
26
|
+
// operation is in progress. This can be useful for styling purposes.
|
27
|
+
// - Clicks inside a container with a replace operation in progress are ignored.
|
28
|
+
// This obviates the need to prevent double submissions of forms.
|
29
|
+
|
30
|
+
(function($, undefined) {
|
31
|
+
// Apply callback to every descendent matching selector that exists at page
|
32
|
+
// load or that is added later. This frees you as the developer from having to
|
33
|
+
// find every place in the codebase where a given widget might be added to the
|
34
|
+
// page and attaching its initialization logic there.
|
35
|
+
//
|
36
|
+
// Example:
|
37
|
+
// $(document).initializeEach('[data-background-url]', function() {
|
38
|
+
// $(this).waypoint(function() {
|
39
|
+
// $(this).css('backgroundUrl', $(this).data('backgroundUrl'));
|
40
|
+
// }, { offset: '100%', triggerOnce: true });
|
41
|
+
// });
|
42
|
+
//
|
43
|
+
// It is also possible to nest these calls.
|
44
|
+
//
|
45
|
+
// $(document).initializeEach('.discussion-board', function() {
|
46
|
+
// $(this).initializeEach('.discussion-post', function() {
|
47
|
+
// // ...
|
48
|
+
// });
|
49
|
+
// });
|
50
|
+
//
|
51
|
+
$.fn.initializeEach = function(selector, callback) {
|
52
|
+
this
|
53
|
+
.on('replace:done', function(event) {
|
54
|
+
$(event.target).find(selector).addBack(selector).each(callback);
|
55
|
+
})
|
56
|
+
.find(selector).each(callback);
|
57
|
+
};
|
58
|
+
|
59
|
+
// Replace an element with the result of an AJAX request.
|
60
|
+
function ajaxReplace($elem, url, options) {
|
61
|
+
if ($elem.closest('.replace-active').length) {
|
62
|
+
return;
|
63
|
+
}
|
64
|
+
options = options || {};
|
65
|
+
var $container = $elem.closest(options.selector || '*');
|
66
|
+
$container.addClass('replace-active').trigger('replace:start');
|
67
|
+
|
68
|
+
$.ajax(url, { type: options.method, data: options.data })
|
69
|
+
.always(function() {
|
70
|
+
$container.removeClass('replace-active');
|
71
|
+
})
|
72
|
+
.done(function(data) {
|
73
|
+
if (options.success) {
|
74
|
+
options.success($container, data);
|
75
|
+
}
|
76
|
+
$(data).replaceAll($container).trigger('replace:done');
|
77
|
+
})
|
78
|
+
.fail(function() {
|
79
|
+
$container.trigger('replace:fail');
|
80
|
+
});
|
81
|
+
}
|
82
|
+
|
83
|
+
$(window).on('popstate', function(event) {
|
84
|
+
var state = event.originalEvent.state;
|
85
|
+
if (state && state.selector) {
|
86
|
+
$(state.data).replaceAll(state.selector)
|
87
|
+
.trigger('replace:done');
|
88
|
+
}
|
89
|
+
});
|
90
|
+
|
91
|
+
$(document).on('click', 'a[data-replace]', function(event) {
|
92
|
+
var $link = $(this);
|
93
|
+
|
94
|
+
if ($link.data('pushstate')) {
|
95
|
+
if (!window.history || !history.pushState) {
|
96
|
+
// If a link would like to use pushState but that is not supported by
|
97
|
+
// the browser, we'll let it do a full page load. Affects IE 8/9 users.
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
if (event.shiftKey || event.metaKey) {
|
101
|
+
// User is trying to open link in a new tab or window. Allow default.
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
event.preventDefault();
|
107
|
+
|
108
|
+
ajaxReplace($link, $link.attr('href'), {
|
109
|
+
selector: $link.data('replace'),
|
110
|
+
success: function($container, data) {
|
111
|
+
if ($link.data('pushstate')) {
|
112
|
+
history.replaceState({
|
113
|
+
selector: $link.data('replace'),
|
114
|
+
data: $container[0].outerHTML
|
115
|
+
}, null);
|
116
|
+
history.pushState({
|
117
|
+
selector: $link.data('replace'),
|
118
|
+
data: data
|
119
|
+
}, null, $link.attr('href'));
|
120
|
+
}
|
121
|
+
}
|
122
|
+
});
|
123
|
+
});
|
124
|
+
|
125
|
+
$(document).on('submit', 'form[data-replace]', function(event) {
|
126
|
+
event.preventDefault();
|
127
|
+
var $form = $(this);
|
128
|
+
|
129
|
+
ajaxReplace($form, $form.attr('action'), {
|
130
|
+
selector: $form.data('replace'),
|
131
|
+
method: $form.attr('method'),
|
132
|
+
data: $form.serialize()
|
133
|
+
});
|
134
|
+
});
|
135
|
+
|
136
|
+
// When you submit a form using the built-in click handler for buttons, the
|
137
|
+
// clicked button's name/value will be added to the query string. This can be
|
138
|
+
// useful for knowing which button was clicked in a form with several of
|
139
|
+
// them. When we submit the form programatically, the clicked button is no
|
140
|
+
// longer active and we have no way of knowing which one it was. This click
|
141
|
+
// handler emulates the built-in functionality.
|
142
|
+
$(document).on('click', 'form[data-replace] button', function(event) {
|
143
|
+
var $button = $(this);
|
144
|
+
$('<input type="hidden">')
|
145
|
+
.prop('name', $button.prop('name'))
|
146
|
+
.val($button.val())
|
147
|
+
.appendTo($button.closest('form'));
|
148
|
+
});
|
149
|
+
|
150
|
+
if (window.releaseClicks) {
|
151
|
+
$.each(releaseClicks(), function(_, event) {
|
152
|
+
$(event.target).trigger(event.type);
|
153
|
+
});
|
154
|
+
}
|
155
|
+
|
156
|
+
// Allows sections of the page to be lazily loaded just before they come into
|
157
|
+
// view. Implementation is simple: just add a 'lazy-url' data attribute like so
|
158
|
+
//
|
159
|
+
// <div data-lazy-url="http://causes.com/expensive/content"></div>
|
160
|
+
//
|
161
|
+
// The rest will be handled by this code, in conjunction with the jQuery
|
162
|
+
// waypoints plug-in (http://imakewebthings.com/jquery-waypoints/); if the
|
163
|
+
// plug-in is not available, we degrade gracefully and revert to eager
|
164
|
+
// loading.
|
165
|
+
//
|
166
|
+
// Note that we support adding yet-more lazy content onto the page after page
|
167
|
+
// initialization, but only when using Replace.js.
|
168
|
+
$(document).initializeEach('[data-lazy-url]', function() {
|
169
|
+
var $this = $(this);
|
170
|
+
if (typeof $.fn.waypoint === 'function') {
|
171
|
+
$this.waypoint({
|
172
|
+
offset: '120%',
|
173
|
+
triggerOnce: true,
|
174
|
+
handler: function() {
|
175
|
+
ajaxReplace($this, $this.data('lazy-url'));
|
176
|
+
}
|
177
|
+
});
|
178
|
+
} else {
|
179
|
+
ajaxReplace($this, $this.data('lazy-url'));
|
180
|
+
}
|
181
|
+
});
|
182
|
+
})(jQuery);
|
@@ -0,0 +1,158 @@
|
|
1
|
+
// Stupid jQuery table plugin.
|
2
|
+
|
3
|
+
// Call on a table
|
4
|
+
// sortFns: Sort functions for your datatypes.
|
5
|
+
(function($) {
|
6
|
+
|
7
|
+
$.fn.stupidtable = function(sortFns) {
|
8
|
+
return this.each(function() {
|
9
|
+
var $table = $(this);
|
10
|
+
sortFns = sortFns || {};
|
11
|
+
|
12
|
+
// ==================================================== //
|
13
|
+
// Utility functions //
|
14
|
+
// ==================================================== //
|
15
|
+
|
16
|
+
// Merge sort functions with some default sort functions.
|
17
|
+
sortFns = $.extend({}, $.fn.stupidtable.default_sort_fns, sortFns);
|
18
|
+
|
19
|
+
// Return the resulting indexes of a sort so we can apply
|
20
|
+
// this result elsewhere. This returns an array of index numbers.
|
21
|
+
// return[0] = x means "arr's 0th element is now at x"
|
22
|
+
var sort_map = function(arr, sort_function, reverse_column) {
|
23
|
+
var map = [];
|
24
|
+
var index = 0;
|
25
|
+
if (reverse_column) {
|
26
|
+
for (var i = arr.length-1; i >= 0; i--) {
|
27
|
+
map.push(i);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
else {
|
31
|
+
var sorted = arr.slice(0).sort(sort_function);
|
32
|
+
for (var i=0; i<arr.length; i++) {
|
33
|
+
index = $.inArray(arr[i], sorted);
|
34
|
+
|
35
|
+
// If this index is already in the map, look for the next index.
|
36
|
+
// This handles the case of duplicate entries.
|
37
|
+
while ($.inArray(index, map) != -1) {
|
38
|
+
index++;
|
39
|
+
}
|
40
|
+
map.push(index);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
return map;
|
44
|
+
};
|
45
|
+
|
46
|
+
// Apply a sort map to the array.
|
47
|
+
var apply_sort_map = function(arr, map) {
|
48
|
+
var clone = arr.slice(0),
|
49
|
+
newIndex = 0;
|
50
|
+
for (var i=0; i<map.length; i++) {
|
51
|
+
newIndex = map[i];
|
52
|
+
clone[newIndex] = arr[i];
|
53
|
+
}
|
54
|
+
return clone;
|
55
|
+
};
|
56
|
+
|
57
|
+
// ==================================================== //
|
58
|
+
// Begin execution! //
|
59
|
+
// ==================================================== //
|
60
|
+
|
61
|
+
// Do sorting when THs are clicked
|
62
|
+
$table.on("click", "th", function() {
|
63
|
+
var trs = $table.children("tbody").children("tr");
|
64
|
+
var $this = $(this);
|
65
|
+
var th_index = 0;
|
66
|
+
var dir = $.fn.stupidtable.dir;
|
67
|
+
|
68
|
+
$this.closest('tr').find("th").slice(0, $this.index()).each(function() {
|
69
|
+
var cols = $(this).attr("colspan") || 1;
|
70
|
+
th_index += parseInt(cols,10);
|
71
|
+
});
|
72
|
+
|
73
|
+
// Determine (and/or reverse) sorting direction, default `asc`
|
74
|
+
var sort_dir = $this.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC;
|
75
|
+
|
76
|
+
// Choose appropriate sorting function. If we're sorting descending, check
|
77
|
+
// for a `data-sort-desc` attribute.
|
78
|
+
if ( sort_dir == dir.DESC )
|
79
|
+
var type = $this.data("sort-desc") || $this.data("sort") || null;
|
80
|
+
else
|
81
|
+
var type = $this.data("sort") || null;
|
82
|
+
|
83
|
+
// Prevent sorting if no type defined
|
84
|
+
if (type === null) {
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
88
|
+
// Trigger `beforetablesort` event that calling scripts can hook into;
|
89
|
+
// pass parameters for sorted column index and sorting direction
|
90
|
+
$table.trigger("beforetablesort", {column: th_index, direction: sort_dir});
|
91
|
+
// More reliable method of forcing a redraw
|
92
|
+
$table.css("display");
|
93
|
+
|
94
|
+
// Run sorting asynchronously on a timout to force browser redraw after
|
95
|
+
// `beforetablesort` callback. Also avoids locking up the browser too much.
|
96
|
+
setTimeout(function() {
|
97
|
+
// Gather the elements for this column
|
98
|
+
var column = [];
|
99
|
+
var sortMethod = sortFns[type];
|
100
|
+
|
101
|
+
// Push either the value of the `data-order-by` attribute if specified
|
102
|
+
// or just the text() value in this column to column[] for comparison.
|
103
|
+
trs.each(function(index,tr) {
|
104
|
+
var $e = $(tr).children().eq(th_index);
|
105
|
+
var sort_val = $e.data("sort-value");
|
106
|
+
var order_by = typeof(sort_val) !== "undefined" ? sort_val : $e.text();
|
107
|
+
column.push(order_by);
|
108
|
+
});
|
109
|
+
|
110
|
+
// Create the sort map. This column having a sort-dir implies it was
|
111
|
+
// the last column sorted. As long as no data-sort-desc is specified,
|
112
|
+
// we're free to just reverse the column.
|
113
|
+
var reverse_column = !!$this.data("sort-dir") && !$this.data("sort-desc");
|
114
|
+
var theMap = sort_map(column, sortMethod, reverse_column);
|
115
|
+
|
116
|
+
// Reset siblings
|
117
|
+
$table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc");
|
118
|
+
$this.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir);
|
119
|
+
|
120
|
+
// Replace the content of tbody with the sortedTRs. Strangely (and
|
121
|
+
// conveniently!) enough, .append accomplishes this for us.
|
122
|
+
var sortedTRs = $(apply_sort_map(trs, theMap));
|
123
|
+
$table.children("tbody").append(sortedTRs);
|
124
|
+
|
125
|
+
// Trigger `aftertablesort` event. Similar to `beforetablesort`
|
126
|
+
$table.trigger("aftertablesort", {column: th_index, direction: sort_dir});
|
127
|
+
// More reliable method of forcing a redraw
|
128
|
+
$table.css("display");
|
129
|
+
}, 10);
|
130
|
+
});
|
131
|
+
});
|
132
|
+
};
|
133
|
+
|
134
|
+
// Enum containing sorting directions
|
135
|
+
$.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"};
|
136
|
+
|
137
|
+
$.fn.stupidtable.default_sort_fns = {
|
138
|
+
"int": function(a, b) {
|
139
|
+
return parseInt(a, 10) - parseInt(b, 10);
|
140
|
+
},
|
141
|
+
"float": function(a, b) {
|
142
|
+
return parseFloat(a) - parseFloat(b);
|
143
|
+
},
|
144
|
+
"string": function(a, b) {
|
145
|
+
if (a < b) return -1;
|
146
|
+
if (a > b) return +1;
|
147
|
+
return 0;
|
148
|
+
},
|
149
|
+
"string-ins": function(a, b) {
|
150
|
+
a = a.toLowerCase();
|
151
|
+
b = b.toLowerCase();
|
152
|
+
if (a < b) return -1;
|
153
|
+
if (a > b) return +1;
|
154
|
+
return 0;
|
155
|
+
}
|
156
|
+
};
|
157
|
+
|
158
|
+
})(jQuery);
|
@@ -0,0 +1,17 @@
|
|
1
|
+
- content_for :title do
|
2
|
+
Twigg
|
3
|
+
%small
|
4
|
+
%span{day_links}
|
5
|
+
|
6
|
+
%h2 Teams
|
7
|
+
.teams.well{ 'data-lazy-url' => teams_path(days: @days) }
|
8
|
+
|
9
|
+
%h2 Pairs
|
10
|
+
.pairs.well{ 'data-lazy-url' => pairs_path(days: @days) }
|
11
|
+
|
12
|
+
- if Twigg::Config.app.gerrit.enabled
|
13
|
+
%h2 Gerrit
|
14
|
+
.gerrit.well{ 'data-lazy-url' => gerrit_path }
|
15
|
+
|
16
|
+
%h2 Authors
|
17
|
+
.authors.well{ 'data-lazy-url' => authors_path(days: @days) }
|
data/views/layout.haml
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title
|
5
|
+
= strip_tags(yield_content :title)
|
6
|
+
- if Twigg::Config.organization
|
7
|
+
—
|
8
|
+
&= Twigg::Config.organization
|
9
|
+
%meta(name='viewport' content='width=device-width, initial-scale=1.0')
|
10
|
+
%link(rel='stylesheet' href='/vendor/bootstrap/dist/css/bootstrap.min.css' media='screen')
|
11
|
+
%link(rel='stylesheet' href='/vendor/bootstrap-glyphicons/css/bootstrap-glyphicons.css' media='screen')
|
12
|
+
%link(rel='stylesheet' href='/stylesheets/application.css' media='screen')
|
13
|
+
%link(rel='shortcut icon' href='/favicon.png')
|
14
|
+
%body
|
15
|
+
.wrap
|
16
|
+
.navbar.navbar-inverse
|
17
|
+
.container
|
18
|
+
.navbar-header
|
19
|
+
%button{ type: 'button', class: 'navbar-toggle', data: { toggle: 'collapse', target: '.navbar-responsive-collapse' }}
|
20
|
+
%span.icon-bar
|
21
|
+
%span.icon-bar
|
22
|
+
%span.icon-bar
|
23
|
+
%a.navbar-brand(href='/') Twigg
|
24
|
+
.navbar-collapse.collapse.navbar-responsive-collapse
|
25
|
+
%ul.nav.navbar-nav
|
26
|
+
%li{ class: active?(teams_path) }
|
27
|
+
%a{ href: teams_path } Teams
|
28
|
+
%li{ class: active?(authors_path) }
|
29
|
+
%a{ href: authors_path } Authors
|
30
|
+
%li{ class: active?(pairs_path) }
|
31
|
+
%a{ href: pairs_path } Pairs
|
32
|
+
- if Twigg::Config.app.gerrit.enabled
|
33
|
+
%li.dropdown{ class: active?(gerrit_changes_path, gerrit_authors_path) }
|
34
|
+
%a{ href: '#', data: { toggle: 'dropdown' } }
|
35
|
+
Gerrit
|
36
|
+
%b.caret
|
37
|
+
%ul.dropdown-menu
|
38
|
+
%li
|
39
|
+
%a{ href: gerrit_changes_path } Changes
|
40
|
+
%li
|
41
|
+
%a{ href: gerrit_authors_path } Authors
|
42
|
+
%li{ class: active?(russian_novels_path) }
|
43
|
+
%a{ href: russian_novels_path } я
|
44
|
+
.container
|
45
|
+
.page-header
|
46
|
+
%h1= yield_content :title
|
47
|
+
= yield
|
48
|
+
.push
|
49
|
+
.footer.navbar
|
50
|
+
- quip = random_quip
|
51
|
+
%p.navbar-text.text-center{ title: quip }
|
52
|
+
Twigg —
|
53
|
+
%em= quip
|
54
|
+
%script(src='/vendor/jquery/jquery.min.js')
|
55
|
+
%script(src='/vendor/bootstrap/dist/js/bootstrap.min.js')
|
56
|
+
%script(src='/vendor/replacejs/replace.js')
|
57
|
+
%script(src='/vendor/d3/d3.min.js')
|
58
|
+
%script(src='/vendor/stupidtable.js')
|
59
|
+
%script(src='/application.js')
|
60
|
+
= yield_content :footer
|