├── docs ├── .nojekyll ├── favicon.ico ├── robots.txt ├── images │ ├── icons │ │ ├── icon-72x72.png │ │ ├── icon-96x96.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ ├── icon-512x512.png │ │ └── desktop │ │ │ ├── favicon.ico │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── mstile-150x150.png │ │ │ ├── android-chrome-192x192.png │ │ │ └── android-chrome-512x512.png │ ├── lighthouse-report.png │ ├── total_recall_landscape.jpg │ └── total_recall_portrait.jpg ├── humans.txt ├── browserconfig.xml ├── 404.html ├── offline.html ├── manifest.json ├── index.html ├── index.js ├── sw.js └── js │ └── zuix.min.js ├── source ├── .nojekyll ├── favicon.ico ├── pages │ ├── home │ │ ├── main_cover.html │ │ ├── main_cover.css │ │ ├── title_details.html │ │ ├── main_cover.js │ │ ├── title_details.css │ │ ├── title_details.js │ │ ├── list_top_rated.html │ │ ├── list_comedy.html │ │ └── list_tv_series.html │ ├── search.html │ ├── about.css │ ├── notifications.html │ ├── notifications.css │ ├── search.css │ ├── about.html │ ├── home.html │ ├── home.js │ └── home.css ├── robots.txt ├── images │ ├── icons │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ ├── icon-512x512.png │ │ ├── icon-72x72.png │ │ ├── icon-96x96.png │ │ └── desktop │ │ │ ├── favicon.ico │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── mstile-150x150.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── android-chrome-192x192.png │ │ │ └── android-chrome-512x512.png │ ├── lighthouse-report.png │ ├── total_recall_landscape.jpg │ └── total_recall_portrait.jpg ├── layout │ ├── header.html │ ├── footer.html │ ├── header.css │ └── footer.css ├── humans.txt ├── service-worker.js ├── browserconfig.xml ├── 404.html ├── offline.html ├── index.css ├── manifest.json ├── controllers │ └── movie_db.js ├── index.html ├── index.js ├── sw.js └── js │ ├── zuix-bundler.min.js │ ├── zuix-bundler.min.js.map │ ├── zuix-bundler.js │ └── zuix.min.js ├── .gitignore ├── package.json ├── LICENSE └── README.md /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /node_modules 3 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /source/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/favicon.ico -------------------------------------------------------------------------------- /docs/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /source/pages/home/main_cover.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | -------------------------------------------------------------------------------- /source/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /docs/images/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/icon-72x72.png -------------------------------------------------------------------------------- /docs/images/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/icon-96x96.png -------------------------------------------------------------------------------- /docs/images/lighthouse-report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/lighthouse-report.png -------------------------------------------------------------------------------- /docs/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /docs/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /docs/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /docs/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /docs/images/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/icon-384x384.png -------------------------------------------------------------------------------- /docs/images/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/icon-512x512.png -------------------------------------------------------------------------------- /source/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /source/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /source/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /source/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /source/images/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/icon-384x384.png -------------------------------------------------------------------------------- /source/images/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/icon-512x512.png -------------------------------------------------------------------------------- /source/images/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/icon-72x72.png -------------------------------------------------------------------------------- /source/images/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/icon-96x96.png -------------------------------------------------------------------------------- /source/images/lighthouse-report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/lighthouse-report.png -------------------------------------------------------------------------------- /source/layout/header.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /docs/images/icons/desktop/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/desktop/favicon.ico -------------------------------------------------------------------------------- /docs/images/total_recall_landscape.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/total_recall_landscape.jpg -------------------------------------------------------------------------------- /docs/images/total_recall_portrait.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/total_recall_portrait.jpg -------------------------------------------------------------------------------- /source/images/icons/desktop/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/desktop/favicon.ico -------------------------------------------------------------------------------- /source/images/total_recall_landscape.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/total_recall_landscape.jpg -------------------------------------------------------------------------------- /source/images/total_recall_portrait.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/total_recall_portrait.jpg -------------------------------------------------------------------------------- /docs/images/icons/desktop/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/desktop/favicon-16x16.png -------------------------------------------------------------------------------- /docs/images/icons/desktop/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/desktop/favicon-32x32.png -------------------------------------------------------------------------------- /docs/images/icons/desktop/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/desktop/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/images/icons/desktop/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/desktop/mstile-150x150.png -------------------------------------------------------------------------------- /source/images/icons/desktop/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/desktop/favicon-16x16.png -------------------------------------------------------------------------------- /source/images/icons/desktop/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/desktop/favicon-32x32.png -------------------------------------------------------------------------------- /source/images/icons/desktop/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/desktop/mstile-150x150.png -------------------------------------------------------------------------------- /source/images/icons/desktop/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/desktop/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/images/icons/desktop/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/desktop/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/images/icons/desktop/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/docs/images/icons/desktop/android-chrome-512x512.png -------------------------------------------------------------------------------- /source/images/icons/desktop/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/desktop/android-chrome-192x192.png -------------------------------------------------------------------------------- /source/images/icons/desktop/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zuixjs/zuix-web-flix/HEAD/source/images/icons/desktop/android-chrome-512x512.png -------------------------------------------------------------------------------- /source/pages/search.html: -------------------------------------------------------------------------------- 1 |
2 |
Search
search 3 |
4 | -------------------------------------------------------------------------------- /source/pages/about.css: -------------------------------------------------------------------------------- 1 | . { 2 | color: white; 3 | padding: 24px; 4 | margin-top: 64px; 5 | margin-bottom: 64px; 6 | } 7 | 8 | h1 { 9 | color: darkgrey; 10 | } 11 | -------------------------------------------------------------------------------- /source/pages/notifications.html: -------------------------------------------------------------------------------- 1 |
2 |
Notifications
notifications 3 |
4 | -------------------------------------------------------------------------------- /docs/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | -------------------------------------------------------------------------------- /source/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | -------------------------------------------------------------------------------- /source/service-worker.js: -------------------------------------------------------------------------------- 1 | if ('serviceWorker' in navigator) { 2 | const p = navigator.serviceWorker 3 | .register('./sw.js') 4 | .then(reg => console.log('PWA service-worker ready.', reg)) 5 | .catch(err => console.error('Could not load service-worker.', err)); 6 | } 7 | -------------------------------------------------------------------------------- /source/pages/notifications.css: -------------------------------------------------------------------------------- 1 | . { 2 | color: white; 3 | } 4 | 5 | .message { 6 | color: darkgrey; 7 | min-height: 100vh; 8 | width: 100vw; 9 | font-size: 200%; 10 | } 11 | .message i { 12 | margin-left: 16px; 13 | color: darkgrey; 14 | font-size: 150%; 15 | } 16 | -------------------------------------------------------------------------------- /source/pages/search.css: -------------------------------------------------------------------------------- 1 | . { 2 | color: white; 3 | padding: 24px; 4 | } 5 | 6 | .message { 7 | color: darkgrey; 8 | min-height: 100vh; 9 | width: 100vw; 10 | font-size: 200%; 11 | } 12 | 13 | .message i { 14 | margin-left: 16px; 15 | color: darkgrey; 16 | font-size: 150%; 17 | } 18 | -------------------------------------------------------------------------------- /docs/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #ffc40d 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /source/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #ffc40d 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /source/layout/footer.html: -------------------------------------------------------------------------------- 1 | 4 | 7 | 10 | 13 | -------------------------------------------------------------------------------- /source/pages/home/main_cover.css: -------------------------------------------------------------------------------- 1 | .cover-wrapper { 2 | overflow: hidden; 3 | margin-left: auto; 4 | margin-right: auto; 5 | height: 90vh; 6 | background-image: linear-gradient(to bottom, rgba(0,0,0, 0.3), transparent 20%, transparent 80%, rgba(0,0,0, 0.3) 90%); 7 | } 8 | .cover { 9 | height: 90vh; 10 | background-size: cover; 11 | background-repeat: no-repeat; 12 | background-position-x: center; 13 | /*transition: transform 150ms ease;*/ 14 | } 15 | -------------------------------------------------------------------------------- /source/layout/header.css: -------------------------------------------------------------------------------- 1 | . { 2 | position: fixed; 3 | top:0; 4 | left:0; 5 | right:0; 6 | height:auto; 7 | z-index: 100; 8 | } 9 | 10 | .header { 11 | height: 56px; 12 | max-height: 56px; 13 | } 14 | 15 | .logo { 16 | padding-left: 12px; 17 | padding-right: 12px; 18 | font-size: 220%; 19 | font-weight: 500; 20 | color: #dd3532; 21 | text-shadow: 22 | 1px 1px 0 #600, 23 | -1px -1px 0 #600, 24 | 1px -1px 0 #600, 25 | -1px 1px 0 #600, 26 | 1px 1px 0 #600; 27 | } 28 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Offline 5 | 20 | 21 | 22 | 23 |
24 |

Not found =/

25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Offline 5 | 20 | 21 | 22 | 23 |
24 |

Not found =/

25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/offline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Offline 5 | 20 | 21 | 22 | 23 |
24 |

Offline =/

25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/offline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Offline 5 | 20 | 21 | 22 | 23 |
24 |

Offline =/

25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zuix-web-flix", 3 | "version": "1.0.0", 4 | "description": "Progressive Web App template with zero build steps.", 5 | "private": true, 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "http-server --cors ./source" 9 | }, 10 | "keywords": [ 11 | "zuix", 12 | "html", 13 | "pwa", 14 | "simple", 15 | "webapp" 16 | ], 17 | "author": "G-Labs", 18 | "contributors": [], 19 | "license": "MIT", 20 | "homepage": "https://github.com/zuixjs", 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/zuixjs/zuix-web-flix" 24 | }, 25 | "devDependencies": { 26 | "http-server": "^0.11.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/index.css: -------------------------------------------------------------------------------- 1 | /* CSS files add styling rules to your content */ 2 | 3 | body { 4 | font-family: "Trebuchet MS", Helvetica, sans-serif; 5 | font-size: 16px; 6 | margin: 0; padding: 0; 7 | background: #1a1a1a; 8 | -webkit-user-select:none; 9 | -moz-user-select:none; 10 | -ms-user-select:none; 11 | user-select:none; 12 | --overscroll-behavior-y: contain; 13 | } 14 | 15 | button:hover, a:hover { 16 | cursor: pointer; 17 | opacity: 0.8; 18 | } 19 | 20 | a { 21 | -webkit-user-select: none; 22 | -khtml-user-select: none; 23 | -moz-user-select: none; 24 | -o-user-select: none; 25 | user-select: none; 26 | } 27 | 28 | a, a:visited, a:hover { 29 | color: darkorange; 30 | text-decoration: none; 31 | } 32 | 33 | .noscroll { 34 | overflow: hidden; 35 | } 36 | -------------------------------------------------------------------------------- /source/pages/home/title_details.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |

6 |
7 | 8 |
9 |
10 |
11 |

Movie title

12 |
13 |
14 |

15 |

Vote 7.5

16 |
17 |
18 | 19 |
20 |
21 | add 22 |
23 |
24 | thumb_up 25 |
26 |
27 | share 28 |
29 |
30 | -------------------------------------------------------------------------------- /source/layout/footer.css: -------------------------------------------------------------------------------- 1 | . { 2 | position: fixed; 3 | left:0; bottom:0; right: 0; 4 | height: 56px; 5 | background: #121212; 6 | color: white; 7 | border-top: solid 1px #711615; 8 | text-align: center; 9 | z-index: 100; 10 | overflow: hidden; 11 | } 12 | 13 | button { 14 | padding: 12px; 15 | max-height: 56px; 16 | margin-left: 8px; 17 | margin-right: 8px; 18 | background: transparent; 19 | border: none; 20 | border-radius: 8px; 21 | color: rgba(0,0,0,0.5); 22 | -webkit-transition: all .3s; /* Safari */ 23 | transition: all .3s; 24 | } 25 | button:hover { 26 | background: rgba(255,255,255,0.25); 27 | } 28 | button:focus {outline:0;} 29 | button i { 30 | color: #7c7c7c; 31 | font-size: 32px !important; 32 | } 33 | button:hover i, .active i { 34 | color: white; 35 | } 36 | .active { 37 | transform: scale(1.2); 38 | text-shadow: 39 | 1px 1px 0 #000, 40 | -1px -1px 0 #000, 41 | 1px -1px 0 #000, 42 | -1px 1px 0 #000, 43 | 1px 1px 0 #000; 44 | } 45 | -------------------------------------------------------------------------------- /source/pages/about.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

About

4 | 5 |

6 | This web application template is built with zuix.js. 7 |
8 | Source code and documentation available from WebFlix repository. 9 |

10 | 11 |

Other resources

12 |
    13 |
  • 14 | zKit
    15 | a collection of components for modern web. 16 |
  • 17 |
  • 18 | HTML-PWA
    19 | a progressive web app template optimized for mobile devices. 20 |
  • 21 |
  • 22 | Web Book
    23 | a progressive web app template of a web book. 24 |
  • 25 |
26 | 27 |

Disclaimer

28 |

This product uses the TMDb API but is not endorsed or certified by TMDb.

29 | 30 |
31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 g-labs 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 | -------------------------------------------------------------------------------- /docs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebFlix Progressive Web App", 3 | "short_name": "WebFlix", 4 | "description": "A very cool web app powered by zUIx.js", 5 | "theme_color": "#121212", 6 | "background_color": "#1a1a1a", 7 | "display": "standalone", 8 | "Scope": "./", 9 | "start_url": "index.html", 10 | "icons": [ 11 | { 12 | "src": "images/icons/icon-72x72.png", 13 | "sizes": "72x72", 14 | "type": "image/png" 15 | }, 16 | { 17 | "src": "images/icons/icon-96x96.png", 18 | "sizes": "96x96", 19 | "type": "image/png" 20 | }, 21 | { 22 | "src": "images/icons/icon-128x128.png", 23 | "sizes": "128x128", 24 | "type": "image/png" 25 | }, 26 | { 27 | "src": "images/icons/icon-144x144.png", 28 | "sizes": "144x144", 29 | "type": "image/png" 30 | }, 31 | { 32 | "src": "images/icons/icon-152x152.png", 33 | "sizes": "152x152", 34 | "type": "image/png" 35 | }, 36 | { 37 | "src": "images/icons/icon-192x192.png", 38 | "sizes": "192x192", 39 | "type": "image/png" 40 | }, 41 | { 42 | "src": "images/icons/icon-384x384.png", 43 | "sizes": "384x384", 44 | "type": "image/png" 45 | }, 46 | { 47 | "src": "images/icons/icon-512x512.png", 48 | "sizes": "512x512", 49 | "type": "image/png" 50 | } 51 | ], 52 | "splash_pages": null 53 | } 54 | -------------------------------------------------------------------------------- /source/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebFlix Progressive Web App", 3 | "short_name": "WebFlix", 4 | "description": "A very cool web app powered by zUIx.js", 5 | "theme_color": "#121212", 6 | "background_color": "#1a1a1a", 7 | "display": "standalone", 8 | "Scope": "./", 9 | "start_url": "index.html", 10 | "icons": [ 11 | { 12 | "src": "images/icons/icon-72x72.png", 13 | "sizes": "72x72", 14 | "type": "image/png" 15 | }, 16 | { 17 | "src": "images/icons/icon-96x96.png", 18 | "sizes": "96x96", 19 | "type": "image/png" 20 | }, 21 | { 22 | "src": "images/icons/icon-128x128.png", 23 | "sizes": "128x128", 24 | "type": "image/png" 25 | }, 26 | { 27 | "src": "images/icons/icon-144x144.png", 28 | "sizes": "144x144", 29 | "type": "image/png" 30 | }, 31 | { 32 | "src": "images/icons/icon-152x152.png", 33 | "sizes": "152x152", 34 | "type": "image/png" 35 | }, 36 | { 37 | "src": "images/icons/icon-192x192.png", 38 | "sizes": "192x192", 39 | "type": "image/png" 40 | }, 41 | { 42 | "src": "images/icons/icon-384x384.png", 43 | "sizes": "384x384", 44 | "type": "image/png" 45 | }, 46 | { 47 | "src": "images/icons/icon-512x512.png", 48 | "sizes": "512x512", 49 | "type": "image/png" 50 | } 51 | ], 52 | "splash_pages": null 53 | } 54 | -------------------------------------------------------------------------------- /source/controllers/movie_db.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | zuix.controller(function(cp) { 3 | // TODO: Do not use this API key !!! 4 | // TODO: Get your free TMDB API key from https://themoviedb.org 5 | const tmdbKey = '--put--your-tmdb-api-key-here--'; 6 | cp.init = function() { 7 | cp.options().html = false; 8 | cp.options().css = false; 9 | }; 10 | cp.create = function(){ 11 | const title = cp.view().attr('title'); 12 | cp.view().css({ 13 | 'background-size': 'cover', 14 | 'background-position-x': 'center' 15 | }); 16 | if (title.length > 0) { 17 | zuix.$.ajax({ 18 | url: 'https://api.themoviedb.org/3/search/multi?api_key=' + tmdbKey + '&query=' + title, 19 | success: function (json) { 20 | const data = JSON.parse(json); 21 | if (data.total_results > 0) { 22 | const item = data.results[0]; 23 | const posterUrl = 'https://image.tmdb.org/t/p/w154' + item.poster_path; 24 | cp.view().css('background-image', 'url("' + posterUrl + '")'); 25 | // set absolute urls for images 26 | item.poster_path = 'https://image.tmdb.org/t/p/w780'+item.poster_path; 27 | item.backdrop_path = 'https://image.tmdb.org/t/p/w1280'+item.backdrop_path; 28 | // add on click listener to open the details page 29 | cp.view().on('click', function(){ 30 | if (detailsPage) detailsPage.show(item); 31 | }); 32 | } 33 | } 34 | }); 35 | } 36 | }; 37 | }); 38 | -------------------------------------------------------------------------------- /source/pages/home.html: -------------------------------------------------------------------------------- 1 |
4 | 5 |
6 |
7 | Bestseller 8 | • 9 | United States 10 | • 11 | Sci-fi 12 | • 13 | Action 14 |
15 |
16 | 19 | 25 | 28 |
29 |
30 | 31 | 32 |
34 | 35 | 36 |
38 | 39 | 40 |
42 | 43 |
44 | 45 |

46 | If you like this template give it a star ⭐ on GitHub, 47 | this will make the programmer happy 😄 that will probably continue to improve it 🚀 48 |

49 |

50 | Feature requests, critics and comments are also welcome 🌈 in the repository issue tracker 📑 51 |

52 | 53 |
54 | 55 | 56 | 57 |
59 | -------------------------------------------------------------------------------- /source/pages/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | zuix.controller(function(cp) { 3 | let coverItem; 4 | let mainCover; 5 | let headerOpacity = 0; 6 | 7 | cp.create = function() { 8 | // register button handlers 9 | cp.field('watch-btn').on('click', playVideo); 10 | cp.field('details-btn').on('click', function() { 11 | detailsPage.show(coverItem); 12 | }); 13 | // hide on startup 14 | cp.view().hide(); 15 | zuix.context(cp.field('main-cover'), function() { 16 | mainCover = this; 17 | // refresh cover pictures 18 | if (coverItem) setCoverItem(coverItem); 19 | }); 20 | // expose public methods 21 | cp.expose('cover', setCoverItem); 22 | cp.expose('sync', syncWithScroll); 23 | }; 24 | 25 | function setCoverItem(item) { 26 | coverItem = item; 27 | if (mainCover) { 28 | mainCover.pictures( 29 | coverItem.poster_path, 30 | coverItem.backdrop_path 31 | ); 32 | } 33 | return cp.context; 34 | } 35 | 36 | function playVideo() { 37 | if (coverItem.trailer) window.location.href = coverItem.trailer; 38 | } 39 | 40 | function syncWithScroll(data) { 41 | if (data == null) { 42 | zuix.field('header-bar') 43 | .css('background-color', 'rgba(18,18,18,' + headerOpacity + ')'); 44 | return; 45 | } 46 | let opacity = 1; 47 | if (data.event === 'hit-top') { 48 | opacity = 0; 49 | } else if (-data.info.viewport.y < data.info.viewport.height) { 50 | opacity = -data.info.viewport.y / (data.info.viewport.height); 51 | } 52 | if (opacity !== headerOpacity) { 53 | zuix.field('header-bar') 54 | .css('background-color', 'rgba(18,18,18,' + opacity + ')'); 55 | // cover parallax effect 56 | if (mainCover) mainCover.translate(data.info); 57 | headerOpacity = opacity; 58 | } 59 | return cp.context; 60 | } 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /source/pages/home/main_cover.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | zuix.controller(function(cp) { 3 | let cover; 4 | let isPortrait = false; 5 | let portraitImage; 6 | let landscapeImage; 7 | 8 | cp.create = function() { 9 | cover = cp.view('.cover'); 10 | // detect orientation change 11 | const orientation = window.matchMedia('(orientation: portrait)'); 12 | if (orientation.matches) isPortrait = true; 13 | orientation.addListener(orientationChange); 14 | // public method to set portrait and landscape images 15 | cp.expose('pictures', setPictures); 16 | // expose translate method for parallax effect 17 | cp.expose('translate', setOffsetY); 18 | }; 19 | cp.destroy = function() { 20 | window.matchMedia('(orientation: portrait)').removeListener(orientationChange); 21 | }; 22 | 23 | function setPictures(imageP, imageL) { 24 | portraitImage = imageP; 25 | landscapeImage = imageL; 26 | refresh(); 27 | return cp.context; 28 | } 29 | 30 | function setOffsetY(scrollInfo) { 31 | const offsetY = -scrollInfo.viewport.y / 6; 32 | cover.css('transform', 'translateY('+offsetY+'px)'); 33 | //const factor = (.08 / (scrollInfo.viewport.height / -scrollInfo.viewport.y)); 34 | //const magnify = 1 + factor; 35 | //cover.css('transform', 'translateY('+offsetY+'px) scale('+magnify+')'); 36 | // TODO: blur is too slow on mobile devices.. =/ 37 | // TODO: uncomment the following line if you want to try it out 38 | //cover.css('filter', 'blur('+(3*factor)+'rem)'); 39 | return cp.context; 40 | } 41 | 42 | function orientationChange(mediaEvent) { 43 | isPortrait = mediaEvent.matches; 44 | refresh(); 45 | } 46 | 47 | function refresh() { 48 | if (isPortrait) { 49 | if (portraitImage) { 50 | cover.css('background-image', 'url("'+portraitImage+'")'); 51 | } 52 | } else if (landscapeImage) { 53 | cover.css('background-image', 'url("'+landscapeImage+'")'); 54 | } 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /source/pages/home.css: -------------------------------------------------------------------------------- 1 | . { 2 | /* temporary hack to test page scroll */ 3 | /* height: 500vh; */ 4 | color: white; 5 | padding-bottom: 96px; 6 | overflow-x: hidden; 7 | } 8 | 9 | h1 { 10 | font-size: 120%; 11 | text-align: left; 12 | margin-top: 28px; 13 | margin-left: 8px; 14 | margin-bottom: 8px; 15 | } 16 | 17 | .content { 18 | position: absolute; 19 | top: 0; left: 0; 20 | padding-bottom: 56px; 21 | width: 100vw; 22 | height: 90vh; 23 | background-image: linear-gradient(to bottom, rgba(0,0,0, 0.15), transparent 20%, transparent 70%, rgba(0,0,0, 0.5) 80%); 24 | } 25 | 26 | .categories, .options { 27 | padding: 8px; 28 | } 29 | 30 | button { 31 | padding-left: 6px; 32 | padding-right: 16px; 33 | font-size: 120%; 34 | font-weight: 500; 35 | min-height: 38px; 36 | } 37 | button div i { 38 | margin-right: 6px; 39 | } 40 | 41 | button.flat-btn { 42 | border: 0; 43 | background: transparent; 44 | height: 56px; 45 | width: 56px; 46 | padding: 0; 47 | text-align: center; 48 | vertical-align: middle; 49 | } 50 | button.flat-btn i { 51 | color: white; 52 | font-size: 36px; 53 | line-height: 56px; 54 | } 55 | 56 | .gallery { 57 | width: 100%; 58 | height: 180px; 59 | } 60 | 61 | .movie { 62 | padding: 4px; 63 | } 64 | .movie span.item { 65 | display: inline-block; 66 | width: 120px; 67 | height: 180px; 68 | border: solid 1px rgba(255,255,255,.1); 69 | background-image: linear-gradient(rgba(0,0,0,0.15) 0, transparent 48px, transparent 40%, black 99%); 70 | } 71 | .movie span.item:hover { 72 | cursor: pointer; 73 | opacity: 0.8; 74 | } 75 | 76 | .about-box { 77 | margin-top: 56px; 78 | margin-bottom: 48px; 79 | font-size: 140%; 80 | font-family: "Benton Sans", "Helvetica Neue", helvetica, arial, sans-serif; 81 | line-height: 160%; 82 | } 83 | .about-box p { 84 | padding: 12px; 85 | } 86 | 87 | @media only screen and (min-device-width : 720px) { 88 | .gallery { 89 | height: 231px; 90 | } 91 | .movie span.item { 92 | width: 154px; 93 | height: 231px; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /source/pages/home/title_details.css: -------------------------------------------------------------------------------- 1 | . { 2 | position: fixed; 3 | z-index: 200; 4 | top: 0; left: 100vh; 5 | transition: left .2s ease-in-out; 6 | touch-action: none; 7 | width: 100vw; 8 | min-height: 100vh; 9 | background: #101010; 10 | color: white; 11 | } 12 | 13 | header { 14 | position: fixed; 15 | top: 0; left: 0; right: 0; height: 56px; 16 | z-index: 200; 17 | } 18 | header button { 19 | position: absolute; 20 | top: 0; left: 0; 21 | z-index: 10; 22 | border: 0; 23 | background: transparent; 24 | width: 56px; 25 | height: 56px; 26 | padding: 0; 27 | text-align: center; 28 | vertical-align: middle; 29 | } 30 | header button i { 31 | color: white; 32 | font-weight: 700; 33 | font-size: 160%; 34 | line-height: 48px; 35 | } 36 | header h1 { 37 | position: absolute; 38 | top: 14px; 39 | left: 56px; 40 | margin: 0; 41 | font-size: 160%; 42 | } 43 | 44 | main { 45 | position: absolute; 46 | top:0;left:0; bottom: 0; 47 | width: 100%; 48 | padding-bottom: 72px; 49 | overflow-y: auto; 50 | } 51 | 52 | footer { 53 | position: fixed; 54 | bottom: 0; left: 0; right: 0; height: 56px; 55 | background: #0a0a0a; 56 | border-top: solid 1px rgba(255,0,0,0.5); 57 | } 58 | 59 | .cover { 60 | background: black no-repeat; 61 | background-size: cover; 62 | background-position-x: center; 63 | position: relative; 64 | width: 100vw; 65 | height: 50vh; 66 | } 67 | 68 | .cover .background { 69 | position: absolute; 70 | left: 0; right: 0; top: 0; bottom: 0; 71 | background-image: linear-gradient(to bottom, rgba(0,0,0, 0.35) 1%, transparent 20%, transparent 80%, rgba(0,0,0, 0.35) 90%); 72 | } 73 | .cover h1 { 74 | z-index: 10; 75 | margin: 0; 76 | padding: 16px; 77 | font-size: 180%; 78 | text-shadow: 79 | 1px 1px 0 #000, 80 | -1px -1px 0 #000, 81 | 1px -1px 0 #000, 82 | -1px 1px 0 #000, 83 | 1px 1px 0 #000; 84 | } 85 | 86 | [data-ui-field="overview"] { 87 | font-size: 120%; 88 | line-height: 160%; 89 | padding: 16px; 90 | margin-top: 8px; 91 | margin-bottom: 0; 92 | color: whitesmoke; 93 | } 94 | 95 | h2 { 96 | color: darkgrey; 97 | } 98 | [data-ui-field="vote"] { 99 | color: white; 100 | } 101 | 102 | @media only screen and (min-device-width : 720px) { 103 | [data-ui-field="overview"] { 104 | font-size: 140%; 105 | line-height: 180%; 106 | } 107 | .cover h1 { 108 | font-size: 220%; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebFlix 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
61 | 62 |
63 | 64 | 65 |
67 | 68 | 69 |
70 | 71 | 72 |
73 | 74 | 75 |
76 | 77 |
78 | 79 | 80 |
83 | 84 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /source/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebFlix 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
61 | 62 |
63 | 64 | 65 |
67 | 68 | 69 |
70 | 71 | 72 |
73 | 74 | 75 |
76 | 77 |
78 | 79 | 80 |
83 | 84 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /source/pages/home/title_details.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | zuix.controller(function(cp) { 3 | let headerBar; 4 | let coverTitle; 5 | let headerTitle; 6 | let scrollHelper; 7 | cp.create = function() { 8 | headerBar = cp.view('header'); 9 | coverTitle = cp.view('main').find('h1'); 10 | headerTitle = headerBar.find('h1'); 11 | // register back button handler 12 | headerBar.find('button').on('click', hidePage); 13 | // load scroll helper used for the cross-fading title/header effect 14 | zuix.load('@lib/controllers/scroll_helper', { 15 | view: cp.view('main'), 16 | on: { 17 | 'scroll:change': function(e, data) { 18 | // make header transparent on top of page and cover title visible 19 | switch (data.event) { 20 | case 'hit-top': 21 | headerBar.css('background-color', 'rgba(33,33,33,0)'); 22 | headerTitle.css('opacity', 0); 23 | coverTitle.css('opacity', 1); 24 | cp.field('cover') 25 | .css('background-position-y', 0); 26 | break; 27 | case 'scroll': 28 | const viewport = data.info.viewport; 29 | if (viewport.y > -viewport.height / 2.5) { 30 | cp.field('cover') 31 | .css('background-position-y', -(viewport.y/5)+'px'); 32 | } 33 | break; 34 | } 35 | } 36 | }, 37 | ready: function() { 38 | scrollHelper = this; 39 | // watch elements with .watchable class (the title) 40 | this.watch('.watchable', function(el, data) { 41 | // synchronize header opacity with cover title position 42 | const opacity = (data.frame.dy/0.3); 43 | if (data.frame.dy < 0.3) { 44 | if (data.frame.dy > -0.3) { 45 | coverTitle.css('opacity', opacity+0.5); 46 | } 47 | if (-opacity >= 0) { 48 | headerBar.css('background-color', 'rgba(33,33,33,' + (-opacity) + ')'); 49 | } 50 | } 51 | if (data.frame.dy < 0.125) { 52 | headerTitle.css('opacity', -opacity); 53 | } 54 | }); 55 | } 56 | }); 57 | // hide on startup 58 | cp.view().hide(); 59 | // expose public methods 60 | cp.expose('show', showPage); 61 | cp.expose('hide', hidePage); 62 | }; 63 | function showPage(item) { 64 | // update location href 65 | window.location.href = "#details"; 66 | // show details page 67 | cp.view().show(); 68 | // go to top of page 69 | scrollHelper.scrollStart(); 70 | // this is a work-around otherwise animation would not start 71 | // any suggestion for a better solution is welcome =) 72 | setTimeout(function(){ 73 | cp.view().css('left', 0); 74 | }, 10); 75 | const backdropUrl = 'url("'+item.backdrop_path+'")'; 76 | cp.field('cover') 77 | .css('background-image', backdropUrl); 78 | cp.field('title').html(item.title || item.original_name); 79 | cp.field('overview').html(item.overview); 80 | cp.field('vote').html(item.vote_average); 81 | cp.trigger('page:show'); 82 | } 83 | function hidePage() { 84 | cp.view() 85 | .css('left', '100vw') 86 | .one('transitionend', function(){ 87 | cp.view().hide(); 88 | }); 89 | // fire 'hide' event 90 | cp.trigger('page:hide'); 91 | } 92 | }); 93 | -------------------------------------------------------------------------------- /source/pages/home/list_top_rated.html: -------------------------------------------------------------------------------- 1 |

Top rated on WebFlix

2 | 77 | -------------------------------------------------------------------------------- /source/pages/home/list_comedy.html: -------------------------------------------------------------------------------- 1 |

Comedy Movies

2 | 80 | -------------------------------------------------------------------------------- /source/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let currentPage; 4 | let mainPage; 5 | let detailsPage; 6 | 7 | zuix.using('script', './service-worker.js'); 8 | zuix.using('style', '//zuixjs.github.io/zkit/css/flex-layout-attribute.min.css'); 9 | zuix.using('style', './index.css'); 10 | 11 | window.options = { 12 | mainPage: { 13 | lazyLoad: false, 14 | ready: function() { 15 | mainPage = this.cover({ 16 | "vote_average":7.2, 17 | "title":"Total Recall", 18 | "poster_path":"images\/total_recall_portrait.jpg", 19 | "backdrop_path":"images\/total_recall_landscape.jpg", 20 | "overview":"Welcome to Rekall, the company that can turn your dreams into real memories. For a factory worker named Douglas Quaid, even though he's got a beautiful wife who he loves, the mind-trip sounds like the perfect vacation from his frustrating life - real memories of life as a super-spy might be just what he needs. But when the procedure goes horribly wrong, Quaid becomes a hunted man. Finding himself on the run from the police - controlled by Chancellor Cohaagen, the leader of the free world - Quaid teams up with a rebel fighter to find the head of the underground resistance and stop Cohaagen. The line between fantasy and reality gets blurred and the fate of his world hangs in the balance as Quaid discovers his true identity, his true love, and his true fate.", 21 | "release_date":"2012-08-02", 22 | "trailer": "https://youtube.com/watch?v=GljhR5rk5eY" 23 | }); 24 | showPage(0); 25 | } 26 | }, 27 | detailsPage: { 28 | lazyLoad: false, 29 | on: { 30 | 'page:show': function() { bodyScrollEnable(false); }, 31 | 'page:hide': function() { bodyScrollEnable(true); } 32 | }, 33 | ready: function() { 34 | detailsPage = this; 35 | } 36 | }, 37 | footerBar: { 38 | ready: function(){ 39 | const view = zuix.$(this.view()); 40 | const buttons = view.find('button'); 41 | buttons.each(function(i, el) { 42 | this.on('click', function() { 43 | buttons.removeClass('active'); 44 | this.addClass('active'); 45 | window.location.href = '#'+this.attr('ref'); 46 | }); 47 | }); 48 | } 49 | }, 50 | pageScroll: { 51 | on: { 52 | 'scroll:change': function(e, data) { 53 | // synchronize/animate main cover with scroll 54 | if (currentPage == 0 && mainPage) { 55 | mainPage.sync(data); 56 | } 57 | } 58 | } 59 | }, 60 | content_no_css: { 61 | css: false 62 | } 63 | }; 64 | 65 | // site navigation 66 | window.onhashchange = function() { 67 | if (window.location.hash.length > 0) { 68 | switch (window.location.hash) { 69 | case '#home': 70 | showPage(0); 71 | break; 72 | case '#search': 73 | showPage(1); 74 | break; 75 | case '#notifications': 76 | showPage(2); 77 | break; 78 | case '#about': 79 | showPage(3); 80 | break; 81 | } 82 | } else showPage(0); 83 | }; 84 | 85 | function showPage(i) { 86 | currentPage = i; 87 | // sync header bar transparency 88 | if (currentPage == 0) { 89 | mainPage.sync(); 90 | } else { 91 | zuix.field('header-bar') 92 | .css('background-color', 'rgba(33,33,33,1)'); 93 | } 94 | // hide details page if open 95 | if (detailsPage && detailsPage.view().style['display'] !== 'none') { 96 | detailsPage.hide(); 97 | } else { 98 | // show page 99 | zuix.field('pages') 100 | .children().hide() 101 | .eq(i).show(); 102 | } 103 | } 104 | 105 | function bodyScrollEnable(enable) { 106 | const body = zuix.$(document.body); 107 | if (enable === false) body.addClass('noscroll'); 108 | else body.removeClass('noscroll'); 109 | } 110 | 111 | // increase lazy-load hit area up to 112 | // 300px off the viewport boundaries 113 | // (circa 3 movie items ahead) 114 | //zuix.lazyLoad(true, -500); 115 | 116 | // Turn off debug output 117 | window.zuixNoConsoleOutput = true; 118 | -------------------------------------------------------------------------------- /docs/index.js: -------------------------------------------------------------------------------- 1 | /* global zuix */ 2 | 'use strict'; 3 | 4 | let currentPage; 5 | let mainPage; 6 | let detailsPage; 7 | 8 | zuix.using('script', './service-worker.js'); 9 | zuix.using('style', '//zuixjs.github.io/zkit/css/flex-layout-attribute.min.css'); 10 | zuix.using('style', './index.css'); 11 | 12 | window.options = { 13 | mainPage: { 14 | lazyLoad: false, 15 | ready: function() { 16 | mainPage = this.cover({ 17 | "vote_average":7.2, 18 | "title":"Total Recall", 19 | "poster_path":"images\/total_recall_portrait.jpg", 20 | "backdrop_path":"images\/total_recall_landscape.jpg", 21 | "overview":"Welcome to Rekall, the company that can turn your dreams into real memories. For a factory worker named Douglas Quaid, even though he's got a beautiful wife who he loves, the mind-trip sounds like the perfect vacation from his frustrating life - real memories of life as a super-spy might be just what he needs. But when the procedure goes horribly wrong, Quaid becomes a hunted man. Finding himself on the run from the police - controlled by Chancellor Cohaagen, the leader of the free world - Quaid teams up with a rebel fighter to find the head of the underground resistance and stop Cohaagen. The line between fantasy and reality gets blurred and the fate of his world hangs in the balance as Quaid discovers his true identity, his true love, and his true fate.", 22 | "release_date":"2012-08-02", 23 | "trailer": "https://youtube.com/watch?v=GljhR5rk5eY" 24 | }); 25 | showPage(0); 26 | } 27 | }, 28 | detailsPage: { 29 | lazyLoad: false, 30 | on: { 31 | 'page:show': function() { bodyScrollEnable(false); }, 32 | 'page:hide': function() { bodyScrollEnable(true); } 33 | }, 34 | ready: function() { 35 | detailsPage = this; 36 | } 37 | }, 38 | footerBar: { 39 | ready: function(){ 40 | const view = zuix.$(this.view()); 41 | const buttons = view.find('button'); 42 | buttons.each(function(i, el) { 43 | this.on('click', function() { 44 | buttons.removeClass('active'); 45 | this.addClass('active'); 46 | window.location.href = '#'+this.attr('ref'); 47 | }); 48 | }); 49 | } 50 | }, 51 | pageScroll: { 52 | on: { 53 | 'scroll:change': function(e, data) { 54 | // synchronize/animate main cover with scroll 55 | if (currentPage == 0 && mainPage) { 56 | mainPage.sync(data); 57 | } 58 | } 59 | } 60 | }, 61 | content_no_css: { 62 | css: false 63 | } 64 | }; 65 | 66 | // site navigation 67 | window.onhashchange = function() { 68 | if (window.location.hash.length > 0) { 69 | switch (window.location.hash) { 70 | case '#home': 71 | showPage(0); 72 | break; 73 | case '#search': 74 | showPage(1); 75 | break; 76 | case '#notifications': 77 | showPage(2); 78 | break; 79 | case '#about': 80 | showPage(3); 81 | break; 82 | } 83 | } else showPage(0); 84 | }; 85 | 86 | function showPage(i) { 87 | currentPage = i; 88 | // sync header bar transparency 89 | if (currentPage == 0) { 90 | mainPage.sync(); 91 | } else { 92 | zuix.field('header-bar') 93 | .css('background-color', 'rgba(33,33,33,1)'); 94 | } 95 | // hide details page if open 96 | if (detailsPage && detailsPage.view().style['display'] !== 'none') { 97 | detailsPage.hide(); 98 | } else { 99 | // show page 100 | zuix.field('pages') 101 | .children().hide() 102 | .eq(i).show(); 103 | } 104 | } 105 | 106 | function bodyScrollEnable(enable) { 107 | const body = zuix.$(document.body); 108 | if (enable === false) body.addClass('noscroll'); 109 | else body.removeClass('noscroll'); 110 | } 111 | 112 | // increase lazy-load hit area up to 113 | // 300px off the viewport boundaries 114 | // (circa 3 movie items ahead) 115 | //zuix.lazyLoad(true, -500); 116 | 117 | // Turn off debug output 118 | window.zuixNoConsoleOutput = true; 119 | -------------------------------------------------------------------------------- /source/pages/home/list_tv_series.html: -------------------------------------------------------------------------------- 1 |

TV Series

2 | 92 | -------------------------------------------------------------------------------- /docs/sw.js: -------------------------------------------------------------------------------- 1 | // TODO: increase `version` number to force cache update when publishing a new release 2 | const version = 'v2'; 3 | 4 | const config = { 5 | cacheRemote: true, 6 | version: version+'::', 7 | preCachingItems: [ 8 | 'app.bundle.js', 9 | 'index.html', 10 | 'index.js', 11 | 'offline.html', 12 | '404.html', 13 | 'sw.js' 14 | ], 15 | blacklistCacheItems: [ 16 | 'index.html', 17 | 'service-worker.js' 18 | ], 19 | offlineImage: '' + 'Offline' + '' + '' + 'offline', 20 | offlinePage: 'offline.html', 21 | notFoundPage: '404.html', 22 | offlineTmdbResults: { 23 | page: 1, 24 | results: [{ 25 | "title": "Offline", 26 | "poster_path": "https://tmdb.org/offline.jpg", 27 | "backdrop_path": "https://tmdb.org/offline.jpg", 28 | "overview": "" 29 | }], 30 | total_pages: 1, 31 | total_results: 1 32 | } 33 | }; 34 | 35 | function cacheName(key, opts) { 36 | return `${opts.version}${key}`; 37 | } 38 | 39 | function addToCache(cacheKey, request, response) { 40 | if (response.ok) { 41 | const copy = response.clone(); 42 | caches.open(cacheKey).then(cache => { 43 | cache.put(request, copy); 44 | }); 45 | } 46 | return response; 47 | } 48 | 49 | function fetchFromCache(event) { 50 | return caches.match(event.request).then(response => { 51 | if (!response) { 52 | throw Error(`${event.request.url} not found in cache`); 53 | } else if (response.status === 404) { 54 | return caches.match(config.notFoundPage); 55 | } 56 | return response; 57 | }); 58 | } 59 | 60 | function offlineResponse(resourceType, opts) { 61 | if (resourceType === 'content') { 62 | return caches.match(opts.offlinePage); 63 | } 64 | if (resourceType === 'image') { 65 | return new Response(opts.offlineImage, { 66 | headers: { 'Content-Type': 'image/svg+xml' } 67 | }); 68 | } 69 | if (resourceType === 'tmdb') { 70 | return new Response(JSON.stringify(opts.offlineTmdbResults), { 71 | headers: { 'Content-Type': 'application/json' } 72 | }); 73 | } 74 | return undefined; 75 | } 76 | 77 | self.addEventListener('install', event => { 78 | event.waitUntil(caches.open( 79 | cacheName('static', config) 80 | ) 81 | .then(cache => cache.addAll(config.preCachingItems)) 82 | .then(() => self.skipWaiting()) 83 | ); 84 | }); 85 | self.addEventListener('activate', event => { 86 | function clearCacheIfDifferent(event, opts) { 87 | return caches.keys().then(cacheKeys => { 88 | const oldCacheKeys = cacheKeys.filter(key => key.indexOf(opts.version) !== 0); 89 | const deletePromises = oldCacheKeys.map(oldKey => caches.delete(oldKey)); 90 | return Promise.all(deletePromises); 91 | }); 92 | } 93 | event.waitUntil( 94 | clearCacheIfDifferent(event, config) 95 | .then(() => self.clients.claim()) 96 | ); 97 | }); 98 | self.addEventListener('fetch', event => { 99 | const request = event.request; 100 | const url = new URL(request.url); 101 | if (request.method !== 'GET' 102 | || (config.cacheRemote !== true && url.origin !== self.location.origin) 103 | || (config.blacklistCacheItems.length > 0 && config.blacklistCacheItems.indexOf(url.pathname) !== -1)) { 104 | // default browser behavior 105 | return; 106 | } 107 | let cacheKey; 108 | let resourceType = 'content'; 109 | if (/(.jpg|.jpeg|.webp|.png|.svg|.gif)$/.test(url.pathname)) { 110 | resourceType = 'image'; 111 | } else if (/.\/fonts.(?:googleapis|gstatic).com/.test(url.origin)) { 112 | resourceType = 'font'; 113 | } else if (url.origin === 'https://api.themoviedb.org') { 114 | // The Movie DB json API 115 | resourceType = 'tmdb'; 116 | } 117 | cacheKey = cacheName(resourceType, config); 118 | if (resourceType === 'content') { 119 | // Network First Strategy 120 | event.respondWith( 121 | fetch(request) 122 | .then(response => addToCache(cacheKey, request, response)) 123 | .catch(() => fetchFromCache(event)) 124 | .catch(() => offlineResponse(resourceType, config)) 125 | ); 126 | } else { 127 | // Cache First Strategy 128 | event.respondWith( 129 | fetchFromCache(event) 130 | .catch(() => fetch(request)) 131 | .then(response => addToCache(cacheKey, request, response)) 132 | .catch(() => offlineResponse(resourceType, config)) 133 | ); 134 | } 135 | }); 136 | -------------------------------------------------------------------------------- /source/sw.js: -------------------------------------------------------------------------------- 1 | // TODO: increase `version` number to force cache update when publishing a new release 2 | const version = 'v2'; 3 | 4 | const config = { 5 | cacheRemote: true, 6 | version: version+'::', 7 | preCachingItems: [ 8 | 'app.bundle.js', 9 | 'index.html', 10 | 'index.js', 11 | 'offline.html', 12 | '404.html', 13 | 'sw.js' 14 | ], 15 | blacklistCacheItems: [ 16 | 'index.html', 17 | 'service-worker.js' 18 | ], 19 | offlineImage: '' + 'Offline' + '' + '' + 'offline', 20 | offlinePage: 'offline.html', 21 | notFoundPage: '404.html', 22 | offlineTmdbResults: { 23 | page: 1, 24 | results: [{ 25 | "title": "Offline", 26 | "poster_path": "https://tmdb.org/offline.jpg", 27 | "backdrop_path": "https://tmdb.org/offline.jpg", 28 | "overview": "" 29 | }], 30 | total_pages: 1, 31 | total_results: 1 32 | } 33 | }; 34 | 35 | function cacheName(key, opts) { 36 | return `${opts.version}${key}`; 37 | } 38 | 39 | function addToCache(cacheKey, request, response) { 40 | if (response.ok) { 41 | const copy = response.clone(); 42 | caches.open(cacheKey).then(cache => { 43 | cache.put(request, copy); 44 | }); 45 | } 46 | return response; 47 | } 48 | 49 | function fetchFromCache(event) { 50 | return caches.match(event.request).then(response => { 51 | if (!response) { 52 | throw Error(`${event.request.url} not found in cache`); 53 | } else if (response.status === 404) { 54 | return caches.match(config.notFoundPage); 55 | } 56 | return response; 57 | }); 58 | } 59 | 60 | function offlineResponse(resourceType, opts) { 61 | if (resourceType === 'content') { 62 | return caches.match(opts.offlinePage); 63 | } 64 | if (resourceType === 'image') { 65 | return new Response(opts.offlineImage, { 66 | headers: { 'Content-Type': 'image/svg+xml' } 67 | }); 68 | } 69 | if (resourceType === 'tmdb') { 70 | return new Response(JSON.stringify(opts.offlineTmdbResults), { 71 | headers: { 'Content-Type': 'application/json' } 72 | }); 73 | } 74 | return undefined; 75 | } 76 | 77 | self.addEventListener('install', event => { 78 | event.waitUntil(caches.open( 79 | cacheName('static', config) 80 | ) 81 | .then(cache => cache.addAll(config.preCachingItems)) 82 | .then(() => self.skipWaiting()) 83 | ); 84 | }); 85 | self.addEventListener('activate', event => { 86 | function clearCacheIfDifferent(event, opts) { 87 | return caches.keys().then(cacheKeys => { 88 | const oldCacheKeys = cacheKeys.filter(key => key.indexOf(opts.version) !== 0); 89 | const deletePromises = oldCacheKeys.map(oldKey => caches.delete(oldKey)); 90 | return Promise.all(deletePromises); 91 | }); 92 | } 93 | event.waitUntil( 94 | clearCacheIfDifferent(event, config) 95 | .then(() => self.clients.claim()) 96 | ); 97 | }); 98 | self.addEventListener('fetch', event => { 99 | const request = event.request; 100 | const url = new URL(request.url); 101 | if (request.method !== 'GET' 102 | || (config.cacheRemote !== true && url.origin !== self.location.origin) 103 | || (config.blacklistCacheItems.length > 0 && config.blacklistCacheItems.indexOf(url.pathname) !== -1)) { 104 | // default browser behavior 105 | return; 106 | } 107 | let cacheKey; 108 | let resourceType = 'content'; 109 | if (/(.jpg|.jpeg|.webp|.png|.svg|.gif)$/.test(url.pathname)) { 110 | resourceType = 'image'; 111 | } else if (/.\/fonts.(?:googleapis|gstatic).com/.test(url.origin)) { 112 | resourceType = 'font'; 113 | } else if (url.origin === 'https://api.themoviedb.org') { 114 | // The Movie DB json API 115 | resourceType = 'tmdb'; 116 | } 117 | cacheKey = cacheName(resourceType, config); 118 | if (resourceType === 'content') { 119 | // Network First Strategy 120 | event.respondWith( 121 | fetch(request) 122 | .then(response => addToCache(cacheKey, request, response)) 123 | .catch(() => fetchFromCache(event)) 124 | .catch(() => offlineResponse(resourceType, config)) 125 | ); 126 | } else { 127 | // Cache First Strategy 128 | event.respondWith( 129 | fetchFromCache(event) 130 | .catch(() => fetch(request)) 131 | .then(response => addToCache(cacheKey, request, response)) 132 | .catch(() => offlineResponse(resourceType, config)) 133 | ); 134 | } 135 | }); 136 | -------------------------------------------------------------------------------- /source/js/zuix-bundler.min.js: -------------------------------------------------------------------------------- 1 | /* zUIx v1.0.1 18.08.30 21:48:09 */ 2 | 3 | /* 4 | @license 5 | Copyright 2015-2018 G-Labs. All Rights Reserved. 6 | https://zuixjs.github.io/zuix 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 20 | 'use strict';(function(a){"object"===typeof exports&&"undefined"!==typeof module?module.exports=a():"function"===typeof define&&define.amd?define([],a):("undefined"!==typeof window?window:"undefined"!==typeof global?global:"undefined"!==typeof self?self:this).zuixBundler=a()})(function(){return function(){function a(e,f,g){function c(k,b){if(!f[k]){if(!e[k]){var q="function"==typeof require&&require;if(!b&&q)return q(k,!0);if(n)return n(k,!0);b=Error("Cannot find module '"+k+"'");throw b.code="MODULE_NOT_FOUND", 21 | b;}b=f[k]={exports:{}};e[k][0].call(b.exports,function(b){return c(e[k][1][b]||b)},b,b.exports,a,e,f,g)}return f[k].exports}for(var n="function"==typeof require&&require,l=0;l\/\u2028\u2029]/g,b={"<":"\\u003C",">":"\\u003E","/":"\\u002F","\u2028":"\\u2028","\u2029":"\\u2029"};e.exports=function(b,a){function e(b,a){if(!a)return a;b=typeof a;return"object"===b?"[object RegExp]"===Object.prototype.toString.call(a)?"@__R-"+c+"-"+(h.push(a)-1)+"__@":a:"function"===b?"@__F-"+c+"-"+(f.push(a)-1)+"__@":a}a||(a={});if("number"===typeof a||"string"===typeof a)a={space:a};const f=[],h=[];b=a.isJSON&& 27 | !a.space?JSON.stringify(b):JSON.stringify(b,a.isJSON?null:e,a.space);if("string"!==typeof b)return String(b);b=b.replace(k,g);return 0===f.length&&0===h.length?b:b.replace(n,function(a,b,c){if("R"===b)return h[c].toString();a=f[c];b=a.toString();if(l.test(b))throw new TypeError("Serializing native function: "+a.name);return b})}},{}],4:[function(a,e,f){function g(){var a=zuix.bundle();var b="\n/*\n * zUIx Application Bundle\n * \n * app.bundle.js generated by *zuix-bundler*\n * on "+(new Date).toISOString(); 28 | b=b+"\n * "+("\n * Resource list ("+a.length+"):");b+="\n * ";for(let c=0;c 73 |
74 | 75 |
76 | 77 | 78 |
80 | 81 | 82 |
83 | 84 | 85 |
86 | 87 | 88 |
89 | 90 |
91 | 92 | 93 |
95 | ``` 96 | 97 | For a deeper insight on using these special attributes for component-based developemnt with **zUIx.js** see related link at the bottom of this page. 98 | 99 | ### Movie lists 100 | 101 | The list of movies shown in the main page are located in the `./pages/home/` folder and consists of a static list of items to which is bound a *controller* that carry the duty of querying the [The Movie Database](https://www.themoviedb.org/) to fetch data and images of each title. 102 | 103 | Each *movie item* is defined as follow: 104 | ``` 105 |
106 | 107 |
108 | ``` 109 | The `data-ui-load="controllers/movie_db"` attribute will make *zUIx.js* to load the `./controllers/movie_db.js` on the element. This contoller will read the `title` attribute of the element and fetch the movie data using *TheMovieDB API*. 110 | 111 | To make this work on your local copy, you must obtain an API key and put the value at the beginning of the `./controllers/movie_db.js` file: 112 | 113 | ```javascript 114 | // TODO: get your free TMDB API key from https://themoviedb.org 115 | const tmdbKey = '--put--your-tmdb-api-key-here--'; 116 | ``` 117 | 118 | You can of course replace the static movie lists with server generated lists that renders the *movie items* as described above. 119 | 120 | ### Main page 121 | 122 | The main page is formed by a cover, which collapses as the page is scrolled, and a *movie details page* which pop-ups when a movie item is tapped. 123 | 124 | The main page is available throught the global object `mainPage` and exposes two method: `cover` and `sync`. 125 | The first method is used to set which movie title to show as the main page cover, while the latter is used to synchronize the cover with the page scroll. 126 | 127 | You can see how these methods are implemented in the `./pages/home.js` file and how these are used in the main `index.js` file. 128 | 129 | The *details page* is available as the global object `detailsPage` and it has two methods: `show` and `hide`. 130 | The first method requires the movie item data as argument. You can see how it is used in the `./controllers/movie_db.js` file to open a movie detail when the preview image is tapped. 131 | 132 | ### About the @lib prefix 133 | 134 | The special *@lib* prefix is used with the `data-ui-include` and `data-ui-load` attribute to load components from a shared location that by default will point to the [zKit](https://zuixjs.github.io/zkit/) components library. 135 | This path is resolved to `https://zuixjs.github.io/zkit/lib/` but can also be customized to point to a different location: 136 | 137 | ```javascript 138 | zuix.store('config', { 139 | libraryPath: 'https://my.shared.components.io/lib/' 140 | }); 141 | ``` 142 | 143 | You can so create your own shared component library to use across all of your websites. 144 | 145 | ### Further implementation details 146 | 147 | If you have more questions about how to use this template do not esitate to [file an issue](https://github.com/zuixjs/zuix-web-flix/issues). 148 | 149 | ## Service Worker and Offline mode 150 | 151 | A service worker is also included with this template. This is a script (`sw.js`) that runs in the background as a seprate thread from the main page and takes care of providing caching of most resources with a fallback mechanism in presence of network errors. 152 | This makes the application to launch even if there's no network connection, like with a real mobile app. It also provide a configurable `404.html` *"not found"* page and `offline.html` page. 153 | When publishing a new version of the application the *version number* found at the top of the `sw.js` file **must be increased** in order to clean the cache and correctly update the application files. 154 | 155 | ## Debugging 156 | 157 | To enable verbose debugging information in the browser console, comment out the last line in the `index.js` file as shown below: 158 | 159 | ``` javascript 160 | // Turn off debug output 161 | //window.zuixNoConsoleOutput = true; 162 | ``` 163 | 164 | 165 | ## Bundling 166 | 167 | Bundling is the process of collecting all resources used in a page and then compiling them into a single and optimized file. 168 | 169 | This will drastically narrow down the number of network requests the browser will make to complete the page loading, thus resulting in a faster startup. 170 | 171 | There are actually two way of doing this: 172 | 173 | - **In-Browser** bundler: 174 | this method does not require any build tool nor plugins, it just works in the browser as-is. 175 | 176 | - **Web-Starter** bundler: 177 | is the [zuix-web-starter](https://github.com/zuixjs/zuix-web-starter) bare template, with a bunch of extra features and enhancements. It requires *Node.js* to be installed. 178 | 179 | This template is already configured for **in-browser** bundling. 180 | 181 | ### In-Browser bundling 182 | 183 | When the website is ready for production you can decide to bundle it in order to gain better performances. All of its components and resources will be crunched into a single file and loaded from memory rather than from network/localhost. 184 | 185 | #### Step by step guide 186 | 187 | Open the development website and generate the application bundle by typing in the **browser console** the command 188 | 189 | ```javascript 190 | zuix.saveBundle() 191 | ``` 192 | 193 | This will create and save the **app.bundle.js** file to the *Downloads* folder. 194 | 195 | Copy all files from the *source* folder to the **production** folder (*./docs*) with the exception of *./components*, *./controllers*, *./pages*, *./layout* and *./index.css*. 196 | 197 | These folders contain *zuix* components and pages that are already packed in the *app.bundle.js* file. 198 | 199 | Copy *app.bundle.js* to the *production* folder. Edit the `index.html` file in the *production* folder and in the `head` section replace the `js/zuix-bundler.min.js` script import with `app.bundle.js`. 200 | 201 | ```html 202 | 203 | 204 | ``` 205 | 206 | You can also remove all `js/zuix*.*` files from the *production* folder but keep the `zuix.min.js` one. 207 | 208 | #### Remarks 209 | 210 | When using *lazy-loading* only components loaded so far will be included in the bundle (incremental bundling). 211 | 212 | To force inclusion of all components/resources used in the page, issue the following commands in the console: 213 | 214 | ```javascript 215 | // disable lazy-loading 216 | zuix.lazyLoad(false) 217 | // force loading of all components 218 | zuix.componentize() 219 | // create the bundle 220 | zuix.saveBundle() 221 | ``` 222 | 223 | Also external JavaScript libraries and CSS files can be included in the page bundle. In order to achieve this, remove the `` or `` and instead use the method `zuix.using(...)` to load the script/css. 224 | 225 | ```javascript 226 | // Loading a .css style 227 | const flaCss = 'https://cdnjs.cloudflare.com/ajax/libs/flex-layout-attribute/1.0.3/css/flex-layout-attribute.min.css'; 228 | zuix.using('style', flaCss, function(res, styleObject) { 229 | console.log("Added Flex Layout Attributes CSS.", res, styleObject); 230 | }); 231 | // Loading a .js script 232 | const momentJs = 'https://momentjs.com/downloads/moment.js'; 233 | zuix.using('script', momentJs, function(res, scriptObject){ 234 | console.log("Added Moment.js.", res, scriptObject); 235 | }); 236 | ``` 237 | 238 | Place the *using* commands preferably at the top of `index.js`. You can remove from the *production* folder all files imported with the *using* command. 239 | 240 | # Further reading 241 | 242 | - [zUIx.js](https://zuixjs.org) 243 | - [zKit](https://zuixjs.github.io/zkit/) 244 | - [Progressive Web App](https://developers.google.com/web/progressive-web-apps) 245 | - [Service Workers](https://developers.google.com/web/fundamentals/primers/service-workers) 246 | -------------------------------------------------------------------------------- /source/js/zuix-bundler.js: -------------------------------------------------------------------------------- 1 | /* zUIx v1.0.1 18.08.30 21:48:09 */ 2 | 3 | /** @typedef {Zuix} window.zuix */ 4 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.zuixBundler = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 30 | */ 31 | 32 | /* global define */ 33 | 34 | 'use strict'; 35 | 36 | // TODO: detect whether running in a browser environment or not 37 | (function(root, factory) { 38 | if (typeof define === 'function' && define.amd) { 39 | // AMD. Register as an anonymous module. 40 | define('zuix', function() { 41 | return (root.zuix = (factory).call(root)); 42 | }); 43 | } else if (typeof module === 'object' && module.exports) { 44 | // Node 45 | module.exports = (factory).call(root); 46 | } else { 47 | // Browser globals 48 | root.zuix = (factory).call(root); 49 | } 50 | }(this, _dereq_('./zuix-bundler/ZuixBundler.js'))); 51 | 52 | },{"./zuix-bundler/ZuixBundler.js":4}],2:[function(_dereq_,module,exports){ 53 | /* FileSaver.js 54 | * A saveAs() FileSaver implementation. 55 | * 1.3.2 56 | * 2016-06-16 18:25:19 57 | * 58 | * By Eli Grey, http://eligrey.com 59 | * License: MIT 60 | * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 61 | */ 62 | 63 | /* eslint-disable */ 64 | 65 | /* global self */ 66 | /* jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 67 | 68 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 69 | 70 | var saveAs = saveAs || (function(view) { 71 | "use strict"; 72 | // IE <10 is explicitly unsupported 73 | if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { 74 | return; 75 | } 76 | var 77 | doc = view.document 78 | // only get URL when necessary in case Blob.js hasn't overridden it yet 79 | , get_URL = function() { 80 | return view.URL || view.webkitURL || view; 81 | } 82 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 83 | , can_use_save_link = "download" in save_link 84 | , click = function(node) { 85 | var event = new MouseEvent("click"); 86 | node.dispatchEvent(event); 87 | } 88 | , is_safari = /constructor/i.test(view.HTMLElement) || view.safari 89 | , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent) 90 | , throw_outside = function(ex) { 91 | (view.setImmediate || view.setTimeout)(function() { 92 | throw ex; 93 | }, 0); 94 | } 95 | , force_saveable_type = "application/octet-stream" 96 | // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to 97 | , arbitrary_revoke_timeout = 1000 * 40 // in ms 98 | , revoke = function(file) { 99 | var revoker = function() { 100 | if (typeof file === "string") { // file is an object URL 101 | get_URL().revokeObjectURL(file); 102 | } else { // file is a File 103 | file.remove(); 104 | } 105 | }; 106 | setTimeout(revoker, arbitrary_revoke_timeout); 107 | } 108 | , dispatch = function(filesaver, event_types, event) { 109 | event_types = [].concat(event_types); 110 | var i = event_types.length; 111 | while (i--) { 112 | var listener = filesaver["on" + event_types[i]]; 113 | if (typeof listener === "function") { 114 | try { 115 | listener.call(filesaver, event || filesaver); 116 | } catch (ex) { 117 | throw_outside(ex); 118 | } 119 | } 120 | } 121 | } 122 | , auto_bom = function(blob) { 123 | // prepend BOM for UTF-8 XML and text/* types (including HTML) 124 | // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF 125 | if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 126 | return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}); 127 | } 128 | return blob; 129 | } 130 | , FileSaver = function(blob, name, no_auto_bom) { 131 | if (!no_auto_bom) { 132 | blob = auto_bom(blob); 133 | } 134 | // First try a.download, then web filesystem, then object URLs 135 | var 136 | filesaver = this 137 | , type = blob.type 138 | , force = type === force_saveable_type 139 | , object_url 140 | , dispatch_all = function() { 141 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 142 | } 143 | // on any filesys errors revert to saving with object URLs 144 | , fs_error = function() { 145 | if ((is_chrome_ios || (force && is_safari)) && view.FileReader) { 146 | // Safari doesn't allow downloading of blob urls 147 | var reader = new FileReader(); 148 | reader.onloadend = function() { 149 | var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;'); 150 | var popup = view.open(url, '_blank'); 151 | if(!popup) view.location.href = url; 152 | url=undefined; // release reference before dispatching 153 | filesaver.readyState = filesaver.DONE; 154 | dispatch_all(); 155 | }; 156 | reader.readAsDataURL(blob); 157 | filesaver.readyState = filesaver.INIT; 158 | return; 159 | } 160 | // don't create more object URLs than needed 161 | if (!object_url) { 162 | object_url = get_URL().createObjectURL(blob); 163 | } 164 | if (force) { 165 | view.location.href = object_url; 166 | } else { 167 | var opened = view.open(object_url, "_blank"); 168 | if (!opened) { 169 | // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html 170 | view.location.href = object_url; 171 | } 172 | } 173 | filesaver.readyState = filesaver.DONE; 174 | dispatch_all(); 175 | revoke(object_url); 176 | } 177 | ; 178 | filesaver.readyState = filesaver.INIT; 179 | 180 | if (can_use_save_link) { 181 | object_url = get_URL().createObjectURL(blob); 182 | setTimeout(function() { 183 | save_link.href = object_url; 184 | save_link.download = name; 185 | click(save_link); 186 | dispatch_all(); 187 | revoke(object_url); 188 | filesaver.readyState = filesaver.DONE; 189 | }); 190 | return; 191 | } 192 | 193 | fs_error(); 194 | } 195 | , FS_proto = FileSaver.prototype 196 | , saveAs = function(blob, name, no_auto_bom) { 197 | return new FileSaver(blob, name || blob.name || "download", no_auto_bom); 198 | } 199 | ; 200 | // IE 10+ (native saveAs) 201 | if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { 202 | return function(blob, name, no_auto_bom) { 203 | name = name || blob.name || "download"; 204 | 205 | if (!no_auto_bom) { 206 | blob = auto_bom(blob); 207 | } 208 | return navigator.msSaveOrOpenBlob(blob, name); 209 | }; 210 | } 211 | 212 | FS_proto.abort = function(){}; 213 | FS_proto.readyState = FS_proto.INIT = 0; 214 | FS_proto.WRITING = 1; 215 | FS_proto.DONE = 2; 216 | 217 | FS_proto.error = 218 | FS_proto.onwritestart = 219 | FS_proto.onprogress = 220 | FS_proto.onwrite = 221 | FS_proto.onabort = 222 | FS_proto.onerror = 223 | FS_proto.onwriteend = 224 | null; 225 | 226 | return saveAs; 227 | }( 228 | typeof self !== "undefined" && self 229 | || typeof window !== "undefined" && window 230 | || this.content 231 | )); 232 | // `self` is undefined in Firefox for Android content script context 233 | // while `this` is nsIContentFrameMessageManager 234 | // with an attribute `content` that corresponds to the window 235 | 236 | if (typeof module !== "undefined" && module.exports) { 237 | module.exports.saveAs = saveAs; 238 | } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) { 239 | define("FileSaver.js", function() { 240 | return saveAs; 241 | }); 242 | } 243 | 244 | },{}],3:[function(_dereq_,module,exports){ 245 | /* 246 | Copyright (c) 2014, Yahoo! Inc. All rights reserved. 247 | Copyrights licensed under the New BSD License. 248 | See the accompanying LICENSE file for terms. 249 | 250 | https://github.com/yahoo/serialize-javascript 251 | 252 | */ 253 | 254 | const isRegExp = function(re) { 255 | return Object.prototype.toString.call(re) === '[object RegExp]'; 256 | }; 257 | 258 | const UID = Math.floor(Math.random() * 0x10000000000).toString(16); 259 | const PLACE_HOLDER_REGEXP = new RegExp('"@__(F|R)-' + UID + '-(\\d+)__@"', 'g'); 260 | 261 | const IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g; 262 | const UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g; 263 | 264 | // Mapping of unsafe HTML and invalid JavaScript line terminator chars to their 265 | // Unicode char counterparts which are safe to use in JavaScript strings. 266 | const ESCAPED_CHARS = { 267 | '<': '\\u003C', 268 | '>': '\\u003E', 269 | '/': '\\u002F', 270 | '\u2028': '\\u2028', 271 | '\u2029': '\\u2029' 272 | }; 273 | 274 | function escapeUnsafeChars(unsafeChar) { 275 | return ESCAPED_CHARS[unsafeChar]; 276 | } 277 | 278 | module.exports = function serialize(obj, options) { 279 | options || (options = {}); 280 | 281 | // component item serialization 282 | 283 | // Backwards-compatability for `space` as the second argument. 284 | if (typeof options === 'number' || typeof options === 'string') { 285 | options = {space: options}; 286 | } 287 | 288 | const functions = []; 289 | const regexps = []; 290 | 291 | // Returns placeholders for functions and regexps (identified by index) 292 | // which are later replaced by their string representation. 293 | function replacer(key, value) { 294 | if (!value) { 295 | return value; 296 | } 297 | 298 | const type = typeof value; 299 | 300 | if (type === 'object') { 301 | if (isRegExp(value)) { 302 | return '@__R-' + UID + '-' + (regexps.push(value) - 1) + '__@'; 303 | } 304 | 305 | return value; 306 | } 307 | 308 | if (type === 'function') { 309 | return '@__F-' + UID + '-' + (functions.push(value) - 1) + '__@'; 310 | } 311 | 312 | return value; 313 | } 314 | 315 | let str; 316 | 317 | // Creates a JSON string representation of the value. 318 | // NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args. 319 | if (options.isJSON && !options.space) { 320 | str = JSON.stringify(obj); 321 | } else { 322 | str = JSON.stringify(obj, options.isJSON ? null : replacer, options.space); 323 | } 324 | 325 | // Protects against `JSON.stringify()` returning `undefined`, by serializing 326 | // to the literal string: "undefined". 327 | if (typeof str !== 'string') { 328 | return String(str); 329 | } 330 | 331 | // Replace unsafe HTML and invalid JavaScript line terminator chars with 332 | // their safe Unicode char counterpart. This _must_ happen before the 333 | // regexps and functions are serialized and added back to the string. 334 | str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars); 335 | 336 | if (functions.length === 0 && regexps.length === 0) { 337 | return str; 338 | } 339 | 340 | // Replaces all occurrences of function and regexp placeholders in the JSON 341 | // string with their string representations. If the original value can not 342 | // be found, then `undefined` is used. 343 | return str.replace(PLACE_HOLDER_REGEXP, function(match, type, valueIndex) { 344 | if (type === 'R') { 345 | return regexps[valueIndex].toString(); 346 | } 347 | 348 | const fn = functions[valueIndex]; 349 | const serializedFn = fn.toString(); 350 | 351 | if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) { 352 | throw new TypeError('Serializing native function: ' + fn.name); 353 | } 354 | 355 | return serializedFn; 356 | }); 357 | }; 358 | 359 | },{}],4:[function(_dereq_,module,exports){ 360 | /* 361 | * Copyright 2015-2018 G-Labs. All Rights Reserved. 362 | * https://zuixjs.github.io/zuix 363 | * 364 | * Licensed under the Apache License, Version 2.0 (the "License"); 365 | * you may not use this file except in compliance with the License. 366 | * You may obtain a copy of the License at 367 | * 368 | * http://www.apache.org/licenses/LICENSE-2.0 369 | * 370 | * Unless required by applicable law or agreed to in writing, software 371 | * distributed under the License is distributed on an "AS IS" BASIS, 372 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 373 | * See the License for the specific language governing permissions and 374 | * limitations under the License. 375 | */ 376 | 377 | /* 378 | * 379 | * This file is part of 380 | * zUIx, Javascript library for component-based development. 381 | * https://zuixjs.github.io/zuix 382 | * 383 | * @author Generoso Martello 384 | */ 385 | 386 | 'use strict'; 387 | 388 | const fileSaver = _dereq_('./FileSaver'); 389 | const serialize = _dereq_('./Serializer'); 390 | const _optionAttributes = _dereq_('../zuix/OptionAttributes')(); 391 | 392 | /** 393 | * Create application bundle containing all components 394 | * and resources used in the app. This method can be called 395 | * from the browser developer console. When using lazy-loading 396 | * only components loaded so far will be bundled (incremental bundle). 397 | * To force inclusion of all components/resources 398 | * disable lazy-loading first by calling 399 | * `zuix.lazyLoad(false)` and then `zuix.saveBundle()`. 400 | * After the bundle is created it will be downloaded 401 | * by the browser as 'app.bundle.js' file that you can 402 | * then compress, copy and include in your app. 403 | * This will speed-up resource loading and improve 404 | * user experience. 405 | * 406 | * @return {string} bundle 407 | */ 408 | function saveBundle() { 409 | const bundleFileName = 'app.bundle.js'; 410 | const bundleObj = zuix.bundle(); 411 | let headerSummary = '\n/*'; 412 | headerSummary += '\n * zUIx Application Bundle'; 413 | headerSummary += '\n * '; 414 | headerSummary += '\n * '+bundleFileName+' generated by *zuix-bundler*'; 415 | headerSummary += '\n * on '+new Date().toISOString(); 416 | headerSummary += '\n * '; 417 | headerSummary += '\n * Resource list ('+bundleObj.length+'):'; 418 | headerSummary += '\n * '; 419 | for (let i = 0; i < bundleObj.length; i++) { 420 | const b = bundleObj[i]; 421 | let ctype = ''; 422 | if (b.view != null) { 423 | ctype += '[html] '; 424 | } 425 | if (b.css != null) { 426 | ctype += '[css] '; 427 | } 428 | if (b.controller != null) { 429 | ctype += '[js] '; 430 | } 431 | let cpath = b.componentId; 432 | if (b.using != null) { 433 | cpath = b.using+' ('+cpath+')'; 434 | } 435 | headerSummary += '\n * - '+ctype; 436 | headerSummary += '\n * '+cpath; 437 | headerSummary += '\n * '; 438 | } 439 | headerSummary += '\n * '; 440 | headerSummary += '\n*/'; 441 | headerSummary += '\n\n'; 442 | let bundle = headerSummary + serialize(zuix.bundle()); 443 | // revert loaded status before exporting 444 | bundle = bundle.replace(new RegExp(_optionAttributes.dataUiLoaded+'="true"', 'g'), 445 | _optionAttributes.dataUiLoaded+'="false"'); 446 | bundle = bundle.replace(new RegExp(_optionAttributes.zuixLoaded+'="true"', 'g'), 447 | _optionAttributes.zuixLoaded+'="false"'); 448 | // save bundle 449 | const blob = new Blob(['zuix.bundle(' + bundle + ');'], {type: 'text/plain;charset=utf-8'}); 450 | fileSaver.saveAs(blob, bundleFileName); 451 | return bundle; 452 | } 453 | 454 | module.exports = function(root) { 455 | if (zuix == null) { 456 | alert('Error: ZuixBundler requires Zuix to be included first.'); 457 | return; 458 | } 459 | zuix.saveBundle = saveBundle; 460 | return zuix; 461 | }; 462 | 463 | },{"../zuix/OptionAttributes":5,"./FileSaver":2,"./Serializer":3}],5:[function(_dereq_,module,exports){ 464 | /* 465 | * Copyright 2015-2018 G-Labs. All Rights Reserved. 466 | * https://zuixjs.github.io/zuix 467 | * 468 | * Licensed under the Apache License, Version 2.0 (the "License"); 469 | * you may not use this file except in compliance with the License. 470 | * You may obtain a copy of the License at 471 | * 472 | * http://www.apache.org/licenses/LICENSE-2.0 473 | * 474 | * Unless required by applicable law or agreed to in writing, software 475 | * distributed under the License is distributed on an "AS IS" BASIS, 476 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 477 | * See the License for the specific language governing permissions and 478 | * limitations under the License. 479 | */ 480 | 481 | /* 482 | * 483 | * This file is part of 484 | * zUIx, Javascript library for component-based development. 485 | * https://zuixjs.github.io/zuix 486 | * 487 | * @author Generoso Martello 488 | */ 489 | 490 | const OptionAttributes = Object.freeze({ 491 | dataBindModel: 492 | 'data-bind-model', 493 | dataBindTo: 494 | 'data-bind-to', 495 | dataUiComponent: 496 | 'data-ui-component', 497 | dataUiContext: 498 | 'data-ui-context', 499 | dataUiField: 500 | 'data-ui-field', 501 | dataUiInclude: 502 | 'data-ui-include', 503 | dataUiLazyload: 504 | 'data-ui-lazyload', 505 | dataUiLoad: 506 | 'data-ui-load', 507 | dataUiLoaded: 508 | 'data-ui-loaded', 509 | dataUiOptions: 510 | 'data-ui-options', 511 | dataUiPriority: 512 | 'data-ui-priority', 513 | dataUiView: 514 | 'data-ui-view', 515 | zuixLoaded: 516 | 'zuix-loaded' 517 | }); 518 | 519 | /** 520 | * @param root 521 | * @return {Zuix} 522 | */ 523 | module.exports = function(root) { 524 | return OptionAttributes; 525 | }; 526 | 527 | },{}]},{},[1])(1) 528 | }); 529 | -------------------------------------------------------------------------------- /docs/js/zuix.min.js: -------------------------------------------------------------------------------- 1 | /* zUIx v1.0.1 18.08.30 21:48:09 */ 2 | 3 | /* 4 | @license 5 | Copyright 2015-2018 G-Labs. All Rights Reserved. 6 | https://zuixjs.github.io/zuix 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | 'use strict';(function(m){"object"===typeof exports&&"undefined"!==typeof module?module.exports=m():"function"===typeof define&&define.amd?define([],m):("undefined"!==typeof window?window:"undefined"!==typeof global?global:"undefined"!==typeof self?self:this).zuix=m()})(function(){return function(){function m(r,x,a){function l(e,h){if(!x[e]){if(!r[e]){var b="function"==typeof require&&require;if(!h&&b)return b(e,!0);if(k)return k(e,!0);h=Error("Cannot find module '"+e+"'");throw h.code="MODULE_NOT_FOUND", 21 | h;}h=x[e]={exports:{}};r[e][0].call(h.exports,function(b){return l(r[e][1][b]||b)},h,h.exports,m,r,x,a)}return x[e].exports}for(var k="function"==typeof require&&require,f=0;f=e.length||b.willBreak())&&k();return!1}function k(d){h=-1;e.length=0;e=[];b.status(null!=d?d:"done")}function f(){const a=e[h];if(null==a)return!1; 22 | const f=function(){null!==d&&(cancelAnimationFrame(d),d=null);b.doWork(a.item,function(){d=requestAnimationFrame(l)})||l()};if(a.cancelable)if(b.willBreak())k("stopped");else if(null==d)d=requestAnimationFrame(f);else return!1;else f();return!0}a.prototype.isReady=function(){return 0===e.length||-1===h};a.prototype.getJobs=function(){return e};a.prototype.setJobs=function(d){0a.priority?1:a.priority>b.priority?-1:0});f.taskCheck()};f.taskCheck=function(){for(let e=0;e=a)break}}return this};g.ajax=function(c){let b;b=d.isNoU(c)||d.isNoU(c.url)?c:c.url;const a=new XMLHttpRequest;a.open("GET",b);a.onload=function(){200===a.status?d.isFunction(c.success)&&c.success(a.responseText):d.isFunction(c.error)&&c.error(a);d.isFunction(c.then)&&c.then(a)};a.send();return this};g.hasClass=function(c,b){b=b.match(/\S+/g)||[];let a=!1;g.each(b,function(b,d){if(a=c.classList?c.classList.contains(d):(new RegExp("(^| )"+ 46 | d+"( |$)","gi")).test(c.className))return!1});return a};g.classExists=function(c){c=c.match(/\S+/g)||[];let b=!1;g.each(c,function(c,a){c=document.styleSheets;if(null!=c)for(let d=0;dd.right-b||e.right+1d.bottom-b||e.bottom+1a?0:+a,b.length)===b});r.exports=g},{"./Logger":2,"./Util.js":4}],6:[function(m,r,x){m=m("./zuix/Zuix.js");"object"===typeof r&&r.exports?r.exports=m.call(this):this.zuix=m.call(this)},{"./zuix/Zuix.js":12}],7:[function(m, 56 | r,x){r.exports=function(a){return null}},{}],8:[function(m,r,x){function a(b,a,e){h=b;this._options=null;this.contextId=null==a||null==a.contextId?null:a.contextId;this.componentId=null;this.trigger=function(b,a,c){"function"===typeof e&&e(b,a,c)};this.behavior=this._controller=this._style=this._css=this._view=this._model=this._container=null;this._eventMap=[];this._behaviorMap=[];this._c=null;this.options(a);return this}const l=m("../helpers/Logger")("ComponentContext.js"),k=m("./OptionAttributes")(), 57 | f=m("../helpers/ZxQuery"),e=m("../helpers/Util");let h=null;a.prototype.container=function(b){if(null==b)return this._container;b instanceof f.ZxQuery&&(b=b.get());this._container=b;return this};a.prototype.view=function(b){if("undefined"===typeof b)return this._view;b instanceof f.ZxQuery&&(b=b.get());l.t(this.componentId,"view:attach","timer:view:start");if("string"===typeof b){b={content:b};this.trigger(this,"html:parse",b);b=b.content;const a=f.wrapElement("div",b);null!=a.firstElementChild&& 58 | (null!=a.firstElementChild.getAttribute(k.dataUiView)?1===a.children.length&&(b=a.firstElementChild.innerHTML):b=a.innerHTML);null!=this._container?(this._view=this._container,this._view.innerHTML+=b):null!=this._view?this._view.innerHTML=b:this._view=a;f(this._view).find("script").each(function(b,a){"true"!==this.attr("zuix-loaded")&&(this.attr("zuix-loaded","true"),eval.call(window,a.innerHTML))});this.trigger(this,"view:process",f(this._view))}else b instanceof f.ZxQuery&&(b=b.get()),null!=this._container? 59 | (this._view=f.wrapElement("div",b.outerHTML).firstElementChild,this._view.removeAttribute(k.dataUiView),this._container.appendChild(this._view),this._view=this._container):this._view=b;b=f(this._view);!1===this._options.css?b.addClass("zuix-css-ignore"):b.removeClass("zuix-css-ignore");b.find("["+k.dataUiLoad+"]").each(function(b,a){this.attr(k.dataUiLoaded,"false")});this.modelToView();l.t(this.componentId,"view:attach","timer:view:stop");return this};a.prototype.style=function(b){if("undefined"=== 60 | typeof b)return this._style;l.t(this.componentId,"view:style","timer:view:start");null==b||b instanceof Element?(this._css=b instanceof Element?b.innerText:b,this._style=f.appendCss(b,this._style,this.componentId)):"string"===typeof b&&(this._css=b,b={content:b},this.trigger(this,"css:parse",b),b=b.content,b=f.wrapCss("["+k.dataUiComponent+'="'+this.componentId+'"]:not(.zuix-css-ignore)',b),this._style=f.appendCss(b,this._style,this.componentId));l.t(this.componentId,"view:style","timer:view:stop"); 61 | return this};a.prototype.model=function(b){if("undefined"===typeof b)return this._model;this._model=b;this.modelToView();null!=this._c&&e.isFunction(this._c.update)&&this._c.update.call(this._c,this._c);return this};a.prototype.controller=function(b){if("undefined"===typeof b)return this._controller;this._controller=b;return this};a.prototype.options=function(b){if(null==b)return this._options;const a=this._options=this._options||{};f.each(b,function(b,d){a[b]=d});null!=a.componentId&&(this.componentId= 62 | a.componentId);this.container(a.container);this.view(a.view);"undefined"!==typeof a.css&&this.style(a.css);this.controller(a.controller);this.model(a.model);return this};a.prototype.on=function(b,a){this._c.on(b,a);return this};a.prototype.loadCss=function(b,a){const d=this;e.isNoU(b)&&(b={});e.isNoU(b.caching)||(a=b.caching);let k=d.componentId+".css";e.isNoU(b.path)||(k=b.path);a||(k+="?"+(new Date).getTime());f.ajax({url:h.getResourcePath(k),success:function(a){d.style(a);e.isFunction(b.success)&& 63 | b.success.call(d,a,d)},error:function(a){l.e(a,d);e.isFunction(b.error)&&b.error.call(d,a,d)},then:function(){e.isFunction(b.then)&&b.then.call(d,d)}});return this};a.prototype.loadHtml=function(b,a){const d=this;let m=d.componentId;e.isNoU(b)&&(b={});e.isNoU(b.caching)||(a=b.caching);e.isNoU(b.path)||(m=b.path);var g=h.store("zuix.inlineViews");null==g&&(g=[],h.store("zuix.inlineViews",g));if(null!=g[m])d.view(g[m]),e.isFunction(b.success)&&b.success.call(d,g[m],d),e.isFunction(b.then)&&b.then.call(d, 64 | d);else{const c=f().find("["+k.dataUiView+'="'+m+'"]:not(['+k.dataUiComponent+'*=""])');0a.indexOf("://")&&(a=(null!=b&&null!=b.resourcePath?b.resourcePath:"")+a);return a}function e(a,b){let n=g(a.componentId);null!==n&&null==b.controller&&null==a.controller()&&(a.controller(n.controller), 87 | q.t(a.componentId+":js","component:cached:js"));if(p.isNoU(b.view)){if(null!==n&&(null!=n.view&&(a.view(n.view),q.t(a.componentId+":html","component:cached:html")),!1!==b.css&&(b.css=!1,n.css_applied||(n.css_applied=!0,a.style(n.css),q.t(a.componentId+":css","component:cached:css")))),p.isNoU(a.view()))return O("resource-loader").queue(a.componentId+":html",function(){y[a.componentId]=this;a.loadHtml({cext:b.cext,caching:I,success:function(d){null==n&&(n=C(a));n.view=d;delete n.controller;!1!==b.css? 88 | (y[a.componentId].step(a.componentId+":css"),a.loadCss({caching:I,success:function(a){n.css=a},error:function(b){q.e(b,a)},then:function(){c(a,y[a.componentId])}})):c(a,y[a.componentId])},error:function(c){q.e(c,a);p.isFunction(b.error)&&a.error.call(a,c,a)}})},b.priority),a}else a.view(b.view);null==a.controller()?O("resource-loader").queue(a.componentId+":js",function(){y[a.componentId]=this;c(a,y[a.componentId])},K.length):c(a);return a}function h(a){if(a instanceof Element){const b=a;a=zuix.context(b); 89 | v.dequeue(b)}p.isNoU(a)||(p.isNoU(a._c)||(p.isNoU(a._c.view())||(a._c.view().attr(A.dataUiComponent,null),a._c.view().reset(),p.isNoU(a._c._fieldCache)||u.each(a._c._fieldCache,function(a,b){b.reset()}),a._c.view().detach()),p.isFunction(a._c.destroy)&&a._c.destroy.call(a,a)),a=a.container(),null!=a&&null!=a.parentNode&&a.parentNode.removeChild(a))}function b(a){a=new w(zuix,a,B);K.push(a);return a}function d(a,b){let c=null;if(a instanceof u.ZxQuery)a=a.get();else if("string"===typeof a){const b= 90 | u.find("["+A.dataUiContext+'="'+a+'"]');0=e.length||b.willBreak())&&k();return!1}function k(d){h=-1;e.length=0;e=[];b.status(null!=d?d:"done")}function f(){const a=e[h];if(null==a)return!1; 22 | const f=function(){null!==d&&(cancelAnimationFrame(d),d=null);b.doWork(a.item,function(){d=requestAnimationFrame(l)})||l()};if(a.cancelable)if(b.willBreak())k("stopped");else if(null==d)d=requestAnimationFrame(f);else return!1;else f();return!0}a.prototype.isReady=function(){return 0===e.length||-1===h};a.prototype.getJobs=function(){return e};a.prototype.setJobs=function(d){0a.priority?1:a.priority>b.priority?-1:0});f.taskCheck()};f.taskCheck=function(){for(let e=0;e=a)break}}return this};g.ajax=function(c){let b;b=d.isNoU(c)||d.isNoU(c.url)?c:c.url;const a=new XMLHttpRequest;a.open("GET",b);a.onload=function(){200===a.status?d.isFunction(c.success)&&c.success(a.responseText):d.isFunction(c.error)&&c.error(a);d.isFunction(c.then)&&c.then(a)};a.send();return this};g.hasClass=function(c,b){b=b.match(/\S+/g)||[];let a=!1;g.each(b,function(b,d){if(a=c.classList?c.classList.contains(d):(new RegExp("(^| )"+ 46 | d+"( |$)","gi")).test(c.className))return!1});return a};g.classExists=function(c){c=c.match(/\S+/g)||[];let b=!1;g.each(c,function(c,a){c=document.styleSheets;if(null!=c)for(let d=0;dd.right-b||e.right+1d.bottom-b||e.bottom+1a?0:+a,b.length)===b});r.exports=g},{"./Logger":2,"./Util.js":4}],6:[function(m,r,x){m=m("./zuix/Zuix.js");"object"===typeof r&&r.exports?r.exports=m.call(this):this.zuix=m.call(this)},{"./zuix/Zuix.js":12}],7:[function(m, 56 | r,x){r.exports=function(a){return null}},{}],8:[function(m,r,x){function a(b,a,e){h=b;this._options=null;this.contextId=null==a||null==a.contextId?null:a.contextId;this.componentId=null;this.trigger=function(b,a,c){"function"===typeof e&&e(b,a,c)};this.behavior=this._controller=this._style=this._css=this._view=this._model=this._container=null;this._eventMap=[];this._behaviorMap=[];this._c=null;this.options(a);return this}const l=m("../helpers/Logger")("ComponentContext.js"),k=m("./OptionAttributes")(), 57 | f=m("../helpers/ZxQuery"),e=m("../helpers/Util");let h=null;a.prototype.container=function(b){if(null==b)return this._container;b instanceof f.ZxQuery&&(b=b.get());this._container=b;return this};a.prototype.view=function(b){if("undefined"===typeof b)return this._view;b instanceof f.ZxQuery&&(b=b.get());l.t(this.componentId,"view:attach","timer:view:start");if("string"===typeof b){b={content:b};this.trigger(this,"html:parse",b);b=b.content;const a=f.wrapElement("div",b);null!=a.firstElementChild&& 58 | (null!=a.firstElementChild.getAttribute(k.dataUiView)?1===a.children.length&&(b=a.firstElementChild.innerHTML):b=a.innerHTML);null!=this._container?(this._view=this._container,this._view.innerHTML+=b):null!=this._view?this._view.innerHTML=b:this._view=a;f(this._view).find("script").each(function(b,a){"true"!==this.attr("zuix-loaded")&&(this.attr("zuix-loaded","true"),eval.call(window,a.innerHTML))});this.trigger(this,"view:process",f(this._view))}else b instanceof f.ZxQuery&&(b=b.get()),null!=this._container? 59 | (this._view=f.wrapElement("div",b.outerHTML).firstElementChild,this._view.removeAttribute(k.dataUiView),this._container.appendChild(this._view),this._view=this._container):this._view=b;b=f(this._view);!1===this._options.css?b.addClass("zuix-css-ignore"):b.removeClass("zuix-css-ignore");b.find("["+k.dataUiLoad+"]").each(function(b,a){this.attr(k.dataUiLoaded,"false")});this.modelToView();l.t(this.componentId,"view:attach","timer:view:stop");return this};a.prototype.style=function(b){if("undefined"=== 60 | typeof b)return this._style;l.t(this.componentId,"view:style","timer:view:start");null==b||b instanceof Element?(this._css=b instanceof Element?b.innerText:b,this._style=f.appendCss(b,this._style,this.componentId)):"string"===typeof b&&(this._css=b,b={content:b},this.trigger(this,"css:parse",b),b=b.content,b=f.wrapCss("["+k.dataUiComponent+'="'+this.componentId+'"]:not(.zuix-css-ignore)',b),this._style=f.appendCss(b,this._style,this.componentId));l.t(this.componentId,"view:style","timer:view:stop"); 61 | return this};a.prototype.model=function(b){if("undefined"===typeof b)return this._model;this._model=b;this.modelToView();null!=this._c&&e.isFunction(this._c.update)&&this._c.update.call(this._c,this._c);return this};a.prototype.controller=function(b){if("undefined"===typeof b)return this._controller;this._controller=b;return this};a.prototype.options=function(b){if(null==b)return this._options;const a=this._options=this._options||{};f.each(b,function(b,d){a[b]=d});null!=a.componentId&&(this.componentId= 62 | a.componentId);this.container(a.container);this.view(a.view);"undefined"!==typeof a.css&&this.style(a.css);this.controller(a.controller);this.model(a.model);return this};a.prototype.on=function(b,a){this._c.on(b,a);return this};a.prototype.loadCss=function(b,a){const d=this;e.isNoU(b)&&(b={});e.isNoU(b.caching)||(a=b.caching);let k=d.componentId+".css";e.isNoU(b.path)||(k=b.path);a||(k+="?"+(new Date).getTime());f.ajax({url:h.getResourcePath(k),success:function(a){d.style(a);e.isFunction(b.success)&& 63 | b.success.call(d,a,d)},error:function(a){l.e(a,d);e.isFunction(b.error)&&b.error.call(d,a,d)},then:function(){e.isFunction(b.then)&&b.then.call(d,d)}});return this};a.prototype.loadHtml=function(b,a){const d=this;let m=d.componentId;e.isNoU(b)&&(b={});e.isNoU(b.caching)||(a=b.caching);e.isNoU(b.path)||(m=b.path);var g=h.store("zuix.inlineViews");null==g&&(g=[],h.store("zuix.inlineViews",g));if(null!=g[m])d.view(g[m]),e.isFunction(b.success)&&b.success.call(d,g[m],d),e.isFunction(b.then)&&b.then.call(d, 64 | d);else{const c=f().find("["+k.dataUiView+'="'+m+'"]:not(['+k.dataUiComponent+'*=""])');0a.indexOf("://")&&(a=(null!=b&&null!=b.resourcePath?b.resourcePath:"")+a);return a}function e(a,b){let n=g(a.componentId);null!==n&&null==b.controller&&null==a.controller()&&(a.controller(n.controller), 87 | q.t(a.componentId+":js","component:cached:js"));if(p.isNoU(b.view)){if(null!==n&&(null!=n.view&&(a.view(n.view),q.t(a.componentId+":html","component:cached:html")),!1!==b.css&&(b.css=!1,n.css_applied||(n.css_applied=!0,a.style(n.css),q.t(a.componentId+":css","component:cached:css")))),p.isNoU(a.view()))return O("resource-loader").queue(a.componentId+":html",function(){y[a.componentId]=this;a.loadHtml({cext:b.cext,caching:I,success:function(d){null==n&&(n=C(a));n.view=d;delete n.controller;!1!==b.css? 88 | (y[a.componentId].step(a.componentId+":css"),a.loadCss({caching:I,success:function(a){n.css=a},error:function(b){q.e(b,a)},then:function(){c(a,y[a.componentId])}})):c(a,y[a.componentId])},error:function(c){q.e(c,a);p.isFunction(b.error)&&a.error.call(a,c,a)}})},b.priority),a}else a.view(b.view);null==a.controller()?O("resource-loader").queue(a.componentId+":js",function(){y[a.componentId]=this;c(a,y[a.componentId])},K.length):c(a);return a}function h(a){if(a instanceof Element){const b=a;a=zuix.context(b); 89 | v.dequeue(b)}p.isNoU(a)||(p.isNoU(a._c)||(p.isNoU(a._c.view())||(a._c.view().attr(A.dataUiComponent,null),a._c.view().reset(),p.isNoU(a._c._fieldCache)||u.each(a._c._fieldCache,function(a,b){b.reset()}),a._c.view().detach()),p.isFunction(a._c.destroy)&&a._c.destroy.call(a,a)),a=a.container(),null!=a&&null!=a.parentNode&&a.parentNode.removeChild(a))}function b(a){a=new w(zuix,a,B);K.push(a);return a}function d(a,b){let c=null;if(a instanceof u.ZxQuery)a=a.get();else if("string"===typeof a){const b= 90 | u.find("["+A.dataUiContext+'="'+a+'"]');0