From cc4e220baf6efac1cfdd70b178b734592d0a7fda Mon Sep 17 00:00:00 2001 From: Lokonli Date: Mon, 25 Jan 2021 16:07:48 +0100 Subject: [PATCH] v3.7.3 beta (#758) * redesign domoticz block framework * improved popup consistency --- css/creative.css | 72 +- docs/blocks/buttons.rst | 30 +- docs/blocks/specials/calendar.rst | 2 + docs/blocks/specials/domoticzlog.rst | 3 - docs/blocks/specials/moon.rst | 18 +- docs/customcss/customcss.rst | 30 + docs/develop/code.rst | 11 + docs/releasenotes/releasenotes.rst | 12 +- docs/releasenotes/upgrading/upgrading.rst | 1 + docs/releasenotes/upgrading/v373.rst | 83 +++ index.html | 4 +- js/blocks.js | 766 ++++------------------ js/components/button.js | 21 +- js/components/calendar.js | 4 +- js/components/camera.js | 6 +- js/components/dial.js | 64 +- js/components/domoticzblock.js | 71 ++ js/components/frame.js | 2 +- js/components/garbage.js | 2 +- js/components/graph.js | 57 +- js/components/html.js | 2 +- js/components/log.js | 46 ++ js/components/longfonds.js | 2 +- js/components/news.js | 12 +- js/components/secpanel.js | 2 +- js/components/simpleblock.js | 423 ++++++++++++ js/components/timegraph.js | 4 +- js/dashticz.js | 153 ++++- js/domoticz-api.js | 29 +- js/dt_function.js | 182 ++++- js/log.js | 4 +- tpl/news_row.tpl | 5 +- version.txt | 5 +- 33 files changed, 1235 insertions(+), 893 deletions(-) create mode 100644 docs/releasenotes/upgrading/v373.rst create mode 100644 js/components/domoticzblock.js create mode 100644 js/components/log.js create mode 100644 js/components/simpleblock.js diff --git a/css/creative.css b/css/creative.css index a80e8108..f5d824ac 100755 --- a/css/creative.css +++ b/css/creative.css @@ -311,19 +311,18 @@ background-color: #555; } -.modal-header { - border-bottom: 0px !important; +.modal-dialog-custom .modal-content:not(.modal-url) .close { + color: white; + opacity: 0.7; } -.graphclose .close{ +.modal-dialog-custom .modal-content:not(.modal-url) .close:focus, .modal-dialog-custom .modal-content:not(.modal-url) .close:hover { color: white; - opacity: 0.7 - } + opacity: 1; +} + + - .graphclose .close:focus, .graphclose .close:hover { - color: white; - opacity: 1 - } /* next css sets the default height of the frame popup @@ -394,8 +393,25 @@ next css sets the default height of the frame popup width: 80%; height: 80%; margin: auto; + margin: 5px; } +.modal-dialog-custom .modal-content:not(.modal-url) { + background-color: #222; + color: white; +} + +.modal-header { + border-bottom: 0px !important; +} + +.modal-content.url { + background-color: white; + color: black; +} + + + .material-switch>input[type='checkbox'] { display: none; } @@ -448,9 +464,11 @@ next css sets the default height of the frame popup /* END || Popup modal settings (need to sort and arrange first) */ .containslog .items { - height: 154px; overflow: auto; - width: calc(100% - 40px); +} + +.containslog:not(.popup) .items { + height: 154px; } .containslog .items .level0 { @@ -465,16 +483,17 @@ next css sets the default height of the frame popup color: #ffffff; } -.containslog .items .level0.popup { - color: #c70626; +.containslog.popup .items .level2 { + color: #727272; } -.containslog .items .level1.popup { - color: #067676; +.containslog .items .timestamp { + white-space: nowrap; + vertical-align: top; } -.containslog .items .level2.popup { - color: #727272; +.containslog .items .sep { + width: 5px } .containsicalendar table.calendar { @@ -497,7 +516,7 @@ next css sets the default height of the frame popup } .containslog.popup .items { - min-height: 300px; + } .colbar.transbg { @@ -779,14 +798,17 @@ a.playlist { } .modal-body { - height: 100% + height: 100%; + min-height: 1px } -.modal.opengraph .modal-content { - background-color: #222; - color: white; +/* +.modal.openpopup .frameclose { + background-color: #fff; + color: black; margin: 5px; } +*/ .graphcontent { height: 100%; @@ -1135,6 +1157,10 @@ p { background-clip: padding-box; } +.dt_block.popup { + background:unset; +} + .weatherfull .transbg { padding-top: 9px !important; padding-bottom: 10px !important; @@ -3051,7 +3077,7 @@ td.agenda-title { } .dt_content .dial { - position: absolute; + position: relative; top: 50%; left: 50%; transform: translate(-50%, -50%); diff --git a/docs/blocks/buttons.rst b/docs/blocks/buttons.rst index a54973ca..d273f6d7 100644 --- a/docs/blocks/buttons.rst +++ b/docs/blocks/buttons.rst @@ -56,6 +56,8 @@ Parameters - ``1..99999``: Refresh time of the button image in seconds. There is no maximum. The default is 60 (=1 minute). * - url - ``''``: URL of the page to open in a popup window on click. + * - popup + - ``'mypopup'``: Opens the 'mypopup' block in a new window on click. * - forceheight - | Set the height of the image in a button | ``'200px'``: Set image height to 200px. @@ -72,9 +74,6 @@ Parameters * - refreshiframe - | ``0``: No automatic refresh of a button popup frame (default) | ``1..99999``: Refresh time of the button popup frame in sec. There is no maximum. The default is 60 (=1 minute). - * - log - - | ``true`` Button will show the Domoticz log info - | ``false`` Default * - level - Domoticz log level used by the log-button. * - newwindow @@ -83,6 +82,11 @@ Parameters | ``2``: open in new frame (default, to prevent a breaking change in default behavior) | ``3``: no new window/frame (for intent handling, api calls). HTTP get request. | ``4``: no new window/frame (for intent handling, api calls). HTTP post request. (forcerefresh not supported) + | ``5``: open in a new browser tab + * - auto_close + - Closes the opened window after a certain period of time (only applicable when newwindow is 1,2 or 5) + - | ``0``: (=Default) No auto close + | ``5``: Closes the popup window after 5 seconds. * - password - | Password protect switches, buttons, thermostats, sliders, blinds | ``'secret'``: Password to use @@ -135,26 +139,6 @@ Example on how to use menu buttons:: columns: ['menu', 5,6] } - -.. _logbutton : - -Domoticz log button -~~~~~~~~~~~~~~~~~~~ - -With a log-button you can show the Domoticz log in a popup window:: - - var buttons = {} - buttons.log = { - key:'log', - width:12, - icon:'fas fa-microchip', - title: 'Domoticz Log', - log:true, - level: 2 - } - -It's also possible to show the Domoticz log directly in the Dashticz dashboard. See :ref:`customlog` - .. _forcerefresh: forcerefresh diff --git a/docs/blocks/specials/calendar.rst b/docs/blocks/specials/calendar.rst index ca8a7266..22a98ed7 100644 --- a/docs/blocks/specials/calendar.rst +++ b/docs/blocks/specials/calendar.rst @@ -1,5 +1,7 @@ .. _customcalendar : +.. note :: From 3.7.3 onwards only the 'new calendar' block is supported. + Calendar ######## diff --git a/docs/blocks/specials/domoticzlog.rst b/docs/blocks/specials/domoticzlog.rst index f3bae6f7..f0a70a22 100644 --- a/docs/blocks/specials/domoticzlog.rst +++ b/docs/blocks/specials/domoticzlog.rst @@ -10,6 +10,3 @@ You can add the Domoticz log to a column as follows:: } .. image :: domoticzlog.png - -It's also possible to show the Domoticz log in a popup window after pressing a button. -See :ref:`logbutton` diff --git a/docs/blocks/specials/moon.rst b/docs/blocks/specials/moon.rst index fe630cce..171a663c 100644 --- a/docs/blocks/specials/moon.rst +++ b/docs/blocks/specials/moon.rst @@ -3,21 +3,17 @@ Moon #### -Via a special button definition you can add a picture of the current moon phase +With the moon block you can add a picture of the current moon phase to your dashboard. Use the following code:: - buttons = {} - buttons.moon = { - width:12, - refresh: 3600, - btnimage: 'moon' - } columns[2] = {} - columns[2]['blocks'] = [ buttons.moon] + columns[2]['blocks'] = [ 'moon'] -As you can see it's in fact a normal button, but with image name ``'moon'``. -Dashticz will automatically use the correct image for the current moon phase from the folder -``/img/moon`` +If needed you can change the width:: + + blocks['moon'] = { + width: 6 + } We have 100 moon images. A moon cycle takes approximately 28 days. That means that the moon picture will refresh approximately 4 times a day. diff --git a/docs/customcss/customcss.rst b/docs/customcss/customcss.rst index ff5e4089..6f72fd22 100644 --- a/docs/customcss/customcss.rst +++ b/docs/customcss/customcss.rst @@ -499,3 +499,33 @@ Change size and color of Standby Screen items font-size:80px !important; color: #4E585B !important; } + +.. _popupstyling: + +Popup windows +------------- + +Popup windows have the following class attached to it: ``modal-dialog-custom``. + +The popup window contains a div with the class ``modal-content``. Depending on the popup type, the following classes will be applied as well: + +* ``modal-url`` For an url opened in a popup window +* ``modal-graph`` For a graph opened in a popup window +* ``modal-popup`` For a popup created from the ``popup`` block parameter, except when the ``popup`` parameter refers to a graph block. In the latter case, the ``modal-graph`` parameter will be applied. + +A ``modal-url`` popup window, will have a white background, white border, and black 'close' button. + +The other popup windows will have a black background, and a white 'close' button. + +The default styling is a black background, with white 'close' button. + +As an example, to give url-popups a green backgrond with a red close button:: + + .modal-content.modal-url { + background-color: green + } + + .modal-content.modal-url .close { + color: red; + opacity: 1; + } diff --git a/docs/develop/code.rst b/docs/develop/code.rst index d950680a..e2685e90 100644 --- a/docs/develop/code.rst +++ b/docs/develop/code.rst @@ -128,6 +128,17 @@ An example of a more extensive implementation:: Add ``'myblock'`` to the components variable at the start of ``js/dashticz.js`` +The following key words are reserved and should not be modified in ``me`` or ``block``: + +* key +* mountPoint +* type +* name + +And specifically for ``me``: + +* block + Github workflow --------------- diff --git a/docs/releasenotes/releasenotes.rst b/docs/releasenotes/releasenotes.rst index e686da51..ba7f2a4c 100644 --- a/docs/releasenotes/releasenotes.rst +++ b/docs/releasenotes/releasenotes.rst @@ -5,8 +5,16 @@ For Dashticz's **beta** version Release Notes go to: https://dashticz.readthedoc For Dashticz's **master** version Release Notes go to: https://dashticz.readthedocs.io/en/master/releasenotes/index.html -Recent changes --------------- +v3.7.3 beta (24-1-2021) +----------------------- + +.. note :: Make a backup of CONFIG.js, custom.css and custom.js + +Code +~~~~ + +* Redesign internal block framework +* Removed old calendar block 'icalendar' and calendarurl config setting Enhancements ~~~~~~~~~~~~ diff --git a/docs/releasenotes/upgrading/upgrading.rst b/docs/releasenotes/upgrading/upgrading.rst index 74991719..e9b42bcf 100644 --- a/docs/releasenotes/upgrading/upgrading.rst +++ b/docs/releasenotes/upgrading/upgrading.rst @@ -4,6 +4,7 @@ Upgrade instructions .. toctree:: :maxdepth: 2 + v373 v349 v341 v320 diff --git a/docs/releasenotes/upgrading/v373.rst b/docs/releasenotes/upgrading/v373.rst new file mode 100644 index 00000000..278c8157 --- /dev/null +++ b/docs/releasenotes/upgrading/v373.rst @@ -0,0 +1,83 @@ +.. _v373: + +v3.7.3: Popup blocks and more +=================================================== + +.. note :: Make a backup of the files in the custom folder when updating! + +Main changes in v3.7.3 are the following: + +* Improved consistency in popup windows +* Possibility to add one or more Dashticz blocks into a popup window + +Both points required some code redesign. Also some cleanup actions were implemented. The main changes are summarized below. + +New blocks for 'moon' and 'log' +------------------------------- + +Both blocks now can be added with there name, or by setting the type parameter. Example:: + + blocks['moon'] = { + width:6 + } + + blocks['logexample'] = { + type: 'log' + } + + columns[1] = { + blocks: ['moon', 'logexample'] + } + +The previous method to define 'moon' via the btnimage parameter value ``moon`` is not supported anymore. + +The 'log' parameter of a button is not supported anymore. + +Removal of 'old' calendar block +------------------------------- + +Until now Dashticz supported the 'old' and the 'new' calendar block. The 'old' calendar block has been removed. + +See :ref:`newcalendar` + +To be able to recognize a calendar block you have to add ``icalurl`` parameter to the block definition. + +Styling of popup windows +------------------------ + +The CSS classes and default styling for popup windows changed. +So if you have some custom popup styling in your ``custom.css`` then probably you have to update this. + +See :ref:`popupstyling` for additional information. + +Popup windows new functionality +------------------------------- + +In the following situation a block click will trigger a new window: + +* If the block is a Domoticz block (but not a switch), and the 'graph' parameter is not ``false``, then Dashticz will open a popup graph. +* The block contains an url parameter. Dashticz will open the url. The ``newwindow`` block parameter determines how the url will be opened. +* The block contains a ``popup`` parameter. The ``popup`` parameter refers to a block parameter. Dashticz will open the block indicated by the ``popup`` parameter in a popup window. + +With the ``newwindow`` block parameter you indicate how the popup will be opened. New option '5' has been added to open the url in a new browser tab. + +The 'auto_close' parameter will work on popups with newwindow value 1 (=new window), 2 (=modal popup) and 5 (=new tab) + +New block parameter ``blocks`` +------------------------------ + +With the 'blocks' parameter a block acts in fact like a column definition. Example:: + + blocks['mypopup'] = { + blocks: [1,2,'mydial','graph_3', 'log'] + } + +In the previous example a block with the name 'mypopup' is created, consisting of two Domoticz devices, a dial, a graph, and a log block. + +You can add 'mypopup' to a column, or use is as value for a ``popup`` parameter:: + + blocks[123] = { + popup: 'mypopup' + } + +In this example a block for Domoticz device 123 will have a popup block consisting of 5 other blocks. diff --git a/index.html b/index.html index 66a3cbe8..f1ecff68 100644 --- a/index.html +++ b/index.html @@ -49,8 +49,8 @@ Dashticz - - + + diff --git a/js/blocks.js b/js/blocks.js index 5e8a5595..3ea1c3ee 100755 --- a/js/blocks.js +++ b/js/blocks.js @@ -1,8 +1,7 @@ /* eslint-disable no-debugger */ -/*global getBlockTypesBlock, language, _TEMP_SYMBOL, settings, getFullScreenIcon, loadWeatherFull, loadWeather*/ -/*global getSpotify, getCoin, loadChromecast, loadSonarr */ -/*global Dashticz, DT_function, Domoticz, getLog, addCalendar */ -/*global getRandomInt, moment, number_format */ +/*global getBlockTypesBlock, language, _TEMP_SYMBOL, settings*/ +/*global Dashticz, DT_function, Domoticz */ +/*global moment, number_format */ /*from bundle.js*/ /*global ion*/ /*from main.js*/ @@ -27,8 +26,16 @@ var alldevices = 'initial value'; var oldstates = []; var onOffstates = []; -var mountedBlocks = {}; //object to store all mounted blocks +/** + * Build all the blocks in a column. + * @param {array} cols - Array containing all block definitions + * @param {string | number} c - Column id + * @param {string} screendiv - Screen div must contain row. Blocks will be mounted into + * @param {boolean} standby - true if building standby screen + * + * Build all the blocks in a column + */ // eslint-disable-next-line no-unused-vars function getBlock(cols, c, screendiv, standby) { // if (c==='bar') debugger; @@ -54,50 +61,53 @@ function getBlock(cols, c, screendiv, standby) { '">' ); } - for (var b in cols['blocks']) { - var myblockselector = Dashticz.mountNewContainer(columndiv); - if (!Dashticz.mount(myblockselector, cols['blocks'][b])) - switch (typeof cols['blocks'][b]) { - case 'object': - handleObjectBlock(cols['blocks'][b], myblockselector, c); - continue; - - case 'string': - handleStringBlock(cols['blocks'][b], myblockselector, c); - continue; - - default: - //then it's an integer, meaning it's a domoticz device id - var block = {}; - block.idx = cols['blocks'][b]; - $.extend(block, blocks[block.idx]); - block.key = block.idx; - var html = - '
'; - mountBlock(myblockselector, block, html, false); - addDeviceUpdateHandler(block); - break; - } - } + cols['blocks'].forEach(function (b) { + addBlock2Column(columndiv, c, b); + }); + } +} +/**Adds a block to a column + * @param {string} columndiv - div to add block to + * @param {string} c - Column id + * @param {object | string | number} b - string, as key for block object, object or number + * + * If b is a number then it represents a device id. + */ +function addBlock2Column(columndiv, c, b) { + var myblockselector = Dashticz.mountNewContainer(columndiv); + var newBlock = b; + if (typeof b !== 'object') newBlock = convertBlock(b, c); + if (c === 'popup') newBlock.isPopup = true; + if (newBlock.blocks) { + newBlock.blocks.forEach(function (aBlock) { + addBlock2Column(myblockselector, '', aBlock); + }); + return; + } + if (Array.isArray(newBlock)) { + newBlock.forEach(function (aBlock) { + addBlock2Column(myblockselector, '', aBlock); + }); + return; } + + if (!Dashticz.mount(myblockselector, newBlock)) + Dashticz.mountDefaultBlock(myblockselector, newBlock); } -function mountBlock(mountPoint, block, html, append) { - block.$mountPoint = $(mountPoint); - if (typeof html !== 'undefined') { - if (append) { - block.$mountPoint.append(html); - } else block.$mountPoint.html(html); - } - block.mountPoint = mountPoint; - block.entry = block.mountPoint.slice(1); - if (typeof block.batteryThreshold === 'undefined') - block.batteryThreshold = settings.batteryThreshold; - mountedBlocks[block.entry] = block; +function convertBlock(blocktype, c) { + var block = {}; + block.type = blocktype; + $.extend(block, blocks[blocktype]); + block.c = c; //c can be 'bar'. Used for sunriseholder + block.key = block.key || blocktype; + + //Check for Domoticz device block + if (isDomoticzDevice(block.type)) { + block.width = (blocks[block.type] && blocks[block.type].width) || 4; + block.idx = block.idx || block.type; + } + return block; } function getCustomFunction(functionname, block, afterupdate) { @@ -120,6 +130,7 @@ function getCustomFunction(functionname, block, afterupdate) { } } +// eslint-disable-next-line no-unused-vars function deviceUpdateHandler(block) { var selector = block.mountPoint; var idx = block.idx; @@ -213,7 +224,6 @@ function deviceUpdateHandler(block) { /*add the battery level indicator*/ function addBatteryLevel($div, block) { - console.log(block); var device = block.device; var $data = $div; //$div.find('.col-data'); var batteryLevel = device.BatteryLevel; @@ -257,491 +267,33 @@ function getBlockClass(block) { return addClass; } -function addDeviceUpdateHandler(block) { - var deviceIdx = block.idx; - if (typeof block.idx === 'string') { - var idxSplit = block.idx.split('_'); - if (idxSplit.length == 2) { - var idx = parseInt(idxSplit[0]); - var subidx = parseInt(idxSplit[1]); - if (typeof idx === 'number' && typeof subidx === 'number') { - deviceIdx = idx; - block.subidx = subidx; - } - } - } - Domoticz.subscribe(deviceIdx, true, function (device) { - block.device = device; - deviceUpdateHandler(block); - }); - - if (block.key) { - Dashticz.subscribeBlock(block.key, function (blockUpdate) { - $.extend(block, blockUpdate); - deviceUpdateHandler(block); - }); - } else { - console.log('key not defined for block ', block.idx); - } -} - -function handleStringBlock(blocktype, columndiv, c) { - var block = {}; - block.type = blocktype; - $.extend(block, blocks[blocktype]); - block.c = c; //c can be 'bar'. Used for sunriseholder - block.key = block.key || blocktype; - mountBlock(columndiv, block, null, null); - - var defaultwidth = 12; - switch (block.type) { - case 'logo': - case 'settings': - defaultwidth = 2; - break; - case 'miniclock': - defaultwidth = 8; - break; - } - - block.width = block.width || defaultwidth; - var width = block.width; - - switch (block.type) { - case 'logo': - $(columndiv).append( - '' - ); - return; - case 'settings': - var icons = ['settings', 'fullscreen']; - if (typeof settings['settings_icons'] !== 'undefined') { - icons = settings['settings_icons']; - } - var content = - '
'; - for (var i = 0; i < icons.length; i++) { - switch (icons[i]) { - case 'settings': - content += - ' '; - break; - - case 'fullscreen': - $.ajax({ - url: 'js/fullscreen.js', - async: false, - dataType: 'script', - }); - content += getFullScreenIcon(); - break; - } - } - content += '
'; - $(columndiv).append(content); - return; - case 'miniclock': - $(columndiv).append( - '
' + - '      ' + - '
' - ); - return; - case 'clock': - $(columndiv).append( - '
' + - '

' + - '
' - ); - return; - case 'responsiveclock': - $(columndiv).append( - '
' + - '

' + - '
' - ); - return; - case 'weather': - if (typeof loadWeatherFull !== 'function') { - $.ajax({ - url: 'js/weather.js', - async: false, - dataType: 'script', - }); - } - $(columndiv).append( - '
' - ); - if (settings['wu_api'] !== '' && settings['wu_city'] !== '') - loadWeatherFull( - settings['wu_city'], - settings['wu_country'], - $('.weatherfull') - ); - return; - case 'currentweather': - if (settings['wu_api'] !== '' && settings['wu_city'] !== '') { - if (typeof loadWeather !== 'function') { - $.ajax({ - url: 'js/weather.js', - async: false, - dataType: 'script', - }); - } - $(columndiv).append( - '
' + - '
' + - '

' + - '
' - ); - loadWeather(settings['wu_city'], settings['wu_country']); - } - return; - case 'currentweather_big': - if (settings['wu_api'] !== '' && settings['wu_city'] !== '') { - if (typeof loadWeather !== 'function') { - $.ajax({ - url: 'js/weather.js', - async: false, - dataType: 'script', - }); - } - $(columndiv).append( - '
' + - '
' + - '
' + - '
' - ); - - loadWeather(settings['wu_city'], settings['wu_country']); - } - return; - case 'weather_owm': - if (typeof loadWeatherFull !== 'function') { - $.ajax({ - url: 'js/weather_owm.js', - async: false, - dataType: 'script', - }); - } - $(columndiv).append( - '
' - ); - if (settings['owm_api'] !== '' && settings['owm_city'] !== '') - loadWeatherFull( - settings['owm_city'], - settings['owm_country'], - $('.weatherfull') - ); - return; - case 'currentweather_owm': - if (settings['owm_api'] !== '' && settings['owm_city'] !== '') { - if (typeof loadWeather !== 'function') { - $.ajax({ - url: 'js/weather_owm.js', - async: false, - dataType: 'script', - }); - } - - $(columndiv).append( - '
' + - '
' + - '

' + - '
' - ); - loadWeather(settings['owm_city'], settings['owm_country']); - } - return; - case 'currentweather_big_owm': - if (settings['owm_api'] !== '' && settings['owm_city'] !== '') { - if (typeof loadWeather !== 'function') { - $.ajax({ - url: 'js/weather_owm.js', - async: false, - dataType: 'script', - }); - } - $(columndiv).append( - '
' + - '
' + - '
' + - '
' - ); - - loadWeather(settings['owm_city'], settings['owm_country']); - } - return; - case 'spotify': - if (typeof getSpotify !== 'function') - $.ajax({ - url: 'js/spotify.js', - async: false, - dataType: 'script', - }); - getSpotify(columndiv); - return; - case 'trafficmap': - $(columndiv).append( - '
' - ); - return; - case 'log': - if (typeof getLog !== 'function') - $.ajax({ - url: 'js/log.js', - async: false, - dataType: 'script', - }); - getLog(columndiv); - return; - case 'sunrise': - var classes = - 'block_' + - block.type + - ' col-xs-' + - width + - ' transbg text-center sunriseholder'; - if (c === 'bar') { - classes = 'block_' + block.type + ' col-xs-2 text-center sunriseholder'; - } - $(columndiv).append( - '
' + - '' + - '
' - ); - return; - case 'horizon': - appendHorizon(columndiv); - return; - case 'icalendar': - var random = getRandomInt(1, 100000); - var html = - '
'; - html += '
'; - html += ''; - html += '
'; - html += - '
' + language.misc.loading + '
'; - html += '
'; - $(columndiv).append(html); - addCalendar($('.containsicalendar' + random), settings['calendarurl']); - return; - case 'chromecast': - $.ajax({ - url: 'js/chromecast.js', - async: false, - dataType: 'script', - }); - loadChromecast(columndiv); - return; - case 'sonarr': - if (typeof loadSonarr !== 'function') - $.ajax({ - url: 'js/sonarr.js', - async: false, - dataType: 'script', - }); - $(columndiv).append(loadSonarr()); - getBlockClick(block); - return; - case 'fullscreen': - $(columndiv).append( - '
' + - getFullScreenIcon() + - '
' - ); - return; - default: - /*4 situations: - '123': Normal Domoticz device id as string - '123_1': subdevice 1 - 's123': group or scene 123 - 'v123': variable with idx 123 - */ - html = - '
'; - block.$mountPoint.append(html); - if (block.idx) { - //also a Domoticz device - block.width = (blocks[block.type] && blocks[block.type].width) || 4; - addDeviceUpdateHandler(block); - return; - } - var idx = parseInt(block.type); - var isDomoticzDevice = !!idx; - if (block.type[0] === 's' || block.type[0] === 'v') { - //scene, group or variable - idx = parseInt(block.type.slice(1)); - if (idx) isDomoticzDevice = true; - } - if (isDomoticzDevice) { - block.width = (blocks[block.type] && blocks[block.type].width) || 4; - block.idx = block.type; - addDeviceUpdateHandler(block); - } else console.log('unknown string block ', block); +/** Checks whether key indicates a Domoticz device + * + * 4 situations: + * + * '123': Normal Domoticz device id as string + * '123_1': subdevice 1 + * 's123': group or scene 123 + * 'v123': variable with idx 123 + * + * @param {string} key - Key identifier to check + */ +function isDomoticzDevice(key) { + if (typeof key === 'number') { + return key; } -} - -function handleObjectBlock(block, el) { - var random = getRandomInt(1, 100000); - var width = 12; - var $el = $(el); - mountBlock(el, block, null, null); - var key = block.key || block.entry; - if (block.width) width = block['width']; - if (block.latitude) { - $el.append(loadMaps(random, block)); - return; + var idx = parseInt(key); + if (idx) { + return idx; } - if (block.empty) { - $el.append( - '
' - ); - } else if (block.currency) { - if (typeof getCoin !== 'function') - $.ajax({ - url: 'js/coins.js', - async: false, - dataType: 'script', - }); - var html = - '
'; - $el.append(html); - getCoin(block); - } else if (block.icalurl || block.calendars) { - var dataId = key; - var classes = 'transbg containsicalendar containsicalendar' + random; - appendTvOrCalendarBlock(dataId, classes, width, block, el); - if (typeof addCalendar !== 'function') - $.ajax({ - url: 'js/calendar.js', - async: false, - dataType: 'script', - }); - addCalendar($('.containsicalendar' + random), block); - } else if (block.idx) { - //+ '" data-block="' + block.key - block.key = block.key || '' + block.idx; - $el.append( - '
' - ); - if (typeof block.idx === 'number') { - addDeviceUpdateHandler(block); - return; - } - var idx = parseInt(block.idx); + if (key[0] === 's' || key[0] === 'v') { + //scene, group or variable + idx = parseInt(key.slice(1)); if (idx) { - addDeviceUpdateHandler(block); - return; - } - if (block.idx[0] === 's' || block.idx[0] === 'v') { - //scene, group or variable - idx = parseInt(block.slice(1)); - if (idx) { - addDeviceUpdateHandler(block); - return; - } + return idx; } - } else { - // Dashticz.mountSpecialBlock(columndiv, block, Dashticz.components["button"]); - Dashticz.mountDefaultBlock(el, block); - // $(columndiv).append(loadButton(index, block)); - } -} -/**/ -function appendTvOrCalendarBlock(dataId, classes, width, block, columndiv) { - var html = ''; - if (block.title) { - html += - '

' + - block['title'] + - '

'; - } - - html += - '
'; - if (block.icon) { - html += '
'; - html += ''; - html += '
'; - html += '
' + language.misc.loading + '
'; - } else if (block.image) { - html += '
'; - html += - ''; - html += '
'; - html += '
' + language.misc.loading + '
'; - } else { - html += '
' + language.misc.loading + '
'; } - - html += '
'; - $(columndiv).append(html); + return 0; } // eslint-disable-next-line no-unused-vars @@ -897,6 +449,19 @@ function getBlockClick(block, selector) { var $div = block.$mountPoint.find( typeof selector === 'undefined' ? '.mh' : selector ); + if (block.popup) { + if ($div.length > 0) { + $div + .addClass('hover') + .off('click') + .click(function () { + /* if (target === '_blank') window.open(block.link); + else if (target === 'iframe') addBlockClickFrame(block);*/ + DT_function.clickHandler({ block: block }); + }); + } + return; + } if (url) { if ($div.length > 0) { $div @@ -929,7 +494,7 @@ function getBlockClick(block, selector) { graph ) { $div.addClass('hover').click(function () { - showPopupGraph(block); + DT_function.clickHandler({ block: block }); }); } } @@ -1059,7 +624,7 @@ function getBlockData(block, textOn, textOff) { var opendiv = '
'; var closediv = '
'; - var data=''; + var data = ''; if (!block['hide_data']) { var value = textOn; @@ -1167,80 +732,36 @@ function triggerStatus(block) { var idx = block.idx; var device = block.device; var value = device.LastUpdate; - var random = getRandomInt(1, 100000); getCustomFunction('getStatus', block, true); if (typeof onOffstates[idx] !== 'undefined' && value !== onOffstates[idx]) { - if (getIconStatusClass(device['Status']) == 'on') { - if (block['playsoundOn']) { - playAudio(block['playsoundOn']); - } - if (block['speakOn']) { - speak(block['speakOn']); - } - if (block['messageOn']) { - infoDevicsSwitch(block['messageOn']); - } - if (block['gotoslideOn']) { - toSlide(block['gotoslideOn'] - 1); - disableStandby(); - } - if (block['openpopupOn']) { - $('.modal.openpopup,.modal-backdrop').remove(); - - $('body').append( - DT_function.createModalDialog( - 'openpopup', - 'popup_' + random, - block['openpopupOn'] - ) - ); - - $('#popup_' + random).modal('show'); - - if (typeof block['openpopupOn']['auto_close'] !== 'undefined') { - setTimeout(function () { - $('.modal.openpopup,.modal-backdrop').remove(); - }, parseFloat(block['openpopupOn']['auto_close']) * 1000); - } - } - } - if (getIconStatusClass(device['Status']) == 'off') { - if (block['playsoundOff']) { - playAudio(block['playsoundOff']); - } - if (block['speakOff']) { - speak(block['speakOff']); - } - if (block['messageOff']) { - infoDevicsSwitch(blocks['messageOff']); - } - if (block['gotoslideOff']) { - toSlide(block['gotoslideOff'] - 1); - disableStandby(); - } - if (block['openpopupOff']) { - $('.modal.openpopup,.modal-backdrop').remove(); - - $('body').append( - DT_function.createModalDialog( - 'openpopup', - 'popup_' + random, - block['openpopupOff'] - ) - ); + onOffHandling(block, getIconStatusClass(device['Status'])); + } + onOffstates[idx] = value; +} - $('#popup_' + random).modal('show'); +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} - if (block['openpopupOff']['auto_close']) { - setTimeout(function () { - $('.modal.openpopup,.modal-backdrop').remove(); - }, parseFloat(block['openpopupOff']['auto_close']) * 1000); - } - } - } +function onOffHandling(block, status) { + var _status = capitalizeFirstLetter(status); + if (block['playsound' + _status]) { + playAudio(block['playsound' + _status]); + } + if (block['speak' + _status]) { + speak(block['speak' + _status]); + } + if (block['message' + _status]) { + infoDevicsSwitch(block['message' + _status]); + } + if (block['gotoslide' + _status]) { + toSlide(block['gotoslide' + _status] - 1); + disableStandby(); + } + if (block['openpopup' + _status]) { + DT_function.clickHandler(block, block['openpopup' + _status]); } - onOffstates[idx] = value; } // eslint-disable-next-line no-unused-vars @@ -1264,35 +785,7 @@ function triggerChange(block) { } } - if (block['playsound']) { - playAudio(block['playsound']); - } - if (block['speak']) { - speak(block['speak']); - } - if (block['gotoslide']) { - toSlide(block['gotoslide'] - 1); - } - if (block['openpopup']) { - var random = getRandomInt(1, 100000); - $('.modal.openpopup,.modal-backdrop').remove(); - - $('body').append( - DT_function.createModalDialog( - 'openpopup', - 'popup_' + random, - block['openpopup'] - ) - ); - - $('#popup_' + random).modal('show'); - - if (typeof block['openpopup']['auto_close'] !== 'undefined') { - setTimeout(function () { - $('.modal.openpopup,.modal-backdrop').remove(); - }, parseFloat(block['openpopup']['auto_close']) * 1000); - } - } + onOffHandling(block, ''); } oldstates[idx] = value; } @@ -1982,8 +1475,6 @@ function createBlocks(blockParent, blockValues) { .find('.block_' + key) .html(html) .addClass(block.addClass); - - //todo: Do we have to store block in mountedBlocks? }); } @@ -2159,25 +1650,6 @@ function loadMaps(b, map) { return html; } -// eslint-disable-next-line no-unused-vars -function appendHorizon(columndiv) { - var html = '
'; - html += - '
'; - html += ''; - html += '
'; - html += - '
'; - html += ''; - html += '
'; - html += - '
'; - html += ''; - html += '
'; - html += '
'; - $(columndiv).append(html); -} - // eslint-disable-next-line no-unused-vars function getAllDevicesHandler(value) { // debugger; diff --git a/js/components/button.js b/js/components/button.js index e1c968d3..1de003d1 100644 --- a/js/components/button.js +++ b/js/components/button.js @@ -4,18 +4,19 @@ var DT_button = { name: 'button', canHandle: function (block) { - return block && (block.btnimage || block.slide); + return block && (block.btnimage || block.slide || block.log); }, defaultCfg: function (button) { var cfg = { containerClass: - (button && button.slide ? 'slide slide' + button.slide : '') + - (DT_button.buttonIsClickable(button) ? ' hover ' : ' '), + (button && button.slide ? 'slide slide' + button.slide : ''), forcerefreshiframe: 0, }; if (button.btnimage) { cfg.refresh = 60; } + if(typeof button.title==='undefined' && typeof button.icon==='undefined' && typeof button.image==='undefined' && typeof button.btnimage==='undefined') + button.title = button.key || button.type || 'Button'; return cfg; }, defaultContent: function (me) { @@ -40,13 +41,13 @@ var DT_button = { return html; }, run: function (me) { - var button = me.block; +/* var button = me.block; if (DT_button.buttonIsClickable(button)) $(me.mountPoint + ' .button').on( 'click', button, DT_button.buttonOnClick - ); + );*/ }, refresh: function (me) { DT_button.reloadImage(me); @@ -99,6 +100,7 @@ var DT_button = { }, button.refreshiframe * 1000); } }, + /* buttonOnClick: function ( m_event //button clickhandler. Assumption: button is clickable ) { @@ -125,14 +127,7 @@ var DT_button = { } else { DT_button.buttonLoadFrame(button); } - }, - buttonIsClickable: function (button) { - var clickable = - typeof button.url !== 'undefined' || - button.log == true || - typeof button.slide !== 'undefined'; - return clickable; - }, + },*/ reloadImage: function (me) { var src; if (typeof me.block.btnimage !== 'undefined') { diff --git a/js/components/calendar.js b/js/components/calendar.js index 33a23b89..0319e795 100644 --- a/js/components/calendar.js +++ b/js/components/calendar.js @@ -5,7 +5,7 @@ var templateEngine = TemplateEngine(); var DT_calendar = { name: 'calendar', canHandle: function (block, key) { - return block && block.type === 'calendar'; + return block && (block.type === 'calendar' || block.icalurl); }, defaultCfg: { icon: 'fas fa-calendar-alt', @@ -154,7 +154,7 @@ function getCalendarData(key, calendars, isnew, ishol) { * @param {string} key The block name of the calendar. */ function generateAgenda(opt, key) { - createModalIframe(key); +// createModalIframe(key); templateEngine.load('calendar_' + opt).then(function (template) { var data = { diff --git a/js/components/camera.js b/js/components/camera.js index c8ddc7d3..ffe18f07 100644 --- a/js/components/camera.js +++ b/js/components/camera.js @@ -41,10 +41,10 @@ var DT_camera = { /* The camera block contains multiple cameras */ if (me.block.cameras.length > 0) { /* Create new mountpoints for each of the cameras */ - var s = $(me.mountPoint).closest('.screen').data('screenindex'); - var c = $(me.mountPoint).closest('.col-xs-12').data('colindex'); + var s = me.$mountPoint.closest('.screen').data('screenindex'); + var c = me.$mountPoint.closest('.col-xs-12').data('colindex'); var columndiv = 'div.screen' + s + ' .row .col' + c; - $(me.mountPoint).remove(); + me.$mountPoint.remove(); $.each(me.block.cameras, function (i) { var mountpoint = Dashticz.mountNewContainer(columndiv); diff --git a/js/components/dial.js b/js/components/dial.js index d5d33fbb..82bd0879 100644 --- a/js/components/dial.js +++ b/js/components/dial.js @@ -55,10 +55,16 @@ var DT_dial = { me.idx = isDefined(me.block.idx) ? me.block.idx : me.key; me.block.idx = me.idx; /* required for existing functions */ me.id = 'dial_' + me.idx; - me.height = isDefined(me.block.height) + var height = isDefined(me.block.height) ? parseInt(me.block.height) - : parseInt($(me.mountPoint + ' div').css('width')); - me.fontsize = 0.8 * me.height; +// : parseInt($(me.mountPoint + ' div').css('width')); + : parseInt($(me.mountPoint + ' div').outerWidth()); + if (height<0) { + console.log('dial width unknown.') + me.height=me.height || 100; + } + else me.height = height; + me.fontsize = 0.9 * me.height; me.dialRange = 280; me.active = true; DT_dial.color(me); @@ -93,7 +99,7 @@ var DT_dial = { if (idx && me.devices.indexOf(idx) === -1) me.devices.push(idx); } me.devices.forEach(function (el) { - Domoticz.subscribe(el, false, function (device) { + Dashticz.subscribeDevice(me, el, false, function (device) { if (me.idx === el) { me.device = device; me.block.device = device; @@ -112,9 +118,17 @@ var DT_dial = { } me.isSetpoint = isDefined(me.device.SetPoint); DT_dial.make(me); + DT_dial.tap(me); }); }, + destroy: function(me) { + if(me.hammer) { + me.hammer.destroy(); + me.hammer=0 + } + }, + /** * Creates or updates the dial and applies current values. * @param {object} me Core component object. @@ -209,6 +223,19 @@ var DT_dial = { var $mount = $(me.mountPoint + ' .dt_content'); $mount.html(template(dataObject)); $mount.addClass('swiper-no-swiping'); + + /*todo: update height*/ + var height = isDefined(me.block.height) + ? parseInt(me.block.height) +// : parseInt($(me.mountPoint + ' div').css('width')); + : parseInt($(me.mountPoint + ' div').outerWidth()); + if (height<0) { + me.height=me.height || 100; + } + else me.height=height; + + me.fontsize = 0.9 * me.height; + $(me.mountPoint + ' .dt_block').css('height', me.height + 'px'); if (me.type === 'evo' || me.type === 'selector') { $(me.select + ' li').each(function () { @@ -234,8 +261,6 @@ var DT_dial = { $(me.mountPoint + ' .dial .fill').addClass('show-ring'); } - DT_dial.tap(me); - /* Add dial calculations */ if (!me.onoff && !me.controller) { me.body = $(me.mountPoint + ' .dt_content .dial'); @@ -256,24 +281,36 @@ var DT_dial = { * @param {object} me Core component object. */ tap: function (me) { - var d = document.getElementById(me.id); - var mc = new Hammer(d); - mc.on('tap', function (ev) { + var d = $(me.mountPoint + ' .dial')[0]; + if(me.hammer) { + me.hammer.destroy(); + } + me.hammer = new Hammer(d); + var block=me.block; + if (block.popup || block.url || block.slide) { + //Clickhandler has been added already! + //DT_function.clickHandler(me); + return; + } + me.hammer.on('tap', function (ev) { if (me.status === 'TemporaryOverride') { me.override = false; me.demand = false; DT_dial.update(me); + return; } if (me.type === 'dim') { me.demand ? (me.value = 0) : (me.value = me.device.Level); me.demand = !me.demand; DT_dial.update(me); + return; } if (me.type === 'dhw') { me.demand = me.state === 'On'; me.state = me.state === 'On' ? 'Off' : 'On'; me.demand = !me.demand; switchEvoHotWater(me, me.state, me.demand); + return; } if (me.type === 'evo' || me.type === 'selector') { $(me.select + ' li').removeClass('selected'); @@ -285,16 +322,17 @@ var DT_dial = { } else { slideDevice(me, status); } + return; } if (me.type === 'onoff') { if (me.device.Type === 'Scene') me.cmd = 'On'; else me.cmd = me.state === 'Off' ? 'On' : 'Off'; me.demand = me.cmd === 'On'; DT_dial.update(me); + return; } - if (me.type === 'default' || me.type === 'temp') { - showPopupGraph(me.block); - } + // (me.type === 'default' or 'temp' or 'p1' or ...) + DT_function.clickHandler({ block: block }) }); }, @@ -519,7 +557,7 @@ var DT_dial = { */ color: function (me) { if (isDefined(me.block.color)) { - var c = $(me.mountPoint) + var c = me.$mountPoint .css('color', me.block.color) .css('color'); /* change all formats to rgb */ me.color = c; diff --git a/js/components/domoticzblock.js b/js/components/domoticzblock.js new file mode 100644 index 00000000..5577b5c2 --- /dev/null +++ b/js/components/domoticzblock.js @@ -0,0 +1,71 @@ +/* global Dashticz settings deviceUpdateHandler Domoticz*/ +//# sourceURL=js/components/domoticzblock.js +var DT_domoticzblock = (function () { + return { + name: 'domoticzblock', + canHandle: function (block) { + return block && block.idx; + }, + defaultCfg: { + width: 4, + batteryThreshold: settings.batteryThreshold, + icon: 'default', + }, + run: function (me) { + var block = me.block; + me.$mountPoint.html( + '
' + ); + me.deviceIdx = block.idx; + if (typeof block.idx === 'string') { + var idxSplit = block.idx.split('_'); + if (idxSplit.length == 2) { + var idx = parseInt(idxSplit[0]); + var subidx = parseInt(idxSplit[1]); + if (typeof idx === 'number' && typeof subidx === 'number') { + me.deviceIdx = idx; + me.subidx = subidx; + } + } + } + me.entry = me.mountPoint.slice(1); + + fixBlock(me); + addDeviceUpdateHandler(me); + }, + refresh: function (me) { + fixBlock(me); + deviceUpdateHandler(me.block); + }, + }; + + function fixBlock(me) { + //This function is needed to make it work with previous block definition + //refactoring needed in the future + var block = me.block; + if (block.icon === 'default') { + block.icon = undefined; + block.image = undefined; + } + if (block.icon) block.image = undefined; + block.$mountPoint = me.$mountPoint; + block.mountPoint = me.mountPoint; + block.entry = me.entry; + block.subidx = me.subidx; + block.device = Domoticz.getAllDevices()[me.deviceIdx]; + } + + function addDeviceUpdateHandler(me) { + var block = me.block; + Dashticz.subscribeDevice(me, me.deviceIdx, true, function (device) { + block.device = device; + deviceUpdateHandler(block); + }); + } +})(); + +Dashticz.register(DT_domoticzblock); diff --git a/js/components/frame.js b/js/components/frame.js index 2991b96b..0cf1ffa6 100644 --- a/js/components/frame.js +++ b/js/components/frame.js @@ -30,7 +30,7 @@ var DT_frame = { }, refresh: function (me) { if (typeof me.block.frameurl !== 'undefined') { - $(me.mountPoint) + me.$mountPoint .find('iframe') .attr( 'src', diff --git a/js/components/garbage.js b/js/components/garbage.js index adf53f9b..7e23ae51 100644 --- a/js/components/garbage.js +++ b/js/components/garbage.js @@ -676,7 +676,7 @@ var DT_garbage = (function () { } function addToContainer(me, returnDates) { - var $div = $(me.mountPoint); + var $div = me.$mountPoint; var $divState = $div.find('.state'); var $divImg = $div.find('img.trashcan'); returnDates = filterReturnDates(me, returnDates); diff --git a/js/components/graph.js b/js/components/graph.js index 7b12e4b6..a2c32b3b 100644 --- a/js/components/graph.js +++ b/js/components/graph.js @@ -8,7 +8,7 @@ var DT_graph = { canHandle: function (block, key) { return ( (block && block.devices) || - (typeof key === 'string' && key.substring(0, 6) === 'graph_') + (typeof block.key === 'string' && block.key.substring(0, 6) === 'graph_') ); }, defaultCfg: getBlockDefaults, @@ -18,7 +18,7 @@ var DT_graph = { $.each(me.graphDevices, function (i, graphDevice) { //install the callback handles - Domoticz.subscribe(graphDevice.idx, false, function (device) { + Dashticz.subscribeDevice(me, graphDevice.idx, false, function (device) { deviceUpdate(me, graphDevice, device); }); }); @@ -48,7 +48,7 @@ function Initialize(me) { me.graphDevices.push(device); } else { var msg = 'For graph ' + me.key + ' device ' + idx + ' does not exist.'; - $(me.mountPoint).append(msg); + me.$mountPoint.append(msg); throw new Error(msg); } }); @@ -333,56 +333,6 @@ function getDeviceDefaults(me, device) { $.extend(device, obj); } -// eslint-disable-next-line no-unused-vars -function showPopupGraph(blockdef) { - //This function can be called from blocks.js to create the popup graph - var popupBlock, graphIdx; - if (blockdef.popup) { - popupBlock = $.extend({}, blocks[blockdef.popup]); - graphIdx = blockdef.popup + '_popup'; - } else { - popupBlock = { - devices: [blockdef.device.idx], - width: 12, - }; - graphIdx = blockdef.device.idx + '_popup'; - } - popupBlock.isPopup = true; - - var device = blockdef.device; - if ($('#opengraph' + graphIdx).length === 0) { - var html = - ''; - $('body').append(html); - - var myblockselector = Dashticz.mountNewContainer( - '.opengraph' + graphIdx + ' .modal-content' - ); - - if (!Dashticz.mount(myblockselector, popupBlock)) { - console.log('Error mounting popup graph', popupBlock); - } - $(myblockselector).addClass('modal-body'); //modal-body is just for styling, so we have to add it. - } - - $('#opengraph' + graphIdx).modal('show'); -} - /** This function handles a device update * * */ @@ -801,6 +751,7 @@ function createGraph(graph) { if (parseInt(height) > 0) //only change height is we have a valid height value $('.' + graphIdx + ' .graphcontent').css('height', height); + //console.log('test'); } if (typeof mergedBlock.legend == 'boolean') { diff --git a/js/components/html.js b/js/components/html.js index 6b4ff4b5..ecf6716c 100644 --- a/js/components/html.js +++ b/js/components/html.js @@ -6,7 +6,7 @@ var DT_html = { return block && block.htmlfile; }, run: function (me) { - if (!me.block.border) $(me.mountPoint).addClass('no-margin'); + if (!me.block.border) me.$mountPoint.addClass('no-margin'); return $.get({ url: 'custom/' + me.block.htmlfile, }).then(function (res) { diff --git a/js/components/log.js b/js/components/log.js new file mode 100644 index 00000000..d93a02a0 --- /dev/null +++ b/js/components/log.js @@ -0,0 +1,46 @@ +//# sourceURL=js/components/log.js +/* global Dashticz DT_function _CORS_PATH settings*/ + +var DT_log = { + name: 'log', + defaultCfg: { + icon: 'fas fa-microchip', + title: 'Domoticz log', + refresh: 5, + containerClass: 'containslog', + level: 268435455, + ascending: true, + }, + defaultContent: '
', + run: function (me) { + // $(me.block.mountPoint + ' .dt_state').addClass('items'); + }, + refresh: function (me) { + var LOG_QUERY = 'type=command¶m=getlog&loglevel=' + me.block.level; + //console.log(LOG_QUERY); + Domoticz.request(LOG_QUERY).then(function (logdata) { + var $items = $(me.mountPoint + ' .items'); + if (me.popup) $(me.mountPoint + ' .log').addClass('popup'); //temporary. Move to generic handler + var res = logdata.result + .sort(function (a, b) { + a.message < b.message ? 1 : a.message > b.message ? -1 : 0; + }) + .reduce(function (acc, el) { + var dotPos = el.message.indexOf('.'); + var timeStamp = el.message.substring(0, dotPos + 4); + var logMessage = el.message.substring(dotPos + 4); + + return acc+'' + + timeStamp + + '' + + logMessage + + '' + }, ''); + $items.html(res + '
'); + $items.scrollTop(function() { return this.scrollHeight; }); + }); + }, +}; + +Dashticz.register(DT_log); diff --git a/js/components/longfonds.js b/js/components/longfonds.js index a67f2201..e6081d40 100644 --- a/js/components/longfonds.js +++ b/js/components/longfonds.js @@ -13,7 +13,7 @@ var DT_longfonds = { containerClass: 'hover', }, run: function (me) { - $(me.mountPoint).click(function () { + me.$mountPoint.click(function () { DT_function.clickHandler(me); }); }, diff --git a/js/components/news.js b/js/components/news.js index 18c0e67f..e7242cba 100644 --- a/js/components/news.js +++ b/js/components/news.js @@ -1,4 +1,5 @@ -/* global settings _CORS_PATH infoMessage Dashticz moment*/ +/* global settings _CORS_PATH infoMessage Dashticz moment isDefined templateEngine DT_function*/ +//# sourceURL=js/components/news.js // eslint-disable-next-line no-unused-vars var DT_news = { name: 'news', @@ -92,6 +93,10 @@ var DT_news = { $(document.body).append(template); }); } + $(me.mountPoint + ' .headline').on("click", function() { + var url = $(this).data('link'); + DT_function.clickHandler(me, {url: url}) + }) if (me.height) { /*set to fixed height*/ @@ -136,9 +141,4 @@ var DT_news = { }); }, }; -/* callback for newsfeed item onclick*/ -// eslint-disable-next-line no-unused-vars -function setSrcRss(cur) { - $($(cur).data('target')).find('iframe').attr('src', $(cur).data('link')); -} Dashticz.register(DT_news); diff --git a/js/components/secpanel.js b/js/components/secpanel.js index 8a6ef457..4621fb65 100644 --- a/js/components/secpanel.js +++ b/js/components/secpanel.js @@ -45,7 +45,7 @@ var DT_secpanel = { DT_secpanel.onResize(me); }) .done(function () { - Domoticz.subscribe('_secstatus', true, function () { + Dashticz.subscribeDevice(me, '_secstatus', true, function () { //subscribe to the security status, and receive the actual status directly DT_secpanel.ShowStatus(); }); diff --git a/js/components/simpleblock.js b/js/components/simpleblock.js new file mode 100644 index 00000000..bb3cc29b --- /dev/null +++ b/js/components/simpleblock.js @@ -0,0 +1,423 @@ +/* global Dashticz DT_function getFullScreenIcon settings loadWeather loadWeatherFull getSpotify*/ +//# sourceURL=js/components/simpleblock.js +var DT_simpleblock = (function () { + var simpleBlocks = { + logo: { + defaultWidth: 2, + render: renderLogo, + }, + settings: { + defaultWidth: 2, + render: renderSettings, + }, + miniclock: { + defaultWidth: 8, + render: renderMiniclock, + }, + clock: { + render: renderClock, + }, + responsiveclock: { + render: renderResponsiveClock, + }, + weather: { + script: 'js/weather.js', + render: renderWeather, + }, + currentweather: { + script: 'js/weather.js', + render: renderCurrentWeather, + }, + currentweather_big: { + script: 'js/weather.js', + render: renderCurrentWeather_big, + }, + weather_owm: { + script: 'js/weather_owm.js', + render: renderWeather_owm, + }, + currentweather_owm: { + script: 'js/weather_owm.js', + render: renderCurrentWeather_owm, + }, + currentweather_big_owm: { + script: 'js/weather_owm.js', + render: renderCurrentWeather_big_owm, + }, + spotify: { + script: 'js/spotify.js', + render: renderSpotify, + }, + trafficmap: { + render: renderTrafficMap, + }, + sunrise: { + render: renderSunrise, + }, + horizon: { + render: renderHorizon, + }, + sonarr: { + script: 'js/sonar.js', + render: renderSonar, + }, + fullscreen: { + script: 'js/fullscreen.js', + render: renderFullScreen, + }, + moon: { + render: renderMoon + } + }; + + var keyBlocks = { + empty: { render: renderEmpty }, + currency: { + script: 'js/coins.js', + render: renderCurrency, + }, + latitude: { + render: renderMaps, + }, + }; + + function findKey(block) { + var blockType = undefined; + $.each(keyBlocks, function (key) { + if (typeof block[key] !== 'undefined') blockType = key; + }); + return blockType; + } + + function getBlock(block) { + return simpleBlocks[block.type] || keyBlocks[findKey(block)]; + } + return { + name: 'simpleblock', + canHandle: function (block) { + return block && (!!simpleBlocks[block.type] || findKey(block)); + }, + defaultCfg: function (block) { + var thisBlock = getBlock(block); + return { + width: (thisBlock && thisBlock.defaultWidth) || 12, + }; + }, + run: function (me) { + var thisBlock = getBlock(me.block); + var script = thisBlock.script; + var render = thisBlock.render; + if (script) + DT_function.loadScript(script).then(function () { + renderBlock(me, render); + }); + else renderBlock(me, render); + }, + }; + + function renderBlock(me, render) { + var addHTML = render(me); + if (addHTML) me.$mountPoint.html(addHTML); + } + + function renderLogo(me) { + return ( + '' + ); + } + + function renderSettings(me) { + var icons = ['settings', 'fullscreen']; + if (typeof settings['settings_icons'] !== 'undefined') { + icons = settings['settings_icons']; + } + var content = + '
'; + for (var i = 0; i < icons.length; i++) { + switch (icons[i]) { + case 'settings': + content += + ' '; + break; + + case 'fullscreen': + $.ajax({ + url: 'js/fullscreen.js', + async: false, + dataType: 'script', + }); + content += getFullScreenIcon(); + break; + } + } + content += '
'; + return content; + } + + function renderMiniclock(me) { + return ( + '
' + + '      ' + + '
' + ); + } + + function renderClock(me) { + return ( + '
' + + '

' + + '
' + ); + } + + function renderResponsiveClock(me) { + return ( + '
' + + '

' + + '
' + ); + } + + function renderWeather(me) { + if (typeof loadWeatherFull !== 'function') { + $.ajax({ + url: 'js/weather.js', + async: false, + dataType: 'script', + }); + } + me.$mountPoint.html( + '
' + ); + if (settings['wu_api'] !== '' && settings['wu_city'] !== '') + loadWeatherFull(settings['wu_city'], settings['wu_country']); + } + + function renderCurrentWeather(me) { + if (settings['wu_api'] !== '' && settings['wu_city'] !== '') { + if (typeof loadWeather !== 'function') { + $.ajax({ + url: 'js/weather.js', + async: false, + dataType: 'script', + }); + } + me.$mountPoint.html( + '
' + + '
' + + '

' + + '
' + ); + loadWeather(settings['wu_city'], settings['wu_country']); + } + } + + function renderCurrentWeather_big(me) { + if (settings['wu_api'] !== '' && settings['wu_city'] !== '') { + if (typeof loadWeather !== 'function') { + $.ajax({ + url: 'js/weather.js', + async: false, + dataType: 'script', + }); + } + me.$mountPoint.html( + '
' + + '
' + + '
' + + '
' + ); + + loadWeather(settings['wu_city'], settings['wu_country']); + } + } + + function renderWeather_owm(me) { + if (typeof loadWeatherFull !== 'function') { + $.ajax({ + url: 'js/weather_owm.js', + async: false, + dataType: 'script', + }); + } + me.$mountPoint.html( + '
' + ); + if (settings['owm_api'] !== '' && settings['owm_city'] !== '') + loadWeatherFull( + settings['owm_city'], + settings['owm_country'], + $('.weatherfull') + ); + } + + function renderCurrentWeather_owm(me) { + if (settings['owm_api'] !== '' && settings['owm_city'] !== '') { + if (typeof loadWeather !== 'function') { + $.ajax({ + url: 'js/weather_owm.js', + async: false, + dataType: 'script', + }); + } + + me.$mountPoint.html( + '
' + + '
' + + '

' + + '
' + ); + loadWeather(settings['owm_city'], settings['owm_country']); + } + } + + function renderCurrentWeather_big_owm(me) { + if (settings['owm_api'] !== '' && settings['owm_city'] !== '') { + if (typeof loadWeather !== 'function') { + $.ajax({ + url: 'js/weather_owm.js', + async: false, + dataType: 'script', + }); + } + me.$mountPoint.html( + '
' + + '
' + + '
' + + '
' + ); + + loadWeather(settings['owm_city'], settings['owm_country']); + } + } + + function renderSpotify(me) { + getSpotify(me.mountPoint); + } + + function renderTrafficMap(me) { + return ( + '
' + ); + } + + function renderSunrise(me) { + var isBar = me.block.c === 'bar'; + var classes = 'block_' + me.block.type; + var width = isBar ? 2 : me.block.width; + classes += ' col-xs-' + width; + if (!isBar) classes += ' transbg'; + classes += ' text-center sunriseholder'; + return ( + '
' + + '' + + '
' + ); + } + + function renderHorizon(me) { + var html = '
'; + html += + '
'; + html += ''; + html += '
'; + html += + '
'; + html += ''; + html += '
'; + html += + '
'; + html += ''; + html += '
'; + html += '
'; + return html; + } + + function renderSonar(me) { + return loadSonarr(); + } + + function renderFullScreen(me) { + return ( + '
' + + getFullScreenIcon() + + '
' + ); + } + + function renderEmpty(me) { + return ( + '
' + ); + } + + function renderCurrency(me) { + var html = + '
'; + me.$mountPoint.html(html); + getCoin(me.block); + } + + function renderMaps(me) { + return loadMaps(me.block.key, me.block); + } + + function renderMoon(me) { + me.block.btnimage='moon'; + return DT_button.defaultContent(me); + } +})(); + +Dashticz.register(DT_simpleblock); diff --git a/js/components/timegraph.js b/js/components/timegraph.js index 4df1e2bf..0b172400 100644 --- a/js/components/timegraph.js +++ b/js/components/timegraph.js @@ -89,7 +89,7 @@ var DT_timegraph = (function () { DT_timegraph.createGraph(me); me.datasets.forEach(function (dataset, idx) { - Domoticz.subscribe(dataset.idx, true, function (device) { + Dashticz.subscribeDevice(me, dataset.idx, true, function (device) { addData(me, idx, device); }); }); @@ -104,7 +104,7 @@ var DT_timegraph = (function () { }, createGraph: function (me) { - var mountPoint = $(me.mountPoint); + var mountPoint = me.$mountPoint; var chartctx = mountPoint.find('canvas')[0].getContext('2d'); var m_moment = moment(); diff --git a/js/dashticz.js b/js/dashticz.js index 462e2685..0ff6aa00 100644 --- a/js/dashticz.js +++ b/js/dashticz.js @@ -7,6 +7,7 @@ // eslint-disable-next-line no-unused-vars var Dashticz = (function () { var specials = [ + 'domoticzblock', 'streamplayer', 'button', 'frame', @@ -32,10 +33,12 @@ var Dashticz = (function () { 'garbage', 'haymanclock', 'flipclock', - 'basicclock' + 'basicclock', + 'log', + 'simpleblock', ]; var components = []; - var mountedBlocks = []; + var mountedBlocks = {}; var blockNumbering = 0; function _init() { @@ -88,10 +91,9 @@ var Dashticz = (function () { function _onResize() { Object.keys(mountedBlocks).forEach(function (key) { - var me = mountedBlocks[key]; - var comp = components[me.name]; + var me = mountedBlocks[key].me; + var comp = me && me.name && components[me.name]; if (!comp) { - console.log('no component for ', key); return; } if (comp.onResize) comp.onResize(me); @@ -99,7 +101,7 @@ var Dashticz = (function () { } function renderBlock(me) { - var $div = $(me.mountPoint).find('.dt_block'); + var $div = me.$mountPoint.find('.dt_block'); var block = $(getSpecialBlock(me)); if (me.block.containerClass) $div.addClass(getProperty(me.block.containerClass, me)); @@ -117,11 +119,11 @@ var Dashticz = (function () { function addClickHandler(me) { var clickHandler = null; - if (!me.block.url) return; + if (!me.block.url && !me.block.slide && !me.block.popup) return; var bCH = me.block.clickHandler; if (typeof bCH === 'function') { clickHandler = bCH; - } else if (bCH) { + } else if (typeof bCH === 'undefined' || bCH) { clickHandler = DT_function.clickHandler; } if (clickHandler) { @@ -133,6 +135,32 @@ var Dashticz = (function () { } } + function removeBlock(id) { + mountedBlocks[id].childs.forEach(function (child) { + removeBlock(child); + }); + mountedBlocks[id].childs = []; + var me = mountedBlocks[id].me; + if (me) { + me.callbacks.timeoutList.forEach(function (el) { + clearTimeout(el); + }); + me.callbacks.intervalList.forEach(function (el) { + clearInterval(el); + }); + me.callbacks.subscriptionList.forEach(function (el) { + el(); + }); + me.callbacks.timeoutList = []; + me.callbacks.intervalList = []; + me.callbacks.subscriptionList = []; + var destroy = components[me.name].destroy; + if (typeof destroy === 'function') destroy(me); + } + $(id).remove(); + delete mountedBlocks[id]; + } + function _mountSpecialBlock(mountPoint, blockdef, special, key) { if (!special.initPromise) special.initPromise = special.init @@ -140,25 +168,23 @@ var Dashticz = (function () { : $.when(); special.initPromise.done(function () { var me = createBlock(mountPoint, blockdef, special, key); - $(mountPoint).html(getContainer(me)); + me.$mountPoint = $(mountPoint); + me.$mountPoint.html(getContainer(me)); // console.log(me); renderBlock(me); - mountedBlocks[me.mountPoint] = me; + mountedBlocks[me.mountPoint].me = me; if (special.run) special.run(me); addClickHandler(me); if (me.block.refresh && special.refresh) { //install refresh handler - setInterval(function () { - special.refresh(me); - }, me.block.refresh * 1000); + _setInterval(me, special.refresh, me.block.refresh * 1000); special.refresh(me); } if (special.refresh) { blocks[me.key] = blockdef; - Dashticz.subscribeBlock(me.key, function (block) { - console.log('updating special block', me); + Dashticz.subscribeBlock(me, function (block) { me.block = getBlockConfig(block, components[me.name], me.key); renderBlock(me); special.refresh(me); @@ -266,6 +292,11 @@ var Dashticz = (function () { block: blockdef, key: blockdef.key ? blockdef.key : mountPoint.slice(1), name: special.name, + callbacks: { + timeoutList: [], + intervalList: [], + subscriptionList: [], + }, }; return newblock; } @@ -282,6 +313,10 @@ var Dashticz = (function () { return true; } } + if (typeof selector === 'object' && components[selector.type]) { + _mountSpecialBlock(mountPoint, selector, components[selector.type], ''); + return true; + } for (var comp in components) { if (typeof selector === 'object') { if ( @@ -311,15 +346,43 @@ var Dashticz = (function () { } function _mountNewContainer(column) { - $(column).append('
'); - return '#block_' + blockNumbering++; + var id = 'block_' + blockNumbering; + var _id = '#' + id; + $(column).append('
'); + mountedBlocks[_id] = { + parent: column, + childs: [], + }; + if (!mountedBlocks[column]) + mountedBlocks[column] = { + childs: [], + }; + mountedBlocks[column].childs.push(_id); + /* + DT_function.onRemove($(column+' #'+id)[0], function() { + console.log('Removed from DOM: ', id); + })*/ + blockNumbering++; + return _id; } var subscribeBlockList = {}; - function subscribeBlock(key, callback) { - if (!subscribeBlockList[key]) subscribeBlockList[key] = []; - subscribeBlockList[key].push(callback); + function subscribeBlock(me, callback) { + var key = me.key; + if (!subscribeBlockList[key]) subscribeBlockList[key] = $.Callbacks(); + var cb = function (data) { + setTimeout(function () { + callback(data); + }, 0); + }; + subscribeBlockList[key].add(cb); + + var unsubscribe = function () { + subscribeBlockList[key].remove(cb); + }; + me.callbacks.subscriptionList.push(unsubscribe); + return unsubscribe; } function setBlock(key, state) { @@ -337,25 +400,47 @@ var Dashticz = (function () { } } if (changed || !state) { - if (subscribeBlockList[key]) - subscribeBlockList[key].forEach(function (callback) { - setTimeout(function () { - callback(block); - }, 0); - }); - else { + if (subscribeBlockList[key]) subscribeBlockList[key].fire(block); + else if (typeof key==='string') { var keySplit = key.split('_'); if (keySplit.length === 2 && subscribeBlockList[keySplit[0]]) { - subscribeBlockList[keySplit[0]].forEach(function (callback) { - setTimeout(function () { - callback({}); //we call the parent call back with empty block update - }, 0); - }); + //we call the parent call back with empty block update + subscribeBlockList[keySplit[0]].fire({}); } } } } + function isMounted(me) { + if (me.$mountPoint.length) return true; + removeBlock(me); + return false; + } + + function _setTimeout(me, callback, timeout) { + me.callbacks.timeoutList.push( + setTimeout(function () { + if (isMounted(me)) callback(me); + }, timeout) + ); + } + + function _setInterval(me, callback, interval) { + me.callbacks.intervalList.push( + setInterval(function () { + if (isMounted(me)) callback(me); + }, interval) + ); + } + + function _subscribeDevice(me, idx, getCurrent, callback) { + var unsubscribe = Domoticz.subscribe(idx, getCurrent, function (data) { + if (isMounted(me)) callback(data); + }); + me.callbacks.subscriptionList.push(unsubscribe); + return unsubscribe; + } + return { init: _init, onResize: _onResize, @@ -366,6 +451,10 @@ var Dashticz = (function () { mountDefaultBlock: _mountDefaultBlock, subscribeBlock: subscribeBlock, setBlock: setBlock, + setTimeout: _setTimeout, + setInterval: _setInterval, + subscribeDevice: _subscribeDevice, + removeBlock: removeBlock, }; })(); diff --git a/js/domoticz-api.js b/js/domoticz-api.js index eb727fbe..c8115862 100644 --- a/js/domoticz-api.js +++ b/js/domoticz-api.js @@ -242,7 +242,6 @@ var Domoticz = (function () { } else { // e.g. server process killed or network down // event.code is usually 1006 in this case - console.log(event); switch (event.code) { case 1006: if (!reconnecting) reconnect(); @@ -520,36 +519,24 @@ function ListObservable() { //value was updated while on hold. Send latest value value = this._values[idx]; if (typeof this._observers[idx] !== 'undefined') - this._observers[idx].forEach(function (el) { - el.callback(value); - }); + this._observers[idx].fire(value); } this._queueState[idx] = 0; }; this.subscribe = function (idx, getCurrent, callback) { - if (typeof this._observers[idx] === 'undefined') this._observers[idx] = []; - this._observers[idx].push({ - callback: callback, - }); + if (typeof this._observers[idx] === 'undefined') this._observers[idx] = $.Callbacks(); + this._observers[idx].add(callback); if (getCurrent && typeof this._values[idx] !== 'undefined') callback(this._values[idx]); var me = this; - var observeridx = this._observers[idx].length - 1; return function () { - me.unsubscribe.call(me, idx, observeridx); + me._observers[idx].remove(callback); }; }; - this.unsubscribe = function (listidx, observeridx) { - if (this._observers[listidx]) { - if (!this._observers[listidx].splice(observeridx, 1).length) - console.log( - 'observer ' + observeridx + ' for list ' + listidx + ' not found.' - ); - } else { - console.log('List idx ' + listidx + ' not found.'); - } + this.unsubscribe = function (listidx, callback) { + this._observers[listidx].remove(callback); }; this.set = function (idx, value) { @@ -560,9 +547,7 @@ function ListObservable() { return; } if (typeof this._observers[idx] !== 'undefined') - this._observers[idx].forEach(function (el) { - el.callback(value); - }); + this._observers[idx].fire(value) }; this.get = function (idx) { diff --git a/js/dt_function.js b/js/dt_function.js index f81603e3..65765fde 100644 --- a/js/dt_function.js +++ b/js/dt_function.js @@ -1,4 +1,4 @@ -/* global toSlide getRandomInt settings infoMessage language*/ +/* global toSlide getRandomInt settings infoMessage*/ // eslint-disable-next-line no-unused-vars var DT_function = (function () { /**Clickhandler for Dashticz block @@ -7,21 +7,45 @@ var DT_function = (function () { */ function clickHandler(me, cfg) { var block = { key: me.key }; - $.extend(block, me.block, cfg); + $.extend( + block, + me.block, + me.block && { + forcerefresh: + me.name == 'button' + ? me.block.forcerefreshiframe + : me.block.forcerefresh, + }, + cfg, + {isPopup: true} + ); + block.zindex = (block.zindex || 2500) + 1; var hasPassword = block.password; if (!promptPassword(hasPassword)) return; if (typeof block.newwindow !== 'undefined') { if (block.newwindow == '0') { - window.open(block.url, '_self'); + //open in same window + blockNewWindow(block, '_self'); } else if (block.newwindow == '1') { - window.open(block.url); + //open in new tab + blockNewWindow(block, block.title); } else if (block.newwindow == '2') { + //open in modal iframe blockLoadFrame(block); } else if (block.newwindow == '3') { + //Ajax get request $.ajax(checkForceRefresh(block.url, block.forcerefresh)); } else if (block.newwindow == '4') { + //Ajax post request $.post(block.url); + } else if (block.newwindow == '5') { + //open in new window + blockNewWindow( + block, + block.title, + 'toolbar=yes,menubar=yes,titlebar=yes,statusbar=yes' + ); } else { blockLoadFrame(block); } @@ -36,12 +60,54 @@ var DT_function = (function () { //Displays the frame of a block after pressing it var id = 'popup_' + (block.key || getRandomInt(1, 100000)); $('body').append(createModalDialog('openpopup', id, block)); + $('#' + id).modal('show'); + var popupBlock = 0; + if(!block.url) { + if (typeof block.popup !== 'undefined') { + popupBlock = typeof block.popup === 'string' ? convertBlock(block.popup): block.popup; + } + else if(block.idx) { + //It's a Domoticz device. We create a graph block + popupBlock = { + devices: [block.idx], + key: block.key + '_popup' + } + } + } + + if(popupBlock) { + var container = '#' + id + ' .modal-content'; + setTimeout(function() { + addBlock2Column(container, 'popup', popupBlock); + }, 200); //after 200ms the modal is visible and has a width. + + } $('#' + id).on('hidden.bs.modal', function () { $(this).data('bs.modal', null); + if(container) Dashticz.removeBlock(container); $(this).remove(); }); - $('#' + id).modal('show'); + if (typeof block['auto_close'] !== 'undefined') { + setTimeout(function () { + $('.modal.openpopup,.modal-backdrop').remove(); + }, parseFloat(block['auto_close']) * 1000); + } + + var $modal = $('#' +id + ' .modal-content'); + if (block.url) $modal.addClass('modal-url'); + if (popupBlock) { + $modal.addClass( DT_graph.canHandle(popupBlock) ? 'modal-graph':'modal-popup'); + } + } + + function blockNewWindow(block, title, params) { + var newWindow = window.open(block.url, title, params); + if (title !== '_self' && block.auto_close && newWindow) { + setTimeout(function () { + newWindow.close(); + }, parseFloat(block['auto_close']) * 1000); + } } // eslint-disable-next-line no-unused-vars @@ -95,7 +161,14 @@ var DT_function = (function () { } return url; } + + var loadedResources = {}; + function loadFont(fontName, fontURL, fontFormat) { + var id = fontName + fontFormat; + if (loadedResources[id]) + return; + loadedResources[id] = true; var newStyle = document.createElement('style'); newStyle.appendChild( document.createTextNode( @@ -118,11 +191,26 @@ var DT_function = (function () { } function loadCSS(filename) { + var id = filename; + if (loadedResources[id]) + return; + loadedResources[id] = true; + $('head').append( '' ); } + function loadScript(filename) { + if (!loadedResources[filename]) { + loadedResources[filename] = $.ajax({ + url: filename, + dataType: 'script', + }); + } + return loadedResources[filename]; + } + /** Prompt for password * @function * @param {string} password Password @@ -160,14 +248,16 @@ var DT_function = (function () { } var html = ''; html += '
'; @@ -199,14 +298,49 @@ var DT_function = (function () { return html; } + /** Attach callback which will be called when element gets removed from the DOM + * @param {object} element - DOM element + * @param {function} callback - Callback function + * + * @example + * + * onRemove(element, function() { + * releaseAResource(element) + * }); + * + */ + function onRemove(element, callback) { + var parent = element.parentNode; + if (!parent) throw new Error('The node must already be attached'); + + var obs = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + mutation.removedNodes.forEach(function (el) { + if (el === element) { + obs.disconnect(); + callback(); + } + }); + }); + }); + obs.observe(document.body, { + // attributes: true, + childList: true, + subtree: true, + // characterData: true + }); + } + return { clickHandler: clickHandler, promptPassword: promptPassword, loadFont: loadFont, loadCSS: loadCSS, + loadScript: loadScript, blockLoadFrame: blockLoadFrame, checkForceRefresh: checkForceRefresh, createModalDialog: createModalDialog, + onRemove: onRemove, }; })(); diff --git a/js/log.js b/js/log.js index cfb8be7d..7eaf3349 100644 --- a/js/log.js +++ b/js/log.js @@ -1,6 +1,7 @@ /* global getRandomInt settings usrEnc pwdEnc*/ // eslint-disable-next-line no-unused-vars function getLog(columndiv, level, popup, random) { +// console.log(popup); if (typeof level == 'undefined') level = 2; if (typeof popup == 'undefined') popup = false; if (typeof random == 'undefined') random = getRandomInt(1, 100000); @@ -23,7 +24,8 @@ function getLog(columndiv, level, popup, random) { } else { html += '
'; } - html += '
'; + html += '
'; +// console.log(html); $(columndiv).append(html); } diff --git a/tpl/news_row.tpl b/tpl/news_row.tpl index 4ef38d0f..46a6adae 100644 --- a/tpl/news_row.tpl +++ b/tpl/news_row.tpl @@ -10,15 +10,12 @@ {{/if}} {{/if}} -
-
+
{{item.title}}
{{{item.desc}}}
Reported {{item.pubd}}
-
{{/each}} diff --git a/version.txt b/version.txt index b5249a8b..f6575060 100644 --- a/version.txt +++ b/version.txt @@ -1,8 +1,9 @@ { -"version": "3.7.2", +"version": "3.7.3", "branch": "beta", -"last_changes": "custom.css and custom.js removed from Dashticz repo", +"last_changes": "Read upgrade instructions!", "changelog" : { + "3.7.2": "custom.css and custom.js removed from Dashticz repo", "3.7.1": "Clock related changes.", "3.7": "Master version" }