From 6a9bf166663310adf02eef7514fe684cade99244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20P=C3=A1la?= Date: Tue, 4 Apr 2017 09:16:19 +0200 Subject: [PATCH] =?UTF-8?q?#30=20M=C3=ADsto=20reloadu=20aktualizovat=20str?= =?UTF-8?q?=C3=A1nku=20p=C5=99es=20websockety?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Gulpfile.js | 44 ++ app/Check/Consumers/Check.php | 19 +- app/Check/IOnCheckChange.php | 10 + app/Check/IOnCheckChangeDispatcher.php | 13 + .../DispatchProjectChangeListener.php | 31 + .../Listeners/NotifyWebSocketListener.php | 26 + app/Check/OnCheckChangeDispatcher.php | 26 + app/DI/Extension.php | 24 +- app/DashBoard/Controls/Check/Control.latte | 7 +- app/DashBoard/Controls/Check/Control.php | 19 + app/DashBoard/Controls/Check/IOnRedraw.php | 10 + .../Controls/LastRefresh/Control.latte | 2 - .../Controls/LastRefresh/Control.php | 13 - .../Controls/LastRefresh/IFactory.php | 9 - .../Controls/LastRefresh/TFactory.php | 24 - app/DashBoard/Controls/Project/Control.latte | 9 +- app/DashBoard/Controls/Project/Control.php | 12 +- .../Controls/ProjectChecks/Control.latte | 12 +- .../Controls/ProjectChecks/Control.php | 27 +- app/DashBoard/Controls/Refresh/Control.latte | 5 - app/DashBoard/Controls/Refresh/Control.php | 13 - app/DashBoard/Controls/Refresh/IFactory.php | 9 - app/DashBoard/Controls/Refresh/TFactory.php | 24 - app/DashBoard/Presenters/BasePresenter.php | 1 - .../Presenters/HomePagePresenter.php | 2 - app/DashBoard/Presenters/ProjectPresenter.php | 2 - .../Presenters/templates/@layout.latte | 17 +- .../templates/HomePage/default.latte | 3 - .../templates/Project/default.latte | 2 - app/Project/IOnProjectChange.php | 10 + app/config/config.neon | 27 +- assets/js/main.js | 8 + assets/js/nette.ajax.js | 552 ++++++++++++++++++ assets/js/websocket.js | 71 +++ bower.json | 22 + package.json | 16 + vagrant/server/bootstrap.sh | 6 + vagrant/server/install.sh | 4 +- www/js/.gitignore | 3 + www/js/lastrefresh.js | 10 - www/js/main.js | 2 +- www/js/nette.ajax.js | 551 +---------------- 43 files changed, 1002 insertions(+), 696 deletions(-) create mode 100644 Gulpfile.js create mode 100644 app/Check/IOnCheckChange.php create mode 100644 app/Check/IOnCheckChangeDispatcher.php create mode 100644 app/Check/Listeners/DispatchProjectChangeListener.php create mode 100644 app/Check/Listeners/NotifyWebSocketListener.php create mode 100644 app/Check/OnCheckChangeDispatcher.php create mode 100644 app/DashBoard/Controls/Check/IOnRedraw.php delete mode 100644 app/DashBoard/Controls/LastRefresh/Control.latte delete mode 100644 app/DashBoard/Controls/LastRefresh/Control.php delete mode 100644 app/DashBoard/Controls/LastRefresh/IFactory.php delete mode 100644 app/DashBoard/Controls/LastRefresh/TFactory.php delete mode 100644 app/DashBoard/Controls/Refresh/Control.latte delete mode 100644 app/DashBoard/Controls/Refresh/Control.php delete mode 100644 app/DashBoard/Controls/Refresh/IFactory.php delete mode 100644 app/DashBoard/Controls/Refresh/TFactory.php create mode 100644 app/Project/IOnProjectChange.php create mode 100644 assets/js/main.js create mode 100644 assets/js/nette.ajax.js create mode 100644 assets/js/websocket.js create mode 100644 bower.json create mode 100644 package.json create mode 100644 www/js/.gitignore delete mode 100644 www/js/lastrefresh.js diff --git a/.gitignore b/.gitignore index 1ca23a3d..92933782 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /vendor /app/config/config.local.neon /.vagrant +/node_modules !.gitignore !.htaccess diff --git a/Gulpfile.js b/Gulpfile.js new file mode 100644 index 00000000..a15af1b9 --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,44 @@ +'use strict'; + +var gulp = require('gulp'); +var gutil = require('gulp-util'); +var plugins = require('gulp-load-plugins')(); +var bower = require('gulp-bower'); +var concat = require('gulp-concat'); +var uglify = require('gulp-uglify'); +var sourcemaps = require('gulp-sourcemaps'); + +var config = { + assetsPath : 'assets', + cssPath : 'www/css', + jsPath : 'www/js', + bowerPath : 'temp/bower' +}; + +var jsFiles = [ + config.bowerPath + '/stomp-websocket/lib/stomp.js', + config.assetsPath + '/js/main.js', + config.assetsPath + '/js/nette.ajax.js', + config.assetsPath + '/js/stomp.js', + config.assetsPath + '/js/websocket.js' +]; + +gulp.task('bower', function () { + return bower('temp/bower'); +}); + +gulp.task('js', ['bower'], function () { + gulp + .src(jsFiles) + .pipe(gulp.dest(config.jsPath)); +}); + + +// Úlohy + +gulp.task('watch', function () { +}); + + +gulp.task('build', ['js']); +gulp.task('default', ['build']); diff --git a/app/Check/Consumers/Check.php b/app/Check/Consumers/Check.php index a13fe75a..6497389b 100644 --- a/app/Check/Consumers/Check.php +++ b/app/Check/Consumers/Check.php @@ -1,4 +1,4 @@ -checksRepository = $checksRepository; $this->dateTimeProvider = $dateTimeProvider; $this->orm = $orm; + $this->onCheckChangeDispatcher = $onCheckChangeDispatcher; } public function process(\PhpAmqpLib\Message\AMQPMessage $message): int { - $checkId = $message->getBody(); + $checkId = (int) $message->getBody(); $this->orm->clearIdentityMapAndCaches(\Nextras\Orm\Model\IModel::I_KNOW_WHAT_I_AM_DOING); @@ -48,12 +55,18 @@ public function process(\PhpAmqpLib\Message\AMQPMessage $message): int $maxAttempts = $this->getMaxAttempts(); $attempts = 0; + $oldStatus = $check->status; + do { $check->lastCheck = $this->dateTimeProvider->getDateTime(); $result = $this->doHardJob($check); } while ( ! $result && ++$attempts < $maxAttempts && sleep(3) === 0); + if ($oldStatus !== $check->status) { + $this->onCheckChangeDispatcher->change($check); + } + $this->checksRepository->persistAndFlush($check); return self::MSG_ACK; diff --git a/app/Check/IOnCheckChange.php b/app/Check/IOnCheckChange.php new file mode 100644 index 00000000..a0493cfc --- /dev/null +++ b/app/Check/IOnCheckChange.php @@ -0,0 +1,10 @@ +producer = $producer; + } + + + public function onCheckChange(\Pd\Monitoring\Check\Check $check): void + { + $this->onProjectChange($check->project); + } + + + public function onProjectChange(\Pd\Monitoring\Project\Project $project): void + { + $this->producer->publish('', $project->id); + } +} diff --git a/app/Check/Listeners/NotifyWebSocketListener.php b/app/Check/Listeners/NotifyWebSocketListener.php new file mode 100644 index 00000000..e7c8dcab --- /dev/null +++ b/app/Check/Listeners/NotifyWebSocketListener.php @@ -0,0 +1,26 @@ +producer = $producer; + } + + + public function onCheckChange(\Pd\Monitoring\Check\Check $check): void + { + $this->producer->publish('', $check->id); + } +} diff --git a/app/Check/OnCheckChangeDispatcher.php b/app/Check/OnCheckChangeDispatcher.php new file mode 100644 index 00000000..a1bc3a0b --- /dev/null +++ b/app/Check/OnCheckChangeDispatcher.php @@ -0,0 +1,26 @@ +listeners as $listener) { + $listener->onChange($check); + } + } + + + public function addListener(IOnCheckChange $onCheckChange): void + { + $this->listeners[] = $onCheckChange; + } +} diff --git a/app/DI/Extension.php b/app/DI/Extension.php index c0aac1c1..eeb73b6e 100644 --- a/app/DI/Extension.php +++ b/app/DI/Extension.php @@ -1,9 +1,9 @@ -getContainerBuilder(); + + /** @var Pd\Monitoring\Check\Listeners\NotifyWebSocketListener $notifyWebSocketListener */ + $notifyWebSocketListener = $container->getByType(Pd\Monitoring\Check\Listeners\NotifyWebSocketListener::class); + + /** @var \Nextras\Orm\Repository\Repository $projectsRepository */ + $projectsRepository = $container->getByType(Pd\Monitoring\Check\ChecksRepository::class); + + $projectsRepository->onFlush[] = function ($persisted, $removed) use ($notifyWebSocketListener) { + foreach ($persisted as $entity) { + $notifyWebSocketListener->onCheckChange($entity); + } + }; + } + } diff --git a/app/DashBoard/Controls/Check/Control.latte b/app/DashBoard/Controls/Check/Control.latte index 7ee0fe7b..11a5698f 100644 --- a/app/DashBoard/Controls/Check/Control.latte +++ b/app/DashBoard/Controls/Check/Control.latte @@ -1,5 +1,10 @@ {snippet} -
+

{$check->fullName}

diff --git a/app/DashBoard/Controls/Check/Control.php b/app/DashBoard/Controls/Check/Control.php index a7124a35..e1b0c485 100644 --- a/app/DashBoard/Controls/Check/Control.php +++ b/app/DashBoard/Controls/Check/Control.php @@ -20,6 +20,11 @@ class Control extends \Nette\Application\UI\Control */ private $rabbitConnection; + /** + * @var array|IOnRedraw + */ + private $onRedrawListeners = []; + public function __construct( \Pd\Monitoring\Check\Check $check, @@ -33,6 +38,12 @@ public function __construct( } + public function addOnRedraw(IOnRedraw $onRedraw) + { + $this->onRedrawListeners[] = $onRedraw; + } + + protected function createTemplate() { /** @var \Latte\Runtime\Template $template */ @@ -79,11 +90,19 @@ public function handleRefresh() $this->processRequest(); } + public function handleRedraw() + { + $this->processRequest(); + } + private function processRequest() { if ($this->getPresenter()->isAjax()) { $this->redrawControl(); + foreach($this->onRedrawListeners as $listener) { + $listener->onRedraw($this, $this->check); + } } else { $this->redirect('this'); } diff --git a/app/DashBoard/Controls/Check/IOnRedraw.php b/app/DashBoard/Controls/Check/IOnRedraw.php new file mode 100644 index 00000000..68918753 --- /dev/null +++ b/app/DashBoard/Controls/Check/IOnRedraw.php @@ -0,0 +1,10 @@ + {$now|date:'H:i:s'} diff --git a/app/DashBoard/Controls/LastRefresh/Control.php b/app/DashBoard/Controls/LastRefresh/Control.php deleted file mode 100644 index 1fae7c6f..00000000 --- a/app/DashBoard/Controls/LastRefresh/Control.php +++ /dev/null @@ -1,13 +0,0 @@ -template->setFile(__DIR__ . '/Control.latte'); - $this->template->render(); - } -} diff --git a/app/DashBoard/Controls/LastRefresh/IFactory.php b/app/DashBoard/Controls/LastRefresh/IFactory.php deleted file mode 100644 index 74954a77..00000000 --- a/app/DashBoard/Controls/LastRefresh/IFactory.php +++ /dev/null @@ -1,9 +0,0 @@ -lastRefreshControlFactory = $factory; - } - - - protected function createComponentLastRefresh(): Control - { - return $this->lastRefreshControlFactory->create(); - } -} diff --git a/app/DashBoard/Controls/Project/Control.latte b/app/DashBoard/Controls/Project/Control.latte index 7afd715f..a2176f40 100644 --- a/app/DashBoard/Controls/Project/Control.latte +++ b/app/DashBoard/Controls/Project/Control.latte @@ -1,4 +1,10 @@ -
+{snippet} +

{if $project->maintenance} {/if}{$project->name}

@@ -22,3 +28,4 @@ {/if}
+{/snippet} diff --git a/app/DashBoard/Controls/Project/Control.php b/app/DashBoard/Controls/Project/Control.php index a248346d..bade1dab 100644 --- a/app/DashBoard/Controls/Project/Control.php +++ b/app/DashBoard/Controls/Project/Control.php @@ -1,4 +1,4 @@ -template->render(); } + + public function handleRedraw() + { + if ($this->getPresenter()->isAjax()) { + $this->redrawControl(); + } else { + $this->redirect('this'); + } + } + } diff --git a/app/DashBoard/Controls/ProjectChecks/Control.latte b/app/DashBoard/Controls/ProjectChecks/Control.latte index dfc5f987..19fdbc5b 100644 --- a/app/DashBoard/Controls/ProjectChecks/Control.latte +++ b/app/DashBoard/Controls/ProjectChecks/Control.latte @@ -1,8 +1,10 @@ - +{snippet tabs} + +{/snippet}
{foreach $checks as $type => $checksForType} diff --git a/app/DashBoard/Controls/ProjectChecks/Control.php b/app/DashBoard/Controls/ProjectChecks/Control.php index 9ddb6d0f..9df13708 100644 --- a/app/DashBoard/Controls/ProjectChecks/Control.php +++ b/app/DashBoard/Controls/ProjectChecks/Control.php @@ -47,6 +47,7 @@ protected function attached($presenter) $this->checks = $this->checksRepository->findBy($conditions)->orderBy('type'); } + protected function createTemplate() { $template = parent::createTemplate(); @@ -93,7 +94,31 @@ protected function createComponentCheck() $cb = function ($id) { $check = $this->checksRepository->getById($id); - return $this->checkControlFactory->create($check); + $control = $this->checkControlFactory->create($check); + + $cb = new class($this) implements \Pd\Monitoring\DashBoard\Controls\Check\IOnRedraw + { + + /** + * @var Control + */ + private $self; + + + public function __construct(Control $self) + { + $this->self = $self; + } + + + public function onRedraw(\Pd\Monitoring\DashBoard\Controls\Check\Control $control, \Pd\Monitoring\Check\Check $check) + { + $this->self->redrawControl('tabs'); + } + }; + $control->addOnRedraw($cb); + + return $control; }; return new \Nette\Application\UI\Multiplier($cb); diff --git a/app/DashBoard/Controls/Refresh/Control.latte b/app/DashBoard/Controls/Refresh/Control.latte deleted file mode 100644 index 5b84ce34..00000000 --- a/app/DashBoard/Controls/Refresh/Control.latte +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/app/DashBoard/Controls/Refresh/Control.php b/app/DashBoard/Controls/Refresh/Control.php deleted file mode 100644 index 9bd1c14b..00000000 --- a/app/DashBoard/Controls/Refresh/Control.php +++ /dev/null @@ -1,13 +0,0 @@ -template->setFile(__DIR__ . '/Control.latte'); - $this->template->render(); - } -} diff --git a/app/DashBoard/Controls/Refresh/IFactory.php b/app/DashBoard/Controls/Refresh/IFactory.php deleted file mode 100644 index fdadd82b..00000000 --- a/app/DashBoard/Controls/Refresh/IFactory.php +++ /dev/null @@ -1,9 +0,0 @@ -refreshControlFactory = $factory; - } - - - protected function createComponentRefresh(): Control - { - return $this->refreshControlFactory->create(); - } -} diff --git a/app/DashBoard/Presenters/BasePresenter.php b/app/DashBoard/Presenters/BasePresenter.php index f150fb27..8f91e307 100644 --- a/app/DashBoard/Presenters/BasePresenter.php +++ b/app/DashBoard/Presenters/BasePresenter.php @@ -10,7 +10,6 @@ abstract class BasePresenter extends Nette\Application\UI\Presenter { use TSecuredPresenter; - use Pd\Monitoring\DashBoard\Controls\LastRefresh\TFactory; const FLASH_MESSAGE_SUCCESS = 'success'; const FLASH_MESSAGE_INFO = 'info'; diff --git a/app/DashBoard/Presenters/HomePagePresenter.php b/app/DashBoard/Presenters/HomePagePresenter.php index 32f18af6..1ce38f29 100644 --- a/app/DashBoard/Presenters/HomePagePresenter.php +++ b/app/DashBoard/Presenters/HomePagePresenter.php @@ -5,8 +5,6 @@ class HomePagePresenter extends BasePresenter { - use \Pd\Monitoring\DashBoard\Controls\Refresh\TFactory; - /** * @var \Pd\Monitoring\DashBoard\Controls\Project\IFactory */ diff --git a/app/DashBoard/Presenters/ProjectPresenter.php b/app/DashBoard/Presenters/ProjectPresenter.php index 50c3d946..3cc678c2 100644 --- a/app/DashBoard/Presenters/ProjectPresenter.php +++ b/app/DashBoard/Presenters/ProjectPresenter.php @@ -5,8 +5,6 @@ class ProjectPresenter extends BasePresenter { - use \Pd\Monitoring\DashBoard\Controls\Refresh\TFactory; - /** * @var \Pd\Monitoring\DashBoard\Forms\Factory */ diff --git a/app/DashBoard/Presenters/templates/@layout.latte b/app/DashBoard/Presenters/templates/@layout.latte index 559bfc59..b4fa50df 100644 --- a/app/DashBoard/Presenters/templates/@layout.latte +++ b/app/DashBoard/Presenters/templates/@layout.latte @@ -40,7 +40,6 @@ Pecka Monitoringbeta
@@ -71,11 +70,19 @@
- + + + - - - + + + + diff --git a/app/DashBoard/Presenters/templates/HomePage/default.latte b/app/DashBoard/Presenters/templates/HomePage/default.latte index e3e910fb..04278af3 100644 --- a/app/DashBoard/Presenters/templates/HomePage/default.latte +++ b/app/DashBoard/Presenters/templates/HomePage/default.latte @@ -22,9 +22,6 @@

Přidat nový projekt

- - {control refresh} - {/block} {block title} diff --git a/app/DashBoard/Presenters/templates/Project/default.latte b/app/DashBoard/Presenters/templates/Project/default.latte index 055f58aa..960c4573 100644 --- a/app/DashBoard/Presenters/templates/Project/default.latte +++ b/app/DashBoard/Presenters/templates/Project/default.latte @@ -2,8 +2,6 @@ {control projectChecks} - {control refresh} - {/block} {block title} diff --git a/app/Project/IOnProjectChange.php b/app/Project/IOnProjectChange.php new file mode 100644 index 00000000..06348dea --- /dev/null +++ b/app/Project/IOnProjectChange.php @@ -0,0 +1,10 @@ +', + all[0] + ); + return v > 4 ? v : undefined; + } + }); + + $.nette.ext('forms', { + init: function () { + var snippets; + if (!window.Nette || !(snippets = this.ext('snippets'))) return; + + snippets.after(function ($el) { + $el.find('form').each(function() { + window.Nette.initForm(this); + }); + }); + }, + prepare: function (settings) { + var analyze = settings.nette; + if (!analyze || !analyze.form) return; + var e = analyze.e; + var originalData = settings.data || {}; + var data = {}; + + if (analyze.isSubmit) { + data[analyze.el.attr('name')] = analyze.el.val() || ''; + } else if (analyze.isImage) { + var offset = analyze.el.offset(); + var name = analyze.el.attr('name'); + var dataOffset = [ Math.max(0, e.pageX - offset.left), Math.max(0, e.pageY - offset.top) ]; + + if (name.indexOf('[', 0) !== -1) { // inside a container + data[name] = dataOffset; + } else { + data[name + '.x'] = dataOffset[0]; + data[name + '.y'] = dataOffset[1]; + } + } + + // https://developer.mozilla.org/en-US/docs/Web/Guide/Using_FormData_Objects#Sending_files_using_a_FormData_object + var formMethod = analyze.form.attr('method'); + if (formMethod && formMethod.toLowerCase() === 'post' && 'FormData' in window) { + var formData = new FormData(analyze.form[0]); + for (var i in data) { + formData.append(i, data[i]); + } + + if (typeof originalData !== 'string') { + for (var i in originalData) { + formData.append(i, originalData[i]); + } + } + + settings.data = formData; + settings.processData = false; + settings.contentType = false; + } else { + if (typeof originalData !== 'string') { + originalData = $.param(originalData); + } + data = $.param(data); + settings.data = analyze.form.serialize() + (data ? '&' + data : '') + '&' + originalData; + } + } + }); + +// default snippet handler + $.nette.ext('snippets', { + success: function (payload) { + console.log('Aktualizuji snippety'); + if (payload.snippets) { + this.updateSnippets(payload.snippets); + } + } + }, { + beforeQueue: $.Callbacks(), + afterQueue: $.Callbacks(), + completeQueue: $.Callbacks(), + before: function (callback) { + this.beforeQueue.add(callback); + }, + after: function (callback) { + this.afterQueue.add(callback); + }, + complete: function (callback) { + this.completeQueue.add(callback); + }, + updateSnippets: function (snippets, back) { + var that = this; + var elements = []; + for (var i in snippets) { + var $el = this.getElement(i); + if ($el.get(0)) { + elements.push($el.get(0)); + } + this.updateSnippet($el, snippets[i], back); + } + $(elements).promise().done(function () { + that.completeQueue.fire(); + }); + }, + updateSnippet: function ($el, html, back) { + // Fix for setting document title in IE + if ($el.is('title')) { + document.title = html; + } else { + this.beforeQueue.fire($el); + this.applySnippet($el, html, back); + this.afterQueue.fire($el); + } + }, + getElement: function (id) { + return $('#' + this.escapeSelector(id)); + }, + applySnippet: function ($el, html, back) { + if (!back && $el.is('[data-ajax-append]')) { + $el.append(html); + } else if (!back && $el.is('[data-ajax-prepend]')) { + $el.prepend(html); + } else if ($el.html() != html || /<[^>]*script/.test(html)) { + $el.html(html); + } + }, + escapeSelector: function (selector) { + // thx to @uestla (https://github.com/uestla) + return selector.replace(/[\!"#\$%&'\(\)\*\+,\.\/:;<=>\?@\[\\\]\^`\{\|\}~]/g, '\\$&'); + } + }); + +// support $this->redirect() + $.nette.ext('redirect', { + success: function (payload) { + if (payload.redirect) { + window.location.href = payload.redirect; + return false; + } + } + }); + +// current page state + $.nette.ext('state', { + success: function (payload) { + if (payload.state) { + this.state = payload.state; + } + } + }, {state: null}); + +// abort last request if new started + $.nette.ext('unique', { + start: function (xhr) { + if (this.xhr) { + this.xhr.abort(); + } + this.xhr = xhr; + }, + complete: function () { + this.xhr = null; + } + }, {xhr: null}); + +// option to abort by ESC (thx to @vrana) + $.nette.ext('abort', { + init: function () { + $('body').keydown($.proxy(function (e) { + if (this.xhr && (e.keyCode.toString() === '27' // Esc + && !(e.ctrlKey || e.shiftKey || e.altKey || e.metaKey)) + ) { + this.xhr.abort(); + } + }, this)); + }, + start: function (xhr) { + this.xhr = xhr; + }, + complete: function () { + this.xhr = null; + } + }, {xhr: null}); + + $.nette.ext('load', { + success: function () { + $.nette.load(); + } + }); + +// default ajaxification (can be overridden in init()) + $.nette.ext('init', { + load: function (rh) { + $(this.linkSelector).off('click.nette', rh).on('click.nette', rh); + $(this.formSelector).off('submit.nette', rh).on('submit.nette', rh) + .off('click.nette', ':image', rh).on('click.nette', ':image', rh) + .off('click.nette', ':submit', rh).on('click.nette', ':submit', rh); + $(this.buttonSelector).closest('form') + .off('click.nette', this.buttonSelector, rh).on('click.nette', this.buttonSelector, rh); + } + }, { + linkSelector: 'a.ajax', + formSelector: 'form.ajax', + buttonSelector: 'input.ajax[type="submit"], button.ajax[type="submit"], input.ajax[type="image"]' + }); + +})(window, window.jQuery); diff --git a/assets/js/websocket.js b/assets/js/websocket.js new file mode 100644 index 00000000..0af4372f --- /dev/null +++ b/assets/js/websocket.js @@ -0,0 +1,71 @@ +(function($, undefined) { + + $.nette.ext('websocket', { + init: function () { + var onConnect = this.onConnect; + + var config = WebSocketConfig || {}; + this.debug = config.debug; + this.server = config.server; + + $('[data-socket]').each(function () { + var $self = $(this); + onConnect.push(function () { + var queue = $self.data('socket'); + var key = $self.data('socket-key'); + var link = $self.data('socket-link'); + this.subscribe("/exchange/" + queue + "/" + key, function (message) { + var options = { + url: link, + method: 'get', + }; + $.nette.ajax(options); + }); + }); + }); + + this.connect(this.server); + } + }, { + connect: function(server) { + var ws = new WebSocket(server); + var client = Stomp.over(ws); + if (!this.debug) { + client.debug = undefined; + } + + var connected = false; + var errorOccurred = false; + + var onConnect = this.onConnect; + + client.connect({ + login: 'guest', + passcode: 'guest', + host: '/' + }, function () { + onConnect.forEach(function (onConnect) { + onConnect.apply(client); + }); + connected = true; + }, function (message) { + if (typeof message === 'object' && 'command' in message && message.command === 'ERROR') { + errorOccurred = true; + } + }); + + var onClose = ws.onclose; //needs to be after client.connect + ws.onclose = function (event) { + onClose.apply(arguments); + if (connected && !errorOccurred) { + this.connect(); //reconnect + } + }; + }, + onConnect: [], + debug: false, + server: 'ws://localhost:15672/ws' + + }); + +})(jQuery); diff --git a/bower.json b/bower.json new file mode 100644 index 00000000..80272d9e --- /dev/null +++ b/bower.json @@ -0,0 +1,22 @@ +{ + "name": "monitoring", + "description": "", + "main": "", + "authors": [ + "Milan Pála " + ], + "license": "GPL-3.0", + "homepage": "https://github.com/peckadesign/Monitoring", + "moduleType": [], + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "test", + "tests" + ], + "dependencies": { + "jquery": "3.2.*", + "stomp-websocket": "~2.3" + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..5f3c3b1d --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "megapixel.cz", + "private": true, + "dependencies": { + "gulp": "~3.9", + "gulp-util": "~3.0", + "gulp-load-plugins": "~0.7", + "gulp-plumber": "~0.6", + "gulp-less": "~1.3", + "gulp-bless": "~3.0", + "gulp-sourcemaps": "~1.2", + "gulp-bower": "0.0.*", + "gulp-concat": "~2.6", + "gulp-uglify": "~2.0" + } +} diff --git a/vagrant/server/bootstrap.sh b/vagrant/server/bootstrap.sh index dbb16ecc..1d8313d9 100644 --- a/vagrant/server/bootstrap.sh +++ b/vagrant/server/bootstrap.sh @@ -20,6 +20,8 @@ debconf-set-selections <<< 'mariadb-server-5.5 mysql-server/root_password_again echo "deb http://www.rabbitmq.com/debian/ testing main" > /etc/apt/sources.list.d/rabbitmq.list wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc -q -O - | apt-key add - +curl -sL https://deb.nodesource.com/setup_4.x | bash - + apt-get update apt-get upgrade -y --force-yes @@ -27,6 +29,7 @@ apt-get install -y --force-yes \ git \ htop \ vim \ + nodejs \ mariadb-server \ rabbitmq-server \ apache2 \ @@ -46,6 +49,8 @@ php -r "readfile('https://getcomposer.org/installer');" > composer-setup.php php composer-setup.php --install-dir=/usr/local/bin --filename=composer php -r "unlink('composer-setup.php');" +npm install -g gulp + if ! [ -L "/var/www" ]; then rm -rf "/var/www" ln -fs "/vagrant" "/var/www" @@ -90,4 +95,5 @@ service mysql restart rabbitmq-plugins enable rabbitmq_management +rabbitmq-plugins enable rabbitmq_web_stomp echo "[{rabbit, [{loopback_users, []}]}]." > /etc/rabbitmq/rabbitmq.config diff --git a/vagrant/server/install.sh b/vagrant/server/install.sh index 21926360..1eccc500 100644 --- a/vagrant/server/install.sh +++ b/vagrant/server/install.sh @@ -3,4 +3,6 @@ touch ~/.ssh/known_hosts && ssh-keyscan -H "github.com" > ~/.ssh/known_hosts && chmod 600 ~/.ssh/known_hosts grep -q "cd /vagrant" ~/.profile 2> "/dev/null" || echo "cd /vagrant" >> ~/.profile cd "/vagrant" -#composer install --no-interaction +composer install --no-interaction +npm install +./node_modules/.bin/gulp diff --git a/www/js/.gitignore b/www/js/.gitignore new file mode 100644 index 00000000..cec9082b --- /dev/null +++ b/www/js/.gitignore @@ -0,0 +1,3 @@ +* + +!.gitignore diff --git a/www/js/lastrefresh.js b/www/js/lastrefresh.js deleted file mode 100644 index 80a22264..00000000 --- a/www/js/lastrefresh.js +++ /dev/null @@ -1,10 +0,0 @@ -var $lastrefresh = $('.js-lastRefresh'); -var lastRefresh = new Date($lastrefresh.data('lastrefreshNow')); - -function lastrefreshReload() { - var now = new Date(); - var difference = Math.floor((now.getTime() - lastRefresh.getTime()) / 1000); - $lastrefresh.text("-" + difference); -} - -setInterval(lastrefreshReload, 1000); diff --git a/www/js/main.js b/www/js/main.js index e2dc60ce..057c2b26 100644 --- a/www/js/main.js +++ b/www/js/main.js @@ -4,5 +4,5 @@ $(function () { $('a[data-confirm]').on('click', function() { return window.confirm('Opravdu?'); }); - }); + diff --git a/www/js/nette.ajax.js b/www/js/nette.ajax.js index c6fdddce..2349f46e 100644 --- a/www/js/nette.ajax.js +++ b/www/js/nette.ajax.js @@ -416,556 +416,7 @@ // default snippet handler $.nette.ext('snippets', { success: function (payload) { - if (payload.snippets) { - this.updateSnippets(payload.snippets); - } - } - }, { - beforeQueue: $.Callbacks(), - afterQueue: $.Callbacks(), - completeQueue: $.Callbacks(), - before: function (callback) { - this.beforeQueue.add(callback); - }, - after: function (callback) { - this.afterQueue.add(callback); - }, - complete: function (callback) { - this.completeQueue.add(callback); - }, - updateSnippets: function (snippets, back) { - var that = this; - var elements = []; - for (var i in snippets) { - var $el = this.getElement(i); - if ($el.get(0)) { - elements.push($el.get(0)); - } - this.updateSnippet($el, snippets[i], back); - } - $(elements).promise().done(function () { - that.completeQueue.fire(); - }); - }, - updateSnippet: function ($el, html, back) { - // Fix for setting document title in IE - if ($el.is('title')) { - document.title = html; - } else { - this.beforeQueue.fire($el); - this.applySnippet($el, html, back); - this.afterQueue.fire($el); - } - }, - getElement: function (id) { - return $('#' + this.escapeSelector(id)); - }, - applySnippet: function ($el, html, back) { - if (!back && $el.is('[data-ajax-append]')) { - $el.append(html); - } else if (!back && $el.is('[data-ajax-prepend]')) { - $el.prepend(html); - } else if ($el.html() != html || /<[^>]*script/.test(html)) { - $el.html(html); - } - }, - escapeSelector: function (selector) { - // thx to @uestla (https://github.com/uestla) - return selector.replace(/[\!"#\$%&'\(\)\*\+,\.\/:;<=>\?@\[\\\]\^`\{\|\}~]/g, '\\$&'); - } - }); - -// support $this->redirect() - $.nette.ext('redirect', { - success: function (payload) { - if (payload.redirect) { - window.location.href = payload.redirect; - return false; - } - } - }); - -// current page state - $.nette.ext('state', { - success: function (payload) { - if (payload.state) { - this.state = payload.state; - } - } - }, {state: null}); - -// abort last request if new started - $.nette.ext('unique', { - start: function (xhr) { - if (this.xhr) { - this.xhr.abort(); - } - this.xhr = xhr; - }, - complete: function () { - this.xhr = null; - } - }, {xhr: null}); - -// option to abort by ESC (thx to @vrana) - $.nette.ext('abort', { - init: function () { - $('body').keydown($.proxy(function (e) { - if (this.xhr && (e.keyCode.toString() === '27' // Esc - && !(e.ctrlKey || e.shiftKey || e.altKey || e.metaKey)) - ) { - this.xhr.abort(); - } - }, this)); - }, - start: function (xhr) { - this.xhr = xhr; - }, - complete: function () { - this.xhr = null; - } - }, {xhr: null}); - - $.nette.ext('load', { - success: function () { - $.nette.load(); - } - }); - -// default ajaxification (can be overridden in init()) - $.nette.ext('init', { - load: function (rh) { - $(this.linkSelector).off('click.nette', rh).on('click.nette', rh); - $(this.formSelector).off('submit.nette', rh).on('submit.nette', rh) - .off('click.nette', ':image', rh).on('click.nette', ':image', rh) - .off('click.nette', ':submit', rh).on('click.nette', ':submit', rh); - $(this.buttonSelector).closest('form') - .off('click.nette', this.buttonSelector, rh).on('click.nette', this.buttonSelector, rh); - } - }, { - linkSelector: 'a.ajax', - formSelector: 'form.ajax', - buttonSelector: 'input.ajax[type="submit"], button.ajax[type="submit"], input.ajax[type="image"]' - }); - -})(window, window.jQuery);/** - * AJAX Nette Framework plugin for jQuery - * - * @copyright Copyright (c) 2009, 2010 Jan Marek - * @copyright Copyright (c) 2009, 2010 David Grudl - * @copyright Copyright (c) 2012-2014 Vojtěch Dobeš - * @license MIT - * - * @version 2.0.0 - */ - -(function(window, $, undefined) { - - if (typeof $ !== 'function') { - return console.error('nette.ajax.js: jQuery is missing, load it please'); - } - - var nette = function () { - var inner = { - self: this, - initialized: false, - contexts: {}, - on: { - init: {}, - load: {}, - prepare: {}, - before: {}, - start: {}, - success: {}, - complete: {}, - error: {} - }, - fire: function () { - var result = true; - var args = Array.prototype.slice.call(arguments); - var props = args.shift(); - var name = (typeof props === 'string') ? props : props.name; - var off = (typeof props === 'object') ? props.off || {} : {}; - args.push(inner.self); - $.each(inner.on[name], function (index, reaction) { - if (reaction === undefined || $.inArray(index, off) !== -1) return true; - var temp = reaction.apply(inner.contexts[index], args); - return result = (temp === undefined || temp); - }); - return result; - }, - requestHandler: function (e) { - var xhr = inner.self.ajax({}, this, e); - if (xhr && xhr._returnFalse) { // for IE 8 - return false; - } - }, - ext: function (callbacks, context, name) { - while (!name) { - name = 'ext_' + Math.random(); - if (inner.contexts[name]) { - name = undefined; - } - } - - $.each(callbacks, function (event, callback) { - inner.on[event][name] = callback; - }); - inner.contexts[name] = $.extend(context ? context : {}, { - name: function () { - return name; - }, - ext: function (name, force) { - var ext = inner.contexts[name]; - if (!ext && force) throw "Extension '" + this.name() + "' depends on disabled extension '" + name + "'."; - return ext; - } - }); - } - }; - - /** - * Allows manipulation with extensions. - * When called with 1. argument only, it returns extension with given name. - * When called with 2. argument equal to false, it removes extension entirely. - * When called with 2. argument equal to hash of event callbacks, it adds new extension. - * - * @param {string} Name of extension - * @param {bool|object|null} Set of callbacks for any events OR false for removing extension. - * @param {object|null} Context for added extension - * @return {$.nette|object} Provides a fluent interface OR returns extensions with given name - */ - this.ext = function (name, callbacks, context) { - if (typeof name === 'object') { - inner.ext(name, callbacks); - } else if (callbacks === undefined) { - return inner.contexts[name]; - } else if (!callbacks) { - $.each(['init', 'load', 'prepare', 'before', 'start', 'success', 'complete', 'error'], function (index, event) { - inner.on[event][name] = undefined; - }); - inner.contexts[name] = undefined; - } else if (typeof name === 'string' && inner.contexts[name] !== undefined) { - throw "Cannot override already registered nette-ajax extension '" + name + "'."; - } else { - inner.ext(callbacks, context, name); - } - return this; - }; - - /** - * Initializes the plugin: - * - fires 'init' event, then 'load' event - * - when called with any arguments, it will override default 'init' extension - * with provided callbacks - * - * @param {function|object|null} Callback for 'load' event or entire set of callbacks for any events - * @param {object|null} Context provided for callbacks in first argument - * @return {$.nette} Provides a fluent interface - */ - this.init = function (load, loadContext) { - if (inner.initialized) throw 'Cannot initialize nette-ajax twice.'; - - if (typeof load === 'function') { - this.ext('init', null); - this.ext('init', { - load: load - }, loadContext); - } else if (typeof load === 'object') { - this.ext('init', null); - this.ext('init', load, loadContext); - } else if (load !== undefined) { - throw 'Argument of init() can be function or function-hash only.'; - } - - inner.initialized = true; - - inner.fire('init'); - this.load(); - return this; - }; - - /** - * Fires 'load' event - * - * @return {$.nette} Provides a fluent interface - */ - this.load = function () { - inner.fire('load', inner.requestHandler); - return this; - }; - - /** - * Executes AJAX request. Attaches listeners and events. - * - * @param {object|string} settings or URL - * @param {Element|null} ussually Anchor or Form - * @param {event|null} event causing the request - * @return {jqXHR|null} - */ - this.ajax = function (settings, ui, e) { - if ($.type(settings) === 'string') { - settings = {url: settings}; - } - if (!settings.nette && ui && e) { - var $el = $(ui), xhr, originalBeforeSend; - var analyze = settings.nette = { - e: e, - ui: ui, - el: $el, - isForm: $el.is('form'), - isSubmit: $el.is('input[type=submit]') || $el.is('button[type=submit]'), - isImage: $el.is('input[type=image]'), - form: null - }; - - if (analyze.isSubmit || analyze.isImage) { - analyze.form = analyze.el.closest('form'); - } else if (analyze.isForm) { - analyze.form = analyze.el; - } - - if (!settings.url) { - settings.url = analyze.form ? analyze.form.attr('action') || window.location.pathname + window.location.search : ui.href; - } - if (!settings.type) { - settings.type = analyze.form ? analyze.form.attr('method') : 'get'; - } - - if ($el.is('[data-ajax-off]')) { - var rawOff = $el.attr('data-ajax-off'); - if (rawOff.indexOf('[') === 0) { - settings.off = $el.data('ajaxOff'); - } else if (rawOff.indexOf(',') !== -1) { - settings.off = rawOff.split(','); - } else if (rawOff.indexOf(' ') !== -1) { - settings.off = rawOff.split(' '); - } else { - settings.off = rawOff; - } - if (typeof settings.off === 'string') settings.off = [settings.off]; - settings.off = $.grep($.each(settings.off, function (off) { - return $.trim(off); - }), function (off) { - return off.length; - }); - } - } - - inner.fire({ - name: 'prepare', - off: settings.off || {} - }, settings); - if (settings.prepare) { - settings.prepare(settings); - } - - originalBeforeSend = settings.beforeSend; - settings.beforeSend = function (xhr, settings) { - var result = inner.fire({ - name: 'before', - off: settings.off || {} - }, xhr, settings); - if ((result || result === undefined) && originalBeforeSend) { - result = originalBeforeSend(xhr, settings); - } - return result; - }; - - return this.handleXHR($.ajax(settings), settings); - }; - - /** - * Binds extension callbacks to existing XHR object - * - * @param {jqXHR|null} - * @param {object} settings - * @return {jqXHR|null} - */ - this.handleXHR = function (xhr, settings) { - settings = settings || {}; - - if (xhr && (typeof xhr.statusText === 'undefined' || xhr.statusText !== 'canceled')) { - xhr.done(function (payload, status, xhr) { - inner.fire({ - name: 'success', - off: settings.off || {} - }, payload, status, xhr, settings); - }).fail(function (xhr, status, error) { - inner.fire({ - name: 'error', - off: settings.off || {} - }, xhr, status, error, settings); - }).always(function (xhr, status) { - inner.fire({ - name: 'complete', - off: settings.off || {} - }, xhr, status, settings); - }); - inner.fire({ - name: 'start', - off: settings.off || {} - }, xhr, settings); - if (settings.start) { - settings.start(xhr, settings); - } - } - return xhr; - }; - }; - - $.nette = new ($.extend(nette, $.nette ? $.nette : {})); - - $.fn.netteAjax = function (e, options) { - return $.nette.ajax(options || {}, this[0], e); - }; - - $.fn.netteAjaxOff = function () { - return this.off('.nette'); - }; - - $.nette.ext('validation', { - before: function (xhr, settings) { - if (!settings.nette) return true; - else var analyze = settings.nette; - var e = analyze.e; - - var validate = $.extend(this.defaults, settings.validate || (function () { - if (!analyze.el.is('[data-ajax-validate]')) return; - var attr = analyze.el.data('ajaxValidate'); - if (attr === false) return { - keys: false, - url: false, - form: false - }; else if (typeof attr === 'object') return attr; - })() || {}); - - var passEvent = false; - if (analyze.el.attr('data-ajax-pass') !== undefined) { - passEvent = analyze.el.data('ajaxPass'); - passEvent = typeof passEvent === 'bool' ? passEvent : true; - } - - if (validate.keys) { - // thx to @vrana - var explicitNoAjax = e.button || e.ctrlKey || e.shiftKey || e.altKey || e.metaKey; - - if (analyze.form) { - if (explicitNoAjax && analyze.isSubmit) { - this.explicitNoAjax = true; - return false; - } else if (analyze.isForm && this.explicitNoAjax) { - this.explicitNoAjax = false; - return false; - } - } else if (explicitNoAjax) return false; - } - - if (validate.form && analyze.form) { - if (analyze.isSubmit || analyze.isImage) { - analyze.form.get(0)["nette-submittedBy"] = analyze.el.get(0); - } - if ((analyze.form.get(0).onsubmit ? analyze.form.triggerHandler('submit') : Nette.validateForm(analyze.form.get(0))) === false) { - e.stopImmediatePropagation(); - e.preventDefault(); - return false; - } - } - - if (validate.url) { - // thx to @vrana - if (/:|^#/.test(analyze.form ? settings.url : analyze.el.attr('href'))) return false; - } - - if (!passEvent) { - e.stopPropagation(); - e.preventDefault(); - xhr._returnFalse = true; // for IE 8 - } - return true; - } - }, { - defaults: { - keys: true, - url: true, - form: true - }, - explicitNoAjax: false, - ie: function (undefined) { // http://james.padolsey.com/javascript/detect-ie-in-js-using-conditional-comments/ - var v = 3; - var div = document.createElement('div'); - var all = div.getElementsByTagName('i'); - while ( - div.innerHTML = '', - all[0] - ); - return v > 4 ? v : undefined; - } - }); - - $.nette.ext('forms', { - init: function () { - var snippets; - if (!window.Nette || !(snippets = this.ext('snippets'))) return; - - snippets.after(function ($el) { - $el.find('form').each(function() { - window.Nette.initForm(this); - }); - }); - }, - prepare: function (settings) { - var analyze = settings.nette; - if (!analyze || !analyze.form) return; - var e = analyze.e; - var originalData = settings.data || {}; - var data = {}; - - if (analyze.isSubmit) { - data[analyze.el.attr('name')] = analyze.el.val() || ''; - } else if (analyze.isImage) { - var offset = analyze.el.offset(); - var name = analyze.el.attr('name'); - var dataOffset = [ Math.max(0, e.pageX - offset.left), Math.max(0, e.pageY - offset.top) ]; - - if (name.indexOf('[', 0) !== -1) { // inside a container - data[name] = dataOffset; - } else { - data[name + '.x'] = dataOffset[0]; - data[name + '.y'] = dataOffset[1]; - } - } - - // https://developer.mozilla.org/en-US/docs/Web/Guide/Using_FormData_Objects#Sending_files_using_a_FormData_object - var formMethod = analyze.form.attr('method'); - if (formMethod && formMethod.toLowerCase() === 'post' && 'FormData' in window) { - var formData = new FormData(analyze.form[0]); - for (var i in data) { - formData.append(i, data[i]); - } - - if (typeof originalData !== 'string') { - for (var i in originalData) { - formData.append(i, originalData[i]); - } - } - - settings.data = formData; - settings.processData = false; - settings.contentType = false; - } else { - if (typeof originalData !== 'string') { - originalData = $.param(originalData); - } - data = $.param(data); - settings.data = analyze.form.serialize() + (data ? '&' + data : '') + '&' + originalData; - } - } - }); - -// default snippet handler - $.nette.ext('snippets', { - success: function (payload) { + console.log('Aktualizuji snippety'); if (payload.snippets) { this.updateSnippets(payload.snippets); }