diff --git a/.vscode/settings.json b/.vscode/settings.json index 84b57c5..ebca767 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,21 @@ { "cSpell.enableFiletypes": [ "javascript" - ] + ], + "liveSassCompile.settings.formats": [ + { + "format": "expanded", + "extensionName": ".css", + "savePath": "/server/styles", + "savePathSegmentKeys": null, + "savePathReplaceSegmentsWith": null + } + ], + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + "**/*.code-search": true, + "**/compiled.css": true, + "**/*.min.js": true, + }, } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a6e82d6..3f042da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,11 +22,13 @@ "gulp-htmlmin": "^5.0.1", "gulp-rename": "^2.0.0", "gulp-s3-upload": "^1.7.3", + "gulp-sass": "^5.1.0", "gulp-terser": "^2.0.0", "jquery": "^3.6.0", "jquery-touchswipe": "^1.6.19", "luxon": "^2.3.1", "nosleep.js": "^0.12.0", + "sass": "^1.54.0", "suncalc": "^1.8.0", "swiped-events": "^1.1.4" } @@ -3162,6 +3164,32 @@ "node": ">=6" } }, + "node_modules/gulp-sass": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.1.0.tgz", + "integrity": "sha512-7VT0uaF+VZCmkNBglfe1b34bxn/AfcssquLKVDYnCDJ3xNBaW7cUuI3p3BQmoKcoKFrs9jdzUxyb+u+NGfL4OQ==", + "dev": true, + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "picocolors": "^1.0.0", + "plugin-error": "^1.0.1", + "replace-ext": "^2.0.0", + "strip-ansi": "^6.0.1", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gulp-sass/node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/gulp-terser": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/gulp-terser/-/gulp-terser-2.1.0.tgz", @@ -3445,6 +3473,12 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -4209,6 +4243,12 @@ "node": ">=4" } }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5293,6 +5333,12 @@ "through": "~2.3" } }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -6086,6 +6132,167 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/sass": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz", + "integrity": "sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/sass/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sass/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/sass/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sass/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/sass/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sass/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sass/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/sass/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", @@ -6405,6 +6612,15 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -10215,6 +10431,28 @@ } } }, + "gulp-sass": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.1.0.tgz", + "integrity": "sha512-7VT0uaF+VZCmkNBglfe1b34bxn/AfcssquLKVDYnCDJ3xNBaW7cUuI3p3BQmoKcoKFrs9jdzUxyb+u+NGfL4OQ==", + "dev": true, + "requires": { + "lodash.clonedeep": "^4.5.0", + "picocolors": "^1.0.0", + "plugin-error": "^1.0.1", + "replace-ext": "^2.0.0", + "strip-ansi": "^6.0.1", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "dependencies": { + "replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true + } + } + }, "gulp-terser": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/gulp-terser/-/gulp-terser-2.1.0.tgz", @@ -10435,6 +10673,12 @@ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -11008,6 +11252,12 @@ "path-exists": "^3.0.0" } }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -11856,6 +12106,12 @@ "through": "~2.3" } }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -12444,6 +12700,118 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sass": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz", + "integrity": "sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", @@ -12712,6 +13080,12 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", diff --git a/package.json b/package.json index 71a16a9..ad91172 100644 --- a/package.json +++ b/package.json @@ -1,41 +1,44 @@ { - "name": "ws4kp", - "version": "4.1.3", - "description": "Welcome to the WeatherStar 4000+ project page!", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/netbymatt/ws4kp.git" - }, - "author": "Matt Walsh", - "license": "MIT", - "bugs": { - "url": "https://github.com/netbymatt/ws4kp/issues" - }, - "homepage": "https://github.com/netbymatt/ws4kp#readme", - "devDependencies": { - "del": "^6.0.0", - "ejs": "^3.1.5", - "eslint": "^8.10.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.22.1", - "express": "^4.17.1", - "gulp": "^4.0.2", - "gulp-clean-css": "^4.3.0", - "gulp-concat": "^2.6.1", - "gulp-ejs": "^5.1.0", - "gulp-htmlmin": "^5.0.1", - "gulp-rename": "^2.0.0", - "gulp-s3-upload": "^1.7.3", - "gulp-terser": "^2.0.0", - "jquery": "^3.6.0", - "jquery-touchswipe": "^1.6.19", - "luxon": "^2.3.1", - "nosleep.js": "^0.12.0", - "suncalc": "^1.8.0", - "swiped-events": "^1.1.4" - } -} + "name": "ws4kp", + "version": "4.1.3", + "description": "Welcome to the WeatherStar 4000+ project page!", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build:css": "sass ./server/styles/scss/style.scss ./server/styles/compiled.css" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/netbymatt/ws4kp.git" + }, + "author": "Matt Walsh", + "license": "MIT", + "bugs": { + "url": "https://github.com/netbymatt/ws4kp/issues" + }, + "homepage": "https://github.com/netbymatt/ws4kp#readme", + "devDependencies": { + "del": "^6.0.0", + "ejs": "^3.1.5", + "eslint": "^8.10.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.22.1", + "express": "^4.17.1", + "gulp": "^4.0.2", + "gulp-clean-css": "^4.3.0", + "gulp-concat": "^2.6.1", + "gulp-ejs": "^5.1.0", + "gulp-htmlmin": "^5.0.1", + "gulp-rename": "^2.0.0", + "gulp-s3-upload": "^1.7.3", + "gulp-sass": "^5.1.0", + "gulp-terser": "^2.0.0", + "jquery": "^3.6.0", + "jquery-touchswipe": "^1.6.19", + "luxon": "^2.3.1", + "nosleep.js": "^0.12.0", + "sass": "^1.54.0", + "suncalc": "^1.8.0", + "swiped-events": "^1.1.4" + } +} \ No newline at end of file diff --git a/server/scripts/modules/currentweather.js b/server/scripts/modules/currentweather.js index b47351e..d1def95 100644 --- a/server/scripts/modules/currentweather.js +++ b/server/scripts/modules/currentweather.js @@ -69,7 +69,7 @@ class CurrentWeather extends WeatherDisplay { data.Temperature = Math.round(observations.temperature.value); data.TemperatureUnit = 'C'; data.DewPoint = Math.round(observations.dewpoint.value); - data.Ceiling = Math.round(observations.cloudLayers[0].base.value); + data.Ceiling = Math.round(observations.cloudLayers[0]?.base?.value ?? 0); data.CeilingUnit = 'm.'; data.Visibility = Math.round(observations.visibility.value / 1000); data.VisibilityUnit = ' km.'; @@ -190,15 +190,17 @@ class CurrentWeather extends WeatherDisplay { draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 560, 365, data.WindChill + String.fromCharCode(176), 2, 'right'); } + if (data.Icon) { // get main icon - this.gifs.push(await utils.image.superGifAsync({ - src: data.Icon, - auto_play: true, - canvas: this.canvas, - x: 140, - y: 175, - max_width: 126, - })); + this.gifs.push(await utils.image.superGifAsync({ + src: data.Icon, + auto_play: true, + canvas: this.canvas, + x: 140, + y: 175, + max_width: 126, + })); + } this.finishDraw(); } diff --git a/server/scripts/modules/hourly.js b/server/scripts/modules/hourly.js index 1f4c85c..9fd5876 100644 --- a/server/scripts/modules/hourly.js +++ b/server/scripts/modules/hourly.js @@ -1,22 +1,17 @@ // hourly forecast list -/* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation, icons, luxon */ +/* globals WeatherDisplay, utils, STATUS, UNITS, navigation, icons, luxon */ // eslint-disable-next-line no-unused-vars class Hourly extends WeatherDisplay { constructor(navId, elemId, defaultActive) { // special height and width for scrolling - super(navId, elemId, 'Hourly Forecast', defaultActive); - // pre-load background image (returns promise) - this.backgroundImage = utils.image.load('images/BackGround6_1.png'); - - // height of one hour in the forecast - this.hourHeight = 72; + super(navId, elemId, 'Hourly Forecast', defaultActive, true); // set up the timing this.timing.baseDelay = 20; // 24 hours = 6 pages const pages = 4; // first page is already displayed, last page doesn't happen - const timingStep = this.hourHeight * 4; + const timingStep = 75 * 4; this.timing.delay = [150 + timingStep]; // add additional pages for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep); @@ -114,52 +109,25 @@ class Hourly extends WeatherDisplay { } async drawLongCanvas() { - // create the "long" canvas if necessary - if (!this.longCanvas) { - this.longCanvas = document.createElement('canvas'); - this.longCanvas.width = 640; - this.longCanvas.height = 24 * this.hourHeight; - this.longContext = this.longCanvas.getContext('2d'); - this.longCanvasGifs = []; - } - - // stop all gifs - this.longCanvasGifs.forEach((gif) => gif.pause()); - // delete the gifs - this.longCanvasGifs.length = 0; - - // clean up existing gifs - this.gifs.forEach((gif) => gif.pause()); - // delete the gifs - this.gifs.length = 0; - - this.longContext.clearRect(0, 0, this.longCanvas.width, this.longCanvas.height); - - // draw the "long" canvas with all cities - draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, 24 * this.hourHeight); - - for (let i = 0; i <= 4; i += 1) { - const y = i * 346; - draw.horizontalGradient(this.longContext, 0, y, 640, y + 346, '#102080', '#001040'); - } + // get the list element and populate + const list = this.elem.querySelector('.hourly-lines'); + list.innerHTML = ''; const startingHour = luxon.DateTime.local(); - await Promise.all(this.data.map(async (data, index) => { - // calculate base y value - const y = 50 + this.hourHeight * index; - + const lines = this.data.map((data, index) => { + const line = this.templates['hourly-row'].cloneNode(true); // hour const hour = startingHour.plus({ hours: index }); const formattedHour = hour.toLocaleString({ weekday: 'short', hour: 'numeric' }); - draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, formattedHour, 2); + line.querySelector('.hour').innerHTML = formattedHour; // temperatures, convert to strings with no decimal const temperature = Math.round(data.temperature).toString().padStart(3); const feelsLike = Math.round(data.apparentTemperature).toString().padStart(3); - draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', 390, y, temperature, 2, 'center'); + line.querySelector('.temp').innerHTML = temperature; // only plot apparent temperature if there is a difference - if (temperature !== feelsLike) draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', 470, y, feelsLike, 2, 'center'); + if (temperature !== feelsLike) line.querySelector('.like').innerHTML = feelsLike; // wind let wind = 'Calm'; @@ -167,44 +135,25 @@ class Hourly extends WeatherDisplay { const windSpeed = Math.round(data.windSpeed).toString(); wind = data.windDirection + (Array(6 - data.windDirection.length - windSpeed.length).join(' ')) + windSpeed; } - draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', 580, y, wind, 2, 'center'); + line.querySelector('.wind').innerHTML = wind; - this.longCanvasGifs.push(await utils.image.superGifAsync({ - src: data.icon, - auto_play: true, - canvas: this.longCanvas, - x: 290, - y: y - 35, - max_width: 47, - })); - })); + // image + line.querySelector('.icon img').src = data.icon; + + return line; + }); + + list.append(...lines); } - async drawCanvas() { - // there are technically 2 canvases: the standard canvas and the extra-long canvas that contains the complete - // list of cities. The second canvas is copied into the standard canvas to create the scroll + drawCanvas() { super.drawCanvas(); - - // draw the standard context - this.context.drawImage(await this.backgroundImage, 0, 0); - draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2); - draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90); - - draw.titleText(this.context, 'Hourly Forecast'); - - draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 390, 105, 'TEMP', 2, 'center'); - draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 470, 105, 'LIKE', 2, 'center'); - draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 580, 105, 'WIND', 2, 'center'); - - // copy the scrolled portion of the canvas for the initial run before the scrolling starts - this.context.drawImage(this.longCanvas, 0, 0, 640, 289, 0, 110, 640, 289); - this.finishDraw(); } - async showCanvas() { - // special to travel forecast to draw the remainder of the canvas - await this.drawCanvas(); + showCanvas() { + // special to hourly to draw the remainder of the canvas + this.drawCanvas(); super.showCanvas(); } @@ -215,17 +164,14 @@ class Hourly extends WeatherDisplay { // base count change callback baseCountChange(count) { - // get a fresh canvas - const longCanvas = this.getLongCanvas(); - // calculate scroll offset and don't go past end - let offsetY = Math.min(longCanvas.height - 289, (count - 150)); + let offsetY = Math.min(this.elem.querySelector('.hourly-lines').getBoundingClientRect().height - 289, (count - 150)); // don't let offset go negative if (offsetY < 0) offsetY = 0; // copy the scrolled portion of the canvas - this.context.drawImage(longCanvas, 0, offsetY, 640, 289, 0, 110, 640, 289); + this.elem.querySelector('.main').scrollTo(0, offsetY); } static getTravelCitiesDayName(cities) { @@ -241,9 +187,4 @@ class Hourly extends WeatherDisplay { return dayName; }, ''); } - - // necessary to get the lastest long canvas when scrolling - getLongCanvas() { - return this.longCanvas; - } } diff --git a/server/scripts/modules/icons.js b/server/scripts/modules/icons.js index c0c6c87..2b80349 100644 --- a/server/scripts/modules/icons.js +++ b/server/scripts/modules/icons.js @@ -142,6 +142,8 @@ const icons = (() => { }; const getWeatherIconFromIconLink = (link, _isNightTime) => { + if (!link) return; + // internal function to add path to returned icon const addPath = (icon) => `images/${icon}`; // extract day or night if not provided diff --git a/server/scripts/modules/progress.js b/server/scripts/modules/progress.js index 6e3ca23..0c8dcb4 100644 --- a/server/scripts/modules/progress.js +++ b/server/scripts/modules/progress.js @@ -94,8 +94,10 @@ class Progress extends WeatherDisplay { } canvasClick(e) { - const x = e.offsetX; - const y = e.offsetY; + // un-scale + const scale = e.target.getBoundingClientRect().width / e.target.width; + const x = e.offsetX / scale; + const y = e.offsetY / scale; // eliminate off canvas and outside area clicks if (!this.isActive()) return; if (y < 100 || y > 410) return; diff --git a/server/scripts/modules/utilities.js b/server/scripts/modules/utilities.js index 2a1ede2..01294c7 100644 --- a/server/scripts/modules/utilities.js +++ b/server/scripts/modules/utilities.js @@ -286,8 +286,15 @@ const utils = (() => { } }; + const elemForEach = (selector, callback) => { + [...document.querySelectorAll(selector)].forEach(callback); + }; + // return an orderly object return { + elem: { + forEach: elemForEach, + }, image: { load: loadImg, superGifAsync, diff --git a/server/scripts/modules/weatherdisplay.js b/server/scripts/modules/weatherdisplay.js index 874212c..e19133b 100644 --- a/server/scripts/modules/weatherdisplay.js +++ b/server/scripts/modules/weatherdisplay.js @@ -1,6 +1,6 @@ // base weather display class -/* globals navigation, utils, draw, UNITS, luxon, currentWeatherScroll */ +/* globals navigation, utils, luxon, currentWeatherScroll */ const STATUS = { loading: Symbol('loading'), @@ -12,7 +12,7 @@ const STATUS = { // eslint-disable-next-line no-unused-vars class WeatherDisplay { - constructor(navId, elemId, name, defaultEnabled) { + constructor(navId, elemId, name, defaultEnabled, isHtml) { // navId is used in messaging this.navId = navId; this.elemId = undefined; @@ -21,6 +21,7 @@ class WeatherDisplay { this.loadingStatus = STATUS.loading; this.name = name ?? elemId; this.getDataCallbacks = []; + this.isHtml = isHtml; // default navigation timing this.timing = { @@ -41,6 +42,9 @@ class WeatherDisplay { this.setStatus(STATUS.disabled); } this.startNavCount(); + + // get any templates + this.loadTemplates(); } addCheckbox(defaultEnabled = true) { @@ -97,6 +101,9 @@ class WeatherDisplay { if (this.elemId) return; this.elemId = elemId; + // no additional work if this is HTML + if (this.isHtml) return; + // create a canvas const canvas = document.createElement('template'); canvas.innerHTML = ``; @@ -136,13 +143,15 @@ class WeatherDisplay { } drawCanvas() { + if (!this.isHtml) { // stop all gifs - this.gifs.forEach((gif) => gif.pause()); - // delete the gifs - this.gifs.length = 0; - // refresh the canvas - this.canvas = document.getElementById(`${this.elemId}Canvas`); - this.context = this.canvas.getContext('2d'); + this.gifs.forEach((gif) => gif.pause()); + // delete the gifs + this.gifs.length = 0; + // refresh the canvas + this.canvas = document.getElementById(`${this.elemId}Canvas`); + this.context = this.canvas.getContext('2d'); + } // clean up the first-run flag in screen index if (this.screenIndex < 0) this.screenIndex = 0; @@ -197,64 +206,31 @@ class WeatherDisplay { // if (OkToDrawCustomScrollText) DrawCustomScrollText(WeatherParameters, context); } - drawCurrentDateTime(bottom) { + drawCurrentDateTime() { // only draw if canvas is active to conserve battery if (!this.isActive()) return; const { DateTime } = luxon; - const font = 'Star4000 Small'; - const size = '24pt'; - const color = '#ffffff'; - const shadow = 2; - - // on the first pass store the background for the date and time - if (!this.dateTimeBackground) { - const bg = this.context.getImageData(410, 30, 175, 60); - // test background draw complete and skip drawing if there is no background yet - if (bg.data[0] === 0) return; - // store the background - this.dateTimeBackground = bg; - } - - // Clear the date and time area. - if (bottom) { - draw.box(this.context, 'rgb(25, 50, 112)', 0, 389, 640, 16); - } else { - this.context.putImageData(this.dateTimeBackground, 410, 30); - } - // Get the current date and time. const now = DateTime.local(); // time = "11:35:08 PM"; const time = now.toLocaleString(DateTime.TIME_WITH_SECONDS).padStart(11, ' '); - let x; let y; - if (bottom) { - x = 400; - y = 402; - } else { - x = 410; - y = 65; + if (this.lastTime !== time) { + utils.elem.forEach('.date-time.time', (elem) => { elem.innerHTML = time.toUpperCase(); }); } - if (navigation.units() === UNITS.metric) { - x += 45; - } - - draw.text(this.context, font, size, color, x, y, time.toUpperCase(), shadow); // y += 20; + this.lastTime = time; const date = now.toFormat(' ccc LLL ') + now.day.toString().padStart(2, ' '); - if (bottom) { - x = 55; - y = 402; - } else { - x = 410; - y = 85; + if (this.lastDate !== date) { + utils.elem.forEach('.date-time.date', (elem) => { elem.innerHTML = date.toUpperCase(); }); } - draw.text(this.context, font, size, color, x, y, date.toUpperCase(), shadow); + this.lastDate = date; } async drawNoaaImage() { + if (this.isHtml) return; // load the image and store locally if (!this.drawNoaaImage.image) { this.drawNoaaImage.image = utils.image.load('images/noaa5.gif'); @@ -265,6 +241,7 @@ class WeatherDisplay { } async drawLogoImage() { + if (this.isHtml) return; // load the image and store locally if (!this.drawLogoImage.image) { this.drawLogoImage.image = utils.image.load('images/Logo3.png'); @@ -281,23 +258,33 @@ class WeatherDisplay { if (navCmd === navigation.msg.command.firstFrame) this.navNext(navCmd); if (navCmd === navigation.msg.command.lastFrame) this.navPrev(navCmd); - // see if the canvas is already showing - if (this.canvas.style.display === 'block') return false; + this.startNavCount(); - // show the canvas - this.canvas.style.display = 'block'; - return false; + if (!this.isHtml) { + // see if the canvas is already showing + if (this.canvas.style.display === 'block') return; + + // show the canvas + this.canvas.style.display = 'block'; + } else { + this.elem.classList.add('show'); + } } hideCanvas() { this.resetNavBaseCount(); - if (!this.canvas) return; - this.canvas.style.display = 'none'; + if (this.canvas) { + this.canvas.style.display = 'none'; + } + if (this.isHtml) { + this.elem.classList.remove('show'); + } } isActive() { - return document.getElementById(`${this.elemId}Canvas`).offsetParent !== null; + if (!this.isHtml) return document.getElementById(`${this.elemId}Canvas`).offsetParent !== null; + return this.elem.offsetParent !== null; } isEnabled() { @@ -452,7 +439,6 @@ class WeatherDisplay { clearInterval(this.navInterval); this.navInterval = undefined; } - this.startNavCount(); } sendNavDisplayMessage(message) { @@ -461,4 +447,20 @@ class WeatherDisplay { type: message, }); } + + loadTemplates() { + this.templates = {}; + if (this.elemId !== 'progress') { + this.elem = document.getElementById(`${this.elemId}-html`); + if (!this.elem) return; + const templates = this.elem.querySelectorAll('.template'); + templates.forEach((template) => { + const className = template.classList[0]; + const node = template.cloneNode(true); + node.classList.remove('template'); + this.templates[className] = node; + template.remove(); + }); + } + } } diff --git a/server/styles/compiled.css b/server/styles/compiled.css new file mode 100644 index 0000000..b631057 --- /dev/null +++ b/server/styles/compiled.css @@ -0,0 +1,237 @@ +.weather-display { + width: 640px; + height: 480px; + overflow: hidden; + position: relative; + background-image: url(../images/BackGround1_1.png); + display: none; +} +.weather-display.show { + display: block; +} +.weather-display .template { + display: none; +} +.weather-display .header { + width: 640px; + height: 60px; + padding-top: 30px; +} +.weather-display .header .title { + color: yellow; + /* eventually, when chrome supports paint-order for html elements */ + /* -webkit-text-stroke: 2px black; */ + /* paint-order: stroke fill; */ + text-shadow: 3px 3px 0 black, -1.5px -1.5px 0 black, 0 -1.5px 0 black, 1.5px -1.5px 0 black, 1.5px 0 0 black, 1.5px 1.5px 0 black, 0 1.5px 0 black, -1.5px 1.5px 0 black, -1.5px 0 0 black; + font-family: "Star4000"; + font-size: 24pt; + position: absolute; +} +.weather-display .header .title.single { + left: 170px; + top: 25px; +} +.weather-display .header .title.dual { + left: 170px; +} +.weather-display .header .title.dual > div { + position: absolute; +} +.weather-display .header .title.dual .top { + top: -3px; +} +.weather-display .header .title.dual .bottom { + top: 26px; +} +.weather-display .header .logo { + top: 30px; + left: 50px; + position: absolute; + z-index: 10; +} +.weather-display .header .noaa-logo { + position: absolute; + top: 39px; + left: 356px; +} +.weather-display .header .title.single { + top: 40px; +} +.weather-display .header .date-time { + white-space: pre; + color: white; + font-family: "Star4000 Small"; + font-size: 24pt; + /* eventually, when chrome supports paint-order for html elements */ + /* -webkit-text-stroke: 2px black; */ + /* paint-order: stroke fill; */ + text-shadow: 3px 3px 0 black, -1.5px -1.5px 0 black, 0 -1.5px 0 black, 1.5px -1.5px 0 black, 1.5px 0 0 black, 1.5px 1.5px 0 black, 0 1.5px 0 black, -1.5px 1.5px 0 black, -1.5px 0 0 black; + left: 415px; + width: 170px; + text-align: right; + position: absolute; +} +.weather-display .header .date-time.date { + padding-top: 22px; +} +.weather-display .main.has-scroll { + width: 640px; + height: 310px; + overflow: hidden; +} +.weather-display .scroll { + width: 640px; + height: 80px; + overflow: hidden; + margin-top: 10px; +} + +#hourly-html.weather-display .main { + overflow-y: hidden; +} +#hourly-html.weather-display .main .column-headers { + background-color: rgb(32, 0, 87); + height: 20px; + position: absolute; + width: 100%; +} +#hourly-html.weather-display .main .column-headers { + position: -webkit-sticky; + position: sticky; + top: 0px; + z-index: 5; +} +#hourly-html.weather-display .main .column-headers div { + display: inline-block; + font-family: "Star4000 Small"; + font-size: 24pt; + color: yellow; + position: absolute; + top: -14px; + z-index: 5; + /* eventually, when chrome supports paint-order for html elements */ + /* -webkit-text-stroke: 2px black; */ + /* paint-order: stroke fill; */ + text-shadow: 3px 3px 0 black, -1.5px -1.5px 0 black, 0 -1.5px 0 black, 1.5px -1.5px 0 black, 1.5px 0 0 black, 1.5px 1.5px 0 black, 0 1.5px 0 black, -1.5px 1.5px 0 black, -1.5px 0 0 black; +} +#hourly-html.weather-display .main .column-headers .temp { + left: 370px; +} +#hourly-html.weather-display .main .column-headers .like { + left: 450px; +} +#hourly-html.weather-display .main .column-headers .wind { + left: 560px; +} +#hourly-html.weather-display .main .hourly-lines { + min-height: 338px; + padding-top: 10px; + background: repeating-linear-gradient(0deg, #001040 0px, #102080 136px, #102080 202px, #001040 338px); +} +#hourly-html.weather-display .main .hourly-lines .hourly-row { + font-family: "Star4000 Large"; + font-size: 24pt; + height: 72px; + color: yellow; + /* eventually, when chrome supports paint-order for html elements */ + /* -webkit-text-stroke: 2px black; */ + /* paint-order: stroke fill; */ + text-shadow: 3px 3px 0 black, -1.5px -1.5px 0 black, 0 -1.5px 0 black, 1.5px -1.5px 0 black, 1.5px 0 0 black, 1.5px 1.5px 0 black, 0 1.5px 0 black, -1.5px 1.5px 0 black, -1.5px 0 0 black; + position: relative; +} +#hourly-html.weather-display .main .hourly-lines .hourly-row > div { + position: absolute; + white-space: pre; + top: 8px; +} +#hourly-html.weather-display .main .hourly-lines .hourly-row .hour { + left: 50px; +} +#hourly-html.weather-display .main .hourly-lines .hourly-row .icon { + left: 280px; + width: 70px; + text-align: center; + top: unset; +} +#hourly-html.weather-display .main .hourly-lines .hourly-row .temp { + left: 370px; +} +#hourly-html.weather-display .main .hourly-lines .hourly-row .like { + left: 450px; +} +#hourly-html.weather-display .main .hourly-lines .hourly-row .wind { + left: 530px; + width: 100px; + text-align: right; +} + +#current-weather-html.weather-display .main .col { + height: 50px; + width: 255px; + display: inline-block; + margin-top: 10px; + position: absolute; + /* eventually, when chrome supports paint-order for html elements */ + /* -webkit-text-stroke: 2px black; */ + /* paint-order: stroke fill; */ + text-shadow: 3px 3px 0 black, -1.5px -1.5px 0 black, 0 -1.5px 0 black, 1.5px -1.5px 0 black, 1.5px 0 0 black, 1.5px 1.5px 0 black, 0 1.5px 0 black, -1.5px 1.5px 0 black, -1.5px 0 0 black; +} +#current-weather-html.weather-display .main .col.left { + left: 65px; + font-family: "Star4000 Extended"; + font-size: 24pt; +} +#current-weather-html.weather-display .main .col.right { + right: 65px; + font-family: "Star4000 Large"; + font-size: 20px; + font-weight: bold; +} +#current-weather-html.weather-display .main .col.right .row { + margin-bottom: 8px; +} +#current-weather-html.weather-display .main .col.right .row .label, +#current-weather-html.weather-display .main .col.right .row .value { + display: inline-block; +} +#current-weather-html.weather-display .main .col.right .row .label { + margin-left: 20px; +} +#current-weather-html.weather-display .main .col.right .row .value { + float: right; + margin-right: 10px; +} +#current-weather-html.weather-display .main .center { + text-align: center; +} +#current-weather-html.weather-display .main .temp { + font-family: "Star4000 Large"; + font-size: 24pt; +} +#current-weather-html.weather-display .main .icon { + height: 100px; +} +#current-weather-html.weather-display .main .icon img { + max-width: 126px; +} +#current-weather-html.weather-display .main .wind-container { + margin-bottom: 10px; +} +#current-weather-html.weather-display .main .wind-container > div { + width: 45%; + display: inline-block; + margin: 0px; +} +#current-weather-html.weather-display .main .wind-container .wind-label { + margin-left: 5px; +} +#current-weather-html.weather-display .main .wind-container .wind { + text-align: right; +} +#current-weather-html.weather-display .main .wind-gusts { + margin-left: 5px; +} +#current-weather-html.weather-display .main .location { + color: yellow; + margin-bottom: 10px; +}/*# sourceMappingURL=compiled.css.map */ \ No newline at end of file diff --git a/server/styles/compiled.css.map b/server/styles/compiled.css.map new file mode 100644 index 0000000..cca0d7d --- /dev/null +++ b/server/styles/compiled.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["scss/_weatherdisplay.scss","compiled.css","scss/_colors.scss","scss/_utils.scss","scss/_hourly.scss","scss/_current-weather.scss"],"names":[],"mappings":"AAGA;EACC,YAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,kDAAA;EACA,aAAA;ACFD;ADIC;EACC,cAAA;ACFF;ADKC;EACC,aAAA;ACHF;ADMC;EACC,YAAA;EACA,YAAA;EACA,iBAAA;ACJF;ADME;EACC,aEzBW;ECGb,mEAAA;EACA,oCAAA;EACA,8BAAA;EACA,0LACC;EHoBC,uBAAA;EACA,eAAA;EACA,kBAAA;ACDH;ADGG;EACC,WAAA;EACA,SAAA;ACDJ;ADIG;EACC,WAAA;ACFJ;ADII;EACC,kBAAA;ACFL;ADKI;EACC,SAAA;ACHL;ADMI;EACC,SAAA;ACJL;ADUE;EACC,SAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;ACRH;ADUE;EACC,kBAAA;EACA,SAAA;EACA,WAAA;ACRH;ADWE;EACC,SAAA;ACTH;ADYE;EACC,gBAAA;EACA,YEvES;EFwET,6BAAA;EACA,eAAA;EGvEF,mEAAA;EACA,oCAAA;EACA,8BAAA;EACA,0LACC;EHqEC,WAAA;EACA,YAAA;EACA,iBAAA;EACA,kBAAA;ACPH;ADSG;EACC,iBAAA;ACPJ;ADaE;EACC,YAAA;EACA,aAAA;EACA,gBAAA;ACXH;ADgBC;EACC,YAAA;EACA,YAAA;EACA,gBAAA;EACA,gBAAA;ACdF;;AGlFC;EACC,kBAAA;AHqFF;AGnFE;EACC,gCFJa;EEKb,YAAA;EACA,kBAAA;EACA,WAAA;AHqFH;AGlFE;EACC,wBAAA;EAAA,gBAAA;EACA,QAAA;EACA,UAAA;AHoFH;AGlFG;EACC,qBAAA;EACA,6BAAA;EACA,eAAA;EACA,aFpBiB;EEqBjB,kBAAA;EACA,UAAA;EACA,UAAA;EDvBH,mEAAA;EACA,oCAAA;EACA,8BAAA;EACA,0LACC;AF2GF;AGpFG;EACC,WAAA;AHsFJ;AGnFG;EACC,WAAA;AHqFJ;AGlFG;EACC,WAAA;AHoFJ;AGhFE;EACC,iBAAA;EACA,iBAAA;EAEA,qGAAA;AHiFH;AG3EG;EACC,6BAAA;EACA,eAAA;EACA,YAAA;EACA,aFzDU;ECGb,mEAAA;EACA,oCAAA;EACA,8BAAA;EACA,0LACC;ECoDE,kBAAA;AHgFJ;AG9EI;EACC,kBAAA;EACA,gBAAA;EACA,QAAA;AHgFL;AG7EI;EACC,UAAA;AH+EL;AG5EI;EACC,WAAA;EACA,WAAA;EACA,kBAAA;EACA,UAAA;AH8EL;AG3EI;EACC,WAAA;AH6EL;AG1EI;EACC,WAAA;AH4EL;AGzEI;EACC,WAAA;EACA,YAAA;EACA,iBAAA;AH2EL;;AI9JE;EACC,YAAA;EACA,YAAA;EACA,qBAAA;EACA,gBAAA;EACA,kBAAA;EFRF,mEAAA;EACA,oCAAA;EACA,8BAAA;EACA,0LACC;AFyKF;AIjKG;EACC,UAAA;EACA,gCAAA;EACA,eAAA;AJmKJ;AI/JG;EACC,WAAA;EACA,6BAAA;EACA,eAAA;EACA,iBAAA;AJiKJ;AI/JI;EACC,kBAAA;AJiKL;AI/JK;;EAEC,qBAAA;AJiKN;AI9JK;EACC,iBAAA;AJgKN;AI7JK;EACC,YAAA;EACA,kBAAA;AJ+JN;AIvJE;EACC,kBAAA;AJyJH;AItJE;EACC,6BAAA;EACA,eAAA;AJwJH;AInJE;EACC,aAAA;AJqJH;AInJG;EACC,gBAAA;AJqJJ;AIjJE;EACC,mBAAA;AJmJH;AIjJG;EACC,UAAA;EACA,qBAAA;EACA,WAAA;AJmJJ;AIhJG;EACC,gBAAA;AJkJJ;AI/IG;EACC,iBAAA;AJiJJ;AI7IE;EACC,gBAAA;AJ+IH;AI5IE;EACC,aH5FW;EG6FX,mBAAA;AJ8IH","file":"compiled.css"} \ No newline at end of file diff --git a/server/styles/scss/_colors.scss b/server/styles/scss/_colors.scss new file mode 100644 index 0000000..d95538d --- /dev/null +++ b/server/styles/scss/_colors.scss @@ -0,0 +1,8 @@ +$title-color: yellow; +$date-time: white; +$text-shadow: black; +$column-header-text: yellow; +$column-header: rgb(32, 0, 87); + +$gradient-main-background-1: #102080; +$gradient-main-background-2: #001040; \ No newline at end of file diff --git a/server/styles/scss/_current-weather.scss b/server/styles/scss/_current-weather.scss new file mode 100644 index 0000000..0113b51 --- /dev/null +++ b/server/styles/scss/_current-weather.scss @@ -0,0 +1,97 @@ +@use 'colors'as c; +@use 'utils'as u; + +#current-weather-html.weather-display { + .main { + + .col { + height: 50px; + width: 255px; + display: inline-block; + margin-top: 10px; + position: absolute; + + @include u.text-shadow(); + + &.left { + left: 65px; + font-family: 'Star4000 Extended'; + font-size: 24pt; + + } + + &.right { + right: 65px; + font-family: 'Star4000 Large'; + font-size: 20px; + font-weight: bold; + + .row { + margin-bottom: 8px; + + .label, + .value { + display: inline-block; + } + + .label { + margin-left: 20px; + } + + .value { + float: right; + margin-right: 10px; + } + + } + + } + } + + .center { + text-align: center; + } + + .temp { + font-family: 'Star4000 Large'; + font-size: 24pt; + } + + .condition {} + + .icon { + height: 100px; + + img { + max-width: 126px; + } + } + + .wind-container { + margin-bottom: 10px; + + &>div { + width: 45%; + display: inline-block; + margin: 0px; + } + + .wind-label { + margin-left: 5px; + } + + .wind { + text-align: right; + } + } + + .wind-gusts { + margin-left: 5px; + } + + .location { + color: c.$title-color; + margin-bottom: 10px; + } + } +} \ No newline at end of file diff --git a/server/styles/scss/_hourly.scss b/server/styles/scss/_hourly.scss new file mode 100644 index 0000000..30f707b --- /dev/null +++ b/server/styles/scss/_hourly.scss @@ -0,0 +1,95 @@ +@use 'colors'as c; +@use 'utils'as u; + +#hourly-html.weather-display { + .main { + overflow-y: hidden; + + .column-headers { + background-color: c.$column-header; + height: 20px; + position: absolute; + width: 100%; + } + + .column-headers { + position: sticky; + top: 0px; + z-index: 5; + + div { + display: inline-block; + font-family: 'Star4000 Small'; + font-size: 24pt; + color: c.$column-header-text; + position: absolute; + top: -14px; + z-index: 5; + @include u.text-shadow(); + } + + .temp { + left: 370px; + } + + .like { + left: 450px; + } + + .wind { + left: 560px; + } + } + + .hourly-lines { + min-height: 338px; + padding-top: 10px; + + background: repeating-linear-gradient(0deg, c.$gradient-main-background-2 0px, + c.$gradient-main-background-1 136px, + c.$gradient-main-background-1 202px, + c.$gradient-main-background-2 338px, + ); + + .hourly-row { + font-family: 'Star4000 Large'; + font-size: 24pt; + height: 72px; + color: c.$title-color; + @include u.text-shadow(); + position: relative; + + >div { + position: absolute; + white-space: pre; + top: 8px; + } + + .hour { + left: 50px; + } + + .icon { + left: 280px; + width: 70px; + text-align: center; + top: unset; + } + + .temp { + left: 370px; + } + + .like { + left: 450px; + } + + .wind { + left: 530px; + width: 100px; + text-align: right; + } + } + } + } +} \ No newline at end of file diff --git a/server/styles/scss/_utils.scss b/server/styles/scss/_utils.scss new file mode 100644 index 0000000..9aff707 --- /dev/null +++ b/server/styles/scss/_utils.scss @@ -0,0 +1,17 @@ +@use 'colors'as c; + +@mixin text-shadow($offset: 3px, $outline: 1.5px) { + /* eventually, when chrome supports paint-order for html elements */ + /* -webkit-text-stroke: 2px black; */ + /* paint-order: stroke fill; */ + text-shadow: + $offset $offset 0 c.$text-shadow, + (-$outline) (-$outline) 0 c.$text-shadow, + 0 (-$outline) 0 c.$text-shadow, + $outline (-$outline) 0 c.$text-shadow, + $outline 0 0 c.$text-shadow, + $outline $outline 0 c.$text-shadow, + 0 $outline 0 c.$text-shadow, + (-$outline) $outline 0 c.$text-shadow, + (-$outline) 0 0 c.$text-shadow; +} \ No newline at end of file diff --git a/server/styles/scss/_weatherdisplay.scss b/server/styles/scss/_weatherdisplay.scss new file mode 100644 index 0000000..38121e5 --- /dev/null +++ b/server/styles/scss/_weatherdisplay.scss @@ -0,0 +1,103 @@ +@use 'colors'as c; +@use 'utils'as u; + +.weather-display { + width: 640px; + height: 480px; + overflow: hidden; + position: relative; + background-image: url(../images/BackGround1_1.png); + display: none; + + &.show { + display: block; + } + + .template { + display: none; + } + + .header { + width: 640px; + height: 60px; + padding-top: 30px; + + .title { + color: c.$title-color; + @include u.text-shadow(3px, 1.5px); + font-family: 'Star4000'; + font-size: 24pt; + position: absolute; + + &.single { + left: 170px; + top: 25px; + } + + &.dual { + left: 170px; + + &>div { + position: absolute; + } + + .top { + top: -3px; + } + + .bottom { + top: 26px; + } + } + + } + + .logo { + top: 30px; + left: 50px; + position: absolute; + z-index: 10; + } + .noaa-logo { + position: absolute; + top: 39px; + left: 356px; + } + + .title.single { + top: 40px; + } + + .date-time { + white-space: pre; + color: c.$date-time; + font-family: 'Star4000 Small'; + font-size: 24pt; + @include u.text-shadow(3px, 1.5px); + left: 415px; + width: 170px; + text-align: right; + position: absolute; + + &.date { + padding-top: 22px; + } + } + } + + .main { + &.has-scroll { + width: 640px; + height: 310px; + overflow: hidden; + } + } + + + .scroll { + width: 640px; + height: 80px; + overflow: hidden; + margin-top: 10px; + } +} \ No newline at end of file diff --git a/server/styles/scss/compiled.scss b/server/styles/scss/compiled.scss new file mode 100644 index 0000000..0bd2463 --- /dev/null +++ b/server/styles/scss/compiled.scss @@ -0,0 +1,3 @@ +@use '_weatherdisplay'; +@use '_hourly'; +@use '_current-weather'; \ No newline at end of file diff --git a/server/styles/weatherdisplay.css b/server/styles/weatherdisplay.css deleted file mode 100644 index 869e64f..0000000 --- a/server/styles/weatherdisplay.css +++ /dev/null @@ -1,61 +0,0 @@ -.weather-display { - width: 640px; - height: 480px; - overflow: hidden; - position: relative; - background-image: url(../images/BackGround1_1.png); - /* background-attachment: fixed; */ -} - -.weather-display .header { - width: 640px; - height: 70px; - padding-top: 30px; -} - -.weather-display .header .title { - color: yellow; - text-shadow: 3px 3px black; - font-family: 'Star4000'; - font-size: 24pt; - position: absolute; - left: 170px; - top: 25px; -} - -.weather-display .header .logo { - top: 30px; - left: 50px; - position: absolute; -} - -.weather-display .header .title.single { - top: 40px; -} - -.weather-display .header .date-time { - white-space: pre; - color: white; - font-family: 'Star4000 Small'; - font-size: 24pt; - text-shadow: 2px 2px black; - left: 430px; - position: absolute; -} -.weather-display .header .date-time#date { - padding-top: 22px; -} - - -.weather-display .main-has-scroll { - width: 640px; - height: 310px; - overflow: hidden; -} - -.weather-display .scroll { - width: 640px; - height: 80px; - overflow: hidden; -} - diff --git a/views/index.ejs b/views/index.ejs index 7868598..378839e 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -1,117 +1,129 @@ +
- + - - - - + + + + -