twigg-app 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|