├── .gitignore
├── LICENSE
├── app.js
├── demo
├── api
│ └── data.json
├── css
│ ├── fonts
│ │ └── roboto.woff
│ └── style.css
├── index.html
├── js
│ ├── arrivals.js
│ ├── main.js
│ ├── page.js
│ └── vendor
│ │ └── knockout-3.3.0.js
├── launcher-icon-144.png
├── launcher-icon-192.png
├── launcher-icon-256.png
├── launcher-icon-96.png
├── launcher-icon.png
├── manifest.json
├── offline.html
└── sw.js
├── grunt
├── aliases.js
├── uglify.js
└── watch.js
├── gruntfile.js
├── package.json
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | # DNX
42 | project.lock.json
43 | artifacts/
44 |
45 | *_i.c
46 | *_p.c
47 | *_i.h
48 | *.ilk
49 | *.meta
50 | *.obj
51 | *.pch
52 | *.pdb
53 | *.pgc
54 | *.pgd
55 | *.rsp
56 | *.sbr
57 | *.tlb
58 | *.tli
59 | *.tlh
60 | *.tmp
61 | *.tmp_proj
62 | *.log
63 | *.vspscc
64 | *.vssscc
65 | .builds
66 | *.pidb
67 | *.svclog
68 | *.scc
69 |
70 | # Chutzpah Test files
71 | _Chutzpah*
72 |
73 | # Visual C++ cache files
74 | ipch/
75 | *.aps
76 | *.ncb
77 | *.opensdf
78 | *.sdf
79 | *.cachefile
80 |
81 | # Visual Studio profiler
82 | *.psess
83 | *.vsp
84 | *.vspx
85 |
86 | # TFS 2012 Local Workspace
87 | $tf/
88 |
89 | # Guidance Automation Toolkit
90 | *.gpState
91 |
92 | # ReSharper is a .NET coding add-in
93 | _ReSharper*/
94 | *.[Rr]e[Ss]harper
95 | *.DotSettings.user
96 |
97 | # JustCode is a .NET coding add-in
98 | .JustCode
99 |
100 | # TeamCity is a build add-in
101 | _TeamCity*
102 |
103 | # DotCover is a Code Coverage Tool
104 | *.dotCover
105 |
106 | # NCrunch
107 | _NCrunch_*
108 | .*crunch*.local.xml
109 |
110 | # MightyMoose
111 | *.mm.*
112 | AutoTest.Net/
113 |
114 | # Web workbench (sass)
115 | .sass-cache/
116 |
117 | # Installshield output folder
118 | [Ee]xpress/
119 |
120 | # DocProject is a documentation generator add-in
121 | DocProject/buildhelp/
122 | DocProject/Help/*.HxT
123 | DocProject/Help/*.HxC
124 | DocProject/Help/*.hhc
125 | DocProject/Help/*.hhk
126 | DocProject/Help/*.hhp
127 | DocProject/Help/Html2
128 | DocProject/Help/html
129 |
130 | # Click-Once directory
131 | publish/
132 |
133 | # Publish Web Output
134 | *.[Pp]ublish.xml
135 | *.azurePubxml
136 | ## TODO: Comment the next line if you want to checkin your
137 | ## web deploy settings but do note that will include unencrypted
138 | ## passwords
139 | #*.pubxml
140 |
141 | *.publishproj
142 |
143 | # NuGet Packages
144 | *.nupkg
145 | # The packages folder can be ignored because of Package Restore
146 | **/packages/*
147 | # except build/, which is used as an MSBuild target.
148 | !**/packages/build/
149 | # Uncomment if necessary however generally it will be regenerated when needed
150 | #!**/packages/repositories.config
151 |
152 | # Windows Azure Build Output
153 | csx/
154 | *.build.csdef
155 |
156 | # Windows Store app package directory
157 | AppPackages/
158 |
159 | # Visual Studio cache files
160 | # files ending in .cache can be ignored
161 | *.[Cc]ache
162 | # but keep track of directories ending in .cache
163 | !*.[Cc]ache/
164 |
165 | # Others
166 | ClientBin/
167 | [Ss]tyle[Cc]op.*
168 | ~$*
169 | *~
170 | *.dbmdl
171 | *.dbproj.schemaview
172 | *.pfx
173 | *.publishsettings
174 | node_modules/
175 | orleans.codegen.cs
176 |
177 | # RIA/Silverlight projects
178 | Generated_Code/
179 |
180 | # Backup & report files from converting an old project file
181 | # to a newer Visual Studio version. Backup files are not needed,
182 | # because we have git ;-)
183 | _UpgradeReport_Files/
184 | Backup*/
185 | UpgradeLog*.XML
186 | UpgradeLog*.htm
187 |
188 | # SQL Server files
189 | *.mdf
190 | *.ldf
191 |
192 | # Business Intelligence projects
193 | *.rdl.data
194 | *.bim.layout
195 | *.bim_*.settings
196 |
197 | # Microsoft Fakes
198 | FakesAssemblies/
199 |
200 | # Node.js Tools for Visual Studio
201 | .ntvs_analysis.dat
202 |
203 | # Visual Studio 6 build log
204 | *.plg
205 |
206 | # Visual Studio 6 workspace options file
207 | *.opt
208 |
209 | # LightSwitch generated files
210 | GeneratedArtifacts/
211 | _Pvt_Extensions/
212 | ModelManifest.xml
213 |
214 | # Custom project files
215 | js/build/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Incredible Web
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 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 |
4 | app.use(express.static('demo'));
5 |
6 | app.listen(3000, function() {
7 | console.log('Demo is running on localhost:3000');
8 | });
--------------------------------------------------------------------------------
/demo/api/data.json:
--------------------------------------------------------------------------------
1 | [{
2 | "title": "Malta to Amsterdam",
3 | "status": "On time",
4 | "time": "08:45"
5 | }, {
6 | "title": "Malta to London",
7 | "status": "Delayed",
8 | "time": "09:45"
9 | }, {
10 | "title": "Malta to Poznan",
11 | "status": "On time",
12 | "time": "10:00"
13 | }, {
14 | "title": "Malta to London",
15 | "status": "On time",
16 | "time": "11:45"
17 | }, {
18 | "title": "Malta to Rome",
19 | "status": "On time",
20 | "time": "12:05"
21 | }, {
22 | "title": "Malta to Paris",
23 | "status": "Delayed",
24 | "time": "12:35"
25 | }]
--------------------------------------------------------------------------------
/demo/css/fonts/roboto.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IncredibleWeb/pwa-tutorial/6e0c5f458529c2c6682f1d9767a00a724dd1fe10/demo/css/fonts/roboto.woff
--------------------------------------------------------------------------------
/demo/css/style.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Roboto';
3 | font-style: normal;
4 | font-weight: 400;
5 | src: local('Roboto Regular'), local('Roboto-Regular'), url('./fonts/roboto.woff') format('woff');
6 | }
7 |
8 | html {
9 | font-family: sans-serif;
10 | -ms-text-size-adjust: 100%;
11 | -webkit-text-size-adjust: 100%;
12 | }
13 |
14 | body {
15 | margin: 0;
16 | }
17 |
18 | article,
19 | aside,
20 | details,
21 | figcaption,
22 | figure,
23 | footer,
24 | header,
25 | hgroup,
26 | main,
27 | menu,
28 | nav,
29 | section,
30 | summary {
31 | display: block;
32 | }
33 |
34 | audio,
35 | canvas,
36 | progress,
37 | video {
38 | display: inline-block;
39 | vertical-align: baseline;
40 | }
41 |
42 | audio:not([controls]) {
43 | display: none;
44 | height: 0;
45 | }
46 |
47 | [hidden],
48 | template {
49 | display: none;
50 | }
51 |
52 | a {
53 | background-color: transparent;
54 | }
55 |
56 | a:active,
57 | a:hover {
58 | outline: 0;
59 | }
60 |
61 | abbr[title] {
62 | border-bottom: 1px dotted;
63 | }
64 |
65 | b,
66 | strong {
67 | font-weight: bold;
68 | }
69 |
70 | dfn {
71 | font-style: italic;
72 | }
73 |
74 | h1 {
75 | font-size: 2em;
76 | margin: 0.67em 0;
77 | }
78 |
79 | mark {
80 | background: #ff0;
81 | color: #000;
82 | }
83 |
84 | small {
85 | font-size: 80%;
86 | }
87 |
88 | sub,
89 | sup {
90 | font-size: 75%;
91 | line-height: 0;
92 | position: relative;
93 | vertical-align: baseline;
94 | }
95 |
96 | sup {
97 | top: -0.5em;
98 | }
99 |
100 | sub {
101 | bottom: -0.25em;
102 | }
103 |
104 | img {
105 | border: 0;
106 | }
107 |
108 | svg:not(:root) {
109 | overflow: hidden;
110 | }
111 |
112 | figure {
113 | margin: 1em 40px;
114 | }
115 |
116 | hr {
117 | -moz-box-sizing: content-box;
118 | box-sizing: content-box;
119 | height: 0;
120 | }
121 |
122 | pre {
123 | overflow: auto;
124 | }
125 |
126 | code,
127 | kbd,
128 | pre,
129 | samp {
130 | font-family: monospace, monospace;
131 | font-size: 1em;
132 | }
133 |
134 | button,
135 | input,
136 | input[type=submit],
137 | .button,
138 | .button:visited,
139 | optgroup,
140 | select,
141 | textarea {
142 | color: inherit;
143 | font: inherit;
144 | margin: 0;
145 | }
146 |
147 | button {
148 | overflow: visible;
149 | }
150 |
151 | button,
152 | select {
153 | text-transform: none;
154 | }
155 |
156 | button,
157 | html input[type="button"],
158 | html [type="button"].button,
159 | input[type="reset"],
160 | [type="reset"].button,
161 | input[type="submit"],
162 | [type="submit"].button {
163 | -webkit-appearance: button;
164 | cursor: pointer;
165 | }
166 |
167 | button[disabled],
168 | html input[disabled],
169 | html [disabled].button {
170 | cursor: default;
171 | }
172 |
173 | button::-moz-focus-inner,
174 | input::-moz-focus-inner,
175 | input[type=submit]::-moz-focus-inner,
176 | .button::-moz-focus-inner,
177 | .button:visited::-moz-focus-inner {
178 | border: 0;
179 | padding: 0;
180 | }
181 |
182 | input,
183 | input[type=submit],
184 | .button,
185 | .button:visited {
186 | line-height: normal;
187 | }
188 |
189 | input[type="checkbox"],
190 | [type="checkbox"].button,
191 | input[type="radio"],
192 | [type="radio"].button {
193 | box-sizing: border-box;
194 | padding: 0;
195 | }
196 |
197 | input[type="number"]::-webkit-inner-spin-button,
198 | [type="number"].button::-webkit-inner-spin-button,
199 | input[type="number"]::-webkit-outer-spin-button,
200 | [type="number"].button::-webkit-outer-spin-button {
201 | height: auto;
202 | }
203 |
204 | input[type="search"],
205 | [type="search"].button {
206 | -webkit-appearance: textfield;
207 | /* 1 */
208 | -moz-box-sizing: content-box;
209 | -webkit-box-sizing: content-box;
210 | /* 2 */
211 | box-sizing: content-box;
212 | }
213 |
214 | input[type="search"]::-webkit-search-cancel-button,
215 | [type="search"].button::-webkit-search-cancel-button,
216 | input[type="search"]::-webkit-search-decoration,
217 | [type="search"].button::-webkit-search-decoration {
218 | -webkit-appearance: none;
219 | }
220 |
221 | fieldset {
222 | border: 1px solid #c0c0c0;
223 | margin: 0 2px;
224 | padding: 0.35em 0.625em 0.75em;
225 | }
226 |
227 | legend {
228 | border: 0;
229 | /* 1 */
230 | padding: 0;
231 | /* 2 */
232 | }
233 |
234 | textarea {
235 | overflow: auto;
236 | }
237 |
238 | optgroup {
239 | font-weight: bold;
240 | }
241 |
242 | table {
243 | border-collapse: collapse;
244 | border-spacing: 0;
245 | }
246 |
247 | td,
248 | th {
249 | padding: 0;
250 | }
251 |
252 |
253 | /* Keyframes */
254 |
255 | @-moz-keyframes ripple-animation {
256 | 0% {
257 | -moz-transform: scale(1);
258 | transform: scale(1);
259 | opacity: 0.4;
260 | }
261 | 100% {
262 | -moz-transform: scale(100);
263 | transform: scale(100);
264 | opacity: 0;
265 | }
266 | }
267 |
268 | @-webkit-keyframes ripple-animation {
269 | 0% {
270 | -webkit-transform: scale(1);
271 | transform: scale(1);
272 | opacity: 0.4;
273 | }
274 | 100% {
275 | -webkit-transform: scale(100);
276 | transform: scale(100);
277 | opacity: 0;
278 | }
279 | }
280 |
281 | @keyframes ripple-animation {
282 | 0% {
283 | -moz-transform: scale(1);
284 | -ms-transform: scale(1);
285 | -webkit-transform: scale(1);
286 | transform: scale(1);
287 | opacity: 0.4;
288 | }
289 | 100% {
290 | -moz-transform: scale(100);
291 | -ms-transform: scale(100);
292 | -webkit-transform: scale(100);
293 | transform: scale(100);
294 | opacity: 0;
295 | }
296 | }
297 |
298 | html {
299 | background-color: #eee;
300 | }
301 |
302 | body,
303 | select,
304 | input,
305 | input[type=submit],
306 | .button,
307 | .button:visited,
308 | textarea {
309 | color: #000;
310 | color: rgba(0, 0, 0, 0.7);
311 | font-family: "Roboto", Arial, sans-serif;
312 | font-weight: 300;
313 | line-height: 1.5em;
314 | font-size: 16px;
315 | -webkit-font-smoothing: antialiased;
316 | -moz-osx-font-smoothing: grayscale;
317 | }
318 |
319 | * *,
320 | * *:before,
321 | * *:after {
322 | -moz-box-sizing: border-box;
323 | -webkit-box-sizing: border-box;
324 | box-sizing: border-box;
325 | }
326 |
327 | body {
328 | background-color: #FDFFFD;
329 | }
330 |
331 | ul,
332 | ol {
333 | padding: 0;
334 | }
335 |
336 | p {
337 | margin: 0;
338 | line-height: 1.5em;
339 | }
340 |
341 | .content {
342 | max-width: 72em;
343 | margin-left: auto;
344 | margin-right: auto;
345 | }
346 |
347 | .container {
348 | min-height: 100vh;
349 | display: flex;
350 | flex-direction: column;
351 | }
352 |
353 | header {
354 | position: fixed;
355 | top: 0;
356 | left: 0;
357 | right: 0;
358 | padding: 0 0.5em;
359 | background-color: #29BDBB;
360 | height: 3em;
361 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
362 | line-height: 3em;
363 | color: #fff;
364 | transform: translateZ(0);
365 | backface-visibility: hidden;
366 | }
367 |
368 | header h3 {
369 | margin: 0;
370 | }
371 |
372 | .arrivals-list {
373 | margin: 0;
374 | list-style: none;
375 | display: flex;
376 | flex-direction: column;
377 | transition: opacity 0.2s ease-in-out;
378 | padding: 0 0.5em;
379 | }
380 |
381 | .arrivals-list.loading {
382 | opacity: 0.2;
383 | }
384 |
385 | #main {
386 | margin-top: 3em;
387 | width: 100%;
388 | position: relative;
389 | }
390 |
391 | .item {
392 | border-radius: 4px;
393 | margin: 0.5em auto;
394 | padding: 1em;
395 | width: 100%;
396 | background-color: #fff;
397 | text-align: left;
398 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
399 | }
400 |
401 | .item .title,
402 | .item .status,
403 | .item .time {
404 | display: block;
405 | }
406 |
407 | .item .title {
408 | font-weight: bold;
409 | }
410 |
411 | .item .status {
412 | float: right;
413 | text-transform: uppercase;
414 | }
415 |
416 | #offline {
417 | background: #f00;
418 | color: #fff;
419 | padding: 1em;
420 | text-align: center;
421 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
422 | position: absolute;
423 | left: 0;
424 | right: 0;
425 | top: 0;
426 | }
427 |
428 | .clearfix:before,
429 | .clearfix:after {
430 | content: " ";
431 | display: table;
432 | }
433 |
434 | .clearfix:after {
435 | clear: both;
436 | }
437 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Sky-High Airport Arrivals
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
Arrivals
19 |
20 |
21 |
22 |
23 |
24 | -
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/demo/js/arrivals.js:
--------------------------------------------------------------------------------
1 | var Arrivals = (function() {
2 | function ArrivalViewModel() {
3 | var self = this;
4 | self.title = "";
5 | self.status = "";
6 | self.time = "";
7 | }
8 |
9 | function ArrivalApiService() {
10 | var self = this;
11 |
12 | // retrieves all arrivals from the API
13 | self.getAll = function() {
14 | return new Promise(function(resolve, reject) {
15 | var request = new XMLHttpRequest();
16 | request.open('GET', './api/data.json');
17 |
18 | request.onload = function() {
19 | // success
20 | if (request.status === 200) {
21 | // resolve the promise with the parsed response text (assumes JSON)
22 | resolve(JSON.parse(request.response));
23 | } else {
24 | // error retrieving file
25 | reject(Error(request.statusText));
26 | }
27 | };
28 |
29 | request.onerror = function() {
30 | // network errors
31 | reject(Error("Network Error"));
32 | };
33 |
34 | request.send();
35 | });
36 | };
37 | }
38 |
39 | function ArrivalAdapter() {
40 | var self = this;
41 |
42 | self.toArrivalViewModel = function(data) {
43 | if (data) {
44 | var vm = new ArrivalViewModel();
45 | vm.title = data.title;
46 | vm.status = data.status;
47 | vm.time = data.time;
48 | return vm;
49 | }
50 | return null;
51 | };
52 |
53 | self.toArrivalViewModels = function(data) {
54 | if (data && data.length > 0) {
55 | return data.map(function(item) {
56 | return self.toArrivalViewModel(item);
57 | });
58 | }
59 | return [];
60 | };
61 | }
62 |
63 | function ArrivalController(arrivalApiService, arrivalAdapter) {
64 | var self = this;
65 |
66 | self.getAll = function() {
67 | // retrieve all the arrivals from the API
68 | return arrivalApiService.getAll().then(function(response) {
69 | return arrivalAdapter.toArrivalViewModels(response);
70 | });
71 | };
72 | }
73 |
74 |
75 | // initialize the services and adapters
76 | var arrivalApiService = new ArrivalApiService();
77 | var arrivalAdapter = new ArrivalAdapter();
78 |
79 | // initialize the controller
80 | var arrivalController = new ArrivalController(arrivalApiService, arrivalAdapter);
81 |
82 | return {
83 | loadData: function() {
84 | // retrieve all routes
85 | document.querySelector(".arrivals-list").classList.add('loading')
86 | arrivalController.getAll().then(function(response) {
87 | // bind the arrivals to the UI
88 | Page.vm.arrivals(response);
89 | document.querySelector(".arrivals-list").classList.remove('loading')
90 | });
91 | }
92 | }
93 |
94 | })();
95 |
--------------------------------------------------------------------------------
/demo/js/main.js:
--------------------------------------------------------------------------------
1 | // register the service worker if available
2 | if ('serviceWorker' in navigator) {
3 | navigator.serviceWorker.register('./sw.js').then(function(reg) {
4 | console.log('Successfully registered service worker', reg);
5 | }).catch(function(err) {
6 | console.warn('Error whilst registering service worker', err);
7 | });
8 | }
9 |
10 | window.addEventListener('online', function(e) {
11 | // re-sync data with server
12 | console.log("You are online");
13 | Page.hideOfflineWarning();
14 | Arrivals.loadData();
15 | }, false);
16 |
17 | window.addEventListener('offline', function(e) {
18 | // queue up events for server
19 | console.log("You are offline");
20 | Page.showOfflineWarning();
21 | }, false);
22 |
23 | // check if the user is connected
24 | if (navigator.onLine) {
25 | Arrivals.loadData();
26 | } else {
27 | // show offline message
28 | Page.showOfflineWarning();
29 | }
30 |
31 | // set knockout view model bindings
32 | ko.applyBindings(Page.vm);
33 |
--------------------------------------------------------------------------------
/demo/js/page.js:
--------------------------------------------------------------------------------
1 | var Page = (function() {
2 |
3 | // declare the view model used within the page
4 | function ViewModel() {
5 | var self = this;
6 | self.arrivals = ko.observableArray([]);
7 | }
8 |
9 | // expose the view model through the Page module
10 | return {
11 | vm: new ViewModel(),
12 | hideOfflineWarning: function() {
13 | // enable the live data
14 | document.querySelector(".arrivals-list").classList.remove('loading')
15 | // remove the offline message
16 | document.getElementById("offline").remove();
17 | // load the live data
18 | },
19 | showOfflineWarning: function() {
20 | // disable the live data
21 | document.querySelector(".arrivals-list").classList.add('loading')
22 | // load html template informing the user they are offline
23 | var request = new XMLHttpRequest();
24 | request.open('GET', './offline.html', true);
25 |
26 | request.onload = function() {
27 | if (request.status === 200) {
28 | // success
29 | // create offline element with HTML loaded from offline.html template
30 | var offlineMessageElement = document.createElement("div");
31 | offlineMessageElement.setAttribute("id", "offline");
32 | offlineMessageElement.innerHTML = request.responseText;
33 | document.getElementById("main").appendChild(offlineMessageElement);
34 | } else {
35 | // error retrieving file
36 | console.warn('Error retrieving offline.html');
37 | }
38 | };
39 |
40 | request.onerror = function() {
41 | // network errors
42 | console.error('Connection error');
43 | };
44 |
45 | request.send();
46 | }
47 | }
48 |
49 | })();
50 |
--------------------------------------------------------------------------------
/demo/js/vendor/knockout-3.3.0.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Knockout JavaScript library v3.3.0
3 | * (c) Steven Sanderson - http://knockoutjs.com/
4 | * License: MIT (http://www.opensource.org/licenses/mit-license.php)
5 | */
6 |
7 | (function() {(function(p){var y=this||(0,eval)("this"),w=y.document,M=y.navigator,u=y.jQuery,E=y.JSON;(function(p){"function"===typeof define&&define.amd?define(["exports","require"],p):"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?p(module.exports||exports):p(y.ko={})})(function(N,O){function J(a,d){return null===a||typeof a in Q?a===d:!1}function R(a,d){var c;return function(){c||(c=setTimeout(function(){c=p;a()},d))}}function S(a,d){var c;return function(){clearTimeout(c);
8 | c=setTimeout(a,d)}}function K(b,d,c,e){a.d[b]={init:function(b,k,h,l,g){var m,x;a.w(function(){var q=a.a.c(k()),n=!c!==!q,r=!x;if(r||d||n!==m)r&&a.Z.oa()&&(x=a.a.la(a.e.childNodes(b),!0)),n?(r||a.e.T(b,a.a.la(x)),a.Ja(e?e(g,q):g,b)):a.e.ma(b),m=n},null,{q:b});return{controlsDescendantBindings:!0}}};a.h.ka[b]=!1;a.e.R[b]=!0}var a="undefined"!==typeof N?N:{};a.b=function(b,d){for(var c=b.split("."),e=a,f=0;fa.a.m(c,b[g])&&c.push(b[g]);return c},Ka:function(a,b){a=a||[];for(var c=[],g=0,d=a.length;gd?g&&b.push(c):g||b.splice(d,1)},za:f,extend:d,Fa:c,Ga:f?c:d,A:b,pa:function(a,b){if(!a)return a;var c={},g;for(g in a)a.hasOwnProperty(g)&&(c[g]=b(a[g],g,a));return c},Ra:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},Jb:function(b){b=a.a.O(b);for(var c=(b[0]&&b[0].ownerDocument||w).createElement("div"),g=0,d=b.length;g<
13 | d;g++)c.appendChild(a.S(b[g]));return c},la:function(b,c){for(var g=0,d=b.length,e=[];gg?a.setAttribute("selected",b):a.selected=b},ib:function(a){return null===a||a===p?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},Dc:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},jc:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?
15 | a.parentNode:a);if(b.compareDocumentPosition)return 16==(b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;return!!a},Qa:function(b){return a.a.jc(b,b.ownerDocument.documentElement)},tb:function(b){return!!a.a.vb(b,a.a.Qa)},v:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},n:function(b,c,d){var m=g&&l[c];if(!m&&u)u(b).bind(c,d);else if(m||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var e=function(a){d.call(b,a)},f="on"+c;b.attachEvent(f,e);a.a.C.fa(b,
16 | function(){b.detachEvent(f,e)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,d,!1)},qa:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var g;"input"===a.a.v(b)&&b.type&&"click"==c.toLowerCase()?(g=b.type,g="checkbox"==g||"radio"==g):g=!1;if(u&&!g)u(b).trigger(c);else if("function"==typeof w.createEvent)if("function"==typeof b.dispatchEvent)g=w.createEvent(h[c]||"HTMLEvents"),g.initEvent(c,
17 | !0,!0,y,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(g);else throw Error("The supplied element doesn't support dispatchEvent");else if(g&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");},c:function(b){return a.F(b)?b():b},cb:function(b){return a.F(b)?b.B():b},Ia:function(b,c,g){var d;c&&("object"===typeof b.classList?(d=b.classList[g?"add":"remove"],a.a.o(c.match(m),function(a){d.call(b.classList,a)})):"string"===
18 | typeof b.className.baseVal?e(b.className,"baseVal",c,g):e(b,"className",c,g))},Ha:function(b,c){var g=a.a.c(c);if(null===g||g===p)g="";var d=a.e.firstChild(b);!d||3!=d.nodeType||a.e.nextSibling(d)?a.e.T(b,[b.ownerDocument.createTextNode(g)]):d.data=g;a.a.mc(b)},Rb:function(a,b){a.name=b;if(7>=g)try{a.mergeAttributes(w.createElement(""),!1)}catch(c){}},mc:function(a){9<=g&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},kc:function(a){if(g){var b=a.style.width;
19 | a.style.width=0;a.style.width=b}},Bc:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var g=[],d=b;d<=c;d++)g.push(d);return g},O:function(a){for(var b=[],c=0,g=a.length;c",""]||!f.indexOf("
",
28 | ""]||(!f.indexOf(" | ","
"]||[0,"",""],k="ignored"+f[1]+b+f[2]+"
";for("function"==typeof c.innerShiv?e.appendChild(c.innerShiv(k)):e.innerHTML=k;f[0]--;)e=e.lastChild;c=a.a.O(e.lastChild.childNodes)}return c};a.a.gb=function(b,d){a.a.Ra(b);d=a.a.c(d);if(null!==d&&d!==p)if("string"!=typeof d&&(d=d.toString()),u)u(b).html(d);else for(var c=a.a.ca(d,b.ownerDocument),e=0;ef[0]?r+f[0]:f[0]),r);for(var r=1===v?r:Math.min(d+(f[1]||0),r),v=d+v-2,t=Math.max(r,v),G=[],A=[],p=2;dc;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.Vb(b);return a.a.jb(b,c,d)};c.prototype={save:function(b,c){var d=a.a.m(this.keys,b);0<=d?this.mb[d]=c:(this.keys.push(b),this.mb.push(c))},get:function(b){b=a.a.m(this.keys,b);return 0<=b?this.mb[b]:p}}})();a.b("toJS",a.Vb);a.b("toJSON",a.toJSON);(function(){a.i={s:function(b){switch(a.a.v(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.f.get(b,a.d.options.ab):7>=a.a.M?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?
51 | b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.i.s(b.options[b.selectedIndex]):p;default:return b.value}},Y:function(b,d,c){switch(a.a.v(b)){case "option":switch(typeof d){case "string":a.a.f.set(b,a.d.options.ab,p);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=d;break;default:a.a.f.set(b,a.d.options.ab,d),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof d?d:""}break;case "select":if(""===d||null===d)d=p;for(var e=-1,f=0,k=b.options.length,
52 | h;f=n){c.push(x&&h.length?{key:x,
53 | value:h.join("")}:{unknown:x||h.join("")});x=n=0;h=[];continue}}else if(58===t){if(!n&&!x&&1===h.length){x=h.pop();continue}}else 47===t&&r&&1a.a.M&&(a.g.register=function(a){return function(b){w.createElement(b);
78 | return a.apply(this,arguments)}}(a.g.register),w.createDocumentFragment=function(b){return function(){var d=b(),f=a.g.$b,k;for(k in f)f.hasOwnProperty(k)&&d.createElement(k);return d}}(w.createDocumentFragment))})();(function(b){function d(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.la(c);a.e.T(d,b)}function c(a,b,c,d){var e=a.createViewModel;return e?e.call(a,d,{element:b,templateNodes:c}):d}var e=0;a.d.component={init:function(f,k,h,l,g){function m(){var a=x&&
79 | x.dispose;"function"===typeof a&&a.call(x);q=null}var x,q,n=a.a.O(a.e.childNodes(f));a.a.C.fa(f,m);a.w(function(){var l=a.a.c(k()),h,t;"string"===typeof l?h=l:(h=a.a.c(l.name),t=a.a.c(l.params));if(!h)throw Error("No component name specified");var p=q=++e;a.g.get(h,function(e){if(q===p){m();if(!e)throw Error("Unknown component '"+h+"'");d(h,e,f);var l=c(e,f,n,t);e=g.createChildContext(l,b,function(a){a.$component=l;a.$componentTemplateNodes=n});x=l;a.Ja(e,f)}})},null,{q:f});return{controlsDescendantBindings:!0}}};
80 | a.e.R.component=!0})();var P={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,d){var c=a.a.c(d())||{};a.a.A(c,function(c,d){d=a.a.c(d);var k=!1===d||null===d||d===p;k&&b.removeAttribute(c);8>=a.a.M&&c in P?(c=P[c],k?b.removeAttribute(c):b[c]=d):k||b.setAttribute(c,d.toString());"name"===c&&a.a.Rb(b,k?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b,d,c){function e(){var e=b.checked,f=x?k():e;if(!a.Z.Ca()&&(!l||e)){var h=a.k.u(d);g?m!==f?(e&&(a.a.ga(h,
81 | f,!0),a.a.ga(h,m,!1)),m=f):a.a.ga(h,f,e):a.h.ra(h,c,"checked",f,!0)}}function f(){var c=a.a.c(d());b.checked=g?0<=a.a.m(c,k()):h?c:k()===c}var k=a.Nb(function(){return c.has("checkedValue")?a.a.c(c.get("checkedValue")):c.has("value")?a.a.c(c.get("value")):b.value}),h="checkbox"==b.type,l="radio"==b.type;if(h||l){var g=h&&a.a.c(d())instanceof Array,m=g?k():p,x=l||g;l&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.w(e,null,{q:b});a.a.n(b,"click",e);a.w(f,null,{q:b})}}};a.h.V.checked=!0;a.d.checkedValue=
82 | {update:function(b,d){b.value=a.a.c(d())}}})();a.d.css={update:function(b,d){var c=a.a.c(d());null!==c&&"object"==typeof c?a.a.A(c,function(c,d){d=a.a.c(d);a.a.Ia(b,c,d)}):(c=String(c||""),a.a.Ia(b,b.__ko__cssValue,!1),b.__ko__cssValue=c,a.a.Ia(b,c,!0))}};a.d.enable={update:function(b,d){var c=a.a.c(d());c&&b.disabled?b.removeAttribute("disabled"):c||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,d){a.d.enable.update(b,function(){return!a.a.c(d())})}};a.d.event={init:function(b,d,c,
83 | e,f){var k=d()||{};a.a.A(k,function(h){"string"==typeof h&&a.a.n(b,h,function(b){var g,m=d()[h];if(m){try{var k=a.a.O(arguments);e=f.$data;k.unshift(e);g=m.apply(e,k)}finally{!0!==g&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===c.get(h+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};a.d.foreach={Ib:function(b){return function(){var d=b(),c=a.a.cb(d);if(!c||"number"==typeof c.length)return{foreach:d,templateEngine:a.P.Va};a.a.c(d);return{foreach:c.data,as:c.as,
84 | includeDestroyed:c.includeDestroyed,afterAdd:c.afterAdd,beforeRemove:c.beforeRemove,afterRender:c.afterRender,beforeMove:c.beforeMove,afterMove:c.afterMove,templateEngine:a.P.Va}}},init:function(b,d){return a.d.template.init(b,a.d.foreach.Ib(d))},update:function(b,d,c,e,f){return a.d.template.update(b,a.d.foreach.Ib(d),c,e,f)}};a.h.ka.foreach=!1;a.e.R.foreach=!0;a.d.hasfocus={init:function(b,d,c){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(m){g=
85 | f.body}e=g===b}f=d();a.h.ra(f,c,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),k=e.bind(null,!1);a.a.n(b,"focus",f);a.a.n(b,"focusin",f);a.a.n(b,"blur",k);a.a.n(b,"focusout",k)},update:function(b,d){var c=!!a.a.c(d());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===c||(c?b.focus():b.blur(),a.k.u(a.a.qa,null,[b,c?"focusin":"focusout"]))}};a.h.V.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.h.V.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},
86 | update:function(b,d){a.a.gb(b,d())}};K("if");K("ifnot",!1,!0);K("with",!0,!1,function(a,d){return a.createChildContext(d)});var L={};a.d.options={init:function(b){if("select"!==a.a.v(b))throw Error("options binding applies only to SELECT elements");for(;0a.a.M)var k=a.a.f.I(),h=a.a.f.I(),l=function(b){var c=this.activeElement;(c=c&&a.a.f.get(c,h))&&c(b)},g=function(b,c){var d=b.ownerDocument;a.a.f.get(d,k)||(a.a.f.set(d,k,!0),a.a.n(d,"selectionchange",l));a.a.f.set(b,h,c)};a.d.textInput={init:function(b,c,l){function h(c,d){a.a.n(b,c,d)}function k(){var d=a.a.c(c());if(null===d||d===p)d="";w!==p&&d===w?setTimeout(k,4):b.value!==d&&(u=d,b.value=d)}function v(){A||
93 | (w=b.value,A=setTimeout(t,4))}function t(){clearTimeout(A);w=A=p;var d=b.value;u!==d&&(u=d,a.h.ra(c(),l,"textInput",d))}var u=b.value,A,w;10>a.a.M?(h("propertychange",function(a){"value"===a.propertyName&&t()}),8==a.a.M&&(h("keyup",t),h("keydown",t)),8<=a.a.M&&(g(b,t),h("dragend",v))):(h("input",t),5>e&&"textarea"===a.a.v(b)?(h("keydown",v),h("paste",v),h("cut",v)):11>d?h("keydown",v):4>f&&(h("DOMAutoComplete",t),h("dragdrop",t),h("drop",t)));h("change",t);a.w(k,null,{q:b})}};a.h.V.textInput=!0;a.d.textinput=
94 | {preprocess:function(a,b,c){c("textInput",a)}}})();a.d.uniqueName={init:function(b,d){if(d()){var c="ko_unique_"+ ++a.d.uniqueName.fc;a.a.Rb(b,c)}}};a.d.uniqueName.fc=0;a.d.value={after:["options","foreach"],init:function(b,d,c){if("input"!=b.tagName.toLowerCase()||"checkbox"!=b.type&&"radio"!=b.type){var e=["change"],f=c.get("valueUpdate"),k=!1,h=null;f&&("string"==typeof f&&(f=[f]),a.a.ia(e,f),e=a.a.wb(e));var l=function(){h=null;k=!1;var e=d(),g=a.i.s(b);a.h.ra(e,c,"value",g)};!a.a.M||"input"!=
95 | b.tagName.toLowerCase()||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.m(e,"propertychange")||(a.a.n(b,"propertychange",function(){k=!0}),a.a.n(b,"focus",function(){k=!1}),a.a.n(b,"blur",function(){k&&l()}));a.a.o(e,function(c){var d=l;a.a.Dc(c,"after")&&(d=function(){h=a.i.s(b);setTimeout(l,0)},c=c.substring(5));a.a.n(b,c,d)});var g=function(){var e=a.a.c(d()),f=a.i.s(b);if(null!==h&&e===h)setTimeout(g,0);else if(e!==f)if("select"===a.a.v(b)){var l=c.get("valueAllowUnset"),
96 | f=function(){a.i.Y(b,e,l)};f();l||e===a.i.s(b)?setTimeout(f,0):a.k.u(a.a.qa,null,[b,"change"])}else a.i.Y(b,e)};a.w(g,null,{q:b})}else a.va(b,{checkedValue:d})},update:function(){}};a.h.V.value=!0;a.d.visible={update:function(b,d){var c=a.a.c(d()),e="none"!=b.style.display;c&&!e?b.style.display="":!c&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(d,c,e,f,k){return a.d.event.init.call(this,d,function(){var a={};a[b]=c();return a},e,f,k)}}})("click");a.J=function(){};a.J.prototype.renderTemplateSource=
97 | function(){throw Error("Override renderTemplateSource");};a.J.prototype.createJavaScriptEvaluatorBlock=function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.J.prototype.makeTemplateSource=function(b,d){if("string"==typeof b){d=d||w;var c=d.getElementById(b);if(!c)throw Error("Cannot find template with ID "+b);return new a.t.l(c)}if(1==b.nodeType||8==b.nodeType)return new a.t.ha(b);throw Error("Unknown template type: "+b);};a.J.prototype.renderTemplate=function(a,d,c,e){a=this.makeTemplateSource(a,
98 | e);return this.renderTemplateSource(a,d,c,e)};a.J.prototype.isTemplateRewritten=function(a,d){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,d).data("isRewritten")};a.J.prototype.rewriteTemplate=function(a,d,c){a=this.makeTemplateSource(a,c);d=d(a.text());a.text(d);a.data("isRewritten",!0)};a.b("templateEngine",a.J);a.kb=function(){function b(b,c,d,h){b=a.h.bb(b);for(var l=a.h.ka,g=0;g]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,c=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{lc:function(b,
100 | c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.kb.xc(b,c)},d)},xc:function(a,f){return a.replace(d,function(a,c,d,e,m){return b(m,c,d,f)}).replace(c,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},dc:function(b,c){return a.H.$a(function(d,h){var l=d.nextSibling;l&&l.nodeName.toLowerCase()===c&&a.va(l,b,h)})}}}();a.b("__tr_ambtns",a.kb.dc);(function(){a.t={};a.t.l=function(a){this.l=a};a.t.l.prototype.text=function(){var b=a.a.v(this.l),b="script"===b?"text":
101 | "textarea"===b?"value":"innerHTML";if(0==arguments.length)return this.l[b];var d=arguments[0];"innerHTML"===b?a.a.gb(this.l,d):this.l[b]=d};var b=a.a.f.I()+"_";a.t.l.prototype.data=function(c){if(1===arguments.length)return a.a.f.get(this.l,b+c);a.a.f.set(this.l,b+c,arguments[1])};var d=a.a.f.I();a.t.ha=function(a){this.l=a};a.t.ha.prototype=new a.t.l;a.t.ha.prototype.text=function(){if(0==arguments.length){var b=a.a.f.get(this.l,d)||{};b.lb===p&&b.Na&&(b.lb=b.Na.innerHTML);return b.lb}a.a.f.set(this.l,
102 | d,{lb:arguments[0]})};a.t.l.prototype.nodes=function(){if(0==arguments.length)return(a.a.f.get(this.l,d)||{}).Na;a.a.f.set(this.l,d,{Na:arguments[0]})};a.b("templateSources",a.t);a.b("templateSources.domElement",a.t.l);a.b("templateSources.anonymousTemplate",a.t.ha)})();(function(){function b(b,c,d){var e;for(c=a.e.nextSibling(c);b&&(e=b)!==c;)b=a.e.nextSibling(e),d(e,b)}function d(c,d){if(c.length){var e=c[0],f=c[c.length-1],h=e.parentNode,k=a.L.instance,r=k.preprocessNode;if(r){b(e,f,function(a,
103 | b){var c=a.previousSibling,d=r.call(k,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):(c.push(e,f),a.a.na(c,h))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.ub(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.H.Xb(b,[d])});a.a.na(c,h)}}function c(a){return a.nodeType?a:0a.a.M?0:b.nodes)?b.nodes():null)return a.a.O(d.cloneNode(!0).childNodes);b=b.text();return a.a.ca(b,e)};a.P.Va=new a.P;a.hb(a.P.Va);a.b("nativeTemplateEngine",a.P);(function(){a.Ya=function(){var a=this.uc=function(){if(!u||!u.tmpl)return 0;try{if(0<=u.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,
114 | e,f,k){k=k||w;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=u.template(null,"{{ko_with $item.koBindingContext}}"+h+"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=u.extend({koBindingContext:e},f.templateOptions);e=u.tmpl(h,b,e);e.appendTo(k.createElement("div"));u.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+
115 | a+" })()) }}"};this.addTemplate=function(a,b){w.write("