├── .eslintignore ├── .eslintrc ├── .eslintrc.es5.json ├── .gitignore ├── .npmignore ├── .rollup.es5.js ├── .rollup.es6.js ├── Dockerfile ├── README.md ├── app ├── assets │ └── modules │ │ ├── UserAgent.js │ │ └── es6.polyfill.js ├── bundle.js ├── index.html └── lib │ ├── App.js │ └── AppMain.js ├── conf └── webapp2-nginx.conf └── package.json /.eslintignore: -------------------------------------------------------------------------------- 1 | # 2 | 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module" 5 | }, 6 | "env": { 7 | "browser": true, 8 | "node": true, 9 | "es6": true 10 | }, 11 | "extends": [ 12 | "eslint:recommended" 13 | ], 14 | "rules": { 15 | "strict": [0], 16 | "no-new": [0], 17 | "new-cap": [0], 18 | "no-empty": [0], 19 | "no-shadow": [0], 20 | "camelcase": [0], 21 | "key-spacing": [0], 22 | "dot-notation": [0], 23 | "comma-dangle": [0], 24 | "no-console": [0], 25 | "no-multi-str": [0], 26 | "no-multi-spaces": [0], 27 | "no-control-regex": [0], 28 | "no-unused-vars": [1, { "vars": "all", "args": "after-used"} ], 29 | "no-underscore-dangle": [0], 30 | "no-use-before-define": [0], 31 | "no-inner-declarations": [0] 32 | }, 33 | "globals": { 34 | "unescape": false, 35 | "escape": false 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /.eslintrc.es5.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 5, 4 | "sourceType": "module" 5 | }, 6 | "env": { 7 | "browser": true, 8 | "node": true, 9 | "es6": false 10 | }, 11 | "extends": [ 12 | "eslint:recommended" 13 | ], 14 | "rules": { 15 | "strict": [0], 16 | "no-new": [0], 17 | "new-cap": [0], 18 | "no-empty": [0], 19 | "no-shadow": [0], 20 | "camelcase": [0], 21 | "key-spacing": [0], 22 | "dot-notation": [0], 23 | "comma-dangle": [0], 24 | "no-console": [0], 25 | "no-multi-str": [0], 26 | "no-multi-spaces": [0], 27 | "no-control-regex": [0], 28 | "no-unused-vars": [1, { "vars": "all", "args": "after-used"} ], 29 | "no-underscore-dangle": [0], 30 | "no-use-before-define": [0], 31 | "no-inner-declarations": [0] 32 | }, 33 | "globals": { 34 | "unescape": false, 35 | "escape": false, 36 | "Map": false, 37 | "Float32Array": false 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## ignore the dependency modules directory 2 | node_modules 3 | 4 | ## ignore the file that is automatically regenerated 5 | conf/*.crt 6 | conf/*.csr 7 | conf/*.key 8 | 9 | ## ignore the setting files 10 | .DS_Store 11 | 12 | ## dust 13 | npm-debug.log 14 | 15 | ## ignore gh-pages dir 16 | gh-pages 17 | 18 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ## ignore the dependency modules directory 2 | node_modules 3 | 4 | ## ignore the file that is automatically regenerated 5 | conf/*.crt 6 | conf/*.csr 7 | conf/*.key 8 | 9 | ## ignore the setting files 10 | .DS_Store 11 | .vimrc* 12 | .git* 13 | 14 | ## dust 15 | npm-debug.log 16 | 17 | ## ignore gh-pages dir 18 | gh-pages 19 | -------------------------------------------------------------------------------- /.rollup.es5.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import resolve from "rollup-plugin-node-resolve"; // resolve node_modules/index.js to ES6 3 | import commonjs from "rollup-plugin-commonjs"; // convert CommonJS -> ES6 4 | import buble from "rollup-plugin-buble"; // convert ES6 -> ES5 5 | import eslint from "rollup-plugin-eslint"; // ESLint 6 | import cleanup from "rollup-plugin-cleanup"; // clear comments and empty lines 7 | import license from "rollup-plugin-license"; // add License header 8 | 9 | // --- ES5/ES6/CommonJS/ESModules -> ES5 bundle --- 10 | export default { 11 | format: "iife", // wrap (function(){ code })(); 12 | entry: "app/lib/AppMain.js", 13 | dest: "app/bundle.js", 14 | plugins: [ 15 | resolve({ jsnext: true }), 16 | commonjs(), 17 | buble(), // ES6 -> ES5 18 | eslint({ configFile: path.resolve("./.eslintrc.es5.json") }), 19 | cleanup(), 20 | license({ 21 | banner: "Copyright 2017 @uupaa", 22 | //thirdParty: { 23 | // output: path.join(__dirname, "dependencies.txt"), 24 | // includePrivate: true, // Default is false. 25 | //}, 26 | }), 27 | ], 28 | intro: "", 29 | outro: "", 30 | banner: "if (typeof WebApp2 === \"undefined\") { // avoid duplicate running", 31 | footer: "}", 32 | } 33 | 34 | -------------------------------------------------------------------------------- /.rollup.es6.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import resolve from "rollup-plugin-node-resolve"; // resolve node_modules/index.js to ES6 3 | import commonjs from "rollup-plugin-commonjs"; // convert CommonJS -> ES6 4 | //import buble from "rollup-plugin-buble"; // convert ES6 -> ES5 5 | import eslint from "rollup-plugin-eslint"; // ESLint 6 | import cleanup from "rollup-plugin-cleanup"; // clear comments and empty lines 7 | import license from "rollup-plugin-license"; // add License header 8 | 9 | // --- ES5/ES6/CommonJS/ESModules -> ES6 bundle --- 10 | export default { 11 | format: "es", 12 | entry: "app/lib/AppMain.js", 13 | dest: "app/bundle.js", 14 | plugins: [ 15 | resolve({ jsnext: true }), 16 | commonjs(), 17 | //buble(), // ES6 -> ES5 18 | eslint({ configFile: path.resolve("./.eslintrc") }), 19 | cleanup(), 20 | license({ 21 | banner: "Copyright 2017 @uupaa", 22 | //thirdParty: { 23 | // output: path.join(__dirname, "dependencies.txt"), 24 | // includePrivate: true, // Default is false. 25 | //}, 26 | }), 27 | ], 28 | intro: "", 29 | outro: "", 30 | banner: "if (typeof WebApp2 === \"undefined\") { // avoid duplicate running", 31 | footer: "}", 32 | } 33 | 34 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | MAINTAINER uupaa 3 | 4 | COPY conf/webapp2-nginx.conf /etc/nginx/nginx.conf 5 | COPY conf/webapp2-server.crt /etc/nginx/webapp2-server.crt 6 | COPY conf/webapp2-server.key /etc/nginx/webapp2-server.key 7 | 8 | EXPOSE 443 9 | EXPOSE 80 10 | 11 | CMD ["nginx", "-g", "daemon off;"] 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebApp/2 2 | 3 | Docker + HTTP/2 + ESModules based WebApplication develop environment. 4 | 5 | ## Document 6 | 7 | - [Overview](https://github.com/uupaa/WebApp2/wiki) 8 | - [Install and Setup](https://github.com/uupaa/WebApp2/wiki/Install-and-Setup) 9 | 10 | 11 | ## Licence 12 | 13 | MIT License 14 | 15 | Copyright (c) 2017 uupaa 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights 20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | copies of the Software, and to permit persons to whom the Software is 22 | furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in all 25 | copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | SOFTWARE. 34 | 35 | -------------------------------------------------------------------------------- /app/assets/modules/UserAgent.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) Copyright (c) 2017 uupaa 2 | // 3 | // --- technical terms / data structures ------------------- 4 | // app.userAgent - https://github.com/uupaa/WebApp2/wiki/app.userAgent 5 | // Microsoft Edge - https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx 6 | // Firefox - https://developer.mozilla.org/ja/docs/Gecko_user_agent_string_reference 7 | // WebView - https://developer.chrome.com/multidevice/user-agent#webview_user_agent 8 | // - https://developer.chrome.com/multidevice/webview/overview#does_the_new_webview_have_feature_parity_with_chrome_for_android_ 9 | // Kindle Silk - http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html 10 | 11 | // --- dependency modules ---------------------------------- 12 | // --- define / local variables ---------------------------- 13 | const GlobalObject = (typeof self !== "undefined") ? self : global; 14 | 15 | // --- class / interfaces ---------------------------------- 16 | export class UserAgent { 17 | static detect(userAgent, options = {}) { 18 | return _detectUserAgent(userAgent, options); 19 | } 20 | } 21 | 22 | // --- implements ------------------------------------------ 23 | function _detectUserAgent(userAgent, // @arg String = navigator.userAgent 24 | options) { // @arg Object = {} - { webView, displayDPR, displayLong, displayShort } 25 | // @options.webView Boolean = false 26 | // @options.displayDPR Number = window.devicePixelRatio || 1.0 27 | // @options.displayLong Number = Math.max(screenWidth, screenHeight) 28 | // @options.displayShort Number = Math.min(screenWidth, screenHeight) 29 | // @ret Object 30 | // --- Runtime Detection --- 31 | const hasGlobal = !!(GlobalObject.global); // Node.js, NW.js, Electron 32 | const processType = !!((GlobalObject.process || 0).type); // Electron(render or main) 33 | const nativeTimer = !!(/native/.test(setTimeout)); // Node.js, Electron(main) 34 | const worker = !hasGlobal && ("WorkerLocation" in GlobalObject); // Worker 35 | const browser = !hasGlobal && !worker && ("document" in GlobalObject); // Browser 36 | const node = hasGlobal && !processType && !nativeTimer; // Node.js 37 | const nw = hasGlobal && !processType && nativeTimer; // NW.js 38 | const electron = hasGlobal && processType; // Electron(render or main) 39 | 40 | // --- UserAgent Detection --- 41 | const nav = GlobalObject["navigator"] || {}; 42 | const ua = userAgent || nav["userAgent"] || ""; 43 | const osName = _detectOSName(ua); 44 | const osVersion = _detectOSVersion(ua, osName); 45 | const browserName = _detectBrowserName(ua); 46 | const browserVersion = _detectBrowserVersion(ua, browserName); 47 | const baseBrowser = _detectBaseBrowser(browserName, parseFloat(osVersion)); 48 | const gl = _detectWebGL(browser || nw || electron); 49 | const deviceName = _detectDevice(ua, osName, osVersion, gl, options); 50 | const lang = _detectLanguage(nav); 51 | const mobile = (/Android|iOS/.test(osName) || /Windows Phone/.test(ua)); 52 | const iOS = osName === "iOS"; 53 | const android = osName === "Android"; 54 | const webView = _isWebView(ua, osName, browserName, browserVersion, options); 55 | const touch3D = /^(iPhone 6s|iPhone 7)/.test(deviceName) || 56 | parseFloat(osVersion) >= 10 && /^iPad Pro/.test(deviceName); 57 | const esVersion = /native/.test(String["raw"] + "") ? 6 : 58 | /native/.test(Object["keys"] + "") ? 5 : 3; 59 | const result = { 60 | // --- OS --- 61 | ios: iOS, // is iOS. (iPhone, iPad, iPod) 62 | mac: osName === "Mac", // is macOS. 63 | android: android, // is Android. 64 | windows: osName === "Windows", // is Windows. (Windows, WindowsPhone) 65 | osName: osName, // OS Name. "iOS", "Mac", "Android", "Windows", "Firefox", "Chrome OS", "" 66 | osVersion: osVersion, // Semver String. "{{Major}},{{Minor}},{{Patch}}" 67 | // --- Browser --- 68 | ie: browserName === "IE", // is IE 69 | edge: browserName === "Edge", // is Edge 70 | firefox: browserName === "Firefox", // is Firefox 71 | chrome: browserName === "Chrome", // is Chrome 72 | safari: browserName === "Safari", // is Safari 73 | silk: browserName === "Silk", // is Kindle Silk Browser. (WebKit or Chromium based browser) 74 | aosp: browserName === "AOSP", // is AOSP Stock Browser. (WebKit based browser) 75 | webkit: baseBrowser === "WebKit", // is WebKit based browser 76 | chromium: baseBrowser === "Chromium", // is Chromium based browser 77 | baseBrowser: baseBrowser, // Base Name. "Chromium", "Firefox", "IE", "Edge", "WebKit" 78 | browserName: browserName, // Browser Name. "Chrome", "Firefox", "IE", "Edge", "AOSP", "Safari", "WebKit", "Chrome for iOS", "Silk", "" 79 | browserVersion: browserVersion, // Semver String. "{{Major}},{{Minor}},{{Patch}}" 80 | // --- Runtime --- 81 | pc: !mobile, // is PC. Windows, Mac, Chrome OS 82 | mobile: mobile, // is Mobile. Android, iOS, WindowsPhone 83 | browser: browser, // Browser 84 | worker: worker, // Worker 85 | node: node, // Node.js 86 | nw: nw, // NW.js 87 | electron: electron, // Electron(render or main) 88 | // --- Device --- 89 | ipod: iOS && /iPod/.test(ua), // is iPod 90 | ipad: iOS && /iPad/.test(ua), // is iPad 91 | iphone: iOS && /iPhone/.test(ua), // is iPhone 92 | kindle: browserName === "Silk", // is Kindle 93 | deviceName: deviceName, // Device name String. 94 | touch3D: touch3D, // Device has 3D touch function. 95 | // --- WebView --- 96 | webView: webView, // is WebView. 97 | // --- WebGL --- 98 | gl: gl, // WebGL info. { type:String, version:String, vendor:String, renderer:String, maxTextureSize:UINT32 } 99 | // --- Other --- 100 | userAgent: ua, // UserAgent String. 101 | language: lang, // Language String. "en", "ja", ... 102 | esVersion: esVersion, // ECMAScript Version. 3 or 5 or 6 103 | has: function(key) { return typeof this[key] !== "undefined"; }, // has(key:String):Boolean 104 | get: function(key) { return this[key]; }, // get(key:String):Any 105 | }; 106 | return result; 107 | } 108 | 109 | function _detectLanguage(nav) { 110 | let lang = nav["language"] || "en"; 111 | 112 | if (nav["languages"] && Array.isArray(nav["languages"])) { 113 | lang = nav["languages"][0] || lang; 114 | } 115 | return lang.split("-")[0]; // "en-us" -> "en" 116 | } 117 | 118 | function _detectOSName(ua) { 119 | switch (true) { 120 | case /Android/.test(ua): return "Android"; 121 | case /iPhone|iPad|iPod/.test(ua): return "iOS"; 122 | case /Windows/.test(ua): return "Windows"; 123 | case /Mac OS X/.test(ua): return "Mac"; 124 | case /CrOS/.test(ua): return "Chrome OS"; 125 | case /Firefox/.test(ua): return "Firefox OS"; 126 | } 127 | return ""; 128 | } 129 | 130 | function _detectOSVersion(ua, osName) { 131 | switch (osName) { 132 | case "Android": return _getVersion(ua, "Android"); 133 | case "iOS": return _getVersion(ua, /OS /); 134 | case "Windows": return _getVersion(ua, /Phone/.test(ua) ? /Windows Phone (?:OS )?/ 135 | : /Windows NT/); 136 | case "Mac": return _getVersion(ua, /Mac OS X /); 137 | } 138 | return "0.0.0"; 139 | } 140 | 141 | function _detectBrowserName(ua) { 142 | const android = /Android/.test(ua); 143 | 144 | switch (true) { 145 | case /CriOS/.test(ua): return "Chrome for iOS"; // https://developer.chrome.com/multidevice/user-agent 146 | case /Edge/.test(ua): return "Edge"; 147 | case android && /Silk\//.test(ua): return "Silk"; // Kidle Silk browser 148 | case /Chrome/.test(ua): return "Chrome"; 149 | case /Firefox/.test(ua): return "Firefox"; 150 | case android: return "AOSP"; // AOSP stock browser 151 | case /MSIE|Trident/.test(ua): return "IE"; 152 | case /Safari\//.test(ua): return "Safari"; 153 | case /AppleWebKit/.test(ua): return "WebKit"; 154 | } 155 | return ""; 156 | } 157 | 158 | function _detectBrowserVersion(ua, browserName) { 159 | switch (browserName) { 160 | case "Chrome for iOS": return _getVersion(ua, "CriOS/"); 161 | case "Edge": return _getVersion(ua, "Edge/"); 162 | case "Chrome": return _getVersion(ua, "Chrome/"); 163 | case "Firefox": return _getVersion(ua, "Firefox/"); 164 | case "Silk": return _getVersion(ua, "Silk/"); 165 | case "AOSP": return _getVersion(ua, "Version/"); 166 | case "IE": return /IEMobile/.test(ua) ? _getVersion(ua, "IEMobile/") 167 | : /MSIE/.test(ua) ? _getVersion(ua, "MSIE ") // IE 10 168 | : _getVersion(ua, "rv:"); // IE 11 169 | case "Safari": return _getVersion(ua, "Version/"); 170 | case "WebKit": return _getVersion(ua, "WebKit/"); 171 | } 172 | return "0.0.0"; 173 | } 174 | 175 | const BASE_BROWSERS = { 176 | "Chrome": "Chromium", 177 | "Firefox": "Firefox", 178 | "IE": "IE", 179 | "Edge": "Edge", 180 | "AOSP": "WebKit", 181 | "Safari": "WebKit", 182 | "WebKit": "WebKit", 183 | "Chrome for iOS": "WebKit", 184 | "Silk": "WebKit", // or "Chromium" if version >= 4.4 185 | }; 186 | 187 | function _detectBaseBrowser(browserName, osVer) { 188 | if (browserName === "Silk" && osVer >= 4.4) { 189 | return "Chromium"; 190 | } 191 | return BASE_BROWSERS[browserName] || ""; 192 | } 193 | 194 | function _detectDevice(ua, osName, osVersion, gl, options) { 195 | const screen = GlobalObject["screen"] || {}; 196 | const screenWidth = screen["width"] || 0; 197 | const screenHeight = screen["height"] || 0; 198 | const dpr = options["displayDPR"] || GlobalObject["devicePixelRatio"] || 1.0; 199 | const long_ = options["displayLong"] || Math.max(screenWidth, screenHeight); 200 | const short_ = options["displayShort"] || Math.min(screenWidth, screenHeight); 201 | const retina = dpr >= 2; 202 | const longEdge = Math.max(long_, short_); // iPhone 4s: 480, iPhone 5: 568 203 | 204 | switch (osName) { 205 | case "Android": return _getAndroidDevice(ua, retina); 206 | case "iOS": return _getiOSDevice(ua, retina, longEdge, osVersion, gl); 207 | } 208 | return ""; 209 | } 210 | 211 | function _getAndroidDevice(ua, retina) { 212 | if (/Firefox/.test(ua)) { return ""; } // exit Firefox for Android 213 | try { 214 | const result = ua.split("Build/")[0].split(";").slice(-1).join().trim(). 215 | replace(/^SonyEricsson/, ""). 216 | replace(/^Sony/, "").replace(/ 4G$/, ""); 217 | if (result === "Nexus 7") { 218 | return retina ? "Nexus 7 (2013)" // Nexus 7 (2013) 219 | : "Nexus 7"; // Nexus 7 (2012) 220 | } 221 | return result; 222 | } catch ( o__o ) { 223 | // ignore 224 | } 225 | return ""; 226 | } 227 | 228 | function _getiOSDevice(ua, retina, longEdge, osVersion, gl) { 229 | // https://github.com/uupaa/WebGLDetector.js/wiki/Examples 230 | // https://developer.apple.com/library/content/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/HardwareGPUInformation/HardwareGPUInformation.html 231 | const glRenderer = gl.renderer + " " + gl.version; 232 | const simulator = /Software/.test(glRenderer); // Simulator: "Apple Software Renderer" 233 | const A10X = /A10X GPU/.test(glRenderer); // A10X: iPad Pro 10.5 (2017) / iPad Pro 12.9 (2017) 234 | const A10 = /A10 GPU/.test(glRenderer); // A10: iPhone 7/7+ 235 | const A9X = /A9X GPU/.test(glRenderer); // A9X: iPad Pro 9.7 (2016), iPad Pro 12.9 (2015) 236 | const A9 = /A9 GPU/.test(glRenderer); // A9: iPhone 6s/6s+/SE, iPad 5 237 | const A8X = /A8X GPU/.test(glRenderer); // A8X: iPad Air 2 238 | const A8 = /A8 GPU/.test(glRenderer); // A8: iPhone 6/6+, iPad mini 4, iPod touch 6 239 | const A7 = /A7 GPU/.test(glRenderer); // A7: iPhone 5s, iPad mini 2/3, iPad Air 240 | const SGX554 = /554/.test(glRenderer); // SGX554: iPad 4 241 | const SGX543 = /543/.test(glRenderer); // SGX543: iPhone 4s/5/5c, iPad 2/3, iPad mini 242 | //const SGX535 = /535/.test(glRenderer); // SGX535: iPhone 3GS, iPhone 4 243 | //const Metal = /Metal/.test(glRenderer); // Metal: iPhone 7/7+ 244 | //const Metal2 = /Metal2/.test(glRenderer); // Metal2: TBD 245 | 246 | // 247 | // | Device name | zoomed | longEdge | view port | 248 | // |------------------------------|--------|----------|-------------| 249 | // | iPhone 3/3GS | | 480 | 320 x 480 | 250 | // | iPhone 4/4s/5/5c/5s/SE | | 568 | 320 x 568 | 251 | // | iPhone 6/6s/7 | YES | 568 | 320 x 568 | 252 | // | iPhone 6/6s/7 | | 667 | 375 x 667 | 253 | // | iPhone 6+/6s+/7+ | YES | 667 | 375 x 667 | 254 | // | iPhone 6+/6s+/7+ | | 736 | 414 x 736 | 255 | // | iPad 1/2/3/4/5 | | 1024 | 768 x 1024 | 256 | // | iPad mini/mini2/mini3/mini4 | | 1024 | 768 x 1024 | 257 | // | iPad Air/Air2 | | 1024 | 768 x 1024 | 258 | // | iPad Pro 9.7 (2016) | | 1024 | 768 x 1024 | 259 | // | iPad Pro 10.5 (2017) | | 1112 | 834 x 1112 | 260 | // | iPad Pro 12.9 (2015, 2017) | | 1366 | 1024 x 1366 | 261 | 262 | if (/iPhone/.test(ua)) { 263 | 264 | // | Device name | zoomed | detected device width x height | 265 | // |------------------|--------|--------------------------------| 266 | // | "iPhone 6" | YES | iPhone 6 (320 x 568) | 267 | // | "iPhone 6s" | YES | iPhone 6s (320 x 568) | 268 | // | "iPhone 7" | YES | iPhone 7 (320 x 568) | 269 | // | "iPhone 6 Plus" | YES | iPhone 6 (375 x 667) [!] | 270 | // | "iPhone 6s Plus" | YES | iPhone 6s (375 x 667) | 271 | // | "iPhone 7 Plus" | YES | iPhone 7 (375 x 667) [!] | 272 | if (simulator) { 273 | return "iPhone Simulator"; 274 | } 275 | return !retina ? "iPhone 3GS" 276 | : longEdge <= 480 ? (SGX543 || osVersion >= 8 ? "iPhone 4s" : "iPhone 4") // iPhone 4 stopped in iOS 7. 277 | : longEdge <= 568 ? (A10 ? "iPhone 7" : // iPhone 7 (zoomed) 278 | A9 ? "iPhone SE" : // iPhone 6s (zoomed) or iPhone SE [!!] 279 | A8 ? "iPhone 6" : // iPhone 6 (zoomed) 280 | A7 ? "iPhone 5s" : // iPhone 5s 281 | SGX543 ? "iPhone 5" // iPhone 5 or iPhone 5c 282 | : "iPhone x") // Unknown device 283 | : longEdge <= 667 ? (A10 ? "iPhone 7" : // iPhone 7 or iPhone 7+ (zoomed) 284 | A9 ? "iPhone 6s" : // iPhone 6s or iPhone 6s+ (zoomed) 285 | A8 ? "iPhone 6" // iPhone 6 or iPhone 6+ (zoomed) 286 | : "iPhone x") // Unknown device 287 | : longEdge <= 736 ? (A10 ? "iPhone 7 Plus" : // iPhone 7+ 288 | A9 ? "iPhone 6s Plus" : // iPhone 6s+ 289 | A8 ? "iPhone 6 Plus" // iPhone 6+ 290 | : "iPhone x") // Unknown device (maybe new iPhone) 291 | : "iPhone x"; 292 | } else if (/iPad/.test(ua)) { 293 | if (simulator) { 294 | return "iPad Simulator"; 295 | } 296 | return !retina ? "iPad 2" // iPad 1/2, iPad mini 297 | : SGX543 ? "iPad 3" 298 | : SGX554 ? "iPad 4" 299 | : A7 ? "iPad mini 2" // iPad mini 3, iPad Air 300 | : A8X ? "iPad Air 2" 301 | : A8 ? "iPad mini 4" 302 | : A9X ? (longEdge <= 1024 ? "iPad Pro 9.7" : "iPad Pro 12.9") 303 | : A9 ? "iPad 5" 304 | : A10X ? (longEdge <= 1112 ? "iPad Pro 10.5" : "iPad Pro 12.9") 305 | : "iPad x"; // Unknown device (maybe new iPad) 306 | } else if (/iPod/.test(ua)) { 307 | if (simulator) { 308 | return "iPod Simulator"; 309 | } 310 | return longEdge <= 480 ? (retina ? "iPod touch 4" : "iPod touch 3") 311 | : (A8 ? "iPod touch 6" : "iPod touch 5"); 312 | } 313 | return "iPhone x"; 314 | } 315 | 316 | function _getVersion(ua, token) { // @ret SemverString - "0.0.0" 317 | try { 318 | return _normalizeSemverString( ua.split(token)[1].trim().split(/[^\w\.]/)[0] ); 319 | } catch ( o_O ) { 320 | // ignore 321 | } 322 | return "0.0.0"; 323 | } 324 | 325 | function _normalizeSemverString(version) { // @arg String - "Major.Minor.Patch" 326 | // @ret SemverString - "Major.Minor.Patch" 327 | let ary = version.split(/[\._]/); // "1_2_3" -> ["1", "2", "3"] 328 | // "1.2.3" -> ["1", "2", "3"] 329 | return ( parseInt(ary[0], 10) || 0 ) + "." + 330 | ( parseInt(ary[1], 10) || 0 ) + "." + 331 | ( parseInt(ary[2], 10) || 0 ); 332 | } 333 | 334 | function _isWebView(ua, osName, browserName, browserVersion, options) { // @ret Boolean - is WebView 335 | switch (osName + browserName) { 336 | case "iOSSafari": return false; 337 | case "iOSWebKit": return _isWebView_iOS(options); 338 | case "AndroidAOSP": return false; // can not accurately detect 339 | case "AndroidChrome": return parseFloat(browserVersion) >= 42 ? /; wv/.test(ua) 340 | : /\d{2}\.0\.0/.test(browserVersion) ? true // 40.0.0, 37.0.0, 36.0.0, 33.0.0, 30.0.0 341 | : _isWebView_Android(options); 342 | } 343 | return false; 344 | } 345 | 346 | function _isWebView_iOS(options) { // @arg Object - { webView } 347 | // @ret Boolean 348 | // Chrome 15++, Safari 5.1++, IE11, Edge, Firefox10++ 349 | // Android 5.0 ChromeWebView 30: webkitFullscreenEnabled === false 350 | // Android 5.0 ChromeWebView 33: webkitFullscreenEnabled === false 351 | // Android 5.0 ChromeWebView 36: webkitFullscreenEnabled === false 352 | // Android 5.0 ChromeWebView 37: webkitFullscreenEnabled === false 353 | // Android 5.0 ChromeWebView 40: webkitFullscreenEnabled === false 354 | // Android 5.0 ChromeWebView 42: webkitFullscreenEnabled === ? 355 | // Android 5.0 ChromeWebView 44: webkitFullscreenEnabled === true 356 | let document = (GlobalObject["document"] || {}); 357 | 358 | if ("webView" in options) { 359 | return options["webView"]; 360 | } 361 | return !("fullscreenEnabled" in document || 362 | "webkitFullscreenEnabled" in document || false); 363 | } 364 | 365 | function _isWebView_Android(options) { // @arg Object - { webView } 366 | // @ret Boolean 367 | // Chrome 8++ 368 | // Android 5.0 ChromeWebView 30: webkitRequestFileSystem === false 369 | // Android 5.0 ChromeWebView 33: webkitRequestFileSystem === false 370 | // Android 5.0 ChromeWebView 36: webkitRequestFileSystem === false 371 | // Android 5.0 ChromeWebView 37: webkitRequestFileSystem === false 372 | // Android 5.0 ChromeWebView 40: webkitRequestFileSystem === false 373 | // Android 5.0 ChromeWebView 42: webkitRequestFileSystem === false 374 | // Android 5.0 ChromeWebView 44: webkitRequestFileSystem === false 375 | if ("webView" in options) { 376 | return options["webView"]; 377 | } 378 | return !("requestFileSystem" in GlobalObject || 379 | "webkitRequestFileSystem" in GlobalObject || false); 380 | } 381 | 382 | function _detectWebGL(hasCanvas) { // @arg Boolean - browser or nw or electron 383 | // @ret Object - { type, version, vendor, renderer, maxTextureSize } 384 | let result = { 385 | type: "", // WebGL context type. "webgl" or "webgl2" or "experimental-webgl", ... 386 | version: "", // WebGL version string. "WebGL 1.0 (OpenGL ES 2.0 Chromium)" 387 | vendor: "", // WebGL vendor string. "WebKit" 388 | renderer: "", // WebGL renderer string. "WebKit WebGL" 389 | maxTextureSize: 0, // MAX_TEXTURE_SIZE (0 or 1024 - 16384) 390 | }; 391 | 392 | if (hasCanvas) { 393 | let canvas = document.createElement("canvas"); 394 | 395 | if (canvas && canvas.getContext) { 396 | const types = ["webgl2", "experimental-webgl2", "webgl", "experimental-webgl"]; 397 | 398 | for (let i = 0, iz = types.length; i < iz; ++i) { 399 | let type = types[i]; 400 | let gl = canvas.getContext(type); 401 | 402 | if (gl) { 403 | result.type = type; 404 | result.version = gl.getParameter(gl.VERSION); 405 | result.vendor = gl.getParameter(gl.VENDOR); 406 | result.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); 407 | 408 | let info = gl.getExtension("WEBGL_debug_renderer_info"); 409 | if (info) { 410 | result.vendor = gl.getParameter(info.UNMASKED_VENDOR_WEBGL); 411 | result.renderer = gl.getParameter(info.UNMASKED_RENDERER_WEBGL); 412 | } 413 | break; 414 | } 415 | } 416 | } 417 | } 418 | return result; 419 | } 420 | 421 | -------------------------------------------------------------------------------- /app/assets/modules/es6.polyfill.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) Copyright (c) 2017 uupaa 2 | // ECMA-262 Script version 6/7 polyfill, fallback and shims for WebApp/2 migration 3 | // https://github.com/uupaa/ES.js 4 | 5 | (function(global) { 6 | "use strict"; 7 | 8 | var ES6 = { 9 | "Object": { 10 | "assign": Object_assign, 11 | "is": Object_is, 12 | }, 13 | "Array": { 14 | "of": Array_of, 15 | "from": Array_from, 16 | "prototype": { 17 | "entries": Array_entries, 18 | "keys": Array_keys, 19 | "fill": Array_fill, 20 | "find": Array_find, 21 | "findIndex": Array_findIndex, 22 | "values": Array_values, 23 | "copyWithin": Array_copyWithin, 24 | } 25 | }, 26 | "String": { 27 | //"raw": String_raw 28 | "fromCodePoint": String_fromCodePoint, 29 | "prototype": { 30 | "repeat": String_repeat, 31 | "codePointAt": String_codePointAt, 32 | "has": String_includes, 33 | "includes": String_includes, 34 | //"normalize": String_normalize, 35 | "startsWith": String_startsWith, 36 | "endsWith": String_endsWith, 37 | } 38 | }, 39 | "Number": { 40 | "isNaN": Number_isNaN, 41 | "isFinite": Number_isFinite, 42 | "isInteger": Number_isInteger, 43 | "isSafeInteger": Number_isSafeInteger, 44 | "parseInt": global.parseInt, 45 | "parseFloat": global.parseFloat, 46 | "NaN": global.NaN, 47 | "EPSILON": 2.2204460492503130808472633361816E-16, 48 | "MAX_VALUE": 1.7976931348623157E+308, 49 | "MIN_VALUE": 5E-324, 50 | "MAX_SAFE_INTEGER": 9007199254740991, 51 | "MIN_SAFE_INTEGER": -9007199254740991, 52 | "POSITIVE_INFINITY":Infinity, 53 | "NEGATIVE_INFINITY":-Infinity, 54 | }, 55 | "Math": { 56 | "acosh": function(x) { return Math.log(x + Math.sqrt(x * x - 1)); }, 57 | "asinh": function(x) { return x === -Infinity ? x : Math.log(x + Math.sqrt(x * x + 1)); }, 58 | "atanh": function(x) { return Math.log( (1 + x) / (1 - x) ) / 2; }, 59 | "cbrt": function(x) { var y = Math.pow( Math.abs(x), 1 / 3 ); return x < 0 ? -y : y; }, 60 | "clz32": Math_clz32, 61 | "cosh": function(x) { var y = Math.exp(x); return (y + 1 / y) / 2; }, 62 | "expm1": function(x) { return Math.exp(x) - 1; }, 63 | "fround": Math_fround, 64 | "hypot": Math_hypot, 65 | "imul": Math_imul, 66 | "log10": function(x) { return Math.log(x) / Math.LN10; }, 67 | "log1p": function(x) { return Math.log(1 + x); }, 68 | "log2": function(x) { return Math.log(x) / Math.LN2; }, 69 | "sign": function(x) { var y = Number(x); return (y === 0 || isNaN(y)) ? y : (y > 0 ? 1 : -1); }, 70 | "sinh": function(x) { var y = Math.exp(x); return (y - 1 / y) / 2; }, 71 | "tanh": Math_tanh, 72 | "trunc": function(x) { return x < 0 ? Math.ceil(x) : Math.floor(x); }, 73 | "LOG2E": 1.442, 74 | "LOG10E": 0.4342944819032518, 75 | }, 76 | "Set": Set, 77 | "WeakSet": WeakSet, 78 | "Map": Map, 79 | "WeakMap": WeakMap, 80 | }; 81 | 82 | var ES7 = { 83 | "Object": { 84 | "values": Object_values, 85 | "entries": Object_entries, 86 | }, 87 | "Array": { 88 | "prototype": { 89 | "includes": Array_includes, 90 | } 91 | } 92 | }; 93 | 94 | if (Function.prototype.name === undefined) { 95 | Object.defineProperty(Function.prototype, "name", { 96 | "configurable": true, 97 | "enumerable": false, 98 | "get": function() { 99 | return this.toString().split(" ")[1].split(/[^\w$]/)[0]; 100 | } 101 | }); 102 | } 103 | if (/a/i["flags"] !== "i") { 104 | Object.defineProperty(RegExp.prototype, "flags", { 105 | "configurable": true, 106 | "enumerable": false, 107 | "get": function() { 108 | return this.toString().match(/[gimuy]*$/)[0]; 109 | } 110 | }); 111 | } 112 | 113 | function Set(iterable) { 114 | Set_constructor.call(this, iterable); 115 | } 116 | Set["prototype"] = (Object.create)(Set, { 117 | "constructor": { "value": Set }, 118 | "size": { "get": Set_size }, 119 | "add": { "value": Set_add }, 120 | "has": { "value": Set_has }, 121 | "values": { "value": Set_values }, 122 | "entries": { "value": Set_entries }, 123 | "forEach": { "value": Set_forEach }, 124 | "delete": { "value": Set_delete }, 125 | "clear": { "value": Set_clear }, 126 | "@@iterator": { "value": Set_entries }, 127 | }); 128 | 129 | function WeakSet(iterable) { 130 | WeakSet_constructor.call(this, iterable); 131 | } 132 | WeakSet["prototype"] = (Object.create)(WeakSet, { 133 | "constructor": { "value": WeakSet }, 134 | "add": { "value": WeakSet_add }, 135 | "has": { "value": WeakSet_has }, 136 | "delete": { "value": WeakSet_delete }, 137 | }); 138 | 139 | function Map(iterable) { 140 | Map_constructor.call(this, iterable); 141 | } 142 | Map["prototype"] = (Object.create)(Map, { 143 | "constructor": { "value": Map }, 144 | "size": { "get": Map_size }, 145 | "get": { "value": Map_get }, 146 | "set": { "value": Map_set }, 147 | "has": { "value": Map_has }, 148 | "keys": { "value": Map_keys }, 149 | "values": { "value": Map_values }, 150 | "entries": { "value": Map_entries }, 151 | "forEach": { "value": Map_forEach }, 152 | "delete": { "value": Map_delete }, 153 | "clear": { "value": Map_clear }, 154 | "@@iterator": { "value": Map_entries }, 155 | }); 156 | 157 | function WeakMap(iterable) { 158 | WeakMap_constructor.call(this, iterable); 159 | } 160 | WeakMap["prototype"] = (Object.create)(WeakMap, { 161 | "constructor": { "value": WeakMap }, 162 | "get": { "value": WeakMap_get }, 163 | "set": { "value": WeakMap_set }, 164 | "has": { "value": WeakMap_has }, 165 | "delete": { "value": WeakMap_delete }, 166 | }); 167 | 168 | function Object_assign(target /*, sources ... */) { 169 | var args = arguments; 170 | 171 | for (var i = 1, iz = args.length; i < iz; ++i) { 172 | var source = args[i]; 173 | 174 | if (source) { 175 | var keys = Object.keys(source); 176 | 177 | for (var k = 0, kz = keys.length; k < kz; ++k) { 178 | var key = keys[k]; 179 | var desc = Object.getOwnPropertyDescriptor(source, key); 180 | 181 | if (desc && desc["enumerable"]) { 182 | target[key] = source[key]; 183 | } 184 | } 185 | } 186 | } 187 | return target; 188 | } 189 | 190 | function Object_is(value1, value2) { 191 | if (typeof value1 !== typeof value2) { 192 | return false; 193 | } 194 | if (isNaN(value1)) { 195 | return isNaN(value2); 196 | } 197 | if (value1 === 0 && value2 === 0) { 198 | return (1 / value1) === (1 / value2); 199 | } 200 | return value1 === value2; 201 | } 202 | 203 | function Array_of(/* values ... */) { 204 | return Array.prototype.slice.call(arguments); 205 | } 206 | 207 | function Array_from(items, mapFn, thisArg) { 208 | if (!mapFn) { 209 | return [].slice.call(items); 210 | } 211 | var that = thisArg || null; 212 | var result = []; 213 | for (var i = 0, iz = items.length; i < iz; ++i) { 214 | result.push( mapFn.call(that, items[i], i, items) ); 215 | } 216 | return result; 217 | } 218 | 219 | function ArrayIterator(data, nextFn) { 220 | this._data = data; 221 | this._cursor = -1; 222 | this["next"] = nextFn; 223 | } 224 | function ArrayIterator_keys() { 225 | var index = ++this._cursor; 226 | var done = index >= this._data.length; 227 | 228 | return done ? { "value": undefined, "done": true } 229 | : { "value": index, "done": false }; 230 | } 231 | function ArrayIterator_values() { 232 | var index = ++this._cursor; 233 | var done = index >= this._data.length; 234 | 235 | return done ? { "value": undefined, "done": true } 236 | : { "value": this._data[index], "done": false }; 237 | } 238 | function ArrayIterator_keyAndValues() { 239 | var index = ++this._cursor; 240 | var done = index >= this._data.length; 241 | 242 | return done ? { "value": undefined, "done": true } 243 | : { "value": [ index, this._data[index] ], "done": false }; 244 | } 245 | 246 | function Array_entries() { 247 | return new ArrayIterator(this, ArrayIterator_keyAndValues); 248 | } 249 | 250 | function Array_keys() { 251 | return new ArrayIterator(this, ArrayIterator_keys); 252 | } 253 | 254 | function Array_fill(value, start, end) { 255 | start = start >> 0; 256 | end = end === undefined ? this.length : end >> 0; 257 | 258 | var iz = this.length; 259 | var i = start < 0 ? Math.max(start + iz, 0) : Math.min(start, iz); 260 | var final = end < 0 ? Math.max(end + iz, 0) : Math.min(end, iz); 261 | 262 | for (; i < final; ++i) { 263 | this[i] = value; 264 | } 265 | return this; 266 | } 267 | 268 | function Array_find(predicate, thisArg) { 269 | var result = this["findIndex"](predicate, thisArg); 270 | 271 | return result === -1 ? undefined : this[result]; 272 | } 273 | 274 | function Array_findIndex(predicate, thisArg) { 275 | for (var i = 0, iz = this.length; i < iz; ++i) { 276 | var result = predicate.call(thisArg, this[i], i, this); 277 | 278 | if (result) { 279 | return i; 280 | } 281 | } 282 | return -1; 283 | } 284 | 285 | function Array_values() { 286 | return new ArrayIterator(this, ArrayIterator_values); 287 | } 288 | 289 | function Array_copyWithin(target, start, end) { 290 | target = target >> 0; 291 | start = start >> 0; 292 | end = end === undefined ? this.length : end >> 0; 293 | 294 | var iz = this.length; 295 | var to = target < 0 ? Math.max(target + iz, 0) : Math.min(target, iz); 296 | var from = start < 0 ? Math.max(start + iz, 0) : Math.min(start, iz); 297 | var final = end < 0 ? Math.max(end + iz, 0) : Math.min(end, iz); 298 | var count = Math.min(final - from, iz - to); 299 | 300 | if (from < to && to < (from + count)) { 301 | for (; count > 0; --from, --to, --count) { 302 | if (from in this) { 303 | this[to] = this[from]; 304 | } else { 305 | delete this[to]; 306 | } 307 | } 308 | } else { 309 | for (; count > 0; ++from, ++to, --count) { 310 | if (from in this) { 311 | this[to] = this[from]; 312 | } else { 313 | delete this[to]; 314 | } 315 | } 316 | } 317 | return this; 318 | } 319 | 320 | function String_fromCodePoint(/* codePoints ... */) { 321 | var args = arguments; 322 | var result = []; 323 | 324 | for (var i = 0, az = args.length; i < az; ++i) { 325 | var codePoint = args[i]; 326 | 327 | if (codePoint < 0x10000) { 328 | result.push(codePoint); 329 | } else { 330 | var offset = codePoint - 0x10000; 331 | 332 | result.push(0xD800 + (offset >> 10), 333 | 0xDC00 + (offset & 0x3FF)); 334 | } 335 | } 336 | return String.fromCharCode.apply(null, result); 337 | } 338 | 339 | function String_repeat(count) { 340 | count = count | 0; 341 | return (this.length && count > 0) ? new Array(count + 1).join(this) : ""; 342 | } 343 | 344 | function String_codePointAt(pos) { 345 | pos = pos || 0; 346 | 347 | var iz = this.length; 348 | var first = this.charCodeAt(pos); 349 | 350 | if ( isNaN(first) ) { 351 | return undefined; 352 | } 353 | if (first < 0xD800 || first > 0xDBFF || pos + 1 >= iz) { 354 | return first; 355 | } 356 | var second = this.charCodeAt(pos + 1); 357 | if (second < 0xDC00 || second > 0xDFFF) { 358 | return first; 359 | } 360 | return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; 361 | } 362 | 363 | function String_includes(searchString, position) { 364 | return this.indexOf(searchString, position) >= 0; 365 | } 366 | 367 | function String_startsWith(searchString, position) { 368 | position = position || 0; 369 | return this.lastIndexOf(searchString, position) === position; 370 | } 371 | 372 | function String_endsWith(searchString, endPosition) { 373 | var position = (endPosition || this.length) - searchString.length; 374 | var lastIndex = this.lastIndexOf(searchString); 375 | 376 | return lastIndex !== -1 && lastIndex === position; 377 | } 378 | 379 | function Number_isNaN(number) { 380 | return typeof number === "number" && number !== number; 381 | } 382 | 383 | function Number_isFinite(number) { 384 | return typeof number === "number" && isFinite(number); 385 | } 386 | 387 | function Number_isInteger(number) { 388 | return typeof number === "number" && isFinite(number) && 389 | Math.floor(number) === number; 390 | } 391 | 392 | function Number_isSafeInteger(number) { 393 | return typeof number === "number" && isFinite(number) && 394 | Math.floor(number) === number && 395 | number <= Number["MAX_SAFE_INTEGER"] && 396 | number >= Number["MIN_SAFE_INTEGER"]; 397 | } 398 | 399 | function Math_clz32(x) { 400 | var u32 = Number(x) >>> 0; 401 | return u32 ? 32 - u32.toString(2).length : 32; 402 | } 403 | 404 | function Math_fround(x) { 405 | return new Float32Array([x])[0]; 406 | } 407 | 408 | function Math_hypot(/* values ... */) { 409 | var args = arguments; 410 | var y = 0; 411 | 412 | if (args.length === 0) { return 0; } 413 | 414 | for (var i = 0, iz = args.length; i < iz; ++i) { 415 | var value = args[i]; 416 | 417 | if (value === Infinity || value === -Infinity) { 418 | return Infinity; 419 | } 420 | if ( isNaN(value) ) { 421 | return NaN; 422 | } 423 | y += value * value; 424 | } 425 | return Math.sqrt(y); 426 | } 427 | 428 | function Math_imul(a, b) { 429 | var a_high = (a >>> 16) & 0xffff; 430 | var a_low = a & 0xffff; 431 | var b_high = (b >>> 16) & 0xffff; 432 | var b_low = b & 0xffff; 433 | 434 | return ((a_low * b_low) + (((a_high * b_low + a_low * b_high) << 16) >>> 0) | 0); 435 | } 436 | 437 | function Math_tanh(x) { 438 | if (x === Infinity) { 439 | return 1; 440 | } else if (x === -Infinity) { 441 | return -1; 442 | } 443 | var y = Math.exp(2 * x); 444 | 445 | return (y - 1) / (y + 1); 446 | } 447 | 448 | function CollectionIterator(keys, values, nextFn) { 449 | var that = this; 450 | 451 | this._keys = keys; 452 | this._values = values; 453 | this["next"] = nextFn; 454 | this._cursor = -1; 455 | 456 | if (global["Symbol"]) { 457 | this[global["Symbol"]["iterator"]] = function() { 458 | return that; 459 | }; 460 | } 461 | } 462 | function CollectionIterator_keys() { 463 | var cursor = ++this._cursor; 464 | var done = cursor >= this._keys.length; 465 | 466 | return done ? { "value": undefined, "done": true } 467 | : { "value": this._keys[cursor], "done": false }; 468 | } 469 | function CollectionIterator_values() { 470 | var cursor = ++this._cursor; 471 | var done = cursor >= this._keys.length; 472 | 473 | return done ? { "value": undefined, "done": true } 474 | : { "value": this._values[cursor], "done": false }; 475 | } 476 | function CollectionIterator_keyAndValues() { 477 | var cursor = ++this._cursor; 478 | var done = cursor >= this._keys.length; 479 | 480 | return done ? { "value": undefined, "done": true } 481 | : { "value": [this._keys[cursor], 482 | this._values[cursor]], "done": false }; 483 | } 484 | 485 | function Set_constructor(iterable) { 486 | this._value = []; 487 | 488 | var that = this; 489 | 490 | if (Array.isArray(iterable)) { 491 | iterable.forEach(function(value) { 492 | that["add"](value); 493 | }); 494 | } else if (iterable && iterable["next"]) { 495 | var result = null; 496 | 497 | while ( (result = iterable["next"]()) ) { 498 | if (result["done"]) { 499 | break; 500 | } 501 | this["add"](result["value"]); 502 | } 503 | } 504 | } 505 | 506 | function Set_size() { 507 | return this._value.length; 508 | } 509 | 510 | function Set_add(value) { 511 | this._value.push(value); 512 | return this; 513 | } 514 | 515 | function Set_has(value) { 516 | return this._value.indexOf(value) >= 0; 517 | } 518 | 519 | function Set_values() { 520 | return new CollectionIterator(this._value, this._value, CollectionIterator_values); 521 | } 522 | 523 | function Set_entries() { 524 | return new CollectionIterator(this._value, this._value, CollectionIterator_keyAndValues); 525 | } 526 | 527 | function Set_forEach(callbackFn, thisArg) { 528 | thisArg = thisArg || null; 529 | for (var i = 0, iz = this.size; i < iz; ++i) { 530 | callbackFn.call(thisArg, this._value[i], this._value[i], this); 531 | } 532 | } 533 | 534 | function Set_delete(value) { 535 | var index = this._value.indexOf(value); 536 | 537 | if (index < 0) { 538 | return false; 539 | } 540 | this._value.splice(index, 1); 541 | return true; 542 | } 543 | 544 | function Set_clear() { 545 | this._value = []; 546 | } 547 | 548 | function WeakSet_constructor(iterable) { 549 | this._value = []; 550 | 551 | var that = this; 552 | 553 | if (Array.isArray(iterable)) { 554 | iterable.forEach(function(value) { 555 | that["add"](value); 556 | }); 557 | } else if (iterable && iterable["next"]) { 558 | var result = null; 559 | 560 | while ( (result = iterable["next"]()) ) { 561 | if (result["done"]) { 562 | break; 563 | } 564 | this["add"](result["value"]); 565 | } 566 | } 567 | } 568 | 569 | function WeakSet_add(value) { 570 | this._value.push(value); 571 | return this; 572 | } 573 | 574 | function WeakSet_has(value) { 575 | return this._value.indexOf(value) >= 0; 576 | } 577 | 578 | function WeakSet_delete(value) { 579 | var index = this._value.indexOf(value); 580 | 581 | if (index < 0) { 582 | return false; 583 | } 584 | this._value.splice(index, 1); 585 | return true; 586 | } 587 | 588 | function Map_constructor(iterable) { 589 | this._index = []; 590 | this._value = []; 591 | 592 | var that = this; 593 | 594 | if (Array.isArray(iterable)) { 595 | iterable.forEach(function(value) { 596 | that["set"](value[0], value[1]); 597 | }); 598 | } else if (iterable && iterable["next"]) { 599 | var result = null; 600 | 601 | while ( (result = iterable["next"]()) ) { 602 | if (result["done"]) { 603 | break; 604 | } 605 | this["set"](result["value"][0], result["value"][1]); 606 | } 607 | } 608 | } 609 | 610 | function Map_size() { 611 | return this._index.length; 612 | } 613 | 614 | function Map_get(key) { 615 | var index = this._index.indexOf(key); 616 | 617 | if (index < 0) { 618 | return undefined; 619 | } 620 | return this._value[index]; 621 | } 622 | 623 | function Map_set(key, value) { 624 | var index = this._index.indexOf(key); 625 | 626 | if (index < 0) { 627 | this._index.push(key); 628 | this._value.push(value); 629 | } else { 630 | this._value[index] = value; 631 | } 632 | } 633 | 634 | function Map_has(key) { 635 | return this._index.indexOf(key) >= 0; 636 | } 637 | 638 | function Map_keys() { 639 | return new CollectionIterator(this._index, this._value, CollectionIterator_keys); 640 | } 641 | 642 | function Map_values() { 643 | return new CollectionIterator(this._index, this._value, CollectionIterator_values); 644 | } 645 | 646 | function Map_forEach(callbackFn, thisArg) { 647 | thisArg = thisArg || null; 648 | for (var i = 0, iz = this.size; i < iz; ++i) { 649 | callbackFn.call(thisArg, this._value[i], this._index[i], this); 650 | } 651 | } 652 | 653 | function Map_entries() { 654 | return new CollectionIterator(this._index, this._value, CollectionIterator_keyAndValues); 655 | } 656 | 657 | function Map_delete(key) { 658 | var index = this._index.indexOf(key); 659 | 660 | if (index < 0) { 661 | return false; 662 | } 663 | this._index.splice(index, 1); 664 | this._value.splice(index, 1); 665 | return true; 666 | } 667 | 668 | function Map_clear() { 669 | this._index = []; 670 | this._value = []; 671 | } 672 | 673 | function WeakMap_constructor(iterable) { 674 | this._index = []; 675 | this._value = []; 676 | 677 | var that = this; 678 | 679 | if (Array.isArray(iterable)) { 680 | iterable.forEach(function(value) { 681 | that["set"](value[0], value[1]); 682 | }); 683 | } else if (iterable && iterable["next"]) { 684 | var result = null; 685 | 686 | while ( (result = iterable["next"]()) ) { 687 | if (result["done"]) { 688 | break; 689 | } 690 | this["set"](result["value"][0], result["value"][1]); 691 | } 692 | } 693 | } 694 | 695 | function WeakMap_get(key, defaultValue) { 696 | var index = this._index.indexOf(key); 697 | 698 | if (index < 0) { 699 | return defaultValue; 700 | } 701 | return this._value[index]; 702 | } 703 | 704 | function WeakMap_set(key, value) { 705 | var index = this._index.indexOf(key); 706 | 707 | if (index < 0) { 708 | this._index.push(key); 709 | this._value.push(value); 710 | } else { 711 | this._value[index] = value; 712 | } 713 | } 714 | 715 | function WeakMap_has(key) { 716 | return this._index.indexOf(key) >= 0; 717 | } 718 | 719 | function WeakMap_delete(key) { 720 | var index = this._index.indexOf(key); 721 | 722 | if (index < 0) { 723 | return false; 724 | } 725 | this._index.splice(index, 1); 726 | this._value.splice(index, 1); 727 | return true; 728 | } 729 | 730 | //{@es7 731 | function Object_values(source) { 732 | var keys = Object.keys(source); 733 | var i = 0, iz = keys.length; 734 | var result = new Array(iz); 735 | 736 | for (; i < iz; ++i) { 737 | result[i] = source[keys[i]]; 738 | } 739 | return result; 740 | } 741 | 742 | function Object_entries(source) { 743 | var keys = Object.keys(source); 744 | var i = 0, iz = keys.length; 745 | var result = new Array(iz); 746 | 747 | for (; i < iz; ++i) { 748 | result[i] = [ keys[i], source[keys[i]] ]; 749 | } 750 | return result; 751 | } 752 | 753 | function Array_includes(searchElement, position) { 754 | position = position || 0; 755 | var iz = this.length; 756 | 757 | if (iz === 0) { 758 | return false; 759 | } 760 | 761 | var i = 0; 762 | 763 | if (position >= 0) { 764 | i = position; 765 | } else { 766 | i = position + iz; 767 | if (i < 0) { 768 | i = 0; 769 | } 770 | } 771 | 772 | if (searchElement === searchElement) { 773 | for (; i < iz; ++i) { 774 | if (this[i] === searchElement) { 775 | return true; 776 | } 777 | } 778 | } else if (isNaN(searchElement)) { 779 | for (; i < iz; ++i) { 780 | if (isNaN(this[i])) { 781 | return true; 782 | } 783 | } 784 | } else { 785 | throw TypeError("Unsupported type"); 786 | } 787 | return false; 788 | } 789 | //}@es7 790 | 791 | 792 | function publish(publishTarget, constructors, override) { 793 | override = override || false; 794 | 795 | for (var klass in constructors) { 796 | if ( !(klass in publishTarget) ) { 797 | publishTarget[klass] = constructors[klass]; 798 | } 799 | _extend(publishTarget[klass], constructors[klass]); 800 | } 801 | 802 | function _extend(extendTarget, object) { 803 | for (var key in object) { 804 | if (key === "prototype") { 805 | if (!(key in extendTarget)) { 806 | extendTarget[key] = {}; 807 | } 808 | for (var prop in object[key]) { 809 | _defineProperty(extendTarget[key], prop, object[key][prop]); 810 | } 811 | } else { 812 | _defineProperty(extendTarget, key, object[key]); 813 | } 814 | } 815 | } 816 | 817 | function _defineProperty(obj, key, value) { 818 | if ( override || !(key in obj) ) { 819 | Object.defineProperty(obj, key, { 820 | "configurable": true, 821 | "enumerable": false, 822 | "writable": true, 823 | "value": value 824 | }); 825 | } 826 | } 827 | } 828 | 829 | publish(global, ES6); 830 | publish(global, ES7); 831 | 832 | })((typeof self !== "undefined") ? self : global); 833 | 834 | -------------------------------------------------------------------------------- /app/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 @uupaa 3 | */ 4 | 5 | if (typeof WebApp2 === "undefined") { // avoid duplicate running 6 | (function () { 7 | 'use strict'; 8 | 9 | var GlobalObject$1 = (typeof self !== "undefined") ? self : global; 10 | var UserAgent = function UserAgent () {}; 11 | UserAgent.detect = function detect (userAgent, options) { 12 | if ( options === void 0 ) options = {}; 13 | return _detectUserAgent(userAgent, options); 14 | }; 15 | function _detectUserAgent(userAgent, 16 | options) { 17 | var hasGlobal = !!(GlobalObject$1.global); 18 | var processType = !!((GlobalObject$1.process || 0).type); 19 | var nativeTimer = !!(/native/.test(setTimeout)); 20 | var worker = !hasGlobal && ("WorkerLocation" in GlobalObject$1); 21 | var browser = !hasGlobal && !worker && ("document" in GlobalObject$1); 22 | var node = hasGlobal && !processType && !nativeTimer; 23 | var nw = hasGlobal && !processType && nativeTimer; 24 | var electron = hasGlobal && processType; 25 | var nav = GlobalObject$1["navigator"] || {}; 26 | var ua = userAgent || nav["userAgent"] || ""; 27 | var osName = _detectOSName(ua); 28 | var osVersion = _detectOSVersion(ua, osName); 29 | var browserName = _detectBrowserName(ua); 30 | var browserVersion = _detectBrowserVersion(ua, browserName); 31 | var baseBrowser = _detectBaseBrowser(browserName, parseFloat(osVersion)); 32 | var gl = _detectWebGL(browser || nw || electron); 33 | var deviceName = _detectDevice(ua, osName, osVersion, gl, options); 34 | var lang = _detectLanguage(nav); 35 | var mobile = (/Android|iOS/.test(osName) || /Windows Phone/.test(ua)); 36 | var iOS = osName === "iOS"; 37 | var android = osName === "Android"; 38 | var webView = _isWebView(ua, osName, browserName, browserVersion, options); 39 | var touch3D = /^(iPhone 6s|iPhone 7)/.test(deviceName) || 40 | parseFloat(osVersion) >= 10 && /^iPad Pro/.test(deviceName); 41 | var esVersion = /native/.test(String["raw"] + "") ? 6 : 42 | /native/.test(Object["keys"] + "") ? 5 : 3; 43 | var result = { 44 | ios: iOS, 45 | mac: osName === "Mac", 46 | android: android, 47 | windows: osName === "Windows", 48 | osName: osName, 49 | osVersion: osVersion, 50 | ie: browserName === "IE", 51 | edge: browserName === "Edge", 52 | firefox: browserName === "Firefox", 53 | chrome: browserName === "Chrome", 54 | safari: browserName === "Safari", 55 | silk: browserName === "Silk", 56 | aosp: browserName === "AOSP", 57 | webkit: baseBrowser === "WebKit", 58 | chromium: baseBrowser === "Chromium", 59 | baseBrowser: baseBrowser, 60 | browserName: browserName, 61 | browserVersion: browserVersion, 62 | pc: !mobile, 63 | mobile: mobile, 64 | browser: browser, 65 | worker: worker, 66 | node: node, 67 | nw: nw, 68 | electron: electron, 69 | ipod: iOS && /iPod/.test(ua), 70 | ipad: iOS && /iPad/.test(ua), 71 | iphone: iOS && /iPhone/.test(ua), 72 | kindle: browserName === "Silk", 73 | deviceName: deviceName, 74 | touch3D: touch3D, 75 | webView: webView, 76 | gl: gl, 77 | userAgent: ua, 78 | language: lang, 79 | esVersion: esVersion, 80 | has: function(key) { return typeof this[key] !== "undefined"; }, 81 | get: function(key) { return this[key]; }, 82 | }; 83 | return result; 84 | } 85 | function _detectLanguage(nav) { 86 | var lang = nav["language"] || "en"; 87 | if (nav["languages"] && Array.isArray(nav["languages"])) { 88 | lang = nav["languages"][0] || lang; 89 | } 90 | return lang.split("-")[0]; 91 | } 92 | function _detectOSName(ua) { 93 | switch (true) { 94 | case /Android/.test(ua): return "Android"; 95 | case /iPhone|iPad|iPod/.test(ua): return "iOS"; 96 | case /Windows/.test(ua): return "Windows"; 97 | case /Mac OS X/.test(ua): return "Mac"; 98 | case /CrOS/.test(ua): return "Chrome OS"; 99 | case /Firefox/.test(ua): return "Firefox OS"; 100 | } 101 | return ""; 102 | } 103 | function _detectOSVersion(ua, osName) { 104 | switch (osName) { 105 | case "Android": return _getVersion(ua, "Android"); 106 | case "iOS": return _getVersion(ua, /OS /); 107 | case "Windows": return _getVersion(ua, /Phone/.test(ua) ? /Windows Phone (?:OS )?/ 108 | : /Windows NT/); 109 | case "Mac": return _getVersion(ua, /Mac OS X /); 110 | } 111 | return "0.0.0"; 112 | } 113 | function _detectBrowserName(ua) { 114 | var android = /Android/.test(ua); 115 | switch (true) { 116 | case /CriOS/.test(ua): return "Chrome for iOS"; 117 | case /Edge/.test(ua): return "Edge"; 118 | case android && /Silk\//.test(ua): return "Silk"; 119 | case /Chrome/.test(ua): return "Chrome"; 120 | case /Firefox/.test(ua): return "Firefox"; 121 | case android: return "AOSP"; 122 | case /MSIE|Trident/.test(ua): return "IE"; 123 | case /Safari\//.test(ua): return "Safari"; 124 | case /AppleWebKit/.test(ua): return "WebKit"; 125 | } 126 | return ""; 127 | } 128 | function _detectBrowserVersion(ua, browserName) { 129 | switch (browserName) { 130 | case "Chrome for iOS": return _getVersion(ua, "CriOS/"); 131 | case "Edge": return _getVersion(ua, "Edge/"); 132 | case "Chrome": return _getVersion(ua, "Chrome/"); 133 | case "Firefox": return _getVersion(ua, "Firefox/"); 134 | case "Silk": return _getVersion(ua, "Silk/"); 135 | case "AOSP": return _getVersion(ua, "Version/"); 136 | case "IE": return /IEMobile/.test(ua) ? _getVersion(ua, "IEMobile/") 137 | : /MSIE/.test(ua) ? _getVersion(ua, "MSIE ") 138 | : _getVersion(ua, "rv:"); 139 | case "Safari": return _getVersion(ua, "Version/"); 140 | case "WebKit": return _getVersion(ua, "WebKit/"); 141 | } 142 | return "0.0.0"; 143 | } 144 | var BASE_BROWSERS = { 145 | "Chrome": "Chromium", 146 | "Firefox": "Firefox", 147 | "IE": "IE", 148 | "Edge": "Edge", 149 | "AOSP": "WebKit", 150 | "Safari": "WebKit", 151 | "WebKit": "WebKit", 152 | "Chrome for iOS": "WebKit", 153 | "Silk": "WebKit", 154 | }; 155 | function _detectBaseBrowser(browserName, osVer) { 156 | if (browserName === "Silk" && osVer >= 4.4) { 157 | return "Chromium"; 158 | } 159 | return BASE_BROWSERS[browserName] || ""; 160 | } 161 | function _detectDevice(ua, osName, osVersion, gl, options) { 162 | var screen = GlobalObject$1["screen"] || {}; 163 | var screenWidth = screen["width"] || 0; 164 | var screenHeight = screen["height"] || 0; 165 | var dpr = options["displayDPR"] || GlobalObject$1["devicePixelRatio"] || 1.0; 166 | var long_ = options["displayLong"] || Math.max(screenWidth, screenHeight); 167 | var short_ = options["displayShort"] || Math.min(screenWidth, screenHeight); 168 | var retina = dpr >= 2; 169 | var longEdge = Math.max(long_, short_); 170 | switch (osName) { 171 | case "Android": return _getAndroidDevice(ua, retina); 172 | case "iOS": return _getiOSDevice(ua, retina, longEdge, osVersion, gl); 173 | } 174 | return ""; 175 | } 176 | function _getAndroidDevice(ua, retina) { 177 | if (/Firefox/.test(ua)) { return ""; } 178 | try { 179 | var result = ua.split("Build/")[0].split(";").slice(-1).join().trim(). 180 | replace(/^SonyEricsson/, ""). 181 | replace(/^Sony/, "").replace(/ 4G$/, ""); 182 | if (result === "Nexus 7") { 183 | return retina ? "Nexus 7 (2013)" 184 | : "Nexus 7"; 185 | } 186 | return result; 187 | } catch ( o__o ) { 188 | } 189 | return ""; 190 | } 191 | function _getiOSDevice(ua, retina, longEdge, osVersion, gl) { 192 | var glRenderer = gl.renderer + " " + gl.version; 193 | var simulator = /Software/.test(glRenderer); 194 | var A10X = /A10X GPU/.test(glRenderer); 195 | var A10 = /A10 GPU/.test(glRenderer); 196 | var A9X = /A9X GPU/.test(glRenderer); 197 | var A9 = /A9 GPU/.test(glRenderer); 198 | var A8X = /A8X GPU/.test(glRenderer); 199 | var A8 = /A8 GPU/.test(glRenderer); 200 | var A7 = /A7 GPU/.test(glRenderer); 201 | var SGX554 = /554/.test(glRenderer); 202 | var SGX543 = /543/.test(glRenderer); 203 | if (/iPhone/.test(ua)) { 204 | if (simulator) { 205 | return "iPhone Simulator"; 206 | } 207 | return !retina ? "iPhone 3GS" 208 | : longEdge <= 480 ? (SGX543 || osVersion >= 8 ? "iPhone 4s" : "iPhone 4") 209 | : longEdge <= 568 ? (A10 ? "iPhone 7" : 210 | A9 ? "iPhone SE" : 211 | A8 ? "iPhone 6" : 212 | A7 ? "iPhone 5s" : 213 | SGX543 ? "iPhone 5" 214 | : "iPhone x") 215 | : longEdge <= 667 ? (A10 ? "iPhone 7" : 216 | A9 ? "iPhone 6s" : 217 | A8 ? "iPhone 6" 218 | : "iPhone x") 219 | : longEdge <= 736 ? (A10 ? "iPhone 7 Plus" : 220 | A9 ? "iPhone 6s Plus" : 221 | A8 ? "iPhone 6 Plus" 222 | : "iPhone x") 223 | : "iPhone x"; 224 | } else if (/iPad/.test(ua)) { 225 | if (simulator) { 226 | return "iPad Simulator"; 227 | } 228 | return !retina ? "iPad 2" 229 | : SGX543 ? "iPad 3" 230 | : SGX554 ? "iPad 4" 231 | : A7 ? "iPad mini 2" 232 | : A8X ? "iPad Air 2" 233 | : A8 ? "iPad mini 4" 234 | : A9X ? (longEdge <= 1024 ? "iPad Pro 9.7" : "iPad Pro 12.9") 235 | : A9 ? "iPad 5" 236 | : A10X ? (longEdge <= 1112 ? "iPad Pro 10.5" : "iPad Pro 12.9") 237 | : "iPad x"; 238 | } else if (/iPod/.test(ua)) { 239 | if (simulator) { 240 | return "iPod Simulator"; 241 | } 242 | return longEdge <= 480 ? (retina ? "iPod touch 4" : "iPod touch 3") 243 | : (A8 ? "iPod touch 6" : "iPod touch 5"); 244 | } 245 | return "iPhone x"; 246 | } 247 | function _getVersion(ua, token) { 248 | try { 249 | return _normalizeSemverString( ua.split(token)[1].trim().split(/[^\w\.]/)[0] ); 250 | } catch ( o_O ) { 251 | } 252 | return "0.0.0"; 253 | } 254 | function _normalizeSemverString(version) { 255 | var ary = version.split(/[\._]/); 256 | return ( parseInt(ary[0], 10) || 0 ) + "." + 257 | ( parseInt(ary[1], 10) || 0 ) + "." + 258 | ( parseInt(ary[2], 10) || 0 ); 259 | } 260 | function _isWebView(ua, osName, browserName, browserVersion, options) { 261 | switch (osName + browserName) { 262 | case "iOSSafari": return false; 263 | case "iOSWebKit": return _isWebView_iOS(options); 264 | case "AndroidAOSP": return false; 265 | case "AndroidChrome": return parseFloat(browserVersion) >= 42 ? /; wv/.test(ua) 266 | : /\d{2}\.0\.0/.test(browserVersion) ? true 267 | : _isWebView_Android(options); 268 | } 269 | return false; 270 | } 271 | function _isWebView_iOS(options) { 272 | var document = (GlobalObject$1["document"] || {}); 273 | if ("webView" in options) { 274 | return options["webView"]; 275 | } 276 | return !("fullscreenEnabled" in document || 277 | "webkitFullscreenEnabled" in document || false); 278 | } 279 | function _isWebView_Android(options) { 280 | if ("webView" in options) { 281 | return options["webView"]; 282 | } 283 | return !("requestFileSystem" in GlobalObject$1 || 284 | "webkitRequestFileSystem" in GlobalObject$1 || false); 285 | } 286 | function _detectWebGL(hasCanvas) { 287 | var result = { 288 | type: "", 289 | version: "", 290 | vendor: "", 291 | renderer: "", 292 | maxTextureSize: 0, 293 | }; 294 | if (hasCanvas) { 295 | var canvas = document.createElement("canvas"); 296 | if (canvas && canvas.getContext) { 297 | var types = ["webgl2", "experimental-webgl2", "webgl", "experimental-webgl"]; 298 | for (var i = 0, iz = types.length; i < iz; ++i) { 299 | var type = types[i]; 300 | var gl = canvas.getContext(type); 301 | if (gl) { 302 | result.type = type; 303 | result.version = gl.getParameter(gl.VERSION); 304 | result.vendor = gl.getParameter(gl.VENDOR); 305 | result.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); 306 | var info = gl.getExtension("WEBGL_debug_renderer_info"); 307 | if (info) { 308 | result.vendor = gl.getParameter(info.UNMASKED_VENDOR_WEBGL); 309 | result.renderer = gl.getParameter(info.UNMASKED_RENDERER_WEBGL); 310 | } 311 | break; 312 | } 313 | } 314 | } 315 | } 316 | return result; 317 | } 318 | 319 | var GlobalObject = (typeof self !== "undefined") ? self : global; 320 | var App = function App() { 321 | GlobalObject.WebApp2 = { app: null }; 322 | this.module = new Map(); 323 | this.global = { object: GlobalObject }; 324 | this.debug= 0; 325 | this.verbose = 0; 326 | this.userAgent = UserAgent.detect(); 327 | }; 328 | App.prototype.init = function init (fn) { 329 | if (this.debug) { 330 | GlobalObject.WebApp2.app = this; 331 | } 332 | if (this.userAgent.browser) { 333 | document.onreadystatechange = function () { 334 | if (/interactive|complete/.test(document.readyState)) { 335 | document.onreadystatechange = null; 336 | fn(); 337 | } 338 | }; 339 | } else { 340 | fn(); 341 | } 342 | }; 343 | var app = new App(); 344 | 345 | app.verbose = 2; 346 | app.debug = 2; 347 | app.init(function () { 348 | console.log("Hello WebApp/2"); 349 | }); 350 | 351 | }()); 352 | } 353 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 |