From patchwork Mon Jan 8 10:48:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Stefan Herbrechtsmeier X-Patchwork-Id: 856763 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4010:c07::240; helo=mail-lf0-x240.google.com; envelope-from=swupdate+bncbdn7dbh5yqibbpuzzxjakgqe4psj42i@googlegroups.com; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.b="CMQK5Bmf"; dkim-atps=neutral Received: from mail-lf0-x240.google.com (mail-lf0-x240.google.com [IPv6:2a00:1450:4010:c07::240]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zFX8d2D0Cz9s9Y for ; Mon, 8 Jan 2018 21:49:37 +1100 (AEDT) Received: by mail-lf0-x240.google.com with SMTP id 18sf2456384lfs.23 for ; Mon, 08 Jan 2018 02:49:37 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1515408574; cv=pass; d=google.com; s=arc-20160816; b=bms15BQANOiI/InfpSE+20OCDLKwFUUFBnej9DG3KbzfN64mA2UMUh+9RslIWic/wV bpCjYJkDOV3Fpp/DVSDgCHN40g58zaXHOetHhhdmO56k4PKR9yVb0uttKqTE9Lsc2ocj CRfDqShS7s+ZzQ230k4gAzpjb+UBwfRH+JN23mXcDOvFsoRsTsjaFFThsQ73lCRLid4f z9GfvDC3XCaNoVl6/gsCzBxLpBf1BPIkdHKyRNIKDG7NO6zq/H6k5L1DCNu4UYF8804o OCWLFUYAqhyScLNePZ2DXnVt9I9pciRRcw74ks5vAeV36TFNWPWgBNst0cNKLS5lv1bC LakA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:arc-authentication-results:arc-message-signature:sender :dkim-signature:arc-authentication-results; bh=oFRNArLftAL83CtvkBadJD8CUIN+IsYg8rVRxePJE7A=; b=lCBAtpnJScUo/6DIV4WqS5kIoYseA7FxtRtvhaY8gjhWbhl+LTA6Sv4npG0fJ8083U RRFyQUBq5OVnQmkWNd6jF6KEGgBp39UxQsLw6nBA2UJiqa+zS8YFsaXeY7Q0Q0rupIb6 kfRllaxxab/VFfCPw3YLJws3VDxXNTC+L+N0YzPOgQe1oQcWAOQTsEsWX51eWIuI2Bql WJECBdhYbeP4/Sof0cooLin19WEHSbbRAk3IuW4sVo4Tv2PE79TuWuCZqyGrFPm30lk8 lL5ahaIBzkWd+G1SOyvXy3GyLiQPnQjPatLq4ivbvBylxYtDaAtFSYRPMs6JSA3Kdc/h XFcA== ARC-Authentication-Results: i=2; gmr-mx.google.com; spf=neutral (google.com: 85.13.157.12 is neither permitted nor denied by best guess record for domain of stefan@herbrechtsmeier.net) smtp.mailfrom=stefan@herbrechtsmeier.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-subscribe:list-unsubscribe; bh=oFRNArLftAL83CtvkBadJD8CUIN+IsYg8rVRxePJE7A=; b=CMQK5Bmf+MjGlnmQm3E9/oIdO83EbHlxhPU90DKdm6boihBU5vci2bAFiRQzefoNa6 aWAX/r9y5E0iUqSaAgp0QDKxU76idqd2HGMHFACbYtoAWydrIeeGz4GAHB1UOQ52grZq oDJbN7baaHyOX33MTXfbfWqVmCP82obZUdYoZ4roflMMGV7NnfrE4uGffX2dvRZM05o7 dWiOFRw10EoXd5VV72oDMzKQrdxwMGXzyan5FjCmLgFS6HL9aQtB/p/50/dFiHif+CZ8 ypyTgfVHJHJllWoItWgOxQiIwM3MKZZjkz8fANGz2g+5fWlFn1VnbiKoMclD/M18O8yy FpXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding :x-original-sender:x-original-authentication-results:precedence :mailing-list:list-id:x-spam-checked-in-group:list-post:list-help :list-archive:list-subscribe:list-unsubscribe; bh=oFRNArLftAL83CtvkBadJD8CUIN+IsYg8rVRxePJE7A=; b=AHeQrQkgGJdNGfH68JzRGJFTHt+bdl5gI3/hf68wQ6lOu5QSMVlU1BtqocWxcdcJ/j A0PxNfjfzIjO+/vdbNqHacMtFR4KsEmCeHLMsLl1z30F2AmSx09wB8B/85o+RGaF4sv4 ubHCB4ezP4L2QYYv2lf08Eeo1hcGNclX3o5NlV8Y71W8oHk9ecQuhG5RAC+JiNNoFLaQ yOZ6Shb0puOnoRfRjX4p+GWzjTUfrXNcT7AjfObzWI6ZOp0v9lLlEcZd0M8Iv1SGFOP3 UGjLo9+OtrGGc6/RE8JQO5SobaWIJ6+rD7UHLUmHlVjCXcz+umQacMWA7qUgcKshY6Nu UIig== Sender: swupdate@googlegroups.com X-Gm-Message-State: AKwxyteJH/47kUfgN3BOc8QIXZrwpjsxjgDkd/b1dYrXndwKwk4GL8P/ kM1QvYHasUMJEoE7NCk4y0Y= X-Google-Smtp-Source: ACJfBosY5Y2t5T8kBVZcp13vXmcvUXfHuN2lYNi04XHzrzkC9UGc8dSQ82mhSnzN34mZDSw25DWSwQ== X-Received: by 10.25.210.148 with SMTP id j142mr24590lfg.9.1515408574364; Mon, 08 Jan 2018 02:49:34 -0800 (PST) X-BeenThere: swupdate@googlegroups.com Received: by 10.46.34.1 with SMTP id i1ls997589lji.14.gmail; Mon, 08 Jan 2018 02:49:33 -0800 (PST) X-Received: by 10.25.155.138 with SMTP id d132mr551436lfe.11.1515408573700; Mon, 08 Jan 2018 02:49:33 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1515408573; cv=none; d=google.com; s=arc-20160816; b=gIt8xhY0C6YJia7yf4IiE4RKDQMeHUWgHbtI2BBLH2hDVtIwPdG4YgZNfTvynN3Qd5 +4Pb3D0sROQOUYMDyH5dxGQlOxW4ncqD80WiJo10lwDc8Edeed5o82GYs9RAdTTHteGF nWeDGF9j+sl10Z1IAeFUxrHo52Ai5NvbqFio5yNerMrNVMYL1eiHvt6GYPT/Z2ShTbrs dpqbkTmIKBg7xTzn7rGP9spCAfOOoP950jE/4LZhgaPRgD8GhInSn3j1KiPoIIosVtmN pwRap3l6m+Cvs/IS+8DmkhZBWE0HFEpZ+CweYwGY0nI52ha6q7KQQE01iEmTeOfUZeak olWQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=DbqtQXwYo2T6Blz10Q3Q3QgowNu4zRctKyoHRV1LFlA=; b=CjRRmICvqFwZNfjXZ/ZAai6MQQACmKWKaozOKQsCtulLUCAOK6BwwJr65q6G2ZeqCB YQMBn2ZtlX2YfGFk1CkpMwxRV5NbmwzEEyZH0koXXqtcIhX9qiwDZuqiCmwN+nLYlStC +2/EGKnb/rqTqaI3zm1FEEC8yIzG+qxzP4ZMmxazOfF1KTfb2flwoxLsu+4HrbbjZL9M 833zS8XLBWds5dRJayPVXEKjxDcKG/pzGy0NAgHnvR/Dqen71kpctiOMDy+4q/nX0tDK HkZvshMbjutofqjmjqtdhG+QVQ6ZRalLaJvwcQdNleF2NReLe82Ku6v9JC3Pu/kyveXO 9Qdw== ARC-Authentication-Results: i=1; gmr-mx.google.com; spf=neutral (google.com: 85.13.157.12 is neither permitted nor denied by best guess record for domain of stefan@herbrechtsmeier.net) smtp.mailfrom=stefan@herbrechtsmeier.net Received: from dd41138.kasserver.com (dd41138.kasserver.com. [85.13.157.12]) by gmr-mx.google.com with ESMTPS id c69si1486275ljd.4.2018.01.08.02.49.33 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 08 Jan 2018 02:49:33 -0800 (PST) Received-SPF: neutral (google.com: 85.13.157.12 is neither permitted nor denied by best guess record for domain of stefan@herbrechtsmeier.net) client-ip=85.13.157.12; Received: from WS08941.weidmueller.com (unknown [217.244.7.16]) by dd41138.kasserver.com (Postfix) with ESMTPSA id 76BCA11A00D0; Mon, 8 Jan 2018 11:49:32 +0100 (CET) From: stefan@herbrechtsmeier.net To: swupdate@googlegroups.com Cc: Stefan Herbrechtsmeier Subject: [swupdate] [PATCH 3/3] www: Add reworked web page with bootstrap, dropzone and websocket support Date: Mon, 8 Jan 2018 11:48:41 +0100 Message-Id: <1515408521-31480-3-git-send-email-stefan@herbrechtsmeier.net> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1515408521-31480-1-git-send-email-stefan@herbrechtsmeier.net> References: <1515408521-31480-1-git-send-email-stefan@herbrechtsmeier.net> MIME-Version: 1.0 X-Original-Sender: stefan@herbrechtsmeier.net X-Original-Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 85.13.157.12 is neither permitted nor denied by best guess record for domain of stefan@herbrechtsmeier.net) smtp.mailfrom=stefan@herbrechtsmeier.net Precedence: list Mailing-list: list swupdate@googlegroups.com; contact swupdate+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: swupdate@googlegroups.com X-Google-Group-Id: 605343134186 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , From: Stefan Herbrechtsmeier The new web page uses WebSocket for the asynchronous communication with the backend, allows a visualization of different image sources and automatically reloads the web page after a connection lost. Signed-off-by: Stefan Herbrechtsmeier --- www2/.gitignore | 2 + www2/LICENSE | 17 +++++++ www2/README.md | 31 +++++++++++++ www2/gulpfile.js | 99 ++++++++++++++++++++++++++++++++++++++++ www2/index.html | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ www2/logo.png | Bin 0 -> 24403 bytes www2/package.json | 38 ++++++++++++++++ www2/recovery.css | 36 +++++++++++++++ www2/recovery.js | 110 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 466 insertions(+) create mode 100644 www2/.gitignore create mode 100644 www2/LICENSE create mode 100644 www2/README.md create mode 100755 www2/gulpfile.js create mode 100755 www2/index.html create mode 100644 www2/logo.png create mode 100755 www2/package.json create mode 100755 www2/recovery.css create mode 100755 www2/recovery.js diff --git a/www2/.gitignore b/www2/.gitignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/www2/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/www2/LICENSE b/www2/LICENSE new file mode 100644 index 0000000..fb950dc --- /dev/null +++ b/www2/LICENSE @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/www2/README.md b/www2/README.md new file mode 100644 index 0000000..abb6199 --- /dev/null +++ b/www2/README.md @@ -0,0 +1,31 @@ +SWUpdate Web Page +================= + +The SWUpdate web page uses the NPM package manager and depends on +Bootstrap and DropzoneJS. + +It uses WebSocket for the asynchronous communication with the backend, +allows a visualization of different image sources and automatically +reloads the web page after a connection lost. + + +Install +------- + +```sh +npm install +``` + +Build +----- + +```sh +npm run build +``` + +Lint +---- + +```sh +npm run lint +``` diff --git a/www2/gulpfile.js b/www2/gulpfile.js new file mode 100755 index 0000000..e740451 --- /dev/null +++ b/www2/gulpfile.js @@ -0,0 +1,99 @@ +var cleanCSS = require('gulp-clean-css') +var del = require('del') +var gulp = require('gulp') +var favicons = require('gulp-favicons') +var htmlmin = require('gulp-htmlmin') +var htmlreplace = require('gulp-html-replace') +var minify = require('gulp-minify') +var rename = require('gulp-rename') +var responsive = require('gulp-responsive') + +gulp.task('minify-css', ['clean'], () => { + return gulp.src('recovery.css') + .pipe(cleanCSS({ compatibility: 'ie8' })) + .pipe(rename({ + suffix: '.min' + })) + .pipe(gulp.dest('dist/css')) +}) + +gulp.task('minify-js', ['clean'], () => { + return gulp.src('recovery.js') + .pipe(minify({ + ext: { + min: '.min.js' + }, + noSource: true + })) + .pipe(gulp.dest('dist/js')) +}) + +gulp.task('minify-html', ['clean'], () => { + return gulp.src('index.html') + .pipe(htmlreplace({ + 'js': ['js/jquery.min.js', 'js/bootstrap.min.js', 'js/dropzone.min.js', 'js/recovery.min.js'], + 'css': ['css/bootstrap.min.css', 'css/dropzone.min.css', 'css/recovery.min.css'] + })) + .pipe(htmlmin({ collapseWhitespace: true })) + .pipe(gulp.dest('dist')) +}) + +gulp.task('copy-css', ['clean'], () => { + return gulp.src([ + 'node_modules/bootstrap/dist/css/bootstrap.min.css', + 'node_modules/dropzone/dist/min/dropzone.min.css' + ]) + .pipe(gulp.dest('dist/css')) +}) + +gulp.task('copy-js', ['clean'], () => { + return gulp.src([ + 'node_modules/jquery/dist/jquery.min.js', + 'node_modules/bootstrap/dist/js/bootstrap.min.js', + 'node_modules/dropzone/dist/min/dropzone.min.js' + ]) + .pipe(gulp.dest('dist/js')) +}) + +gulp.task('copy-fonts', ['clean'], () => { + return gulp.src([ + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf', + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff', + 'node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2' + ]) + .pipe(gulp.dest('dist/fonts')) +}) + +gulp.task('resize-images', function () { + return gulp.src('logo.png') + .pipe(responsive({ + 'logo.png': { + height: 200 + } + })) + .pipe(gulp.dest('dist')) +}) + +gulp.task('favicons', ['clean'], () => { + return gulp.src('logo.png').pipe(favicons({ + icons: { + android: false, + appleIcon: false, + appleStartup: false, + coast: false, + favicons: true, + firefox: false, + windows: false, + yandex: false + } + })) + .pipe(gulp.dest('dist')) +}) + +gulp.task('clean', () => { + return del('dist/**', { force: true }) +}) + +gulp.task('build', ['copy-css', 'copy-js', 'copy-fonts', 'resize-images', 'minify-js', 'minify-css', 'minify-html', 'favicons']) + +gulp.task('default', ['build']) diff --git a/www2/index.html b/www2/index.html new file mode 100755 index 0000000..1177081 --- /dev/null +++ b/www2/index.html @@ -0,0 +1,133 @@ + + + + + + Firmware Update + + + + + + + + + + + + + + + + + +
+ + + +
+
+ +

+ Firmware Update +
+ + +
+

+
+
+
+ + +
+
+
+
+ Drop file here or click to upload a firmware update image. +
+
+
+
+
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+
+
+
+

+ +

+
+
+
+
+
+
+
+
+ + + +
+ + + \ No newline at end of file diff --git a/www2/logo.png b/www2/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..12ee4c41284a56a092499d4452641f6ef67fec83 GIT binary patch literal 24403 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 literal 0 HcmV?d00001 diff --git a/www2/package.json b/www2/package.json new file mode 100755 index 0000000..4dc22e1 --- /dev/null +++ b/www2/package.json @@ -0,0 +1,38 @@ +{ + "name": "swupdate", + "version": "0.1.0", + "description": "", + "main": "index.html", + "scripts": { + "htmlhint": "htmlhint *.html", + "standard": "standard --fix *.js", + "lint": "standard && htmlhint", + "build": "gulp", + "clean": "gulp clean" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "del": "^3.0.0", + "gulp": "^3.9.1", + "gulp-clean-css": "^3.9.0", + "gulp-favicons": "^2.2.7", + "gulp-html-replace": "^1.6.2", + "gulp-htmlmin": "^3.0.0", + "gulp-minify": "^2.1.0", + "gulp-rename": "^1.2.2", + "gulp-responsive": "^2.8.0", + "htmlhint": "^0.9.13", + "standard": "^10.0.3" + }, + "dependencies": { + "bootstrap": "^3.3.7", + "dropzone": "5.2.0", + "jquery": "^3.2.1" + }, + "standard": { + "ignore": [ + "dist" + ] + } +} diff --git a/www2/recovery.css b/www2/recovery.css new file mode 100755 index 0000000..3fe3492 --- /dev/null +++ b/www2/recovery.css @@ -0,0 +1,36 @@ +h1 img { + margin-left: 10px; + margin-right: 10px; +} + +.dropzone .dz-preview .dz-image { + width: 240px; +} + +.loader { + border: 10px solid LightGray; /* Light grey */ + border-top: 10px solid Black; /* Blue */ + border-bottom: 10px solid Black; /* Blue */ + border-radius: 50%; + width: 80px; + height: 80px; + animation: spin 2s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.panel-group .panel-title>a:before { + font-family: 'Glyphicons Halflings'; + content: "\e114"; + display: inline-block; + font-size: 13px; + margin-right: 8px; + text-align: center; +} + +.panel-group .panel-title>a.collapsed:before { + content: "\e080"; +} diff --git a/www2/recovery.js b/www2/recovery.js new file mode 100755 index 0000000..9836831 --- /dev/null +++ b/www2/recovery.js @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2017 Weidmüller Interface GmbH & Co. KG + * Stefan Herbrechtsmeier + * + * SPDX-License-Identifier: MIT + */ + +/* global $, Dropzone, WebSocket */ + +function restart () { + $.post('restart', {}, function (data) { + showRestart() + }) +}; + +function showRestart () { + $('#swu-restart').modal({backdrop: 'static', keyboard: false}) + window.setTimeout(tryReload, 3000) +} +function tryReload () { + $.ajax({ + cache: false, + timeout: 1000, + success: function (response) { + window.location.reload(true) + }, + error: function (xhr, textStatus, errorThrown) { + if (textStatus === 'timeout') { tryReload() } + window.setTimeout(tryReload, 1000) + } + }) +} + +Dropzone.options.dropzone = { + clickable: true, + acceptedFiles: '.swu' +} + +window.onload = function () { + var protocol + + if (window.location.protocol === 'https:') { protocol = 'wss:' } else { protocol = 'ws:' } + + var ws = new WebSocket(protocol + '//' + window.location.host + '/ws') + + ws.onclose = function (event) { + showRestart() + } + + ws.onmessage = function (event) { + var msg = JSON.parse(event.data) + + switch (msg.type) { + case 'message': + if (msg.level <= 3) { + $('#messages').append('

' + msg.text + '

') + } else { + $('#messages').append('

' + msg.text + '

') + } + break + case 'status': + switch (msg.status) { + case 'START': + $('#swu-progress').hide() + $('#swu-success').hide() + $('#swu-failure').hide() + $('#swu-warning').show() + break + case 'RUN': + break + case 'SUCCESS': + $('#swu-progress').hide() + $('#swu-warning').hide() + $('#swu-failure').hide() + $('#swu-success').show() + if ($('#swu-auto-restart').prop('checked')) { restart() } + break + case 'FAILURE': + $('#swu-progress').hide() + $('#swu-warning').hide() + $('#swu-success').hide() + $('#swu-failure').show() + break + case 'DONE': + $('#swu-progress').hide() + $('#swu-warning').hide() + $('#swu-success').hide() + $('#swu-failure').hide() + break + default: + console.log(msg) + break + } + break + case 'source': + break + case 'step': + var percent = Math.round((100 * (Number(msg.step) - 1) + Number(msg.percent)) / Number(msg.number)) + + $('#swu-progress-value').html(percent + '%' + ' (' + msg.step + ' of ' + msg.number + ')') + $('#swu-progress-name').html(msg.name) + $('#swu-progress-bar') + .css('width', percent + '%') + .attr('aria-valuenow', percent) + + $('#swu-progress').show() + break + } + } +}