Skip to content

Commit c04d064

Browse files
Initial commit
0 parents  commit c04d064

17 files changed

+2778
-0
lines changed

Diff for: .gitignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
._*
2+
.DS_Store
3+
.DS_Store?
4+
.idea
5+
.Spotlight-V100
6+
.Trashes
7+
docs
8+
Icon?
9+
node_modules
10+
public/scripts/bower_components

Diff for: .jshintrc

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"node": true,
3+
"browser": true,
4+
"bitwise": true,
5+
"devel": false,
6+
"eqeqeq": false,
7+
"forin": true,
8+
"immed": true,
9+
"latedef": "nofunc",
10+
"newcap": true,
11+
"noarg": true,
12+
"noempty": true,
13+
"nonew": true,
14+
"nomen": false,
15+
"sub": true,
16+
"trailing": true,
17+
"undef": true,
18+
"unused": true,
19+
"asi": true
20+
}

Diff for: LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 bestguy
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

Diff for: Readme.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Lazy load example
2+
3+
* [ES6 (via BabelJS)](http://babeljs.io/)
4+
* [Ractive](http://www.ractivejs.org/)
5+
* [Webpack](http://webpack.github.io)
6+
* [lodash](https://lodash.com/docshttps://github.com/chjj/marked)
7+
* [xhttp](https://github.com/Mitranim/xhttp)
8+
* [Ractive-route](https://github.com/MartinKolarik/ractive-route)
9+
10+
----
11+
12+
## Install & Run:
13+
14+
### For development:
15+
16+
npm install
17+
npm start
18+
19+
Open: http://localhost:3000
20+
21+
### For production bundle:
22+
23+
webpack -p
24+
npm start
25+
26+
Open: http://localhost:3000

Diff for: app.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
let config = require('config');
4+
let express = require('express');
5+
let logger = require('morgan');
6+
let request = require('request-promise');
7+
let data = require('./data.json');
8+
9+
let app = new express();
10+
11+
// ## Middleware
12+
13+
app.use(logger('dev'));
14+
app.use(express.static(`${__dirname}/public`));
15+
16+
// ## Routes
17+
if (config.env == 'dev') {
18+
let webpackMiddleware = require('webpack-dev-middleware');
19+
let webpack = require('webpack');
20+
let webpackConfig = require('./webpack.config.js');
21+
app.use(webpackMiddleware(webpack(webpackConfig), {}));
22+
}
23+
24+
app.get('/api/data', (req, resp) => {
25+
let start = parseInt(req.query.start || 0);
26+
let size = parseInt(req.query.size || 1000);
27+
let results = {
28+
results: data.slice(start, start+size),
29+
total: data.length
30+
}
31+
32+
setTimeout(() => resp.json(results), 3500); // Fake delay to simulate reality
33+
});
34+
35+
app.listen(config.port, () => console.log(`Server running on port ${config.port}...`));

Diff for: config/default.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
env: process.env.NODE_ENV || 'dev',
3+
port: process.env.PORT || 3000
4+
};

Diff for: data.json

+2,347
Large diffs are not rendered by default.

Diff for: package.json

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "lazy-load-example",
3+
"version": "0.1.0",
4+
"private": true,
5+
"main": "app.js",
6+
"scripts": {
7+
"start": "node app.js"
8+
},
9+
"dependencies": {
10+
"config": "^1.8.1",
11+
"express": "^4.10.2",
12+
"morgan": "^1.5.0"
13+
},
14+
"devDependencies": {
15+
"animate.css": "^3.5.1",
16+
"autoprefixer-loader": "^3.2.0",
17+
"babel-core": "^5.8.35",
18+
"babel-loader": "^5.4.0",
19+
"css-loader": "^0.23.1",
20+
"html-loader": "^0.4.3",
21+
"lodash": "^4.6.1",
22+
"ractive": "^0.7.3",
23+
"ractive-loader": "^0.5.6",
24+
"ractive-route": "github:martinkolarik/ractive-route",
25+
"ractive-transitions-fade": "^0.3.1",
26+
"ractive-transitions-slide": "^0.4.0",
27+
"request-promise": "^3.0.0",
28+
"style-loader": "^0.13.0",
29+
"webpack": "^1.12.14",
30+
"webpack-dev-middleware": "^1.5.1",
31+
"xhttp": "0.0.8"
32+
}
33+
}

Diff for: public/index.html

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>Lazy-load example</title>
7+
<meta charset="utf-8">
8+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
9+
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
10+
<meta name="apple-mobile-web-app-capable" content="yes">
11+
<meta name="apple-mobile-web-app-status-bar-style" content="black">
12+
<meta name="format-detection" content="telephone=no">
13+
<link rel="stylesheet" href="/styles/bootstrap.min.css">
14+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
15+
</head>
16+
17+
<body>
18+
<div id="main" class="container"></div>
19+
</body>
20+
21+
<script type="text/javascript" src="./bundle.js"></script>
22+
23+
</html>

Diff for: public/scripts/Home.es6

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import Ractive from 'ractive';
2+
import xhttp from 'xhttp';
3+
import _ from 'lodash';
4+
5+
export default Ractive.extend({ // jshint ignore:line
6+
template: require('./Home.html'),
7+
data() {
8+
return {
9+
loading: true,
10+
search_index: 0
11+
}
12+
},
13+
computed: {
14+
search_results() {
15+
let { data, search } = this.get();
16+
search = search.trim().toLowerCase();
17+
if (search.trim().length > 0) {
18+
return _.filter(data, user => user.first.startsWith(search) || user.last.startsWith(search)) || [];
19+
} else {
20+
return []
21+
}
22+
}
23+
},
24+
oninit() {
25+
let page_size = 500;
26+
27+
this.observe({
28+
search: () => this.set({ search_index: 0 }) // Reset index on new search
29+
});
30+
31+
// Request first page of data, up to page_size results:
32+
xhttp({ method: 'get', url: `/api/data?size=${page_size}`})
33+
.then(data => {
34+
// Get total results from first request:
35+
let total_results = data.total;
36+
37+
// Display first results and total:
38+
this.set({
39+
data: data.results,
40+
total: total_results
41+
});
42+
43+
// If results length < page size, we are done; otherwise queue up requests:
44+
if (total_results > page_size) {
45+
46+
// Build array of page start indexes:
47+
let page_indexes = _.range(page_size, total_results, page_size);
48+
49+
// Create array of promises for requesting each page, then appending results:
50+
let page_requests =
51+
page_indexes.map(start_index => () => {
52+
return xhttp({ method: 'get', url: `/api/data?size=${page_size}&start=${start_index}`})
53+
.then(data => this.push('data', ...data.results)); // Appends data
54+
});
55+
56+
// Sets loading indicator to false when all requests are complete:
57+
page_requests.push(() => this.set({ loading: false }));
58+
59+
// This calls the promises above in sequence:
60+
page_requests.reduce((promise, next_request) => promise.then(next_request), Promise.resolve());
61+
62+
// Parallel
63+
// let page_requests =
64+
// page_indexes.map(start_index => new Promise(() => {xhttp({ method: 'get', url: `/api/data?size=${page_size}&start=${start_index}`})
65+
// .then(data => this.push('data', ...data.results))); // Appends data
66+
// // Start all requests, set loading indicator to false when all are complete:
67+
// Promise.all(page_requests)
68+
// .then(data => this.set({ loading: false }))
69+
// .catch(err => console.error);
70+
}
71+
})
72+
.catch(err => console.error);
73+
}
74+
});

Diff for: public/scripts/Home.html

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<div intro="animate:fadeIn">
2+
<div class="card p-t-2">
3+
4+
<div class="container">
5+
<div class="row">
6+
<div class="col-md-3">
7+
8+
<div class="input-group input-group-sm p-b-2">
9+
<input type="text" class="form-control" value="{{search}}">
10+
{{#if search_results.length}}
11+
<span class="input-group-addon">
12+
{{search_index + 1}} of {{search_results.length}}
13+
{{#loading}}<i class="fa fa-spinner fa-pulse"></i>{{/loading}}
14+
</span>
15+
{{/if}}
16+
<div class="input-group-btn">
17+
<button class="btn btn-default"
18+
on-click="subtract('search_index')"
19+
{{^search_results && search_index > 0}} disabled="disabled"{{/search_results}}>
20+
<i class="fa fa-chevron-up"></i>
21+
</button>
22+
</div>
23+
<div class="input-group-btn">
24+
<button class="btn btn-default"
25+
on-click="add('search_index')"
26+
{{^search_results && search_index < search_results.length - 1}} disabled="disabled"{{/search_results}}>
27+
<i class="fa fa-chevron-down"></i>
28+
</button>
29+
</div>
30+
</div>
31+
32+
</div>
33+
<div class="col-md-9">
34+
<button class="btn btn-sm btn-default"><i class="fa fa-copy"></i> Save</button>
35+
<button class="btn btn-sm btn-default">ACTIONS <i class="fa fa-caret-down"></i></button>
36+
<button class="btn btn-sm btn-default"><i class="fa fa-refresh"></i> Refresh</button>
37+
<button class="btn btn-sm btn-default"><i class="fa fa-gear"></i> Customize</button>
38+
</div>
39+
</div>
40+
</div>
41+
42+
{{#if !data && loading}}
43+
<h5 class="text-info text-xs-center">
44+
<i class="fa fa-spinner fa-pulse"></i>
45+
Loading...
46+
</h5>
47+
{{/if}}
48+
49+
<div class="container">
50+
{{#if data}}
51+
52+
{{#if loading}}
53+
<progress class="progress progress-info progress-animated" value="{{data.length}}" max="{{total}}">{{Math.floor(data.length/total*100)}}%</progress>
54+
<h5 class="text-info text-xs-center">
55+
<i class="fa fa-spinner fa-pulse"></i>
56+
Loading... ({{data.length}} of {{total}})
57+
</h5>
58+
{{/if}}
59+
60+
<div class="table-responsive">
61+
<table class="table table-sm table-striped table-bordered">
62+
<thead class="thead-inverse">
63+
<tr>
64+
<th>Email</th>
65+
<th>First</th>
66+
<th>Last</th>
67+
<th>Phone</th>
68+
</tr>
69+
</thead>
70+
<tbody>
71+
{{#each data}}
72+
{{#if !search || (search && (first.startsWith(search.toLowerCase() || last.startsWith(search.toLowerCase()))))}}
73+
<tr>
74+
<td>{{email}}</td>
75+
<td class="text-capitalize {{#if search && (first.startsWith(search.toLowerCase()))}}font-weight-bold table-warning{{/if}}">{{first}}</td>
76+
<td class="text-capitalize {{#if search && (last.startsWith(search.toLowerCase()))}}font-weight-bold table-warning{{/if}}">{{last}}</td>
77+
<td>{{phone}}</td>
78+
</tr>
79+
{{/if}}
80+
{{/each}}
81+
</tbody>
82+
<tfoot class="thead-inverse">
83+
<tr>
84+
<th colspan="4">Total ({{total}} Results)</th>
85+
</tr>
86+
</tfoot>
87+
</table>
88+
</div>
89+
{{/if}}
90+
91+
</div>
92+
</div>
93+
</div>

Diff for: public/scripts/lib/ractive/transitions.es6

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var Ractive = require('ractive');
2+
Ractive.transitions.fade = require('ractive-transitions-fade');
3+
Ractive.transitions.slide = require('ractive-transitions-slide');
4+
require('./transitions/animate.js');

Diff for: public/scripts/lib/ractive/transitions/animate.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
var Ractive = require('Ractive');
2+
require('animate.css');
3+
4+
5+
var ANIMATION_END_EVENT = (function () {
6+
var eventName,
7+
ANIMATION_END_EVENT_NAMES = {
8+
'animation': 'animationend',
9+
'-o-animation': 'oAnimationEnd',
10+
'-moz-animation': 'animationend',
11+
'-webkit-animation': 'webkitAnimationEnd',
12+
'-ms-animation': 'animationend'
13+
},
14+
fakeEl = document.createElement('fakeelement');
15+
16+
for (eventName in ANIMATION_END_EVENT_NAMES) {
17+
if (typeof (fakeEl.style[eventName]) !== 'undefined') {
18+
return ANIMATION_END_EVENT_NAMES[eventName];
19+
}
20+
}
21+
return null;
22+
})();
23+
24+
var animateCSS = function (t, transition) {
25+
// Process parameters. No error checking for non-existing transitions(!!)
26+
transition = transition || (t.isIntro ? Ractive.transitions.animate.defaultIntro : Ractive.transitions.animate.defaultOutro);
27+
28+
// add 'animated' class if not present
29+
if (!t.node.classList.contains('animated')) t.node.classList.add('animated');
30+
31+
// add the transition class, the transitions starts immediately
32+
t.node.classList.add(transition);
33+
34+
// After the animation has ended, call `t.complete()` to signal that we've finished
35+
t.node.addEventListener(ANIMATION_END_EVENT, function () {
36+
t.node.classList.remove(transition);
37+
t.complete();
38+
// TODO remove event listener?
39+
});
40+
};
41+
42+
Ractive.transitions.animate = animateCSS;
43+
Ractive.transitions.animate.defaultIntro = 'slideInDown';
44+
Ractive.transitions.animate.defaultOutro = 'slideOutUp';

0 commit comments

Comments
 (0)