diff --git a/.gitignore b/.gitignore index 1e7dbf9a..fc90aca3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ docs/build .idea/ config platypush/backend/http/static/css/*/.sass-cache/ +.vscode diff --git a/platypush/__main__.py b/platypush/__main__.py index 30fd0e97..19417792 100644 --- a/platypush/__main__.py +++ b/platypush/__main__.py @@ -3,4 +3,3 @@ from platypush import main main() # vim:sw=4:ts=4:et: - diff --git a/platypush/backend/http/app/routes/dashboard.py b/platypush/backend/http/app/routes/dashboard.py index e129c79c..7340ddf8 100644 --- a/platypush/backend/http/app/routes/dashboard.py +++ b/platypush/backend/http/app/routes/dashboard.py @@ -1,6 +1,6 @@ from flask import Blueprint, request, render_template -from platypush.backend.http.app import template_folder +from platypush.backend.http.app import template_folder, static_folder from platypush.backend.http.app.utils import authenticate, authentication_ok, \ get_websocket_port @@ -14,17 +14,21 @@ __routes__ = [ dashboard, ] + @dashboard.route('/dashboard', methods=['GET']) def dashboard(): """ Route for the fullscreen dashboard """ - if not authentication_ok(request): return authenticate() + if not authentication_ok(request): + return authenticate() http_conf = Config.get('backend.http') dashboard_conf = http_conf.get('dashboard', {}) return render_template('dashboard.html', config=dashboard_conf, utils=HttpUtils, token=Config.get('token'), - websocket_port=get_websocket_port()) + static_folder=static_folder, template_folder=template_folder, + websocket_port=get_websocket_port(), + has_ssl=http_conf.get('ssl_cert') is not None) # vim:sw=4:ts=4:et: diff --git a/platypush/backend/http/app/routes/map.py b/platypush/backend/http/app/routes/map.py deleted file mode 100644 index d6e10ca7..00000000 --- a/platypush/backend/http/app/routes/map.py +++ /dev/null @@ -1,95 +0,0 @@ -import datetime -import dateutil.parser - -from flask import abort, request, render_template, Blueprint - -from platypush.backend.http.app import template_folder -from platypush.backend.http.app.utils import authenticate, authentication_ok -from platypush.config import Config - - -map_ = Blueprint('map', __name__, template_folder=template_folder) - -# Declare routes list -__routes__ = [ - map_, -] - -def parse_time(time_string): - if not time_string: - return None - - now = datetime.datetime.now() - - if time_string == 'now': - return now.isoformat() - if time_string == 'yesterday': - return (now - datetime.timedelta(days=1)).isoformat() - - try: - return dateutil.parser.parse(time_string).isoformat() - except ValueError: - pass - - m = re.match('([-+]?)([0-9]+)([dhms])', time_string) - if not m: - raise RuntimeError('Invalid time interval string representation: "{}"'. - format(time_string)) - - time_delta = (-1 if m.group(1) == '-' else 1) * int(m.group(2)) - time_unit = m.group(3) - - if time_unit == 'd': - params = { 'days': time_delta } - elif time_unit == 'h': - params = { 'hours': time_delta } - elif time_unit == 'm': - params = { 'minutes': time_delta } - elif time_unit == 's': - params = { 'seconds': time_delta } - - return (now + datetime.timedelta(**params)).isoformat() - - -@map_.route('/map', methods=['GET']) -def map(): - """ - Query parameters: - start -- Map timeline start timestamp - end -- Map timeline end timestamp - zoom -- Between 1-20. Set it if you want to override the - Google's API auto-zoom. You may have to set it if you are - trying to embed the map into an iframe - - Supported values for `start` and `end`: - - now - - yesterday - - -30s (it means '30 seconds ago') - - -10m (it means '10 minutes ago') - - -24h (it means '24 hours ago') - - -7d (it means '7 days ago') - - 2018-06-04T17:39:22.742Z (ISO strings) - - Default: start=yesterday, end=now - """ - - if not authentication_ok(request): return authenticate() - map_conf = (Config.get('backend.http') or {}).get('maps', {}) - if not map_conf: - abort(500, 'The maps plugin is not configured in backend.http') - - api_key = map_conf.get('api_key') - if not api_key: - abort(500, 'Google Maps api_key not set in the maps configuration') - - start = parse_time(request.args.get('start', default='yesterday')) - end = parse_time(request.args.get('end', default='now')) - zoom = request.args.get('zoom', default=None) - - return render_template('map.html', config=map_conf, utils=HttpUtils, - start=start, end=end, zoom=zoom, - token=Config.get('token'), api_key=api_key, - websocket_port=get_websocket_port()) - - -# vim:sw=4:ts=4:et: diff --git a/platypush/backend/http/static/css/application.css b/platypush/backend/http/static/css/application.css deleted file mode 100644 index 149dcd91..00000000 --- a/platypush/backend/http/static/css/application.css +++ /dev/null @@ -1,227 +0,0 @@ -body { - width: 100%; - overflow-x: hidden; -} - -header { - width: 100%; - background: #f4f5f6; - padding: 10px 25px; - margin: 0px 10px 35px -10px; - border-bottom: 1px solid #e1e4e8; -} - -.logo { - font-size: 25px; - margin-top: 10px; -} - - .logo > .logo-1 { - font-weight: bold; - } - -.modal { - display: none; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - z-index: 999; - background-color: rgba(10,10,10,0.85); - font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; -} - - .modal-container { - margin: 5% auto auto auto; - width: 70%; - background: white; - border-radius: 10px; - } - - .modal-header { - border-bottom: 1px solid #ccc; - margin: 0.5rem auto; - padding: 0.5rem; - text-align: center; - background-color: #f0f0f0; - border-radius: 10px 10px 0 0; - text-transform: uppercase; - font-weight: 400px; - letter-spacing: .1rem; - line-height: 38px; - } - - .modal-body { - padding: 2.5rem 2rem 1.5rem 2rem; - } - - .form-footer { - text-align: right; - margin-top: 2rem; - border-top: 1px solid #ddd; - } - - .form-footer * > input[type=button], - .form-footer * > button { - margin-top: 2rem; - text-transform: uppercase; - font-size: 1.3rem; - } - -#date-time { - text-align: right; - padding-right: 30px; -} - - #date-time > .date { - color: #666; - } - - #date-time > .time { - font-weight: bold; - font-size: 30px; - } - -main { - width: 95%; - margin: auto; -} - - ul.tab-nav { - padding-left: 0 !important; /* Override skeleton-tabs default */ - } - -.tab-content { - border: 1px solid #bbb; - border-top: 0; - padding: 20px; - margin-top: -25px; - border-radius: 0 0 7.5px 7.5px; -} - -button[disabled] { - color: #bbb; - border: 1px solid; -} - - .button[disabled] { - background: rgba(240,240,240,1) - } - -.slider { - -webkit-appearance: none; - appearance: none; - width: 100%; - height: 15px; - border-radius: 5px; - background: #e4e4e4; - outline: none; - opacity: 0.7; - -webkit-transition: .2s; - transition: opacity .2s; -} - - .slider:hover { - opacity: 1; - } - - .slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 25px; - height: 25px; - border-radius: 50%; - background: #4CAF50; - cursor: pointer; - } - - .slider[disabled]::-webkit-slider-thumb { - display: none; - } - - .slider::-moz-range-thumb { - width: 25px; - height: 25px; - background: #4CAF50; - cursor: pointer; - } - -.btn-primary { - background-color: #d8ffe0 !important; - border: 1px solid #98efb0 !important; -} - -.right-side { - text-align: right !important; -} - -#notification-container { - position: fixed; - bottom: 0; - right: 0; - width: 25em; -} - - #notification-container > .notification { - background: rgba(180,245,188,0.85); - border: 1px solid #bbb; - border-radius: 5px; - margin-bottom: 1rem; - margin-right: 1rem; - cursor: pointer; - display: none; - } - - #notification-container > .notification:hover { - background: rgba(160,235,168,0.9); - } - - #notification-container * > .notification-title { - padding: 4px 10px; - font-weight: bold; - text-transform: capitalize; - line-height: 30px; - letter-spacing: .1rem; - } - - #notification-container * > .notification-body { - height: 6em; - overflow: hidden; - padding-bottom: 1rem; - letter-spacing: .05rem; - } - - #notification-container * > .notification-text { - margin-left: 0 !important; - } - - #notification-container * > .notification-image { - height: 100%; - text-align: center; - padding-top: .6em; - padding-bottom: .6em; - } - - #notification-container * > .notification-image-item { - width: 100%; - height: 100%; - margin: auto; - } - - #notification-container * > .fa.notification-image-item { - margin-top: .8em; - margin-left: .2em; - font-size: 25px; - } - - #notification-container * > .notification-image img { - width: 80%; - height: 80%; - } - -#hidden-plugins-container > .plugin { - display: none; -} - diff --git a/platypush/backend/http/static/css/assistant.google.css b/platypush/backend/http/static/css/assistant.google.css deleted file mode 100644 index e69de29b..00000000 diff --git a/platypush/backend/http/static/css/dashboard.css b/platypush/backend/http/static/css/dashboard.css deleted file mode 100644 index 9b31fb18..00000000 --- a/platypush/backend/http/static/css/dashboard.css +++ /dev/null @@ -1,20 +0,0 @@ -html { - min-height: 100%; -} - -body { - background-image: url('/img/dashboard-background.jpg'); - background-size: 100% 100%; - background-repeat: no-repeat; - font-family: Lato; -} - -.widget { - background: white; - margin-top: 2em; - border-radius: 5px; - height: 22.5em; - overflow: hidden; - box-shadow: 0 3px 3px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08); -} - diff --git a/platypush/backend/http/static/css/gpio.css b/platypush/backend/http/static/css/gpio.css deleted file mode 100644 index 952678fa..00000000 --- a/platypush/backend/http/static/css/gpio.css +++ /dev/null @@ -1,32 +0,0 @@ -#gpio-container { - background-color: #f8f8f8; - padding: 12px; - border: 1px solid #ddd; - border-radius: 10px; - font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: 400; - line-height: 38px; - letter-spacing: .1rem; -} - -.gpio-pin { - padding: 1.5em 1em; - border-radius: 10px; -} - - .gpio-pin:nth-of-type(odd) { - background-color: #ececec; - } - - .gpio-pin:hover { - background-color: #daf8e2 !important; - } - -.pin-value { - text-align: right; -} - -.pin-ctrl { - width: 8em; -} - diff --git a/platypush/backend/http/static/css/gpio.sensor.mcp3008.css b/platypush/backend/http/static/css/gpio.sensor.mcp3008.css deleted file mode 100644 index 51edfd61..00000000 --- a/platypush/backend/http/static/css/gpio.sensor.mcp3008.css +++ /dev/null @@ -1,25 +0,0 @@ -#sensors-container { - width: 80%; - max-width: 60rem; - margin: 3em auto; - background: rgba(235,235,235,0.8); - padding: 2em; - border: 1px solid rgba(220,220,220,1.0); - border-radius: 10px; -} - -.sensor-data { - text-transform: capitalize; -} - - .sensor-data:not(:last-child) { - margin-bottom: .5rem; - padding-bottom: .5rem; - border-bottom: 1px solid rgba(220,220,220,1.0); - } - -.sensor-value { - text-align: right; - font-weight: bold; -} - diff --git a/platypush/backend/http/static/css/gpio.zeroborg.css b/platypush/backend/http/static/css/gpio.zeroborg.css deleted file mode 100644 index cdafb117..00000000 --- a/platypush/backend/http/static/css/gpio.zeroborg.css +++ /dev/null @@ -1,24 +0,0 @@ -#zb-container { - outline: none !important; -} - -.zb-controls-container { - margin: auto; - width: 25%; -} - -.zb-ctrl-btn { - white-space: normal; - height: 6rem; -} - - .zb-ctrl-btn.selected { - color: #78ff00 !important; - } - -.ctrl-bottom-row { - margin-top: 20px; - padding-top: 20px; - border-top: 1px solid #ddd; -} - diff --git a/platypush/backend/http/static/css/jquery-ui.css b/platypush/backend/http/static/css/jquery-ui.css deleted file mode 100644 index 294452f1..00000000 --- a/platypush/backend/http/static/css/jquery-ui.css +++ /dev/null @@ -1,1311 +0,0 @@ -/*! jQuery UI - v1.12.1 - 2016-09-14 -* http://jqueryui.com -* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px -* Copyright jQuery Foundation and other contributors; Licensed MIT */ - -/* Layout helpers -----------------------------------*/ -.ui-helper-hidden { - display: none; -} -.ui-helper-hidden-accessible { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} -.ui-helper-reset { - margin: 0; - padding: 0; - border: 0; - outline: 0; - line-height: 1.3; - text-decoration: none; - font-size: 100%; - list-style: none; -} -.ui-helper-clearfix:before, -.ui-helper-clearfix:after { - content: ""; - display: table; - border-collapse: collapse; -} -.ui-helper-clearfix:after { - clear: both; -} -.ui-helper-zfix { - width: 100%; - height: 100%; - top: 0; - left: 0; - position: absolute; - opacity: 0; - filter:Alpha(Opacity=0); /* support: IE8 */ -} - -.ui-front { - z-index: 100; -} - - -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { - cursor: default !important; - pointer-events: none; -} - - -/* Icons -----------------------------------*/ -.ui-icon { - display: inline-block; - vertical-align: middle; - margin-top: -.25em; - position: relative; - text-indent: -99999px; - overflow: hidden; - background-repeat: no-repeat; -} - -.ui-widget-icon-block { - left: 50%; - margin-left: -8px; - display: block; -} - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -.ui-accordion .ui-accordion-header { - display: block; - cursor: pointer; - position: relative; - margin: 2px 0 0 0; - padding: .5em .5em .5em .7em; - font-size: 100%; -} -.ui-accordion .ui-accordion-content { - padding: 1em 2.2em; - border-top: 0; - overflow: auto; -} -.ui-autocomplete { - position: absolute; - top: 0; - left: 0; - cursor: default; -} -.ui-menu { - list-style: none; - padding: 0; - margin: 0; - display: block; - outline: 0; -} -.ui-menu .ui-menu { - position: absolute; -} -.ui-menu .ui-menu-item { - margin: 0; - cursor: pointer; - /* support: IE10, see #8844 */ - list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); -} -.ui-menu .ui-menu-item-wrapper { - position: relative; - padding: 3px 1em 3px .4em; -} -.ui-menu .ui-menu-divider { - margin: 5px 0; - height: 0; - font-size: 0; - line-height: 0; - border-width: 1px 0 0 0; -} -.ui-menu .ui-state-focus, -.ui-menu .ui-state-active { - margin: -1px; -} - -/* icon support */ -.ui-menu-icons { - position: relative; -} -.ui-menu-icons .ui-menu-item-wrapper { - padding-left: 2em; -} - -/* left-aligned */ -.ui-menu .ui-icon { - position: absolute; - top: 0; - bottom: 0; - left: .2em; - margin: auto 0; -} - -/* right-aligned */ -.ui-menu .ui-menu-icon { - left: auto; - right: 0; -} -.ui-button { - padding: .4em 1em; - display: inline-block; - position: relative; - line-height: normal; - margin-right: .1em; - cursor: pointer; - vertical-align: middle; - text-align: center; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - - /* Support: IE <= 11 */ - overflow: visible; -} - -.ui-button, -.ui-button:link, -.ui-button:visited, -.ui-button:hover, -.ui-button:active { - text-decoration: none; -} - -/* to make room for the icon, a width needs to be set here */ -.ui-button-icon-only { - width: 2em; - box-sizing: border-box; - text-indent: -9999px; - white-space: nowrap; -} - -/* no icon support for input elements */ -input.ui-button.ui-button-icon-only { - text-indent: 0; -} - -/* button icon element(s) */ -.ui-button-icon-only .ui-icon { - position: absolute; - top: 50%; - left: 50%; - margin-top: -8px; - margin-left: -8px; -} - -.ui-button.ui-icon-notext .ui-icon { - padding: 0; - width: 2.1em; - height: 2.1em; - text-indent: -9999px; - white-space: nowrap; - -} - -input.ui-button.ui-icon-notext .ui-icon { - width: auto; - height: auto; - text-indent: 0; - white-space: normal; - padding: .4em 1em; -} - -/* workarounds */ -/* Support: Firefox 5 - 40 */ -input.ui-button::-moz-focus-inner, -button.ui-button::-moz-focus-inner { - border: 0; - padding: 0; -} -.ui-controlgroup { - vertical-align: middle; - display: inline-block; -} -.ui-controlgroup > .ui-controlgroup-item { - float: left; - margin-left: 0; - margin-right: 0; -} -.ui-controlgroup > .ui-controlgroup-item:focus, -.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus { - z-index: 9999; -} -.ui-controlgroup-vertical > .ui-controlgroup-item { - display: block; - float: none; - width: 100%; - margin-top: 0; - margin-bottom: 0; - text-align: left; -} -.ui-controlgroup-vertical .ui-controlgroup-item { - box-sizing: border-box; -} -.ui-controlgroup .ui-controlgroup-label { - padding: .4em 1em; -} -.ui-controlgroup .ui-controlgroup-label span { - font-size: 80%; -} -.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item { - border-left: none; -} -.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item { - border-top: none; -} -.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content { - border-right: none; -} -.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content { - border-bottom: none; -} - -/* Spinner specific style fixes */ -.ui-controlgroup-vertical .ui-spinner-input { - - /* Support: IE8 only, Android < 4.4 only */ - width: 75%; - width: calc( 100% - 2.4em ); -} -.ui-controlgroup-vertical .ui-spinner .ui-spinner-up { - border-top-style: solid; -} - -.ui-checkboxradio-label .ui-icon-background { - box-shadow: inset 1px 1px 1px #ccc; - border-radius: .12em; - border: none; -} -.ui-checkboxradio-radio-label .ui-icon-background { - width: 16px; - height: 16px; - border-radius: 1em; - overflow: visible; - border: none; -} -.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon, -.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon { - background-image: none; - width: 8px; - height: 8px; - border-width: 4px; - border-style: solid; -} -.ui-checkboxradio-disabled { - pointer-events: none; -} -.ui-datepicker { - width: 17em; - padding: .2em .2em 0; - display: none; -} -.ui-datepicker .ui-datepicker-header { - position: relative; - padding: .2em 0; -} -.ui-datepicker .ui-datepicker-prev, -.ui-datepicker .ui-datepicker-next { - position: absolute; - top: 2px; - width: 1.8em; - height: 1.8em; -} -.ui-datepicker .ui-datepicker-prev-hover, -.ui-datepicker .ui-datepicker-next-hover { - top: 1px; -} -.ui-datepicker .ui-datepicker-prev { - left: 2px; -} -.ui-datepicker .ui-datepicker-next { - right: 2px; -} -.ui-datepicker .ui-datepicker-prev-hover { - left: 1px; -} -.ui-datepicker .ui-datepicker-next-hover { - right: 1px; -} -.ui-datepicker .ui-datepicker-prev span, -.ui-datepicker .ui-datepicker-next span { - display: block; - position: absolute; - left: 50%; - margin-left: -8px; - top: 50%; - margin-top: -8px; -} -.ui-datepicker .ui-datepicker-title { - margin: 0 2.3em; - line-height: 1.8em; - text-align: center; -} -.ui-datepicker .ui-datepicker-title select { - font-size: 1em; - margin: 1px 0; -} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { - width: 45%; -} -.ui-datepicker table { - width: 100%; - font-size: .9em; - border-collapse: collapse; - margin: 0 0 .4em; -} -.ui-datepicker th { - padding: .7em .3em; - text-align: center; - font-weight: bold; - border: 0; -} -.ui-datepicker td { - border: 0; - padding: 1px; -} -.ui-datepicker td span, -.ui-datepicker td a { - display: block; - padding: .2em; - text-align: right; - text-decoration: none; -} -.ui-datepicker .ui-datepicker-buttonpane { - background-image: none; - margin: .7em 0 0 0; - padding: 0 .2em; - border-left: 0; - border-right: 0; - border-bottom: 0; -} -.ui-datepicker .ui-datepicker-buttonpane button { - float: right; - margin: .5em .2em .4em; - cursor: pointer; - padding: .2em .6em .3em .6em; - width: auto; - overflow: visible; -} -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { - float: left; -} - -/* with multiple calendars */ -.ui-datepicker.ui-datepicker-multi { - width: auto; -} -.ui-datepicker-multi .ui-datepicker-group { - float: left; -} -.ui-datepicker-multi .ui-datepicker-group table { - width: 95%; - margin: 0 auto .4em; -} -.ui-datepicker-multi-2 .ui-datepicker-group { - width: 50%; -} -.ui-datepicker-multi-3 .ui-datepicker-group { - width: 33.3%; -} -.ui-datepicker-multi-4 .ui-datepicker-group { - width: 25%; -} -.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, -.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { - border-left-width: 0; -} -.ui-datepicker-multi .ui-datepicker-buttonpane { - clear: left; -} -.ui-datepicker-row-break { - clear: both; - width: 100%; - font-size: 0; -} - -/* RTL support */ -.ui-datepicker-rtl { - direction: rtl; -} -.ui-datepicker-rtl .ui-datepicker-prev { - right: 2px; - left: auto; -} -.ui-datepicker-rtl .ui-datepicker-next { - left: 2px; - right: auto; -} -.ui-datepicker-rtl .ui-datepicker-prev:hover { - right: 1px; - left: auto; -} -.ui-datepicker-rtl .ui-datepicker-next:hover { - left: 1px; - right: auto; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane { - clear: right; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane button { - float: left; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, -.ui-datepicker-rtl .ui-datepicker-group { - float: right; -} -.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, -.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { - border-right-width: 0; - border-left-width: 1px; -} - -/* Icons */ -.ui-datepicker .ui-icon { - display: block; - text-indent: -99999px; - overflow: hidden; - background-repeat: no-repeat; - left: .5em; - top: .3em; -} -.ui-dialog { - position: absolute; - top: 0; - left: 0; - padding: .2em; - outline: 0; -} -.ui-dialog .ui-dialog-titlebar { - padding: .4em 1em; - position: relative; -} -.ui-dialog .ui-dialog-title { - float: left; - margin: .1em 0; - white-space: nowrap; - width: 90%; - overflow: hidden; - text-overflow: ellipsis; -} -.ui-dialog .ui-dialog-titlebar-close { - position: absolute; - right: .3em; - top: 50%; - width: 20px; - margin: -10px 0 0 0; - padding: 1px; - height: 20px; -} -.ui-dialog .ui-dialog-content { - position: relative; - border: 0; - padding: .5em 1em; - background: none; - overflow: auto; -} -.ui-dialog .ui-dialog-buttonpane { - text-align: left; - border-width: 1px 0 0 0; - background-image: none; - margin-top: .5em; - padding: .3em 1em .5em .4em; -} -.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { - float: right; -} -.ui-dialog .ui-dialog-buttonpane button { - margin: .5em .4em .5em 0; - cursor: pointer; -} -.ui-dialog .ui-resizable-n { - height: 2px; - top: 0; -} -.ui-dialog .ui-resizable-e { - width: 2px; - right: 0; -} -.ui-dialog .ui-resizable-s { - height: 2px; - bottom: 0; -} -.ui-dialog .ui-resizable-w { - width: 2px; - left: 0; -} -.ui-dialog .ui-resizable-se, -.ui-dialog .ui-resizable-sw, -.ui-dialog .ui-resizable-ne, -.ui-dialog .ui-resizable-nw { - width: 7px; - height: 7px; -} -.ui-dialog .ui-resizable-se { - right: 0; - bottom: 0; -} -.ui-dialog .ui-resizable-sw { - left: 0; - bottom: 0; -} -.ui-dialog .ui-resizable-ne { - right: 0; - top: 0; -} -.ui-dialog .ui-resizable-nw { - left: 0; - top: 0; -} -.ui-draggable .ui-dialog-titlebar { - cursor: move; -} -.ui-draggable-handle { - -ms-touch-action: none; - touch-action: none; -} -.ui-resizable { - position: relative; -} -.ui-resizable-handle { - position: absolute; - font-size: 0.1px; - display: block; - -ms-touch-action: none; - touch-action: none; -} -.ui-resizable-disabled .ui-resizable-handle, -.ui-resizable-autohide .ui-resizable-handle { - display: none; -} -.ui-resizable-n { - cursor: n-resize; - height: 7px; - width: 100%; - top: -5px; - left: 0; -} -.ui-resizable-s { - cursor: s-resize; - height: 7px; - width: 100%; - bottom: -5px; - left: 0; -} -.ui-resizable-e { - cursor: e-resize; - width: 7px; - right: -5px; - top: 0; - height: 100%; -} -.ui-resizable-w { - cursor: w-resize; - width: 7px; - left: -5px; - top: 0; - height: 100%; -} -.ui-resizable-se { - cursor: se-resize; - width: 12px; - height: 12px; - right: 1px; - bottom: 1px; -} -.ui-resizable-sw { - cursor: sw-resize; - width: 9px; - height: 9px; - left: -5px; - bottom: -5px; -} -.ui-resizable-nw { - cursor: nw-resize; - width: 9px; - height: 9px; - left: -5px; - top: -5px; -} -.ui-resizable-ne { - cursor: ne-resize; - width: 9px; - height: 9px; - right: -5px; - top: -5px; -} -.ui-progressbar { - height: 2em; - text-align: left; - overflow: hidden; -} -.ui-progressbar .ui-progressbar-value { - margin: -1px; - height: 100%; -} -.ui-progressbar .ui-progressbar-overlay { - background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw=="); - height: 100%; - filter: alpha(opacity=25); /* support: IE8 */ - opacity: 0.25; -} -.ui-progressbar-indeterminate .ui-progressbar-value { - background-image: none; -} -.ui-selectable { - -ms-touch-action: none; - touch-action: none; -} -.ui-selectable-helper { - position: absolute; - z-index: 100; - border: 1px dotted black; -} -.ui-selectmenu-menu { - padding: 0; - margin: 0; - position: absolute; - top: 0; - left: 0; - display: none; -} -.ui-selectmenu-menu .ui-menu { - overflow: auto; - overflow-x: hidden; - padding-bottom: 1px; -} -.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { - font-size: 1em; - font-weight: bold; - line-height: 1.5; - padding: 2px 0.4em; - margin: 0.5em 0 0 0; - height: auto; - border: 0; -} -.ui-selectmenu-open { - display: block; -} -.ui-selectmenu-text { - display: block; - margin-right: 20px; - overflow: hidden; - text-overflow: ellipsis; -} -.ui-selectmenu-button.ui-button { - text-align: left; - white-space: nowrap; - width: 14em; -} -.ui-selectmenu-icon.ui-icon { - float: right; - margin-top: 0; -} -.ui-slider { - position: relative; - text-align: left; -} -.ui-slider .ui-slider-handle { - position: absolute; - z-index: 2; - width: 1.2em; - height: 1.2em; - cursor: default; - -ms-touch-action: none; - touch-action: none; -} -.ui-slider .ui-slider-range { - position: absolute; - z-index: 1; - font-size: .7em; - display: block; - border: 0; - background-position: 0 0; -} - -/* support: IE8 - See #6727 */ -.ui-slider.ui-state-disabled .ui-slider-handle, -.ui-slider.ui-state-disabled .ui-slider-range { - filter: inherit; -} - -.ui-slider-horizontal { - height: .8em; -} -.ui-slider-horizontal .ui-slider-handle { - top: -.3em; - margin-left: -.6em; -} -.ui-slider-horizontal .ui-slider-range { - top: 0; - height: 100%; -} -.ui-slider-horizontal .ui-slider-range-min { - left: 0; -} -.ui-slider-horizontal .ui-slider-range-max { - right: 0; -} - -.ui-slider-vertical { - width: .8em; - height: 100px; -} -.ui-slider-vertical .ui-slider-handle { - left: -.3em; - margin-left: 0; - margin-bottom: -.6em; -} -.ui-slider-vertical .ui-slider-range { - left: 0; - width: 100%; -} -.ui-slider-vertical .ui-slider-range-min { - bottom: 0; -} -.ui-slider-vertical .ui-slider-range-max { - top: 0; -} -.ui-sortable-handle { - -ms-touch-action: none; - touch-action: none; -} -.ui-spinner { - position: relative; - display: inline-block; - overflow: hidden; - padding: 0; - vertical-align: middle; -} -.ui-spinner-input { - border: none; - background: none; - color: inherit; - padding: .222em 0; - margin: .2em 0; - vertical-align: middle; - margin-left: .4em; - margin-right: 2em; -} -.ui-spinner-button { - width: 1.6em; - height: 50%; - font-size: .5em; - padding: 0; - margin: 0; - text-align: center; - position: absolute; - cursor: default; - display: block; - overflow: hidden; - right: 0; -} -/* more specificity required here to override default borders */ -.ui-spinner a.ui-spinner-button { - border-top-style: none; - border-bottom-style: none; - border-right-style: none; -} -.ui-spinner-up { - top: 0; -} -.ui-spinner-down { - bottom: 0; -} -.ui-tabs { - position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ - padding: .2em; -} -.ui-tabs .ui-tabs-nav { - margin: 0; - padding: .2em .2em 0; -} -.ui-tabs .ui-tabs-nav li { - list-style: none; - float: left; - position: relative; - top: 0; - margin: 1px .2em 0 0; - border-bottom-width: 0; - padding: 0; - white-space: nowrap; -} -.ui-tabs .ui-tabs-nav .ui-tabs-anchor { - float: left; - padding: .5em 1em; - text-decoration: none; -} -.ui-tabs .ui-tabs-nav li.ui-tabs-active { - margin-bottom: -1px; - padding-bottom: 1px; -} -.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, -.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, -.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { - cursor: text; -} -.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { - cursor: pointer; -} -.ui-tabs .ui-tabs-panel { - display: block; - border-width: 0; - padding: 1em 1.4em; - background: none; -} -.ui-tooltip { - padding: 8px; - position: absolute; - z-index: 9999; - max-width: 300px; -} -body .ui-tooltip { - border-width: 2px; -} -/* Component containers -----------------------------------*/ -.ui-widget { - font-family: Verdana,Arial,sans-serif; - font-size: 1.1em; -} -.ui-widget .ui-widget { - font-size: 1em; -} -.ui-widget input, -.ui-widget select, -.ui-widget textarea, -.ui-widget button { - font-family: Verdana,Arial,sans-serif; - font-size: 1em; -} -.ui-widget.ui-widget-content { - border: 1px solid #d3d3d3; -} -.ui-widget-content { - border: 1px solid #aaaaaa; - background: #ffffff; - color: #222222; -} -.ui-widget-content a { - color: #222222; -} -.ui-widget-header { - border: 1px solid #aaaaaa; - background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x; - color: #222222; - font-weight: bold; -} -.ui-widget-header a { - color: #222222; -} - -/* Interaction states -----------------------------------*/ -.ui-state-default, -.ui-widget-content .ui-state-default, -.ui-widget-header .ui-state-default, -.ui-button, - -/* We use html here because we need a greater specificity to make sure disabled -works properly when clicked or hovered */ -html .ui-button.ui-state-disabled:hover, -html .ui-button.ui-state-disabled:active { - border: 1px solid #d3d3d3; - background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #555555; -} -.ui-state-default a, -.ui-state-default a:link, -.ui-state-default a:visited, -a.ui-button, -a:link.ui-button, -a:visited.ui-button, -.ui-button { - color: #555555; - text-decoration: none; -} -.ui-state-hover, -.ui-widget-content .ui-state-hover, -.ui-widget-header .ui-state-hover, -.ui-state-focus, -.ui-widget-content .ui-state-focus, -.ui-widget-header .ui-state-focus, -.ui-button:hover, -.ui-button:focus { - border: 1px solid #999999; - background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #212121; -} -.ui-state-hover a, -.ui-state-hover a:hover, -.ui-state-hover a:link, -.ui-state-hover a:visited, -.ui-state-focus a, -.ui-state-focus a:hover, -.ui-state-focus a:link, -.ui-state-focus a:visited, -a.ui-button:hover, -a.ui-button:focus { - color: #212121; - text-decoration: none; -} - -.ui-visual-focus { - box-shadow: 0 0 3px 1px rgb(94, 158, 214); -} -.ui-state-active, -.ui-widget-content .ui-state-active, -.ui-widget-header .ui-state-active, -a.ui-button:active, -.ui-button:active, -.ui-button.ui-state-active:hover { - border: 1px solid #aaaaaa; - background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #212121; -} -.ui-icon-background, -.ui-state-active .ui-icon-background { - border: #aaaaaa; - background-color: #212121; -} -.ui-state-active a, -.ui-state-active a:link, -.ui-state-active a:visited { - color: #212121; - text-decoration: none; -} - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, -.ui-widget-content .ui-state-highlight, -.ui-widget-header .ui-state-highlight { - border: 1px solid #fcefa1; - background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x; - color: #363636; -} -.ui-state-checked { - border: 1px solid #fcefa1; - background: #fbf9ee; -} -.ui-state-highlight a, -.ui-widget-content .ui-state-highlight a, -.ui-widget-header .ui-state-highlight a { - color: #363636; -} -.ui-state-error, -.ui-widget-content .ui-state-error, -.ui-widget-header .ui-state-error { - border: 1px solid #cd0a0a; - background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x; - color: #cd0a0a; -} -.ui-state-error a, -.ui-widget-content .ui-state-error a, -.ui-widget-header .ui-state-error a { - color: #cd0a0a; -} -.ui-state-error-text, -.ui-widget-content .ui-state-error-text, -.ui-widget-header .ui-state-error-text { - color: #cd0a0a; -} -.ui-priority-primary, -.ui-widget-content .ui-priority-primary, -.ui-widget-header .ui-priority-primary { - font-weight: bold; -} -.ui-priority-secondary, -.ui-widget-content .ui-priority-secondary, -.ui-widget-header .ui-priority-secondary { - opacity: .7; - filter:Alpha(Opacity=70); /* support: IE8 */ - font-weight: normal; -} -.ui-state-disabled, -.ui-widget-content .ui-state-disabled, -.ui-widget-header .ui-state-disabled { - opacity: .35; - filter:Alpha(Opacity=35); /* support: IE8 */ - background-image: none; -} -.ui-state-disabled .ui-icon { - filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ -} - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - width: 16px; - height: 16px; -} -.ui-icon, -.ui-widget-content .ui-icon { - background-image: url("images/ui-icons_222222_256x240.png"); -} -.ui-widget-header .ui-icon { - background-image: url("images/ui-icons_222222_256x240.png"); -} -.ui-state-hover .ui-icon, -.ui-state-focus .ui-icon, -.ui-button:hover .ui-icon, -.ui-button:focus .ui-icon { - background-image: url("images/ui-icons_454545_256x240.png"); -} -.ui-state-active .ui-icon, -.ui-button:active .ui-icon { - background-image: url("images/ui-icons_454545_256x240.png"); -} -.ui-state-highlight .ui-icon, -.ui-button .ui-state-highlight.ui-icon { - background-image: url("images/ui-icons_2e83ff_256x240.png"); -} -.ui-state-error .ui-icon, -.ui-state-error-text .ui-icon { - background-image: url("images/ui-icons_cd0a0a_256x240.png"); -} -.ui-button .ui-icon { - background-image: url("images/ui-icons_888888_256x240.png"); -} - -/* positioning */ -.ui-icon-blank { background-position: 16px 16px; } -.ui-icon-caret-1-n { background-position: 0 0; } -.ui-icon-caret-1-ne { background-position: -16px 0; } -.ui-icon-caret-1-e { background-position: -32px 0; } -.ui-icon-caret-1-se { background-position: -48px 0; } -.ui-icon-caret-1-s { background-position: -65px 0; } -.ui-icon-caret-1-sw { background-position: -80px 0; } -.ui-icon-caret-1-w { background-position: -96px 0; } -.ui-icon-caret-1-nw { background-position: -112px 0; } -.ui-icon-caret-2-n-s { background-position: -128px 0; } -.ui-icon-caret-2-e-w { background-position: -144px 0; } -.ui-icon-triangle-1-n { background-position: 0 -16px; } -.ui-icon-triangle-1-ne { background-position: -16px -16px; } -.ui-icon-triangle-1-e { background-position: -32px -16px; } -.ui-icon-triangle-1-se { background-position: -48px -16px; } -.ui-icon-triangle-1-s { background-position: -65px -16px; } -.ui-icon-triangle-1-sw { background-position: -80px -16px; } -.ui-icon-triangle-1-w { background-position: -96px -16px; } -.ui-icon-triangle-1-nw { background-position: -112px -16px; } -.ui-icon-triangle-2-n-s { background-position: -128px -16px; } -.ui-icon-triangle-2-e-w { background-position: -144px -16px; } -.ui-icon-arrow-1-n { background-position: 0 -32px; } -.ui-icon-arrow-1-ne { background-position: -16px -32px; } -.ui-icon-arrow-1-e { background-position: -32px -32px; } -.ui-icon-arrow-1-se { background-position: -48px -32px; } -.ui-icon-arrow-1-s { background-position: -65px -32px; } -.ui-icon-arrow-1-sw { background-position: -80px -32px; } -.ui-icon-arrow-1-w { background-position: -96px -32px; } -.ui-icon-arrow-1-nw { background-position: -112px -32px; } -.ui-icon-arrow-2-n-s { background-position: -128px -32px; } -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } -.ui-icon-arrow-2-e-w { background-position: -160px -32px; } -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } -.ui-icon-arrowstop-1-n { background-position: -192px -32px; } -.ui-icon-arrowstop-1-e { background-position: -208px -32px; } -.ui-icon-arrowstop-1-s { background-position: -224px -32px; } -.ui-icon-arrowstop-1-w { background-position: -240px -32px; } -.ui-icon-arrowthick-1-n { background-position: 1px -48px; } -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } -.ui-icon-arrowthick-1-e { background-position: -32px -48px; } -.ui-icon-arrowthick-1-se { background-position: -48px -48px; } -.ui-icon-arrowthick-1-s { background-position: -64px -48px; } -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } -.ui-icon-arrowthick-1-w { background-position: -96px -48px; } -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } -.ui-icon-arrow-4 { background-position: 0 -80px; } -.ui-icon-arrow-4-diag { background-position: -16px -80px; } -.ui-icon-extlink { background-position: -32px -80px; } -.ui-icon-newwin { background-position: -48px -80px; } -.ui-icon-refresh { background-position: -64px -80px; } -.ui-icon-shuffle { background-position: -80px -80px; } -.ui-icon-transfer-e-w { background-position: -96px -80px; } -.ui-icon-transferthick-e-w { background-position: -112px -80px; } -.ui-icon-folder-collapsed { background-position: 0 -96px; } -.ui-icon-folder-open { background-position: -16px -96px; } -.ui-icon-document { background-position: -32px -96px; } -.ui-icon-document-b { background-position: -48px -96px; } -.ui-icon-note { background-position: -64px -96px; } -.ui-icon-mail-closed { background-position: -80px -96px; } -.ui-icon-mail-open { background-position: -96px -96px; } -.ui-icon-suitcase { background-position: -112px -96px; } -.ui-icon-comment { background-position: -128px -96px; } -.ui-icon-person { background-position: -144px -96px; } -.ui-icon-print { background-position: -160px -96px; } -.ui-icon-trash { background-position: -176px -96px; } -.ui-icon-locked { background-position: -192px -96px; } -.ui-icon-unlocked { background-position: -208px -96px; } -.ui-icon-bookmark { background-position: -224px -96px; } -.ui-icon-tag { background-position: -240px -96px; } -.ui-icon-home { background-position: 0 -112px; } -.ui-icon-flag { background-position: -16px -112px; } -.ui-icon-calendar { background-position: -32px -112px; } -.ui-icon-cart { background-position: -48px -112px; } -.ui-icon-pencil { background-position: -64px -112px; } -.ui-icon-clock { background-position: -80px -112px; } -.ui-icon-disk { background-position: -96px -112px; } -.ui-icon-calculator { background-position: -112px -112px; } -.ui-icon-zoomin { background-position: -128px -112px; } -.ui-icon-zoomout { background-position: -144px -112px; } -.ui-icon-search { background-position: -160px -112px; } -.ui-icon-wrench { background-position: -176px -112px; } -.ui-icon-gear { background-position: -192px -112px; } -.ui-icon-heart { background-position: -208px -112px; } -.ui-icon-star { background-position: -224px -112px; } -.ui-icon-link { background-position: -240px -112px; } -.ui-icon-cancel { background-position: 0 -128px; } -.ui-icon-plus { background-position: -16px -128px; } -.ui-icon-plusthick { background-position: -32px -128px; } -.ui-icon-minus { background-position: -48px -128px; } -.ui-icon-minusthick { background-position: -64px -128px; } -.ui-icon-close { background-position: -80px -128px; } -.ui-icon-closethick { background-position: -96px -128px; } -.ui-icon-key { background-position: -112px -128px; } -.ui-icon-lightbulb { background-position: -128px -128px; } -.ui-icon-scissors { background-position: -144px -128px; } -.ui-icon-clipboard { background-position: -160px -128px; } -.ui-icon-copy { background-position: -176px -128px; } -.ui-icon-contact { background-position: -192px -128px; } -.ui-icon-image { background-position: -208px -128px; } -.ui-icon-video { background-position: -224px -128px; } -.ui-icon-script { background-position: -240px -128px; } -.ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-notice { background-position: -32px -144px; } -.ui-icon-help { background-position: -48px -144px; } -.ui-icon-check { background-position: -64px -144px; } -.ui-icon-bullet { background-position: -80px -144px; } -.ui-icon-radio-on { background-position: -96px -144px; } -.ui-icon-radio-off { background-position: -112px -144px; } -.ui-icon-pin-w { background-position: -128px -144px; } -.ui-icon-pin-s { background-position: -144px -144px; } -.ui-icon-play { background-position: 0 -160px; } -.ui-icon-pause { background-position: -16px -160px; } -.ui-icon-seek-next { background-position: -32px -160px; } -.ui-icon-seek-prev { background-position: -48px -160px; } -.ui-icon-seek-end { background-position: -64px -160px; } -.ui-icon-seek-start { background-position: -80px -160px; } -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ -.ui-icon-seek-first { background-position: -80px -160px; } -.ui-icon-stop { background-position: -96px -160px; } -.ui-icon-eject { background-position: -112px -160px; } -.ui-icon-volume-off { background-position: -128px -160px; } -.ui-icon-volume-on { background-position: -144px -160px; } -.ui-icon-power { background-position: 0 -176px; } -.ui-icon-signal-diag { background-position: -16px -176px; } -.ui-icon-signal { background-position: -32px -176px; } -.ui-icon-battery-0 { background-position: -48px -176px; } -.ui-icon-battery-1 { background-position: -64px -176px; } -.ui-icon-battery-2 { background-position: -80px -176px; } -.ui-icon-battery-3 { background-position: -96px -176px; } -.ui-icon-circle-plus { background-position: 0 -192px; } -.ui-icon-circle-minus { background-position: -16px -192px; } -.ui-icon-circle-close { background-position: -32px -192px; } -.ui-icon-circle-triangle-e { background-position: -48px -192px; } -.ui-icon-circle-triangle-s { background-position: -64px -192px; } -.ui-icon-circle-triangle-w { background-position: -80px -192px; } -.ui-icon-circle-triangle-n { background-position: -96px -192px; } -.ui-icon-circle-arrow-e { background-position: -112px -192px; } -.ui-icon-circle-arrow-s { background-position: -128px -192px; } -.ui-icon-circle-arrow-w { background-position: -144px -192px; } -.ui-icon-circle-arrow-n { background-position: -160px -192px; } -.ui-icon-circle-zoomin { background-position: -176px -192px; } -.ui-icon-circle-zoomout { background-position: -192px -192px; } -.ui-icon-circle-check { background-position: -208px -192px; } -.ui-icon-circlesmall-plus { background-position: 0 -208px; } -.ui-icon-circlesmall-minus { background-position: -16px -208px; } -.ui-icon-circlesmall-close { background-position: -32px -208px; } -.ui-icon-squaresmall-plus { background-position: -48px -208px; } -.ui-icon-squaresmall-minus { background-position: -64px -208px; } -.ui-icon-squaresmall-close { background-position: -80px -208px; } -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } -.ui-icon-grip-solid-vertical { background-position: -32px -224px; } -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.ui-icon-grip-diagonal-se { background-position: -80px -224px; } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, -.ui-corner-top, -.ui-corner-left, -.ui-corner-tl { - border-top-left-radius: 4px; -} -.ui-corner-all, -.ui-corner-top, -.ui-corner-right, -.ui-corner-tr { - border-top-right-radius: 4px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-left, -.ui-corner-bl { - border-bottom-left-radius: 4px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-right, -.ui-corner-br { - border-bottom-right-radius: 4px; -} - -/* Overlays */ -.ui-widget-overlay { - background: #aaaaaa; - opacity: .3; - filter: Alpha(Opacity=30); /* support: IE8 */ -} -.ui-widget-shadow { - -webkit-box-shadow: -8px -8px 8px #aaaaaa; - box-shadow: -8px -8px 8px #aaaaaa; -} diff --git a/platypush/backend/http/static/css/map.css b/platypush/backend/http/static/css/map.css deleted file mode 100644 index b23160e8..00000000 --- a/platypush/backend/http/static/css/map.css +++ /dev/null @@ -1,9 +0,0 @@ -#map { - width: 100%; - height: 100%; - position: static !important; - margin: 0; - padding: 0; - overflow: hidden; -} - diff --git a/platypush/backend/http/static/css/media.css b/platypush/backend/http/static/css/media.css deleted file mode 100644 index f0959643..00000000 --- a/platypush/backend/http/static/css/media.css +++ /dev/null @@ -1,180 +0,0 @@ -#video-search { - max-width: 60em; - margin: 1em auto; -} - - #video-search input[type=text] { - width: 100%; - } - -form#video-ctrl { - text-align: center; -} - form#video-ctrl * > button[data-modal="#media-subtitles-modal"] { - display: none; - } - -#video-seeker-container { - margin-top: 0.5em; - margin-bottom: 1em; -} - -#video-volume-ctrl-container { - margin-top: 1em; -} - -#video-results { - padding: 1.5rem 1.5rem 0 .5rem; - background: #f8f8f8; -} - - .video-result { - padding: 5px; - letter-spacing: .1rem; - line-height: 3.3rem; - cursor: pointer; - } - - .video-icon-container { - font-size: 2rem; - margin-right: 2rem; - } - - .video-result.selected { - background-color: #c8ffd0 !important; - } - - .video-result:hover { - background-color: #daf8e2 !important; - } - - .video-result:nth-child(odd) { - background-color: #f2f2f2; - } - - .video-result.active { - height: 4rem; - padding-top: 1.5rem; - font-size: 1.7rem; - border-radius: 10px; - animation: active-track 5s; - -moz-animation: active-track 5s infinite; - -webkit-animation: active-track 5s infinite; - } - - @keyframes active-track { - 0% { background: #d4ffe3; } - 50% { background: #9cdfb0; } - 100% { background: #d4ffe3; } - } - - @-moz-keyframes active-track { - 0% { background: #d4ffe3; } - 50% { background: #9cdfb0; } - 100% { background: #d4ffe3; } - } - - @-webkit-keyframes active-track { - 0% { background: #d4ffe3; } - 50% { background: #9cdfb0; } - 100% { background: #d4ffe3; } - } - - button.remote[data-panel="#media-devices-panel"] { - color: #34b868; - } - -#media-devices-panel, -#media-item-panel { - display: none; - position: absolute; - padding: 1rem; - background: #f0f0f0; - z-index: 10; - border: 1px solid #d0d0d0; - border-radius: 5px; - min-width: 10em; -} - - #media-devices-panel .refresh-devices-container { - position: relative; - height: 1em; - } - - #media-devices-panel * > .refresh-devices { - text-align: right; - cursor: pointer; - position: absolute; - top: -.5rem; - right: .5rem; - } - - #media-devices-panel * > .cast-device, - #media-item-panel > .media-item-action { - padding: 0.5rem; - cursor: pointer; - } - - #media-devices-panel * > .cast-device-local { - border-bottom: 1px solid #ddd; - margin-bottom: 0.25rem; - } - - #media-devices-panel * > .cast-device.selected { - font-weight: bold; - color: #34b868; - } - - #media-devices-panel * > .cast-device.disabled, - #media-devices-panel * > .refresh-devices.disabled, - #media-item-panel > .media-item-action.disabled { - cursor: default; - color: #999 !important; - } - - #media-devices-panel * > .cast-device:hover, - #media-item-panel > .media-item-action:hover { - background-color: #daf8e2 !important; - } - - #media-devices-panel * > .cast-device-icon - #media-item-panel > .media-item-item { - color: #666; - } - - #media-subtitles-modal * > .media-subtitles-results-container { - display: none; - padding: .75rem; - } - - #media-subtitles-modal * > .media-subtitles-results-header { - background: #eee; - margin-bottom: 1rem; - padding: 1rem .25rem; - border: 1px solid #ccc; - } - - #media-subtitles-modal * > .media-subtitles-results { - padding: .75rem; - } - - #media-subtitles-modal * > .media-subtitles-message { - display: none; - } - - #media-subtitles-modal * > .media-subtitle-container { - cursor: pointer; - } - - #media-subtitles-modal * > .media-subtitle-container:nth-child(odd) { - background-color: #f2f2f2; - } - - #media-subtitles-modal * > .media-subtitle-container.selected { - background-color: #c8ffd0 !important; - } - - #media-subtitles-modal * > .media-subtitle-container:hover { - background-color: #daf8e2 !important; - } - diff --git a/platypush/backend/http/static/css/music.mpd.css b/platypush/backend/http/static/css/music.mpd.css deleted file mode 100644 index 7c8def7b..00000000 --- a/platypush/backend/http/static/css/music.mpd.css +++ /dev/null @@ -1,173 +0,0 @@ -#player-left-side { - overflow-y: hidden; -} - -#player-right-side { - margin-left: 0; - width: 78%; -} - - .playback-controls { - text-align: center; - border-bottom: 1px solid #e8eaf0; - padding-bottom: 12px; - } - - .playback-controls * > button.enabled { - color: #59df3e; - } - - - .track-info { - text-align: center; - margin: -20px -20px 0 -20px; - padding: 10px 20px; - } - - .track-info > .artist { - font-weight: bold; - display: block; - } - -#playlist-controls, #browser-controls { - margin-bottom: 7.5px; - padding-bottom: 7.5px; - border-bottom: 1px solid #ddd; - height: 3.8rem; -} - -#playlist-controls { - text-align: right; -} - -#playlist-content, #music-browser { - height: 27.2rem; - overflow-y: scroll; -} - -#music-browser { - padding-top: 0; -} - -#browser-filter { - width: 95%; - margin-bottom: 1.5rem; -} - -#playlist-filter-container { - height: 5rem; -} - -#playlist-filter { - width: 100%; - margin-bottom: 1.5rem; -} - -.music-pane { - height: 40rem; - padding: 15px 15px 0 5px; - background: #f8f8f8; -} - - .music-item { - padding: 5px; - cursor: pointer; - } - - .music-item.selected { - background-color: #c8ffd0 !important; - } - - .music-item:hover { - background-color: #daf8e2 !important; - } - - .music-item:nth-child(odd) { - background-color: #f2f2f2; - } - - .playlist-track.active { - height: 4rem; - padding-top: 1.5rem; - font-size: 1.7rem; - border-radius: 10px; - animation: active-track 5s; - -moz-animation: active-track 5s infinite; - -webkit-animation: active-track 5s infinite; - } - - @keyframes active-track { - 0% { background: #d4ffe3; } - 50% { background: #9cdfb0; } - 100% { background: #d4ffe3; } - } - - @-moz-keyframes active-track { - 0% { background: #d4ffe3; } - 50% { background: #9cdfb0; } - 100% { background: #d4ffe3; } - } - - @-webkit-keyframes active-track { - 0% { background: #d4ffe3; } - 50% { background: #9cdfb0; } - 100% { background: #d4ffe3; } - } - - .playlist-track > .track-time { - text-align: right; - color: #666; - } - -#track-seeker-container { - margin-bottom: 10px; -} - -#volume-ctrl-container { - margin-top: 15px; -} - -#music-search-form { - margin-bottom: 1rem; -} - - #music-search-form * > input[type=text] { - width: 100%; - } - - #music-search-form > .row { - padding: 0.5rem; - } - - .music-form-bottom { - text-align: right; - margin-top: 2rem; - border-top: 1px solid #ddd; - } - - .music-form-bottom input { - margin-top: 2rem; - } - -#music-search-results-form { - display: none; - margin-top: -2rem; -} - -#music-search-results-container { - display: none; - max-height: 50rem; - margin-top: -1.4rem; - overflow-y: auto; - overflow-x: hidden; -} - - #music-search-results-head { - padding: 0.5rem 1rem; - background-color: #eaeaea; - border-radius: 5px 5px 0 0; - border-bottom: 1px solid #bbb; - letter-spacing: .1rem; - line-height: 3.5rem; - } - diff --git a/platypush/backend/http/static/css/music.snapcast.css b/platypush/backend/http/static/css/music.snapcast.css deleted file mode 100644 index 5cb18d1d..00000000 --- a/platypush/backend/http/static/css/music.snapcast.css +++ /dev/null @@ -1,153 +0,0 @@ -.snapcast-host-container { - min-width: 40em; - max-width: 80em; - margin: 1em auto; - background: rgba(245,245,245,0.6); - border: 1px solid rgba(220,220,220,1.0); - border-radius: 10px; -} - -.snapcast-host-header { - border-bottom: 1px solid rgba(220,220,220,1.0); - font-size: 1em; - text-transform: uppercase; - padding: 0 .5em; -} - - .snapcast-host-header h1 { - font-size: 1.9em; - } - -.snapcast-group-header { - padding: .5em; - margin: 0 1.4em 1.8em .2em; - border-bottom: 1px solid #e8e8e8; -} - - .snapcast-settings-btn { - cursor: pointer; - padding: .2em; - } - - .snapcast-settings-btn:hover { - border: .05em solid #e0e0e0; - padding: .15em; - } - - .snapcast-group-header h2 { - font-size: 1.5em; - margin-top: .6em; - } - -.snapcast-client-disconnected { - color: rgba(0, 0, 0, 0.35); -} - -.snapcast-client-row { - padding: 0 1.4em; - margin: 1.5em .5em; -} - - .snapcast-client-row h3 { - font-size: 1.2em; - } - -.snapcast-client-mute-toggle { - margin-top: -1.2em; -} - -.snapcast-form { - margin-bottom: 1rem; -} - - .snapcast-form * > input[type=text] { - width: 100%; - } - - .snapcast-form > .row { - padding: 0.5rem; - } - - .snapcast-form-bottom { - text-align: right; - margin-top: 2rem; - border-top: 1px solid #ddd; - } - - .snapcast-form-bottom input { - margin-top: 2rem; - } - - .snapcast-form * > label { - transform: translateY(25%); - } - - .snapcast-form > .snapcast-client-info, - .snapcast-form > .snapcast-host-info, - .snapcast-form > .snapcast-group-clients-container { - margin: 1em auto .5em auto; - padding: 2em; - background: rgba(240,240,240,0.6); - border-radius: 10px; - border: .05em solid rgba(225,225,225,1.0) - } - - .snapcast-form > .snapcast-group-clients-container { - max-width: 40em; - } - - .snapcast-form > .snapcast-host-info, - .snapcast-form > .snapcast-client-info { - max-width: 70%; - } - - .snapcast-form > .snapcast-host-info > .row, - .snapcast-form > .snapcast-client-info > .row { - padding: .2em; - } - - .snapcast-form > .snapcast-host-info > .row:hover, - .snapcast-form > .snapcast-client-info > .row:hover { - background-color: #daf8e2 !important; - } - - .snapcast-form > .snapcast-host-info * > .info-name, - .snapcast-form > .snapcast-client-info * > .info-name { - font-weight: bold; - } - - .snapcast-form > .snapcast-host-info * > .info-value, - .snapcast-form > .snapcast-client-info * > .info-value { - text-align: right; - } - - .snapcast-form > .snapcast-group-stream, - .snapcast-form > .snapcast-client-delete { - width: 30%; - margin: 1em auto 0 auto; - text-align: center; - } - - .snapcast-form > .snapcast-group-stream > label, - .snapcast-form > .snapcast-client-delete > label { - display: inline; - margin-left: .5em; - text-align: right; - } - - .snapcast-form > .snapcast-client-delete > label { - color: rgba(200, 44, 23, 1.0); - } - - .snapcast-form * > .snapcast-group-clients { - max-width: 15em; - margin: auto; - text-align: right; - } - - .snapcast-form * > .snapcast-group-clients * > label { - display: inline; - float: right; - width: 80%; - } - diff --git a/platypush/backend/http/static/css/source/dashboard/index.scss b/platypush/backend/http/static/css/source/dashboard/index.scss new file mode 100644 index 00000000..79f7e82f --- /dev/null +++ b/platypush/backend/http/static/css/source/dashboard/index.scss @@ -0,0 +1,49 @@ +@import 'common/vars'; + +@import 'common/mixins'; +@import 'common/layout'; +@import 'common/elements'; +@import 'common/animations'; +@import 'common/modal'; +@import 'common/notifications'; + +$background-image: url('/img/dashboard-background.jpg') !default; +$background-color: white !default; +$font-family: Lato !default; + +html { + min-height: 100%; +} + +body { + --background-image: $background-image; + --background-color: $background-color; + + background-image: var(--background-image); + background-color: var(--background-color); + background-size: 100% 100%; + background-repeat: no-repeat; + font-family: $font-family; +} + +main { + display: flex; + flex-flow: column; + width: 100%; + height: 100%; + margin: 0; + + .widgets-row { + margin: 2em 1em; + display: flex; + } + + .widget { + background: $background-color; + border-radius: 5px; + height: 22.5em; + overflow: hidden; + box-shadow: 0 3px 3px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08); + } +} + diff --git a/platypush/backend/http/static/css/source/dashboard/widgets/calendar/index.scss b/platypush/backend/http/static/css/source/dashboard/widgets/calendar/index.scss new file mode 100644 index 00000000..57f37753 --- /dev/null +++ b/platypush/backend/http/static/css/source/dashboard/widgets/calendar/index.scss @@ -0,0 +1,16 @@ +@import 'common/vars'; + +.widget .calendar { + padding: 1rem; + + .upcoming-event { + text-align: center; + margin-bottom: 1.5rem; + + .summary { + text-transform: uppercase; + font-size: 1.35em; + } + } +} + diff --git a/platypush/backend/http/static/css/source/dashboard/widgets/date-time-weather/index.scss b/platypush/backend/http/static/css/source/dashboard/widgets/date-time-weather/index.scss new file mode 100644 index 00000000..c2713883 --- /dev/null +++ b/platypush/backend/http/static/css/source/dashboard/widgets/date-time-weather/index.scss @@ -0,0 +1,48 @@ +@import 'common/vars'; + +.widget .date-time-weather { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + padding-top: 1rem; + + .date { + font-size: 1.15em; + height: 8%; + } + + .time { + font-size: 1.7em; + height: 11%; + } + + .weather { + height: 40%; + display: flex; + align-items: center; + + .temperature { + font-size: 2em; + margin-left: 1rem; + } + } + + .summary { + height: 28%; + } + + .sensors { + width: 100%; + height: 13%; + + .sensor { + padding: 0 1rem; + } + + .humidity { + text-align: right; + } + } +} + diff --git a/platypush/backend/http/static/css/source/dashboard/widgets/image-carousel/index.scss b/platypush/backend/http/static/css/source/dashboard/widgets/image-carousel/index.scss new file mode 100644 index 00000000..346aa94d --- /dev/null +++ b/platypush/backend/http/static/css/source/dashboard/widgets/image-carousel/index.scss @@ -0,0 +1,30 @@ +@import 'common/vars'; + +.widget .image-carousel { + height: 100%; + position: relative; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0,0,0,0); + + .background { + position: absolute; + top: 0; + width: 100%; + height: 100vh; + background-color: rgba(0,0,0,0); + background-position: center; + background-size: cover; + background-repeat: no-repeat; + filter: blur(13px); + -webkit-filter: blur(13px); + } + + img { + position: absolute; + max-height: 100%; + z-index: 2; + } +} + diff --git a/platypush/backend/http/static/css/source/dashboard/widgets/music/index.scss b/platypush/backend/http/static/css/source/dashboard/widgets/music/index.scss new file mode 100644 index 00000000..6d043c61 --- /dev/null +++ b/platypush/backend/http/static/css/source/dashboard/widgets/music/index.scss @@ -0,0 +1,92 @@ +@import 'common/vars'; + +$progress-bar-bg: #ddd; +$playback-status-color: #757f70; + +.widget .music { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + + .track { + text-align: center; + + .unknown, + .no-track { + font-size: 1.5em; + } + + .artist { + font-size: 1.4em; + font-weight: bold; + margin-bottom: .25em; + } + + .title { + font-size: 1.25em; + } + } + + .time { + width: 100%; + margin-top: .75em; + + .row { + padding: 0 .5em; + } + + .time-total { + text-align: right; + } + + .progress-bar { + width: 100%; + height: 1em; + position: relative; + margin-bottom: .75em; + + .total { + position: absolute; + width: 100%; + height: 100%; + top: 0; + background: $progress-bar-bg; + border-radius: 5rem; + } + + .elapsed { + position: absolute; + width: 100%; + height: 100%; + top: 0; + background: $selected-bg; + border-radius: 5rem; + z-index: 1; + } + } + } + + .playback-status { + position: absolute; + bottom: 0; + border-top: $default-border-2; + color: $playback-status-color; + width: 100%; + height: 2em; + + .status-property { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + } + + .active { + color: $default-hover-fg; + } + } +} + diff --git a/platypush/backend/http/static/css/source/dashboard/widgets/rss-news/index.scss b/platypush/backend/http/static/css/source/dashboard/widgets/rss-news/index.scss new file mode 100644 index 00000000..53b38324 --- /dev/null +++ b/platypush/backend/http/static/css/source/dashboard/widgets/rss-news/index.scss @@ -0,0 +1,29 @@ +@import 'common/vars'; + +.widget .rss-news { + height: 100%; + display: flex; + align-items: center; + + .article { + width: 100%; + padding: 0 2em; + + .source { + font-size: 1.25em; + font-weight: bold; + margin-bottom: .5em; + } + + .title { + font-size: 1.25em; + margin-bottom: .5em; + } + + .published { + text-align: right; + font-size: .9em; + } + } +} + diff --git a/platypush/backend/http/static/css/switch.switchbot.css b/platypush/backend/http/static/css/switch.switchbot.css deleted file mode 100644 index d0367404..00000000 --- a/platypush/backend/http/static/css/switch.switchbot.css +++ /dev/null @@ -1,29 +0,0 @@ -#switchbot-container { - background-color: #f8f8f8; - padding: 12px; - border: 1px solid #ddd; - border-radius: 10px; - font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: 400; - line-height: 38px; - letter-spacing: .1rem; -} - -.switchbot-device { - padding: 1.5em 1em .2em .5em; - border-radius: 10px; -} - - .switchbot-device:nth-of-type(odd) { - background-color: #ececec; - } - - .switchbot-device:hover { - background-color: #daf8e2 !important; - } - -.toggle-container { - text-align: right; - padding-right: 1em; -} - diff --git a/platypush/backend/http/static/css/switch.tplink.css b/platypush/backend/http/static/css/switch.tplink.css deleted file mode 100644 index 4ab62f91..00000000 --- a/platypush/backend/http/static/css/switch.tplink.css +++ /dev/null @@ -1,29 +0,0 @@ -#tplink-container { - background-color: #f8f8f8; - padding: 12px; - border: 1px solid #ddd; - border-radius: 10px; - font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: 400; - line-height: 38px; - letter-spacing: .1rem; -} - -.tplink-device { - padding: 1.5em 1em .2em .5em; - border-radius: 10px; -} - - .tplink-device:nth-of-type(odd) { - background-color: #ececec; - } - - .tplink-device:hover { - background-color: #daf8e2 !important; - } - -.toggle-container { - text-align: right; - padding-right: 1em; -} - diff --git a/platypush/backend/http/static/css/switch.wemo.css b/platypush/backend/http/static/css/switch.wemo.css deleted file mode 100644 index ba849c87..00000000 --- a/platypush/backend/http/static/css/switch.wemo.css +++ /dev/null @@ -1,29 +0,0 @@ -#wemo-container { - background-color: #f8f8f8; - padding: 12px; - border: 1px solid #ddd; - border-radius: 10px; - font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: 400; - line-height: 38px; - letter-spacing: .1rem; -} - -.wemo-device { - padding: 1.5em 1em .2em .5em; - border-radius: 10px; -} - - .wemo-device:nth-of-type(odd) { - background-color: #ececec; - } - - .wemo-device:hover { - background-color: #daf8e2 !important; - } - -.toggle-container { - text-align: right; - padding-right: 1em; -} - diff --git a/platypush/backend/http/static/css/tts.css b/platypush/backend/http/static/css/tts.css deleted file mode 100644 index 2714662e..00000000 --- a/platypush/backend/http/static/css/tts.css +++ /dev/null @@ -1,9 +0,0 @@ -#tts-container { - max-width: 60em; - margin: 3em auto; -} - - #tts-form input[type=text] { - width: 100%; - } - diff --git a/platypush/backend/http/static/css/widgets/calendar.css b/platypush/backend/http/static/css/widgets/calendar.css deleted file mode 100644 index a01415ea..00000000 --- a/platypush/backend/http/static/css/widgets/calendar.css +++ /dev/null @@ -1,30 +0,0 @@ -.calendar-container { - padding: 2rem; - overflow: hidden; -} - - .calendar-container * > .date, - .calendar-container * > .time { - font-size: 0.9em; - } - -.calendar-next-event-container { - width: 70%; - margin: auto; - text-align: center; -} - - .calendar-next-event-container > .summary { - font-size: 1.5em; - text-transform: uppercase; - } - -.calendar-events-list-container { - margin-top: 2.5rem; -} - - .calendar-event > .date, - .calendar-event > .time { - padding-top: .15rem; - } - diff --git a/platypush/backend/http/static/css/widgets/date-time-weather.css b/platypush/backend/http/static/css/widgets/date-time-weather.css deleted file mode 100644 index 47f6c229..00000000 --- a/platypush/backend/http/static/css/widgets/date-time-weather.css +++ /dev/null @@ -1,50 +0,0 @@ -.widget.date-time-weather { - text-align: center; -} - -.date-time-weather-container { - padding: 2rem; - position: relative; - min-height: 20.5em; -} - -#weather-icon { - margin-left: 2rem; -} - -h1.temperature { - font-size: 45px; - margin: 4rem 2rem; - font-weight: bold; -} - - h1.temperature > [data-bind=temperature] { - margin-left: -2.5rem; - } - - .widget.date-time-weather * > .time { - font-size: 22px; - } - - .widget.date-time-weather * > .sensors { - position: absolute; - bottom: 2rem; - width: 90%; - margin-left: -2rem; - padding-left: 1rem; - } - - .widget.date-time-weather * > .sensor-temperature, - .widget.date-time-weather * > .sensor-humidity { - display: none; - font-weight: bold; - } - - .widget.date-time-weather * > .sensor-temperature { - text-align: left; - } - - .widget.date-time-weather * > .sensor-humidity { - text-align: right; - } - diff --git a/platypush/backend/http/static/css/widgets/image-carousel.css b/platypush/backend/http/static/css/widgets/image-carousel.css deleted file mode 100644 index ced953ea..00000000 --- a/platypush/backend/http/static/css/widgets/image-carousel.css +++ /dev/null @@ -1,33 +0,0 @@ -.carousel { - overflow: hidden; - color: #999; - position: relative; - width: 100%; - height: 22.5em; -} - -.carousel > img { - position: absolute; - top: 0; - left: 0; - right: 0; - margin-left: auto; - margin-right: auto; - border-radius: 5px; - z-index: 2; -} - -.carousel-background { - position: absolute; - width: 100%; - height: 22.5em; - top: 0; - left: 0; - background-color: black; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; - filter: blur(10px); - z-index: 1; -} - diff --git a/platypush/backend/http/static/css/widgets/music.css b/platypush/backend/http/static/css/widgets/music.css deleted file mode 100644 index 126c625f..00000000 --- a/platypush/backend/http/static/css/widgets/music.css +++ /dev/null @@ -1,68 +0,0 @@ -.music-container { - position: relative; - width: inherit; - height: inherit; - display: table-cell; - vertical-align: middle; - text-align: center; -} - -.track-info { - font-size: 20px; -} - - .track-info > .artist { - font-weight: bold; - margin-bottom: 1rem; - } - - .track-info > .title { - margin-bottom: 2rem; - } - -.time-bar { - height: 7.5px; - background: #ddd; - margin: 7.5px; - border-radius: 10px; -} - - .time-bar > .elapsed { - height: 7.5px; - background: #98ffb0; - border-radius: 10px; - } - -.time-elapsed { - text-align: left; - margin-left: 1rem; -} - -.time-total { - float: right; - margin-right: 1rem; -} - -.time-elapsed, .time-total { - color: rgba(0, 0, 0, 0.7); - letter-spacing: 1px; -} - -.no-track-info { - font-size: 25px; -} - -.playback-status-container { - position: absolute; - bottom: 0; - width: 100%; - border-top: 1px solid #ddd; - padding: 1rem 0; - font-size: 1.22rem; - color: #757f70; -} - - .playback-status-container > .playback-status-values { - font-weight: bold; - } - diff --git a/platypush/backend/http/static/css/widgets/rss-news.css b/platypush/backend/http/static/css/widgets/rss-news.css deleted file mode 100644 index 6c5e1afc..00000000 --- a/platypush/backend/http/static/css/widgets/rss-news.css +++ /dev/null @@ -1,28 +0,0 @@ -.news-container { - position: relative; - height: 100%; -} - - .news-container > .article { - width: 80%; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - - .news-container > .article > .source { - font-weight: bold; - margin-bottom: 1rem; - font-size: 1.7em; - } - - .news-container > .article > .title { - font-size: 1.5em; - } - - .news-container > .article > .publish-time { - margin-top: 1rem; - float: right; - } - diff --git a/platypush/backend/http/static/js/dashboard.js b/platypush/backend/http/static/js/dashboard.js index 63b26218..1bba3fe0 100644 --- a/platypush/backend/http/static/js/dashboard.js +++ b/platypush/backend/http/static/js/dashboard.js @@ -1,46 +1,75 @@ -$(document).ready(function() { - var onEvent = function(event) { - if (event.args.type == 'platypush.message.event.web.widget.WidgetUpdateEvent') { - var $widget = $('#' + event.args.widget); - delete event.args.widget; - - for (var key of Object.keys(event.args)) { - $widget.find('[data-bind=' + key + ']').text(event.args[key]); - } - } else if (event.args.type == 'platypush.message.event.web.DashboardIframeUpdateEvent') { - var url = event.args.url; - var $modal = $('#iframe-modal'); - var $iframe = $modal.find('iframe'); - $iframe.attr('src', url); - $iframe.prop('width', event.args.width || '100%'); - $iframe.prop('height', event.args.height || '600'); - - if ('timeout' in event.args) { - setTimeout(function() { - $iframe.removeAttr('src'); - $modal.fadeOut(); - }, parseFloat(event.args.timeout) * 1000); - } - - $modal.fadeIn(); - } - }; - - var initDashboard = function() { - if ('background_image' in window.config) { - $('body').css('background-image', 'url(' + window.config.background_image + ')'); - } - }; - - var initEvents = function() { - window.registerEventListener(onEvent); - }; - - var init = function() { - initDashboard(); - initEvents(); - }; - - init(); +Vue.component('widget', { + template: '#tmpl-widget', + props: ['config','tag'], }); +// Declaration of the main vue app +window.vm = new Vue({ + el: '#app', + + props: { + config: { + type: Object, + default: () => window.config, + }, + }, + + data: function() { + return { + iframeModal: { + visible: false, + }, + }; + }, + + created: function() { + initEvents(); + }, +}); + +// $(document).ready(function() { +// var onEvent = function(event) { +// if (event.args.type == 'platypush.message.event.web.widget.WidgetUpdateEvent') { +// var $widget = $('#' + event.args.widget); +// delete event.args.widget; + +// for (var key of Object.keys(event.args)) { +// $widget.find('[data-bind=' + key + ']').text(event.args[key]); +// } +// } else if (event.args.type == 'platypush.message.event.web.DashboardIframeUpdateEvent') { +// var url = event.args.url; +// var $modal = $('#iframe-modal'); +// var $iframe = $modal.find('iframe'); +// $iframe.attr('src', url); +// $iframe.prop('width', event.args.width || '100%'); +// $iframe.prop('height', event.args.height || '600'); + +// if ('timeout' in event.args) { +// setTimeout(function() { +// $iframe.removeAttr('src'); +// $modal.fadeOut(); +// }, parseFloat(event.args.timeout) * 1000); +// } + +// $modal.fadeIn(); +// } +// }; + +// var initDashboard = function() { +// if (window.config.dashboard.background_image) { +// $('body').css('background-image', 'url(' + window.config.dashboard.background_image + ')'); +// } +// }; + +// var initEvents = function() { +// registerEventListener(onEvent); +// }; + +// var init = function() { +// initDashboard(); +// initEvents(); +// }; + +// init(); +// }); + diff --git a/platypush/backend/http/static/js/dashboard_old.js b/platypush/backend/http/static/js/dashboard_old.js new file mode 100644 index 00000000..63b26218 --- /dev/null +++ b/platypush/backend/http/static/js/dashboard_old.js @@ -0,0 +1,46 @@ +$(document).ready(function() { + var onEvent = function(event) { + if (event.args.type == 'platypush.message.event.web.widget.WidgetUpdateEvent') { + var $widget = $('#' + event.args.widget); + delete event.args.widget; + + for (var key of Object.keys(event.args)) { + $widget.find('[data-bind=' + key + ']').text(event.args[key]); + } + } else if (event.args.type == 'platypush.message.event.web.DashboardIframeUpdateEvent') { + var url = event.args.url; + var $modal = $('#iframe-modal'); + var $iframe = $modal.find('iframe'); + $iframe.attr('src', url); + $iframe.prop('width', event.args.width || '100%'); + $iframe.prop('height', event.args.height || '600'); + + if ('timeout' in event.args) { + setTimeout(function() { + $iframe.removeAttr('src'); + $modal.fadeOut(); + }, parseFloat(event.args.timeout) * 1000); + } + + $modal.fadeIn(); + } + }; + + var initDashboard = function() { + if ('background_image' in window.config) { + $('body').css('background-image', 'url(' + window.config.background_image + ')'); + } + }; + + var initEvents = function() { + window.registerEventListener(onEvent); + }; + + var init = function() { + initDashboard(); + initEvents(); + }; + + init(); +}); + diff --git a/platypush/backend/http/static/js/map.js b/platypush/backend/http/static/js/map.js deleted file mode 100644 index f8cf7dbc..00000000 --- a/platypush/backend/http/static/js/map.js +++ /dev/null @@ -1,201 +0,0 @@ -function initMapFromGeopoints(points) { - var $mapContainer = $('#map-container'), - $map = $('#map'); - - var markerArray = []; - var directionsService = new google.maps.DirectionsService; - - var map = new google.maps.Map($map.get(0), { - mapTypeId: google.maps.MapTypeId.ROADMAP, - }); - - var bounds = new google.maps.LatLngBounds(); - var infowindow = new google.maps.InfoWindow(); - var maxPoints = 22; - - var minDist = 100; - var start = points.length > maxPoints ? points.length-maxPoints-1 : 0; - var lastPoint; - - for (i = points.length-1; i >= 0 && markerArray.length <= maxPoints; i--) { - if (lastPoint && latLngDistance( - [points[i]['latitude'], points[i]['longitude']], - [lastPoint['latitude'], lastPoint['longitude']]) < minDist) { - continue; - } - - lastPoint = points[i]; - var marker = new google.maps.Marker({ - position: new google.maps.LatLng(points[i]['latitude'], points[i]['longitude']), - map: map - }); - - google.maps.event.addListener(marker, 'click', (function(marker, i) { - return function() { - infowindow.setContent( - (points[i]['address'] || '[No address]') + ' @ ' + - Date.parse(points[i]['created_at']).toLocaleString()); - infowindow.open(map, marker); - } - })(marker, i)); - - // Extend the bounds to include each marker's position - bounds.extend(marker.position); - markerArray.push(marker); - } - - var listener = google.maps.event.addListener(map, 'idle', function () { - // Now fit the map to the newly inclusive bounds - map.fitBounds(bounds); - setTimeout(function() { - if (window.zoom) { - map.setZoom(window.zoom); - } else { - map.setZoom(getBoundsZoomLevel(bounds, $map.children().width(), $map.children().height())); - } - }, 1000); - - google.maps.event.removeListener(listener); - }); - - // Create a renderer for directions and bind it to the map. - var directionsDisplay = new google.maps.DirectionsRenderer({map: map}); - - // Instantiate an info window to hold step text. - var stepDisplay = new google.maps.InfoWindow; - - // Display the route between the initial start and end selections. - calculateAndDisplayRoute( - directionsDisplay, directionsService, markerArray, stepDisplay, map); -} - -function calculateAndDisplayRoute(directionsDisplay, directionsService, - markerArray, stepDisplay, map) { - // First, remove any existing markers from the map. - for (var i = 0; i < markerArray.length; i++) { - markerArray[i].setMap(null); - } - - var waypoints = []; - for (i=1; i < markerArray.length-1; i++) { - if (!waypoints) waypoints = []; - waypoints.push({ - location: markerArray[i].getPosition(), - stopover: true, - }); - } - - // Retrieve the start and end locations and create a DirectionsRequest using - // WALKING directions. - directionsService.route({ - origin: markerArray[0].getPosition(), - destination: markerArray[markerArray.length-1].getPosition(), - waypoints: waypoints, - travelMode: 'WALKING' - }, function(response, status) { - // Route the directions and pass the response to a function to create - // markers for each step. - if (status === 'OK') { - directionsDisplay.setDirections(response); - showSteps(response, markerArray, stepDisplay, map); - } else { - // window.alert('Directions request failed due to ' + status); - } - }); -} - -function showSteps(directionResult, markerArray, stepDisplay, map) { - // For each step, place a marker, and add the text to the marker's infowindow. - // Also attach the marker to an array so we can keep track of it and remove it - // when calculating new routes. - var myRoute = directionResult.routes[0].legs[0]; - for (var i = 0; i < myRoute.steps.length; i++) { - var marker = markerArray[i] = markerArray[i] || new google.maps.Marker; - marker.setMap(map); - marker.setPosition(myRoute.steps[i].start_location); - } -} - -function getBoundsZoomLevel(bounds, width, height) { - var WORLD_DIM = { height: 256, width: 256 }; - var ZOOM_MAX = 21; - - function latRad(lat) { - var sin = Math.sin(lat * Math.PI / 180); - var radX2 = Math.log((1 + sin) / (1 - sin)) / 2; - return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2; - } - - function zoom(mapPx, worldPx, fraction) { - return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2); - } - - var ne = bounds.getNorthEast(); - var sw = bounds.getSouthWest(); - - var latFraction = (latRad(ne.lat()) - latRad(sw.lat())) / Math.PI; - - var lngDiff = ne.lng() - sw.lng(); - var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360; - - var latZoom = zoom(height, WORLD_DIM.height, latFraction); - var lngZoom = zoom(width, WORLD_DIM.width, lngFraction); - - return Math.min(latZoom, lngZoom, ZOOM_MAX); -} - -function latLngDistance(a, b) { - if (typeof(Number.prototype.toRad) === "undefined") { - Number.prototype.toRad = function() { - return this * Math.PI / 180; - } - } - - var R = 6371e3; // metres - var phi1 = a[0].toRad(); - var phi2 = b[0].toRad(); - var delta_phi = (b[0]-a[0]).toRad(); - var delta_lambda = (b[1]-a[1]).toRad(); - - var a = Math.sin(delta_phi/2) * Math.sin(delta_phi/2) + - Math.cos(phi1) * Math.cos(phi2) * - Math.sin(delta_lambda/2) * Math.sin(delta_lambda/2); - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); - - return R * c; -} - -function updateGeoPoints() { - var from = new Date(window.map_start).toISOString(); - var to = new Date(window.map_end).toISOString(); - from = from.substring(0, 10) + ' ' + from.substring(11, 19) - to = to.substring(0, 10) + ' ' + to.substring(11, 19) - - var engine = window.db_conf.engine; - var table = window.db_conf.table; - var columns = window.db_conf.columns; - - execute( - { - type: 'request', - action: 'db.select', - args: { - engine: engine, - query: "SELECT " + columns['latitude'] + " AS latitude, " + - columns['longitude'] + " AS longitude, " + - columns['address'] + " AS address, " + - "DATE_FORMAT(" + columns['created_at'] + ", '%Y-%m-%dT%TZ') " + - "AS created_at FROM " + table + " WHERE created_at BETWEEN '" + - from + "' AND '" + to + "' ORDER BY " + columns['created_at'] + " DESC" } - }, - - onSuccess = function(response) { - initMapFromGeopoints(response.response.output); - } - ); -} - -function initMap() { - updateGeoPoints(); -} - diff --git a/platypush/backend/http/static/js/plugins/music.mpd/index.js b/platypush/backend/http/static/js/plugins/music.mpd/index.js index c21e346a..eb930c60 100644 --- a/platypush/backend/http/static/js/plugins/music.mpd/index.js +++ b/platypush/backend/http/static/js/plugins/music.mpd/index.js @@ -45,8 +45,6 @@ Vue.component('music-mpd', { timestamp: null, elapsed: null, }, - - newTrackLock: false, }; }, @@ -467,9 +465,6 @@ Vue.component('music-mpd', { } } }; - - // adjust(this)(); - // setInterval(adjust(this), 2000); }, _parseStatus: async function(status) { diff --git a/platypush/backend/http/static/js/widgets/calendar.js b/platypush/backend/http/static/js/widgets/calendar.js deleted file mode 100644 index 566ef9c7..00000000 --- a/platypush/backend/http/static/js/widgets/calendar.js +++ /dev/null @@ -1,78 +0,0 @@ -$(document).ready(function() { - var $widget = $('.widget.calendar'), - $nextEventContainer = $widget.find('.calendar-next-event-container'), - $eventsListContainer = $widget.find('.calendar-events-list-container'); - - var formatDateString = function(date) { - return date.toDateString().substring(0, 10); - }; - - var formatTimeString = function(date) { - return date.toTimeString().substring(0, 5); - }; - - var refreshStatus = function(status) { - setState(state=status.state); - if ('elapsed' in status) { - setTrackElapsed(status.elapsed); - } - }; - - var refreshCalendar = function() { - execute( - { - type: 'request', - action: 'calendar.get_upcoming_events', - args: { - max_results: 9, - } - }, - - onSuccess = function(response) { - var events = response.response.output; - $eventsListContainer.html(''); - - for (var i=0; i < events.length; i++) { - var event = events[i]; - var start = new Date('dateTime' in event.start ? event.start.dateTime : event.start.date); - var end = new Date('dateTime' in event.end ? event.end.dateTime : event.end.date); - var summary = event.summary; - - if (i == 0) { - $nextEventContainer.find('.summary').text(summary); - $nextEventContainer.find('.date').text(formatDateString(start)); - $nextEventContainer.find('.time').text( - formatTimeString(start) + ' - ' + formatTimeString(end)); - } else { - var $event = $('
').addClass('calendar-event').addClass('row'); - var $eventDate = $('
').addClass('date') - .addClass('three columns').text(formatDateString(start)); - - var $eventTime = $('
').addClass('time').addClass('one column') - .text('dateTime' in event.start ? formatTimeString(start) : '-'); - - var $eventSummary = $('
').addClass('summary') - .addClass('eight columns').text(summary); - - $eventDate.appendTo($event); - $eventTime.appendTo($event); - $eventSummary.appendTo($event); - $event.appendTo($eventsListContainer); - } - } - } - ); - }; - - var initWidget = function() { - refreshCalendar(); - setInterval(refreshCalendar, 900000); - }; - - var init = function() { - initWidget(); - }; - - init(); -}); - diff --git a/platypush/backend/http/static/js/widgets/calendar/index.js b/platypush/backend/http/static/js/widgets/calendar/index.js new file mode 100644 index 00000000..c5a299b0 --- /dev/null +++ b/platypush/backend/http/static/js/widgets/calendar/index.js @@ -0,0 +1,37 @@ +Vue.component('calendar', { + template: '#tmpl-widget-calendar', + props: ['config'], + + data: function() { + return { + events: [], + }; + }, + + methods: { + refresh: async function() { + this.events = (await request('calendar.get_upcoming_events')).map(event => { + if (event.start) + event.start = new Date(event.start.dateTime || event.start.date); + if (event.end) + event.end = new Date(event.end.dateTime || event.end.date); + + return event; + }); + }, + + formatDate: function(date) { + return date.toDateString().substring(0, 10); + }, + + formatTime: function(date) { + return date.toTimeString().substring(0, 5); + }, + }, + + mounted: function() { + this.refresh(); + setInterval(this.refresh, 600000); + }, +}); + diff --git a/platypush/backend/http/static/js/widgets/date-time-weather.js b/platypush/backend/http/static/js/widgets/date-time-weather.js deleted file mode 100644 index fba1b722..00000000 --- a/platypush/backend/http/static/js/widgets/date-time-weather.js +++ /dev/null @@ -1,108 +0,0 @@ -$(document).ready(function() { - var $widget = $('.widget.date-time-weather'), - $dateElement = $widget.find('[data-bind=date]'), - $timeElement = $widget.find('[data-bind=time]'), - $sensorTempElement = $widget.find('[data-bind=sensor-temperature]'), - $sensorHumidityElement = $widget.find('[data-bind=sensor-humidity]'), - $forecastElement = $widget.find('[data-bind=forecast]'), - $tempElement = $widget.find('[data-bind=temperature]'), - currentIcon = undefined; - - var onEvent = function(event) { - if (event.args.type == 'platypush.message.event.weather.NewWeatherConditionEvent') { - updateTemperature(event.args.temperature); - updateWeatherIcon(event.args.icon); - } else if (event.args.type == 'platypush.message.event.sensor.SensorDataChangeEvent') { - if ('temperature' in event.args.data) { - updateSensorTemperature(event.args.data.temperature); - } - - if ('humidity' in event.args.data) { - updateSensorHumidity(event.args.data.humidity); - } - } - }; - - var updateTemperature = function(temperature) { - $tempElement.text(Math.round(temperature)); - }; - - var updateWeatherIcon = function(icon) { - var skycons = new Skycons({ - 'color':'#333', 'resizeClear':'true' - }); - - if (currentIcon) { - skycons.remove('weather-icon'); - } - - skycons.add('weather-icon', icon); - currentIcon = icon; - }; - - var updateSensorTemperature = function(temperature) { - $sensorTempElement.text(Math.round(temperature*10)/10); - $sensorTempElement.parent().show(); - }; - - var updateSensorHumidity = function(humidity) { - $sensorHumidityElement.text(Math.round(humidity)); - $sensorHumidityElement.parent().show(); - }; - - var initEvents = function() { - window.registerEventListener(onEvent); - }; - - var refreshDateTime = function() { - var now = new Date(); - $dateElement.text(now.toDateString()); - $timeElement.text(now.getHours() + ':' + - (now.getMinutes() < 10 ? '0' : '') + now.getMinutes() + ':' + - (now.getSeconds() < 10 ? '0' : '') + now.getSeconds()); - }; - - - var initWeather = function() { - execute( - { - type: 'request', - action: 'weather.forecast.get_current_weather', - }, - - onSuccess = function(response) { - updateTemperature(status=response.response.output.temperature); - updateWeatherIcon(response.response.output.icon); - } - ); - }; - - var refreshForecast = function() { - execute( - { - type: 'request', - action: 'weather.forecast.get_hourly_forecast', - }, - - onSuccess = function(response) { - $forecastElement.text(response.response.output.summary); - } - ); - }; - - var initWidget = function() { - refreshDateTime(); - setInterval(refreshDateTime, 500); - refreshForecast(); - setInterval(refreshForecast, 1200000); - initWeather(); - }; - - var init = function() { - initEvents(); - initWidget(); - }; - - init(); -}); - diff --git a/platypush/backend/http/static/js/widgets/date-time-weather/index.js b/platypush/backend/http/static/js/widgets/date-time-weather/index.js new file mode 100644 index 00000000..8b9e0c8b --- /dev/null +++ b/platypush/backend/http/static/js/widgets/date-time-weather/index.js @@ -0,0 +1,68 @@ +Vue.component('date-time-weather', { + template: '#tmpl-widget-date-time-weather', + props: ['config'], + + data: function() { + return { + weather: undefined, + sensors: {}, + now: new Date(), + weatherIcon: undefined, + }; + }, + + methods: { + refresh: async function() { + let weather = (await request('weather.forecast.get_hourly_forecast')).data[0]; + this.onWeatherChange(weather); + }, + + refreshTime: function() { + this.now = new Date(); + }, + + formatDate: function(date) { + return date.toDateString().substring(0, 10); + }, + + formatTime: function(date) { + return date.toTimeString().substring(0, 8); + }, + + onWeatherChange: function(event) { + if (!this.weather) + this.weather = {}; + + Vue.set(this, 'weather', {...this.weather, ...event}); + + var skycons = new Skycons({ + 'color':'#333', 'resizeClear':'true' + }); + + if (this.weatherIcon) { + skycons.remove('weather-icon'); + } + + skycons.add('weather-icon', this.weather.icon); + this.weatherIcon = this.weather.icon; + }, + + onSensorData: function(event) { + if ('temperature' in event.data) + this.sensors.temperature = event.data.temperature; + + if ('humidity' in event.data) + this.sensors.temperature = event.data.humidity; + }, + }, + + mounted: function() { + this.refresh(); + setInterval(this.refresh, 900000); + setInterval(this.refreshTime, 1000); + + registerEventHandler(this.onWeatherChange, 'platypush.message.event.weather.NewWeatherConditionEvent'); + registerEventHandler(this.onSensorData, 'platypush.message.event.sensor.SensorDataChangeEvent'); + }, +}); + diff --git a/platypush/backend/http/static/js/widgets/skycons.js b/platypush/backend/http/static/js/widgets/date-time-weather/skycons.js similarity index 100% rename from platypush/backend/http/static/js/widgets/skycons.js rename to platypush/backend/http/static/js/widgets/date-time-weather/skycons.js diff --git a/platypush/backend/http/static/js/widgets/image-carousel.js b/platypush/backend/http/static/js/widgets/image-carousel.js deleted file mode 100644 index de95196e..00000000 --- a/platypush/backend/http/static/js/widgets/image-carousel.js +++ /dev/null @@ -1,57 +0,0 @@ -$(document).ready(function() { - var $imgContainer = $('.image-carousel').find('.carousel'), - $imgBackground = $('.image-carousel').find('.carousel-background'), - config = window.widgets['image-carousel'], - images = config.imageUrls, - processedImages = 0; - - var shuffleImages = function() { - for (var i=images.length-1; i > 0; i--) { - var j = Math.floor(Math.random() * (i + 1)); - var x = images[i]; - images[i] = images[j]; - images[j] = x; - } - }; - - var refreshImage = function() { - var nextImage = images[processedImages++]; - var $oldImg = $imgContainer.find('img'); - var $newImg = $('') - .attr('src', nextImage) - .attr('alt', 'Could not load image') - .appendTo('body').hide(); - - $newImg.on('load', function() { - $oldImg.remove(); - if ($newImg.width() > $newImg.height()) { - $newImg.css('width', '100%'); - $imgBackground.css('background-image', ''); - } else { - $imgBackground.css('background-image', 'url(' + nextImage + ')'); - } - - $newImg.css('max-height', '100%'); - $newImg.remove().appendTo($imgContainer).show(); - }); - - if (processedImages == images.length-1) { - shuffleImages(); - processedImages = 0; - } - }; - - var initWidget = function() { - shuffleImages(); - refreshImage(); - setInterval(refreshImage, - 'refresh_seconds' in config ? config.refresh_seconds * 1000 : 15000); - }; - - var init = function() { - initWidget(); - }; - - init(); -}); - diff --git a/platypush/backend/http/static/js/widgets/image-carousel/index.js b/platypush/backend/http/static/js/widgets/image-carousel/index.js new file mode 100644 index 00000000..b5d5090e --- /dev/null +++ b/platypush/backend/http/static/js/widgets/image-carousel/index.js @@ -0,0 +1,60 @@ +Vue.component('image-carousel', { + template: '#tmpl-widget-image-carousel', + props: ['config'], + + data: function() { + return { + images: [], + currentImage: undefined, + }; + }, + + methods: { + refresh: async function() { + if (!this.images.length) { + this.images = await request('utils.search_web_directory', { + directory: this.config.images_path, + extensions: ['.jpg', '.jpeg', '.png'], + }); + + this.shuffleImages(); + } + + this.currentImage = this.images.pop(); + }, + + onNewImage: function() { + this.$refs.background.style['background-image'] = 'url(' + this.currentImage + ')'; + + if (this.$refs.img.width > this.$refs.img.height) { + this.$refs.img.style.width = 'auto'; + + if ((this.$refs.img.width / this.$refs.img.height) >= 4/3) { + this.$refs.img.style.width = '100%'; + } + + if ((this.$refs.img.width / this.$refs.img.height) <= 16/9) { + this.$refs.img.style.height = '100%'; + } + } + }, + + shuffleImages: function() { + for (var i=this.images.length-1; i > 0; i--) { + let j = Math.floor(Math.random() * (i+1)); + let x = this.images[i]; + Vue.set(this.images, i, this.images[j]); + Vue.set(this.images, j, x); + } + }, + }, + + mounted: function() { + this.$refs.img.addEventListener('load', this.onNewImage); + this.$refs.img.addEventListener('error', this.refresh); + + this.refresh(); + setInterval(this.refresh, 'refresh_seconds' in this.config ? this.config.refresh_seconds*1000 : 15000); + }, +}); + diff --git a/platypush/backend/http/static/js/widgets/music.js b/platypush/backend/http/static/js/widgets/music.js deleted file mode 100644 index 4d73dcdf..00000000 --- a/platypush/backend/http/static/js/widgets/music.js +++ /dev/null @@ -1,192 +0,0 @@ -$(document).ready(function() { - var $widget = $('.widget.music'), - $trackContainer = $widget.find('.track-container'), - $timeContainer = $widget.find('.time-container'), - $playbackStatusContainer = $widget.find('.playback-status-container'), - $noTrackElement = $trackContainer.find('.no-track-info'), - $trackElement = $trackContainer.find('.track-info'), - $artistElement = $trackElement.find('[data-bind=artist]'), - $titleElement = $trackElement.find('[data-bind=title]'), - $timeElapsedElement = $timeContainer.find('.time-elapsed'), - $timeTotalElement = $timeContainer.find('.time-total'), - $elapsedTimeBar = $widget.find('.time-bar > .elapsed'), - $volumeElement = $playbackStatusContainer.find('[data-bind=playback-volume]'), - $randomElement = $playbackStatusContainer.find('[data-bind=playback-random]'), - $repeatElement = $playbackStatusContainer.find('[data-bind=playback-repeat]'), - $singleElement = $playbackStatusContainer.find('[data-bind=playback-single]'), - $consumeElement = $playbackStatusContainer.find('[data-bind=playback-consume]'), - timeElapsed, - timeTotal, - refreshElapsedInterval; - - var onEvent = function(event) { - switch (event.args.type) { - case 'platypush.message.event.music.NewPlayingTrackEvent': - createNotification({ - 'icon': 'play', - 'html': '' + ('artist' in event.args.track ? event.args.track.artist : '') - + '
' - + ('title' in event.args.track ? event.args.track.title : '[No name]'), - }); - - case 'platypush.message.event.music.MusicPlayEvent': - case 'platypush.message.event.music.MusicPauseEvent': - refreshTrack(event.args.track); - - case 'platypush.message.event.music.MusicStopEvent': - refreshStatus(event.args.status); - break; - - case 'platypush.message.event.music.VolumeChangeEvent': - case 'platypush.message.event.music.PlaybackRepeatModeChangeEvent': - case 'platypush.message.event.music.PlaybackRandomModeChangeEvent': - case 'platypush.message.event.music.PlaybackConsumeModeChangeEvent': - case 'platypush.message.event.music.PlaybackSingleModeChangeEvent': - refreshPlaybackStatus(event.args.status); - break; - } - }; - - var initEvents = function() { - window.registerEventListener(onEvent); - }; - - - var setState = function(state) { - if (state === 'play') { - $noTrackElement.hide(); - $trackElement.show(); - $timeContainer.show(); - } else if (state === 'pause') { - $noTrackElement.hide(); - $trackElement.show(); - $timeContainer.hide(); - } else if (state === 'stop') { - $noTrackElement.show(); - $trackElement.hide(); - $timeContainer.hide(); - } - }; - - var secondsToTimeString = function(seconds) { - seconds = parseInt(seconds); - - if (seconds) { - return (parseInt(seconds/60) + ':' + - (seconds%60 < 10 ? '0' : '') + seconds%60); - } else { - return '-:--'; - } - }; - - var setTrackTime = function(time) { - $timeTotalElement.text(secondsToTimeString(time)); - timeTotal = parseInt(time); - }; - - var setTrackElapsed = function(time) { - if (refreshElapsedInterval) { - clearInterval(refreshElapsedInterval); - refreshElapsedInterval = undefined; - } - - if (time === undefined) { - $timeElapsedElement.text('-:--'); - return; - } - - timeElapsed = parseInt(time); - $timeElapsedElement.text(secondsToTimeString(timeElapsed)); - - var ratio = 100 * Math.min(timeElapsed/timeTotal, 1); - $elapsedTimeBar.css('width', ratio + '%'); - - refreshElapsedInterval = setInterval(function() { - timeElapsed += 1; - ratio = 100 * Math.min(timeElapsed/timeTotal, 1); - $elapsedTimeBar.css('width', ratio + '%'); - $timeElapsedElement.text(secondsToTimeString(timeElapsed)); - }, 1000); - }; - - var refreshStatus = function(status) { - if (!status) { - return; - } - - if ('state' in status) { - setState(state=status.state); - if (status.state === 'stop') { - setTrackElapsed(); - } - } - - if ('elapsed' in status) { - setTrackElapsed(status.elapsed); - } else if ('position' in status) { - setTrackElapsed(status.position); - } - }; - - var refreshTrack = function(track) { - if (!track) { - return; - } - - if ('time' in track) { - setTrackTime(track.time); - } - - $artistElement.text(track.artist); - $titleElement.text(track.title); - }; - - var refreshPlaybackStatus = function(status) { - if (!status) { - return; - } - - if ('volume' in status) { - $volumeElement.text(status.volume + '%'); - } - - if ('random' in status) { - var state = !!parseInt(status.random); - $randomElement.text(state ? 'ON' : 'OFF'); - } - - if ('repeat' in status) { - var state = !!parseInt(status.repeat); - $repeatElement.text(state ? 'ON' : 'OFF'); - } - - if ('single' in status) { - var state = !!parseInt(status.single); - $singleElement.text(state ? 'ON' : 'OFF'); - } - - if ('consume' in status) { - var state = !!parseInt(status.consume); - $consumeElement.text(state ? 'ON' : 'OFF'); - } - }; - - var initWidget = function() { - $.when( - execute({ type: 'request', action: 'music.mpd.currentsong' }), - execute({ type: 'request', action: 'music.mpd.status' }) - ).done(function(t, s) { - refreshTrack(t[0].response.output); - refreshStatus(s[0].response.output); - refreshPlaybackStatus(s[0].response.output); - }); - }; - - var init = function() { - initEvents(); - initWidget(); - }; - - init(); -}); - diff --git a/platypush/backend/http/static/js/widgets/music/index.js b/platypush/backend/http/static/js/widgets/music/index.js new file mode 100644 index 00000000..0626f101 --- /dev/null +++ b/platypush/backend/http/static/js/widgets/music/index.js @@ -0,0 +1,233 @@ +Vue.component('music', { + template: '#tmpl-widget-music', + props: ['config'], + + data: function() { + return { + track: undefined, + status: undefined, + timer: undefined, + + syncTime: { + timestamp: null, + elapsed: null, + }, + }; + }, + + methods: { + refresh: async function() { + let status = await request('music.mpd.status'); + let track = await request('music.mpd.currentsong'); + + this._parseStatus(status); + this._parseTrack(track); + + if (status.state === 'play' && !this.timer) + this.startTimer(); + else if (status.state !== 'play' && this.timer) + this.stopTimer(); + }, + + convertTime: function(time) { + time = parseFloat(time); // Normalize strings + var t = {}; + t.h = '' + parseInt(time/3600); + t.m = '' + parseInt(time/60 - t.h*60); + t.s = '' + parseInt(time - (t.h*3600 + t.m*60)); + + for (var attr of ['m','s']) { + if (parseInt(t[attr]) < 10) { + t[attr] = '0' + t[attr]; + } + } + + var ret = []; + if (parseInt(t.h)) { + ret.push(t.h); + } + + ret.push(t.m, t.s); + return ret.join(':'); + }, + + _parseStatus: async function(status) { + if (!status || status.length === 0) + status = await request('music.mpd.status'); + + if (!this.status) + this.status = {}; + + for (const [attr, value] of Object.entries(status)) { + if (['consume','random','repeat','single','bitrate'].indexOf(attr) >= 0) { + Vue.set(this.status, attr, !!parseInt(value)); + } else if (['nextsong','nextsongid','playlist','playlistlength', + 'volume','xfade','song','songid'].indexOf(attr) >= 0) { + Vue.set(this.status, attr, parseInt(value)); + } else if (['elapsed'].indexOf(attr) >= 0) { + Vue.set(this.status, attr, parseFloat(value)); + } else { + Vue.set(this.status, attr, value); + } + } + }, + + _parseTrack: async function(track) { + if (!track || track.length === 0) { + track = await request('music.mpd.currentsong'); + } + + if (!this.track) + this.track = {}; + + for (const [attr, value] of Object.entries(track)) { + if (['id','pos','time','track','disc'].indexOf(attr) >= 0) { + Vue.set(this.track, attr, parseInt(value)); + } else { + Vue.set(this.track, attr, value); + } + } + }, + + showNewTrackNotification: function() { + createNotification({ + html: '' + (this.track.artist || '[No Artist]') + '
' + + (this.track.title || '[No Title]'), + image: { + icon: 'play', + } + }); + }, + + onNewPlayingTrack: async function(event) { + let previousTrack = undefined; + + if (this.track) { + previousTrack = { + file: this.track.file, + artist: this.track.artist, + title: this.track.title, + }; + } + + this.status.state = 'play'; + Vue.set(this.status, 'elapsed', 0); + this.track = {}; + this._parseTrack(event.track); + + let status = event.status ? event.status : await request('music.mpd.status'); + this._parseStatus(status); + this.startTimer(); + + if (previousTrack && this.track.file != previousTrack.file + || this.track.artist != previousTrack.artist + || this.track.title != previousTrack.title) { + this.showNewTrackNotification(); + } + }, + + onMusicStop: function(event) { + this.status.state = 'stop'; + Vue.set(this.status, 'elapsed', 0); + this._parseStatus(event.status); + this._parseTrack(event.track); + this.stopTimer(); + }, + + onMusicPlay: function(event) { + this.status.state = 'play'; + this._parseStatus(event.status); + this._parseTrack(event.track); + this.startTimer(); + }, + + onMusicPause: function(event) { + this.status.state = 'pause'; + this._parseStatus(event.status); + this._parseTrack(event.track); + + Vue.set(this.syncTime, 'timestamp', new Date()); + Vue.set(this.syncTime, 'elapsed', this.status.elapsed); + }, + + onSeekChange: function(event) { + if (event.position != null) + Vue.set(this.status, 'elapsed', parseFloat(event.position)); + if (event.status) + this._parseStatus(event.status); + if (event.track) + this._parseTrack(event.track); + + Vue.set(this.syncTime, 'timestamp', new Date()); + Vue.set(this.syncTime, 'elapsed', this.status.elapsed); + }, + + onVolumeChange: function(event) { + if (event.volume != null) + this.status.volume = parseFloat(event.volume); + if (event.status) + this._parseStatus(event.status); + if (event.track) + this._parseTrack(event.track); + }, + + onRepeatChange: function(event) { + this.status.repeat = event.state; + }, + + onRandomChange: function(event) { + this.status.random = event.state; + }, + + onConsumeChange: function(event) { + this.status.consume = event.state; + }, + + onSingleChange: function(event) { + this.status.single = event.state; + }, + + startTimer: function() { + if (this.timer != null) { + this.stopTimer(); + } + + Vue.set(this.syncTime, 'timestamp', new Date()); + Vue.set(this.syncTime, 'elapsed', this.status.elapsed); + this.timer = setInterval(this.timerFunc, 1000); + }, + + stopTimer: function() { + if (this.timer == null) { + clearInterval(this.timer); + this.timer = null; + } + }, + + timerFunc: function() { + if (this.status.state !== 'play' || this.status.elapsed == null) { + return; + } + + Vue.set(this.status, 'elapsed', this.syncTime.elapsed + + ((new Date()).getTime()/1000) - (this.syncTime.timestamp.getTime()/1000)); + }, + }, + + mounted: function() { + this.refresh(); + setInterval(this.refresh, 60000); + + registerEventHandler(this.onNewPlayingTrack, 'platypush.message.event.music.NewPlayingTrackEvent'); + registerEventHandler(this.onMusicStop, 'platypush.message.event.music.MusicStopEvent'); + registerEventHandler(this.onMusicPlay, 'platypush.message.event.music.MusicPlayEvent'); + registerEventHandler(this.onMusicPause, 'platypush.message.event.music.MusicPauseEvent'); + registerEventHandler(this.onSeekChange, 'platypush.message.event.music.SeekChangeEvent'); + registerEventHandler(this.onVolumeChange, 'platypush.message.event.music.VolumeChangeEvent'); + registerEventHandler(this.onRepeatChange, 'platypush.message.event.music.PlaybackRepeatModeChangeEvent'); + registerEventHandler(this.onRandomChange, 'platypush.message.event.music.PlaybackRandomModeChangeEvent'); + registerEventHandler(this.onConsumeChange, 'platypush.message.event.music.PlaybackConsumeModeChangeEvent'); + registerEventHandler(this.onSingleChange, 'platypush.message.event.music.PlaybackSingleModeChangeEvent'); + }, +}); + diff --git a/platypush/backend/http/static/js/widgets/rss-news.js b/platypush/backend/http/static/js/widgets/rss-news.js deleted file mode 100644 index 576d5222..00000000 --- a/platypush/backend/http/static/js/widgets/rss-news.js +++ /dev/null @@ -1,80 +0,0 @@ -$(document).ready(function() { - var $newsElement = $('.rss-news').find('.news-container'), - config = window.widgets['rss-news'], - db = config.db, - news = [], - default_limit = 10, - cur_article_index = -1; - - var getNews = function() { - execute( - { - type: 'request', - action: 'db.select', - args: { - engine: db, - query: "select s.title as source, e.title, e.summary, " + - "strftime('%Y-%m-%dT%H:%M:%fZ', e.published) as published " + - "from FeedEntry e join FeedSource s " + - "on e.source_id = s.id order by e.published desc limit " + - ('limit' in config ? config.limit : default_limit) - } - }, - - onSuccess = function(response) { - if (!response.response.output) { - return; - } - - var firstRun = news.length === 0; - news = response.response.output; - cur_article_index = -1; - - if (firstRun) { - refreshNews(); - } - } - ); - }; - - var refreshNews = function() { - if (news.length === 0) { - return; - } - - var updateNewsList = cur_article_index == news.length-1; - var nextArticle = news[++cur_article_index % news.length]; - var dt = new Date(nextArticle.published); - var $article = $('
').addClass('article'); - var $source = $('
').addClass('source').text(nextArticle.source); - var $title = $('
').addClass('title').text(nextArticle.title); - var $publishTime = $('
').addClass('publish-time') - .text(dt.toDateString() + ', ' + dt.toTimeString().substring(0, 5)); - - $source.appendTo($article); - $title.appendTo($article); - $publishTime.appendTo($article); - - if ($newsElement.find('.article').length) { - $newsElement.find('.article').remove(); - } - - $article.hide().appendTo($newsElement).show(); - - if (updateNewsList) { - getNews(); - } - }; - - var initWidget = function() { - getNews(); - setInterval(refreshNews, 15000); - }; - - var init = function() { - initWidget(); - }; - - init(); -}); - diff --git a/platypush/backend/http/static/js/widgets/rss-news/index.js b/platypush/backend/http/static/js/widgets/rss-news/index.js new file mode 100644 index 00000000..2f878959 --- /dev/null +++ b/platypush/backend/http/static/js/widgets/rss-news/index.js @@ -0,0 +1,40 @@ +Vue.component('rss-news', { + template: '#tmpl-widget-rss-news', + props: ['config'], + + data: function() { + return { + articles: [], + queue: [], + currentArticle: undefined, + }; + }, + + methods: { + refresh: async function() { + if (!this.queue.length) { + this.articles = await request('db.select', { + engine: this.config.db, + query: "select s.title as source, e.title, e.summary, " + + "strftime('%Y-%m-%dT%H:%M:%fZ', e.published) as published " + + "from FeedEntry e join FeedSource s " + + "on e.source_id = s.id order by e.published limit " + + ('limit' in this.config ? this.config.limit : 10) + }); + + this.queue = [...this.articles]; + } + + if (!this.queue.length) + return; + + this.currentArticle = this.queue.pop(); + }, + }, + + mounted: function() { + this.refresh(); + setInterval(this.refresh, 'refresh_seconds' in this.config ? this.config.refresh_seconds*1000 : 15000); + }, +}); + diff --git a/platypush/backend/http/templates/dashboard.html b/platypush/backend/http/templates/dashboard.html index 48a8b211..5021c01e 100644 --- a/platypush/backend/http/templates/dashboard.html +++ b/platypush/backend/http/templates/dashboard.html @@ -1,72 +1,107 @@ Platypush Dashboard + - - - - - - - - + + + + + + + + + + + + + + + - - - - - - + + {% include 'elements.html' %} + + {% for widget in config['widgets'] %} + {% with name = widget['widget'] %} + {% include 'widgets/' + name + '/index.html' %} + + {% with js_file = static_folder + '/js/widgets/' + name + '/index.js' %} + {% if utils.isfile(js_file) %} + + {% endif %} + {% endwith %} + + {% with css_file = static_folder + '/css/dist/dashboard/widgets/' + name + '.css' %} + {% if utils.isfile(css_file) %} + + {% endif %} + {% endwith %} + {% endwith %} + {% endfor %} -
- - +
+
+ + + + -
- {% set used_columns = [0] %} - {% for widget in config['widgets'] %} - {% if used_columns[0] % 12 == 0 %} -
- {% endif %} +
+ {% set used_columns = [0] %} + {% for widget in config['widgets'] %} + {% with name = widget['widget'] %} + {% if used_columns[0] % 12 == 0 %} +
+ {% endif %} -
- {% with properties=widget %} - {% include 'widgets/' + widget['widget'] + '.html' %} + {% with properties=widget %} + + + {% endwith %} + + {# increment counter #} + {% if used_columns.append(used_columns.pop() + widget['columns']) %}{% endif %} + + {% if used_columns[0] % 12 == 0 %} +
+ {% endif %} {% endwith %} -
+ {% endfor %} +
- {# increment counter #} - {% if used_columns.append(used_columns.pop() + widget['columns']) %}{% endif %} + {% include 'notifications.html' %} +
+
- {% if used_columns[0] % 12 == 0 %} - - {% endif %} - {% endfor %} - + {% include 'widgets/template.html' %} -
-
- + + diff --git a/platypush/backend/http/templates/dashboard_old.html b/platypush/backend/http/templates/dashboard_old.html new file mode 100644 index 00000000..48a8b211 --- /dev/null +++ b/platypush/backend/http/templates/dashboard_old.html @@ -0,0 +1,72 @@ + + + Platypush Dashboard + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ {% set used_columns = [0] %} + {% for widget in config['widgets'] %} + {% if used_columns[0] % 12 == 0 %} +
+ {% endif %} + +
+ {% with properties=widget %} + {% include 'widgets/' + widget['widget'] + '.html' %} + {% endwith %} +
+ + {# increment counter #} + {% if used_columns.append(used_columns.pop() + widget['columns']) %}{% endif %} + + {% if used_columns[0] % 12 == 0 %} +
+ {% endif %} + {% endfor %} +
+ +
+
+ + diff --git a/platypush/backend/http/templates/index.html b/platypush/backend/http/templates/index.html index c6def109..e00fbb85 100644 --- a/platypush/backend/http/templates/index.html +++ b/platypush/backend/http/templates/index.html @@ -68,7 +68,8 @@ :tag="plugin.replace('.', '-')" :key="plugin" :config="conf" - :class="{hidden: plugin != selectedPlugin}"/> + :class="{hidden: plugin != selectedPlugin}"> + diff --git a/platypush/backend/http/templates/plugins/assistant.google.html b/platypush/backend/http/templates/plugins/assistant.google.html deleted file mode 100644 index a6e62c1d..00000000 --- a/platypush/backend/http/templates/plugins/assistant.google.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/platypush/backend/http/templates/plugins/gpio.html b/platypush/backend/http/templates/plugins/gpio.html deleted file mode 100644 index ac652297..00000000 --- a/platypush/backend/http/templates/plugins/gpio.html +++ /dev/null @@ -1,5 +0,0 @@ - - - -
- diff --git a/platypush/backend/http/templates/plugins/gpio.sensor.mcp3008.html b/platypush/backend/http/templates/plugins/gpio.sensor.mcp3008.html deleted file mode 100644 index be1a5151..00000000 --- a/platypush/backend/http/templates/plugins/gpio.sensor.mcp3008.html +++ /dev/null @@ -1,5 +0,0 @@ - - - -
- diff --git a/platypush/backend/http/templates/plugins/gpio.zeroborg.html b/platypush/backend/http/templates/plugins/gpio.zeroborg.html deleted file mode 100644 index 8ef6e3db..00000000 --- a/platypush/backend/http/templates/plugins/gpio.zeroborg.html +++ /dev/null @@ -1,44 +0,0 @@ - - - -
-
-
-
 
- -
 
-
- -
- -
 
- -
- -
-
 
- -
 
-
- -
-
 
- - -
 
-
-
-
- diff --git a/platypush/backend/http/templates/plugins/media.html b/platypush/backend/http/templates/plugins/media.html deleted file mode 100644 index ec1ca751..00000000 --- a/platypush/backend/http/templates/plugins/media.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - -
- - -
- - - - - - - - -
-
- - - - - - - - - - - - - -
-
- -
-
-   - -   -
-
-
- -
-
-
- -
-
Play
-
- -
-
- -
-
Download
-
-
- - - -
-
-
-
- diff --git a/platypush/backend/http/templates/plugins/media.mplayer.html b/platypush/backend/http/templates/plugins/media.mplayer.html deleted file mode 120000 index b1a53826..00000000 --- a/platypush/backend/http/templates/plugins/media.mplayer.html +++ /dev/null @@ -1 +0,0 @@ -media.html \ No newline at end of file diff --git a/platypush/backend/http/templates/plugins/media.mpv.html b/platypush/backend/http/templates/plugins/media.mpv.html deleted file mode 120000 index b1a53826..00000000 --- a/platypush/backend/http/templates/plugins/media.mpv.html +++ /dev/null @@ -1 +0,0 @@ -media.html \ No newline at end of file diff --git a/platypush/backend/http/templates/plugins/media.omxplayer.html b/platypush/backend/http/templates/plugins/media.omxplayer.html deleted file mode 120000 index b1a53826..00000000 --- a/platypush/backend/http/templates/plugins/media.omxplayer.html +++ /dev/null @@ -1 +0,0 @@ -media.html \ No newline at end of file diff --git a/platypush/backend/http/templates/plugins/media.vlc.html b/platypush/backend/http/templates/plugins/media.vlc.html deleted file mode 120000 index b1a53826..00000000 --- a/platypush/backend/http/templates/plugins/media.vlc.html +++ /dev/null @@ -1 +0,0 @@ -media.html \ No newline at end of file diff --git a/platypush/backend/http/templates/plugins/serial.html b/platypush/backend/http/templates/plugins/serial.html deleted file mode 120000 index 20963d36..00000000 --- a/platypush/backend/http/templates/plugins/serial.html +++ /dev/null @@ -1 +0,0 @@ -gpio.sensor.mcp3008.html \ No newline at end of file diff --git a/platypush/backend/http/templates/plugins/switch.switchbot.html b/platypush/backend/http/templates/plugins/switch.switchbot.html deleted file mode 100644 index a0113fce..00000000 --- a/platypush/backend/http/templates/plugins/switch.switchbot.html +++ /dev/null @@ -1,17 +0,0 @@ - - - -
- {% for addr, name in configuration['devices'].items() %} -
-
{{ name }}
-
-
- - -
-
-
- {% endfor %} -
- diff --git a/platypush/backend/http/templates/plugins/switch.tplink.html b/platypush/backend/http/templates/plugins/switch.tplink.html deleted file mode 100644 index ad4c74fb..00000000 --- a/platypush/backend/http/templates/plugins/switch.tplink.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/platypush/backend/http/templates/plugins/switch.wemo.html b/platypush/backend/http/templates/plugins/switch.wemo.html deleted file mode 100644 index 94b5754f..00000000 --- a/platypush/backend/http/templates/plugins/switch.wemo.html +++ /dev/null @@ -1,5 +0,0 @@ - - - -
- diff --git a/platypush/backend/http/templates/widgets/calendar.html b/platypush/backend/http/templates/widgets/calendar.html deleted file mode 100644 index 1f2bdf3d..00000000 --- a/platypush/backend/http/templates/widgets/calendar.html +++ /dev/null @@ -1,13 +0,0 @@ - - - -
-
-
-
-
-
- -
-
- diff --git a/platypush/backend/http/templates/widgets/calendar/index.html b/platypush/backend/http/templates/widgets/calendar/index.html new file mode 100644 index 00000000..2be6e8d4 --- /dev/null +++ b/platypush/backend/http/templates/widgets/calendar/index.html @@ -0,0 +1,15 @@ + diff --git a/platypush/backend/http/templates/widgets/date-time-weather.html b/platypush/backend/http/templates/widgets/date-time-weather.html deleted file mode 100644 index a43ebb44..00000000 --- a/platypush/backend/http/templates/widgets/date-time-weather.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - -
-
-
- -

-   - N/A° -

- -
- -
-
-   - N/A° -
- -
-   - N/A% -
-
-
- diff --git a/platypush/backend/http/templates/widgets/date-time-weather/index.html b/platypush/backend/http/templates/widgets/date-time-weather/index.html new file mode 100644 index 00000000..866e119a --- /dev/null +++ b/platypush/backend/http/templates/widgets/date-time-weather/index.html @@ -0,0 +1,39 @@ + + + diff --git a/platypush/backend/http/templates/widgets/image-carousel.html b/platypush/backend/http/templates/widgets/image-carousel.html deleted file mode 100644 index 8ab899b5..00000000 --- a/platypush/backend/http/templates/widgets/image-carousel.html +++ /dev/null @@ -1,15 +0,0 @@ - - - -{% set images = utils.search_web_directory( - widget['images_path'], '.jpg', 'jpeg', '.png') %} - - - - - diff --git a/platypush/backend/http/templates/widgets/image-carousel/index.html b/platypush/backend/http/templates/widgets/image-carousel/index.html new file mode 100644 index 00000000..4768849c --- /dev/null +++ b/platypush/backend/http/templates/widgets/image-carousel/index.html @@ -0,0 +1,6 @@ + diff --git a/platypush/backend/http/templates/widgets/music.html b/platypush/backend/http/templates/widgets/music.html deleted file mode 100644 index 40e64f9c..00000000 --- a/platypush/backend/http/templates/widgets/music.html +++ /dev/null @@ -1,63 +0,0 @@ - - - -
-
-
No media is being played
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
- -
-
-
Volume
-
Random
-
Repeat
-
Single
-
Consume
-
- -
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- diff --git a/platypush/backend/http/templates/widgets/music/index.html b/platypush/backend/http/templates/widgets/music/index.html new file mode 100644 index 00000000..08f59925 --- /dev/null +++ b/platypush/backend/http/templates/widgets/music/index.html @@ -0,0 +1,44 @@ + + diff --git a/platypush/backend/http/templates/widgets/rss-news.html b/platypush/backend/http/templates/widgets/rss-news.html deleted file mode 100644 index adef9603..00000000 --- a/platypush/backend/http/templates/widgets/rss-news.html +++ /dev/null @@ -1,5 +0,0 @@ - - - -
- diff --git a/platypush/backend/http/templates/widgets/rss-news/index.html b/platypush/backend/http/templates/widgets/rss-news/index.html new file mode 100644 index 00000000..aad0a9fb --- /dev/null +++ b/platypush/backend/http/templates/widgets/rss-news/index.html @@ -0,0 +1,9 @@ + diff --git a/platypush/backend/http/templates/widgets/template.html b/platypush/backend/http/templates/widgets/template.html new file mode 100644 index 00000000..1ace1c38 --- /dev/null +++ b/platypush/backend/http/templates/widgets/template.html @@ -0,0 +1,6 @@ + + diff --git a/platypush/backend/http/utils.py b/platypush/backend/http/utils.py index f509f45c..71ed46ee 100644 --- a/platypush/backend/http/utils.py +++ b/platypush/backend/http/utils.py @@ -13,35 +13,16 @@ class HttpUtils(object): @staticmethod def widget_columns_to_html_class(columns): if not isinstance(columns, int): - raise RuntimeError('columns should be a number, got "{}"'.format(columns)) + try: + columns = int(columns) + except ValueError: + raise RuntimeError('columns should be a number, got {} ({})'.format(type(columns), columns)) - if columns == 1: - return 'one column' - elif columns == 2: - return 'two columns' - elif columns == 3: - return 'three columns' - elif columns == 4: - return 'four columns' - elif columns == 5: - return 'five columns' - elif columns == 6: - return 'six columns' - elif columns == 7: - return 'seven columns' - elif columns == 8: - return 'eight columns' - elif columns == 9: - return 'nine columns' - elif columns == 10: - return 'ten columns' - elif columns == 11: - return 'eleven columns' - elif columns == 12: - return 'twelve columns' - else: - raise RuntimeError('Constraint violation: should be 1 <= columns <= 12, ' + - 'got columns={}'.format(columns)) + if 1 <= columns <= 12: + return 'col-{}'.format(columns) + + raise RuntimeError('Constraint violation: should be 1 <= columns <= 12, ' + + 'got columns={}'.format(columns)) @staticmethod def search_directory(directory, *extensions, recursive=False): @@ -127,5 +108,8 @@ class HttpUtils(object): with open(file) as f: return f.read() + @classmethod + def isfile(cls, file): + return os.path.isfile(file) # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/utils.py b/platypush/plugins/utils.py index 12ab7519..482dc8bc 100644 --- a/platypush/plugins/utils.py +++ b/platypush/plugins/utils.py @@ -2,6 +2,7 @@ import json import threading import time +from platypush.backend.http.utils import HttpUtils from platypush.plugins import Plugin, action from platypush.procedure import Procedure @@ -313,4 +314,13 @@ class UtilsPlugin(Plugin): } } + @action + def search_directory(self, directory, extensions, recursive=False): + return HttpUtils.search_directory(directory, recursive=recursive, *extensions) + + @action + def search_web_directory(self, directory, extensions): + return HttpUtils.search_web_directory(directory, *extensions) + + # vim:sw=4:ts=4:et: