Initial commit

This commit is contained in:
Marcelo
2025-11-20 15:27:34 -06:00
commit cc72c9fc5d
3221 changed files with 737477 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

BIN
node_modules/node-red-dashboard/nodes/icons/ui_form.png generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

BIN
node_modules/node-red-dashboard/nodes/icons/ui_text.png generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

View File

@@ -0,0 +1,100 @@
{
"ui_base": {
"label": {
"dashboard": "Dashboard",
"category": "Dashboard",
"title": "Titel",
"options": "Optionen",
"date-format": "Datumsformat",
"sizes": "Größen",
"horizontal": "Horizontal",
"vertical": "Vertikal",
"widget-size": "1x1 Widget-Größe",
"widget-spacing": "Widget-Abstände",
"group-padding": "Gruppenabstände innen",
"group-spacing": "Gruppenabstände außen",
"layout": "Layout",
"angular": "Angular",
"theme": "Theme",
"site": "Site"
},
"auto": "Auto",
"title": "Node-RED Dashboard",
"layout": {
"tab-and-link": "Tabs & Links",
"tab": "Tab",
"link": "Link",
"group": "Gruppe",
"edit": "Bearbeiten",
"spacer": "Abstand",
"layout": "Layout",
"layout-editor": "Dashboard Layout-Editor",
"width": "Breite",
"auto": "Automatische Größenanpassung",
"manual": "Manuelle Größenanpassung"
},
"theme": {
"style": "Stil",
"custom-profile": "Benutzerdefiniertes Profil",
"custom-profile-name": "Erscheinungsbild ohne Titel 1",
"base-settings": "Basiseinstellungen",
"page-settings": "Seiteneinstellungen",
"page": {
"title": "Hintergrund Titelleiste",
"page": "Hintergrund Seite",
"side": "Hintergrund Seitenleiste"
},
"group-settings": "Gruppeneinstellungen",
"group": {
"text": "Gruppentext",
"border": "Gruppenrand",
"background": "Gruppenhintergrund"
},
"widget-settings": "Widget-Einstellungen",
"widget": {
"text": "Widget-Text",
"colour": "Widget-Farbe",
"background": "Widget-Hintergrund"
}
},
"style": {
"light": "Hell (Standard)",
"dark": "Dunkel",
"custom": "Benutzerdefiniert",
"primary": "Primär",
"accents": "Akzente",
"background": "Hintergrund",
"warnings": "Warnungen",
"palette": "Hell / Dunkel"
},
"base": {
"colour": "Farbe",
"font": "Schriftart"
},
"font": {
"system": "System-Schriftart (Standard)"
},
"site": {
"title": "Node-RED Dashboard",
"date-format": "DD.MM.YYYY"
},
"title-bar": {
"show": "Titelleiste anzeigen",
"hide": "Titelleiste verbergen"
},
"swipe": {
"no-swipe": "Kein Wischen zwischen Tabs",
"allow-swipe": "Wischen zwischen Tabs zulassen"
},
"lock": {
"clicked": "Klicken, um das Seitenmenü anzuzeigen",
"locked": "Seitenmenü immer anzeigen",
"locked-icon": "Nur Icons anzeigen"
},
"temp": {
"allow-theme": "Node-RED-Erscheinungsbild",
"no-theme": "Angular-Erscheinungsbild in ui_template",
"none": "Angular-Erscheinungsbild"
}
}
}

View File

@@ -0,0 +1,24 @@
{
"ui_button": {
"label": {
"group": "Gruppe",
"size": "Größe",
"icon": "Icon",
"optionalIcon": "Optionales Icon",
"label": "Beschriftung",
"optionalLabel": "Optionale Beschriftung",
"tooltip": "Tooltipp",
"optionalTooltip": "Optionaler Tooltipp",
"color": "Farbe",
"optionalColor": "Optionale Text/Icon-Farbe",
"background": "Hintergrund",
"optionalBackgroundColor": "Optionale Hintergrundfarbe",
"whenClicked": "Sende beim Klicken:",
"payload": "Payload",
"topic": "Topic",
"emulateClick": "Emuliere einen Klick bei einer eingehenden Nachricht:",
"className": "Klasse",
"classNamePlaceholder": "Optionale(r) CSS-Klassenname(n) für das Widget"
}
}
}

View File

@@ -0,0 +1,25 @@
<script type="text/html" data-help-name="ui_chart">
<p>Zeichnet Eingangswerte in ein Diagramm, welches entweder ein zeitbasiertes Liniendiagramm,
ein Balkendiagramm (vertikal oder horizontal) oder ein Kreisdiagramm sein kann.</p>
<p>Jede eingehende <code>msg.payload</code>-Nachricht wird in einen Zahlenwert konvertiert.
Wenn die Konvertierung fehlschlägt, wird die Nachricht ignoriert.</p>
<p>Die Minimum- und Maximum-Werte für die <b>Y-Achse</b> sind optional.
Ohne diese wird das Diagramm automatisch anhand aller empfangenen Werte skaliert.</p>
<p>Mehrere Serien können im gleichen Diagramm angezeigt werden,
indem für jede Eingangsnachricht andere <code>msg.topic</code>-Werte verwendet werden.
Mit der Eigenschaft <code>msg.label</code> können mehrere Balken der selben Serie angezeigt werden.</p>
<p>Der Wertebereich der <b>X-Achse</b> wird durch ein Zeitfenster oder eine maximale Anzahl anzuzeigender Punkte vorgegeben.
Ältere Daten werden automatisch aus dem Diagramm entfernt.
Die Achsenbeschriftung kann mittels einer <a href="https://momentjs.com/docs/#/displaying/format/" target="_blank">
Moment.js-zeitformatierten</a> Zeichenfolge benutzerdefiniert werden (z.B. D.M.Y für 21.3.2021).</p>
<p>Wird eine <code>msg.payload</code>-Nachricht mit einem leeren Array <code>[]</code> an den Eingang gesendet,
so wird das Diagramm gelöscht.</p>
<p><b><a href="https://github.com/node-red/node-red-dashboard/blob/master/Charts.md" target="_new">Hier</a></b>
sind weitere Informationen zum Vorformatieren von Daten verfügbar, um sie als vollständiges Diagramm zu übergeben.</p>
<p>Das <b>Leer-Text</b>-Feld kann verwendet werden, um den Text im Diagramm anzuzeigen, bevor gültige Daten empfangen wurden.
<p>Der <b>Beschriftung</b> kann auch durch eine Nachrichteneigenschaft festgelegt werden.
Dazu muss der Name der Eigenschaft in das Feld eingetragen werden, zum Beispiel <code>{{msg.topic}}</code>.</p>
<p>Der Node-Ausgang enthält ein Array des Diagrammstatus, das bei Bedarf gespeichert werden kann.
Wird dieses zum <code>ui_chart</code>-Node gesendet, so werden die gespeicherten Daten erneut angezeigt.</p>
<p>Wenn eine <b>Klasse</b> angegeben ist, wird sie der übergeordneten Karte hinzugefügt. Auf diese Weise können Sie CSS-Stile auf die ui-card und ihre untergeordneten Elemente anwenden. Die Klasse kann zur Laufzeit festgelegt werden, indem eine Zeichenfolgeneigenschaft <code>msg.className</code> festgelegt wird.</p>
</script>

View File

@@ -0,0 +1,56 @@
{
"ui_chart": {
"label": {
"group": "Gruppe",
"size": "Größe",
"label": "Beschriftung",
"optionalChartTitle": "Optionaler Diagrammtitel",
"type": "Typ",
"lineChart": " &#xf201; Liniendiagramm",
"barChart": " &#xf080; Balkendiagramm",
"barChartH": " &#xf080; Balkendiagramm (H)",
"pieChart": " &#xf200; Kreisdiagramm",
"polarAreaChart": " &#xf200; Polargebietskarte",
"radarChart": " &#xf200; Radarkarte",
"enlargePoints": "Punkte vergrößern",
"xAxis": "X-Achse",
"last": "Letzten",
"seconds": "Sekunden",
"minutes": "Minuten",
"hours": "Stunden",
"days": "Tage",
"weeks": "Wochen",
"or": "oder",
"points": "Punkte",
"xAxisLabel": "X-Beschriftung",
"HHmmss": "HH:mm:ss",
"HHmm": "HH:mm",
"yearMonthDate": "Jahr-Monat-Tag",
"dateMonth": "Tag/Monat",
"dayHHmm": "Wochentag HH:mm",
"custom": "benutzerdefiniert",
"automatic": "automatisch",
"asUTC": "als UTC",
"yAxis": "Y-Achse",
"min": "min",
"max": "max",
"legend": "Legende",
"none": "Keine",
"show": "Anzeigen",
"interpolate": "Interpolation",
"linear": "Linear",
"step": "Stufen",
"bezier": "Bezier",
"cubic": "Kubisch",
"cubicMono": "Kubisch-Mono",
"cutout": "Ausschnitt",
"useFirstColourForAllBars": "Erste Farbe für alle Balken verwenden",
"seriesColours": "Serienfarben",
"blankLabel": "Leer-Text",
"displayThisTextBeforeValidDataArrives": "Anzuzeigender Text bevor gültige Daten eintreffen",
"useDifferentColor": "Unterschiedliche Farben für Datenserie verwenden",
"className": "Klasse",
"classNamePlaceholder": "Optionale(r) CSS-Klassenname(n) für das Widget"
}
}
}

View File

@@ -0,0 +1,18 @@
<script type="text/html" data-help-name="ui_form">
<p>Fügt dem Dashboard ein Eingabeformular hinzu.</p>
<p>Der <code>ui_form</code>-Node ermöglicht die Eingabe mehrerer Werte durch den Benutzer.
Beim Klicken auf die Schaltfläche <b>Senden</b> wird ein Objekt als <code>msg.payload</code> versendet.</p>
<p>Über die Schaltfläche <b>+Element</b> können Eingabeelemente hinzugefügt werden.</p>
<p>Jedes Element enthält folgende Einstellmöglichkeiten:</p>
<ul>
<li><b>Beschriftung:</b> Beschriftung des Elements in der Benutzeroberfläche</li>
<li><b>Name:</b> Schlüssel (Variablennamen), mit dem der Eingabewert in <code>msg.payload</code> abgelegt wird</li>
<li><b>Typ:</b> Typ des Eingabeelements</li>
<li><b>Erforderlich:</b> Markierund des Eingabefeldes als erfolderliche Benutzereingabe</li>
<li><b>Zeilen:</b> Zeilenanzahl bei mehrzeiligen Texteingabefeldern</li>
<li><b>Entfernen:</b> Entfernung des Elements aus dem Formular</li>
</ul>
<p>Über <b>Topic</b> kann die Eigenschaft <code>msg.topic</code> optional festgelegt werden.</p>
<p>Die Schaltfläche <b>Abbrechen</b> kann ausgeblendet werden, indem sein Feld leer gelassen wird.</p>
<p>Wenn eine <b>Klasse</b> angegeben ist, wird sie der übergeordneten Karte hinzugefügt. Auf diese Weise können Sie CSS-Stile auf die ui-card und ihre untergeordneten Elemente anwenden. Die Klasse kann zur Laufzeit festgelegt werden, indem eine Zeichenfolgeneigenschaft <code>msg.className</code> festgelegt wird.</p>
</script>

View File

@@ -0,0 +1,35 @@
{
"ui_form": {
"label": {
"group": "Gruppe",
"size": "Größe",
"label": "Beschriftung",
"optionalLabel": "Optionale Beschriftung",
"formElements": "Formular- elemente",
"type": "Typ",
"required": "Erforderlich",
"rows": "UiZeilen",
"remove": "Entfernen",
"egName": "z.B. Name",
"egName2": "z.B. Name",
"text": "Text",
"multiline": "Mehrzeilig",
"number": "Zahl",
"email": "E-Mail",
"password": "Kennwort",
"checkbox": "Auswahlkästchen",
"switch": "Schalter",
"date": "Datum",
"time": "Zeit",
"element": "Element",
"buttons": "Schaltflächen",
"submitButtonText": "Text Absenden-Schaltfläche",
"cancelButtonText": "Text Abbrechen-Schaltfläche",
"topic": "Topic",
"optionalMsgTopic": "Optionaler msg.topic",
"splitLayout": "Platzieren sie die formularelemente in 2 spalten",
"className": "Klasse",
"classNamePlaceholder": "Optionale(r) CSS-Klassenname(n) für das Widget"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"ui_group": {
"label": {
"name": "Name",
"tab": "Tab",
"width": "Breite",
"default": "Standard",
"group": "Gruppe",
"unassigned": "nicht zugewiesen",
"className": "Klasse",
"classNamePlaceholder": "Optionale(r) CSS-Klassenname(n) für das Widget"
},
"display-name": "Gruppenname anzeigen",
"collapse-name": "Gruppenreduzierung zulassen"
}
}

View File

@@ -0,0 +1,16 @@
{
"ui_link": {
"label": {
"name": "Name",
"link": "Link",
"icon": "Icon",
"open-in": "Öffnen im",
"new-tab": "neuen Tab",
"this-tab": "selben Tab",
"iframe": "iframe",
"className": "Klasse",
"classNamePlaceholder": "Optionale(r) CSS-Klassenname(n) für das Widget"
},
"tip": "Das <b>Icon</b> kann entweder ein <a href=\"https://klarsys.github.io/angular-material-icons/\" target=\"_blank\">Material-Design-Icon</a> (z.B. <code>check</code> oder <code>close</code>), ein <a href=\"https://fontawesome.com/v4.7.0/icons/\" target=\"_blank\">Font-Awesome-Icon</a> (z.B. <code>fa-fire</code>) oder ein <a href=\"https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md\" target=\"_blank\">Wetter-Icon</a> (z.B. <code>wi-wu-sunny</code>) sein.</p><p>Des Weiteren können alle Google-Material-Icons verwendet werden, indem dem Icon-Namen <code>mi-</code> vorangestellt wird (z.B. <code>mi-videogame_asset</code>).</p>"
}
}

View File

@@ -0,0 +1,21 @@
{
"ui_tab": {
"label": {
"home": "Home",
"tab": "Tab",
"name": "Name",
"icon": "Icon",
"state": "Status",
"navmenu": "Nav. Menü",
"enabled": "Aktiviert",
"disabled": "Deaktivert",
"visible": "Sichtbar",
"hidden": "Versteckt"
},
"info": {
"disabled": " Tab im Dashboard deaktiviert.",
"hidden": " Tab im Nav.-Menü versteckt."
},
"tip": "Das <b>Icon</b> kann entweder ein <a href=\"https://klarsys.github.io/angular-material-icons/\" target=\"_blank\">Material-Design-Icon</a> (z.B. <code>check</code> oder <code>close</code>), ein <a href=\"https://fontawesome.com/v4.7.0/icons/\" target=\"_blank\">Font-Awesome-Icon</a> (z.B. <code>fa-fire</code>) oder ein <a href=\"https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md\" target=\"_blank\">Wetter-Icon</a> (z.B. <code>wi-wu-sunny</code>) sein.</p><p>Des Weiteren können alle Google-Material-Icons verwendet werden, indem dem Icon-Namen <code>mi-</code> vorangestellt wird (z.B. <code>mi-videogame_asset</code>).</p>"
}
}

View File

@@ -0,0 +1,48 @@
<script type="text/html" data-help-name="ui_template">
<p>Das template-Widget kann alle gültigen HTML- und Angular/Angular-Material-Anweisungen enthalten.</p>
<p>Dieser Node kann verwendet werden, um ein dynamisches Benutzeroberflächenelement zu erstellen, dessen Erscheinungsbild
basierend auf der Eingangsnachricht geändert wird, und kann Nachrichten an Node-RED zurücksenden.</p>
<p><b>Zum Beispiel:</b><br>
<pre style="font-size:smaller;">
&lt;div layout=&quot;row&quot; layout-align=&quot;space-between&quot;&gt;
&lt;p&gt;The number is&lt;/p&gt;
&lt;font color=&quot;{{((msg.payload || 0) % 2 === 0) ? 'green' : 'red'}}&quot;&gt;
{{(msg.payload || 0) % 2 === 0 ? 'even' : 'odd'}}
&lt;/font&gt;
&lt;/div&gt;</pre>
Wird angezeigt, wenn die als <code>msg.payload</code> empfangene Zahl gerade oder ungerade ist.
Es wird auch die Farbe des Textes zu grün geändert, wenn die Zahl gerade ist, oder rot, wenn ungerade.</p>
<p>Das nächste Beispiel zeigt, wie eine eindeutige ID für Ihre Vorlage erstellt werden kann,
die Standardfarbe fürs Erscheinungsbild ausgewählt wird und wie auf eingehende Nachrichten geprüft werden kann.
<pre style="font-size:smaller;">
&lt;div id="{{'my_'+$id}}" style="{{'color:'+theme.base_color}}"&gt;Some text&lt;/div&gt;
&lt;script&gt;
(function(scope) {
scope.$watch('msg', function(msg) {
if (msg) {
// Do something when msg arrives
$("#my_"+scope.$id).html(msg.payload);
}
});
})(scope);
&lt;/script&gt;</pre>
Auf diese Weise erstellte Vorlagen können kopiert werden und bleiben unabhängig voneinander.</p>
<p><b>Senden einer Nachricht:</b><br>
<pre style="font-size:smaller;">
&lt;script&gt;
var value = "hello world";
// or overwrite value in your callback function ...
this.scope.action = function() { return value; }
&lt;/script&gt;
&lt;md-button ng-click=&quot;send({payload:action()})&quot;&gt;
Click me to send a hello world
&lt;/md-button&gt;</pre>
Zeigt einen Button an, welcher beim Klicken eine Nachricht mit Payload <code>'Hello world'</code> sendet.</p>
<p><b>Verwenden von <code>msg.template</code>:</b><br>
Der Vorlageninhalt kann auch über <code>msg.template</code> definiert werden.
So können beispielsweise externe Dateien verwendet werden.<br>
Die Vorlage wird bei eingehenden Nachrichten neu geladen, wenn sie verändert wurde.<br>
In das Vorlagefeld geschriebener Code wird ignoriert, wenn <code>msg.template</code> vorhanden ist.</p>
<p>Die folgenden Icon-Schriftarten sind verfügbar: <a href=\"https://klarsys.github.io/angular-material-icons/\" target=\"_blank\">Material-Design-Icon</a> (z.B. <code>check</code> oder <code>close</code>), ein <a href=\"https://fontawesome.com/v4.7.0/icons/\" target=\"_blank\">Font-Awesome-Icon</a> (z.B. <code>fa-fire</code>) oder ein <a href=\"https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md\" target=\"_blank\">Wetter-Icon</a> (z.B. <code>wi-wu-sunny</code>) sein.</p><p>Des Weiteren können alle Google-Material-Icons verwendet werden, indem dem Icon-Namen <code>mi-</code> vorangestellt wird (z.B. <code>mi-videogame_asset</code>).</p>
<p>Wenn eine <b>Klasse</b> angegeben ist, wird sie der übergeordneten Karte hinzugefügt. Auf diese Weise können Sie CSS-Stile auf die ui-card und ihre untergeordneten Elemente anwenden. Die Klasse kann zur Laufzeit festgelegt werden, indem eine Zeichenfolgeneigenschaft <code>msg.className</code> festgelegt wird.</p>
</script>

View File

@@ -0,0 +1,19 @@
{
"ui_template": {
"label": {
"type": "Vorlagentyp",
"local": "Widget in Gruppe",
"global": "Hinzugefügt zur <head>-Sektion der Seite",
"group": "Gruppe",
"size": "Größe",
"name": "Name",
"pass-through": "Nachrichten vom Eingang weiterleiten",
"store-state": "Ausgehende Nachrichten speichern",
"template": "Vorlage",
"expand": "Erweitern",
"resend": "Letzten Wert beim Aktualisieren neuladen",
"className": "Klasse",
"classNamePlaceholder": "Optionale(r) CSS-Klassenname(n) für das Widget"
}
}
}

View File

@@ -0,0 +1,29 @@
<script type="text/html" data-help-name="ui_ui_control">
<p>Ermöglicht die dynamische Steuerung des Dashboards.</p>
<p>Die Standardfunktion besteht darin, den aktuell angezeigten Tab zu ändern.
<code>msg.payload</code> sollte entweder ein Objekt der Form <code>{"tab":"my_tab_name"}</code> sein,
oder einfach der <b>Tab-Name</b> oder ein <b>numerischer Index</b> (ab 0) vom Tab oder Link, der angezeigt werden soll.</p>
<p>Das Senden eines leeren Namens "" aktualisiert die aktuell angezeigte Seite.
Es kann auch "+1" für den nächsten und "-1" für den vorherigen Tab gesendet werden.</p>
<p>Dashboard Seiten (also Tabs) können durch Senden eines <code>msg.payload</code>-Objektes im Format
<pre>{"tabs": {"hide": "tab_name_to_hide", "disable": ["secret_tab", "unused_stuff"]}}</pre> gesteuert werden.
Es sind zwei Zustände verfügbar: <b>show</b>/<b>hide</b> und <b>enable</b>/<b>disable</b></p>
<p>Die Sichtbarkeit von individuellen Widget-Gruppen können durch folgenden Payload gesteuert werden:
<pre>{"group": {"hide": ["tab_name_group_name_with_underscores"], "show": ["reveal_another_group"], "focus": true}}</pre>
<b>focus</b> ist optional und sorgt dafür, dass zu der richtigen Gruppe gescrollt wird.
Es können auch folgende Eigenschafen wie `open` und `close` verwendet werden, um den Status einer Gruppe zu setzen.
Die Gruppennamen sind die IDs der Gruppen und setzen sich aus <i>tab name</i> plus <i>group name</i> zusammen,
zusammengeschrieben mittels Leerzeichen-ersetzender Unterstriche.</p>
<p>Wenn ein Browser die Verbindung aufbaut oder verliert, den Tab wechselt oder eine Gruppe auf- bzw. zuklappt,
sendet dieser Node eine <code>msg</code> mit folgendem Inhalt:</p>
<ul>
<li><code>payload</code> - <i>connect</i>, <i>lost</i>, <i>change</i> oder <i>group</i>
<li><code>socketid</code> - die ID des Sockets (dies ändert sich jedes Mal, wenn der Browser die Seite neu lädt)
<li><code>socketip</code> - die IP-Adresse, von der die Verbindung stammt
<li><code>tab</code> - die Nummer des Tabs (nur für 'change' Ereignisse)
<li><code>name</code> - der Name des Tabs (nur für 'change' Ereignisse)
<li><code>group</code> - der Name der Gruppe (nur für 'group' Ereignisse)
<li><code>open</code> - der Status der Gruppe (nur für 'group' Ereignisse)
</ul>
<p>Optional - Nur neue Verbindungen melden - nützlich, um ein erneutes Senden von Daten an einen neuen Client auszulösen, ohne dass andere Ereignisse herausgefiltert werden müssen.</p>
</script>

View File

@@ -0,0 +1,10 @@
{
"ui_ui_control": {
"label": {
"name": "Name"
},
"placeholder": {
"name": "Name"
}
}
}

View File

@@ -0,0 +1,16 @@
<script type="text/html" data-help-name="ui_audio">
<p>Plays audio or text to speech (TTS) in the dashboard.</p>
<p>To work the dashboard web page must be open.</p>
<p>Expects <code>msg.payload</code> to contain a buffer of a <b>wav</b> or <b>mp3</b> file.</p>
<p>If your browser has native support for Text-to-Speech then a <code>msg.payload</code>
can also be a <b>string</b> to be read aloud.</p>
<p>Optionally setting <code>msg.level</code> from 0 to 100 will change the volume from 0 to 100%. Default is 100%.
In audio mode you can go up to 300, but you may get distortion.</p>
<p>When a <code>msg.reset</code> is available with value <code>true</code>, then playback of the current audio fragment will be stopped.</p>
<p>The <b>node status</b> reflects the current playback status:
<ul>
<li><b>started:</b> the audio fragment playback has been started.</li>
<li><b>reset:</b> the audio fragment playback has been reset (i.e. stopped before completed).</li>
</ul>
As soon as the audio fragment playback is completed, the node status will be cleared automatically.</p>
</script>

View File

@@ -0,0 +1,2 @@
<script type="text/html" data-help-name="ui_base">
</script>

View File

@@ -0,0 +1,102 @@
{
"ui_base": {
"label": {
"dashboard": "dashboard",
"category": "dashboard",
"title": "Title",
"options": "Options",
"date-format": "Date Format",
"sizes": "Sizes",
"horizontal": "Horizontal",
"vertical": "Vertical",
"widget-size": "1x1 Widget Size",
"widget-spacing": "Widget Spacing",
"group-padding": "Group Padding",
"group-spacing": "Group Spacing",
"layout": "Layout",
"angular": "Angular",
"theme": "Theme",
"site": "Site"
},
"auto": "auto",
"title": "Node-RED Dashboard",
"layout": {
"tab-and-link": "Tabs & Links",
"tab": "tab",
"link": "link",
"group": "group",
"edit": "edit",
"spacer": "spacer",
"layout": "layout",
"layout-editor": "Dashboard layout editor",
"width": "Width",
"auto": "auto-sizing",
"manual": "manual resize"
},
"theme": {
"style": "Style",
"custom-profile": "Custom Profile",
"custom-profile-name": "Untitled Theme 1",
"base-settings": "Base Settings",
"page-settings": "Page Settings",
"page": {
"title": "Title Bar Background",
"page": "Page Background",
"side": "Side Bar Background"
},
"group-settings": "Group Settings",
"group": {
"text": "Group Text",
"border": "Group Border",
"background": "Group Background"
},
"widget-settings": "Widget Settings",
"widget": {
"text": "Widget Text",
"colour": "Widget Colour",
"background": "Widget Background"
}
},
"style": {
"light": "Light (default)",
"dark": "Dark",
"custom": "Custom",
"primary": "Primary",
"accents": "Accents",
"background": "Background",
"warnings": "Warnings",
"palette": "Light / Dark"
},
"base": {
"colour": "Colour",
"font": "Font"
},
"font": {
"system": "System Font (default)"
},
"site": {
"title": "Node-RED Dashboard",
"date-format": "DD/MM/YYYY"
},
"title-bar": {
"show": "Show the title bar",
"hide": "Hide the title bar"
},
"swipe": {
"no-swipe": "No swipe between tabs",
"allow-swipe": "Allow swipe between tabs",
"allow-swipe-mouse": "Allow swipe (+mouse) between tabs",
"show-menu": "Swipe to open/close menu"
},
"lock": {
"clicked": "Click to show side menu",
"locked": "Always show side menu",
"locked-icon": "Always show icons only"
},
"temp": {
"allow-theme": "Node-RED theme everywhere",
"no-theme": "Use Angular theme in ui_template",
"none": "Angular theme everywhere"
}
}
}

View File

@@ -0,0 +1,20 @@
<script type="text/html" data-help-name="ui_button">
<p>Adds a button to the user interface.</p>
<p>Clicking the button generates a message with <code>msg.payload</code> set to the <b>Payload</b> field.
If no payload is specified, the node id is used.</p>
<p>The <b>Size</b> defaults to 3 by 1.</p>
<p>The <b>Icon</b> can be defined, as either a <a href="https://klarsys.github.io/angular-material-icons/" target="_blank">Material Design icon</a>
<i>(e.g. 'check', 'close')</i> or a <a href="https://fontawesome.com/v4.7.0/icons/" target="_blank">Font Awesome icon</a>
<i>(e.g. 'fa-fire')</i>, or a <a href="https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md">Weather icon</a>.
You can use the full set of google material icons if you add 'mi-' to the icon name. e.g. 'mi-videogame_asset'.</p>
<p>The colours of the text and background may be set. They can also be set by a message property by setting
the field to the name of the property, for example <code>{{background}}</code>.
You don't need to prepend the <i>msg.</i> part of the property name.</p>
<p>The label and icon can also be set by a message property by setting
the field to the name of the property, for example <code>{{topic}}</code> or <code>{{myicon}}</code>.</p>
<p>If set to pass through mode a message arriving on the input will act like pressing the button.
The output payload will be as defined in the node configuration.</p>
<p>The incoming <b>topic</b> field can be used to set the <code>msg.topic</code> property that is output.</p>
<p>Setting <code>msg.enabled</code> to <code>false</code> will disable the button.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,24 @@
{
"ui_button": {
"label": {
"group": "Group",
"size": "Size",
"icon": "Icon",
"optionalIcon": "optional icon",
"label": "Label",
"optionalLabel": "optional label",
"tooltip": "Tooltip",
"optionalTooltip": "optional tooltip",
"color": "Color",
"optionalColor": "optional text/icon color",
"background": "Background",
"optionalBackgroundColor": "optional background color",
"whenClicked": "When clicked, send:",
"payload": "Payload",
"topic": "Topic",
"emulateClick": "If msg arrives on input, emulate a button click:",
"className": "Class",
"classNamePlaceholder": "Optional CSS class name(s) for widget"
}
}
}

View File

@@ -0,0 +1,21 @@
<script type="text/html" data-help-name="ui_chart">
<p>Plots the input values on a chart. This can either be a time based line chart, a bar chart (vertical or horizontal),
or a pie chart.</p>
<p>Each input <code>msg.payload</code> value will be converted to a number. If the
conversion fails, the message is ignored.</p>
<p>Minimum and Maximum <b>Y</b> axis values are optional. The graph will auto-scale to any values received.</p>
<p>Multiple series can be shown on the same chart by using a different <code>msg.topic</code>
value on each input message. Multiple bars of the same series can be shown by using the <code>msg.label</code> property.</p>
<p>The <b>X</b> axis defines a time window or a maximum number of points to display. Older data will be automatically removed from the graph.
The axis labels can be formatted using a <a href="https://momentjs.com/docs/#/displaying/format/" target="_blank">
Moment.js time formatted</a> string.</p>
<p>Inputting a <code>msg.payload</code> containing a blank array <code>[]</code> will clear the chart.</p>
<p>See <b><a href="https://github.com/node-red/node-red-dashboard/blob/master/Charts.md" target="_new">this information</a></b>
for how to pre-format data to be passed in as a complete chart.</p>
<p>The <b>Blank label</b> field can be used to display some text before any valid data is received.</p>
<p>The label can also be set by a message property by setting
the field to the name of the property, for example <code>{{msg.topic}}</code>.</p>
<p>The node output contains an array of the chart state that can be persisted if needed. This can be passed
into the chart node to re-display the persisted data.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,56 @@
{
"ui_chart": {
"label": {
"group": "Group",
"size": "Size",
"label": "Label",
"optionalChartTitle": "optional chart title",
"type": "Type",
"lineChart": " &#xf201; Line chart",
"barChart": " &#xf080; Bar chart",
"barChartH": " &#xf080; Bar chart (H)",
"pieChart": " &#xf200; Pie chart",
"polarAreaChart": " &#xf200; Polar area chart",
"radarChart": " &#xf200; Radar chart",
"enlargePoints": "enlarge points",
"xAxis": "X-axis",
"last": "last",
"seconds": "seconds",
"minutes": "minutes",
"hours": "hours",
"days": "days",
"weeks": "weeks",
"or": "OR",
"points": "points",
"xAxisLabel": "X-axis Label",
"HHmmss": "HH:mm:ss",
"HHmm": "HH:mm",
"yearMonthDate": "Year-Month-Date",
"dateMonth": "Date/Month",
"dayHHmm": "Day HH:mm",
"custom": "custom",
"automatic": "automatic",
"asUTC": "as UTC",
"yAxis": "Y-axis",
"min": "min",
"max": "max",
"legend": "Legend",
"none": "None",
"show": "Show",
"interpolate": "Interpolate",
"linear": "linear",
"step": "step",
"bezier": "bezier",
"cubic": "cubic",
"cubicMono": "cubic-mono",
"cutout": "Cutout",
"useFirstColourForAllBars": "Use first colour for all bars",
"seriesColours": "Series Colours",
"blankLabel": "Blank label",
"displayThisTextBeforeValidDataArrives": "display this text before valid data arrives",
"useDifferentColor": "Use different colour for series data",
"className": "Class",
"classNamePlaceholder": "Optional CSS class name(s) for widget"
}
}
}

View File

@@ -0,0 +1,10 @@
<script type="text/html" data-help-name="ui_colour_picker">
<p>Adds a colour picker to the dashboard.</p>
<p>If the group width is 4 or greater then the picker can be set to be visible at all times.</p>
<p><b>Format</b> can be rgb, hex, hex8, hsv, or hsl. Transparency is supported for all except hex.</p>
<p>If a <b>Topic</b> is specified, it will be added as <code>msg.topic</code>.</p>
<p>Setting <code>msg.enabled</code> to <code>false</code> will disable the input.</p>
<p>If set to pass through mode a message arriving on the input will be evaluated for any colour format available
as Format. If the conversion fails #000000 will be used.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,7 @@
<script type="text/html" data-help-name="ui_date_picker">
<p>Adds a date picker widget to the user interface.</p>
<p>The date display can be formatted in the Dashboard - Site tab using <a href="https://momentjs.com/docs/#/displaying/">
moment.js</a> formatting. For example <code>MM/DD/YYYY</code>, <code>Do MMM YYYY</code> or <code>YYYY-MM-DD</code>.</p>
<p>Setting <code>msg.enabled</code> to <code>false</code> will disable the input.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,17 @@
<script type="text/html" data-help-name="ui_dropdown">
<p>Adds a dropdown select box to the user interface.</p>
<p>Multiple value / label pairs can be added as required. If the label is not specified the
value will be used for both.</p>
<p>The configured value of the selected item will be returned as <code>msg.payload</code>.</p>
<p>Setting <code>msg.payload</code> to one of the item values will preset the choice in the dropdown.
If using the multi-select option then the payload should be an array of values.</p>
<p>Optionally the user search term can deleted if the <code>msg.resetSearch</code> property is present and set to <i>true</i>.</p>
<p>Optionally the <b>Topic</b> field can be used to set the <code>msg.topic</code> property.</p>
<p>The Options may be configured by inputting <code>msg.options</code> containing an array.
If just text then the value will be the same as the label, otherwise you can specify both by
using an object of <code>"label":"value"</code> pairs :</p>
<code>[ "Choice 1", "Choice 2", {"Choice 3":"3"} ]</code>
<p></p>
<p>If the "Allow multiple selections" output option is enabled - the result will be returned as an array instead of a string.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,17 @@
<script type="text/html" data-help-name="ui_form">
<p>Adds a form to user interface.</p>
<p>Helps to collect multiple value from the user on submit button click as an object in <code>msg.payload</code> </p>
<p>Multiple input elements can be added using add elements button</p>
<p>Each element contains following components:</p>
<ul>
<li> <b>Label</b> : Value that will be the label of the element in the user interface</li>
<li> <b>Name</b> : Represents the key (variable name) in the <code>msg.payload</code> in which the value of the corresponding element present</li>
<li> <b>Type</b> : Drop down option to select the type of input element</li>
<li> <b>Required</b> : On switching on the user has to supply the value before submitting</li>
<li> <b>Rows</b> : number of UI rows for multiline text input</li>
<li> <b>Delete</b> : To remove the current element from the form</li>
</ul>
<p>Optionally the <b>Topic</b> field can be used to set the <code>msg.topic</code> property.</p>
<p>The Cancel button can be hidden by setting it's value to be blank "".</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,35 @@
{
"ui_form": {
"label": {
"group": "Group",
"size": "Size",
"label": "Label",
"optionalLabel": "optional label",
"formElements": "Form elements",
"type": "Type",
"required": "Required",
"rows": "UiRows",
"remove": "Remove",
"egName": "e.g. Name",
"egName2": "e.g. name",
"text": "Text",
"multiline": "Multiline",
"number": "Number",
"email": "E-mail",
"password": "Password",
"checkbox": "Checkbox",
"switch": "Switch",
"date": "Date",
"time": "Time",
"element": "element",
"buttons": "Buttons",
"submitButtonText": "submit button text",
"cancelButtonText": "cancel button text",
"topic": "Topic",
"optionalMsgTopic": "optional msg.topic",
"splitLayout":"Place the form elements in two columns",
"className": "Class",
"classNamePlaceholder": "Optional CSS class name(s) for widget"
}
}
}

View File

@@ -0,0 +1,15 @@
<script type="text/html" data-help-name="ui_gauge">
<p>Adds a gauge type widget to the user interface.</p>
<p>The <code>msg.payload</code> is searched for a numeric <i>value</i> and is formatted in accordance with
the defined <b>Value Format</b>, which can then be formatted using
<a href="https://docs.angularjs.org/api/ng/filter" target="_blank">Angular filters</a>.</p>
<p>For example : <code>{{value | number:1}}%</code> will round the value to one decimal place and append a % sign.</p>
<p>The colours of each of 3 sectors can be specified and the gauge will blend between them.
The colours should be specified in hex (#rrggbb) format.</p>
<p>If you specify numbers for the sectors then the colours changes per sector.
If not specified the colours are blended across the total range.</p>
<p>The gauge has several modes. Regular gauge, donut, compass and wave.</p>
<p>The label can also be set by a message property by setting
the field to the name of the property, for example <code>{{msg.topic}}</code>.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,4 @@
<script type="text/html" data-help-name="ui_group">
<p>Group</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,16 @@
{
"ui_group" : {
"label" : {
"name" : "Name",
"tab" : "Tab",
"width" : "Width",
"default" : "Default",
"group" : "Group",
"unassigned" : "unassigned",
"className": "Class",
"classNamePlaceholder": "Optional CSS class name(s) for widget"
},
"display-name" : "Display group name",
"collapse-name" : "Allow group to be collapsed"
}
}

View File

@@ -0,0 +1,8 @@
<script type="text/html" data-help-name="ui_link">
<p>The <b>Icon</b> can be defined, as either a <a href="https://klarsys.github.io/angular-material-icons/" target="_blank">Material Design icon</a>
<i>(e.g. 'check', 'close')</i> or a <a href="https://fontawesome.com/v4.7.0/icons/" target="_blank">Font Awesome icon</a>
<i>(e.g. 'fa-fire')</i>, or a <a href="https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md">Weather icon</a>.
You can use the full set of google material icons if you add 'mi-' to the icon name. e.g. 'mi-videogame_asset'.</p>
<p>The <b>Open in</b> field controls whether the link opens in a <i>New Tab</i>, or if the link is opened within an <i>iframe</i> on the same page. Some sites, including Google, do not allow the rendering of their page inside an iframe. If you select the <i>iframe</i> option and the site does not show, this is simply because that site forbids the use of it inside an iframe.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,16 @@
{
"ui_link" : {
"label" : {
"name" : "Name",
"link" : "Link",
"icon" : "Icon",
"open-in" : "Open in",
"new-tab" : "New Tab",
"this-tab" : "This Tab",
"iframe" : "iframe",
"className": "Class",
"classNamePlaceholder": "Optional CSS class name(s) for widget"
},
"tip" : "The <b>Icon</b> field can be either a <a href=\"https://klarsys.github.io/angular-material-icons/\" target=\"_blank\">Material Design icon</a> <i>(e.g. 'check', 'close')</i> or a <a href=\"https://fontawesome.com/v4.7.0/icons/\" target=\"_blank\">Font Awesome icon</a> <i>(e.g. 'fa-fire')</i>, or a <a href=\"https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md\" target=\"_blank\">Weather icon</a> <i>(e.g. 'wi-wu-sunny')</i>.</p><p>You can use the full set of google material icons if you add 'mi-' to the icon name. e.g. 'mi-videogame_asset'.</p>"
}
}

View File

@@ -0,0 +1,17 @@
<script type="text/html" data-help-name="ui_numeric">
<p>Adds a numeric input widget to the user interface.</p>
<p>The user can set the value between
the limits (<b>min</b> and <b>max</b>). Each value change will generate a <code>msg.payload</code>.</p>
<p>If <b>Topic</b> is specified, it will be added as <code>msg.topic</code>.<p>
<p>Any input <code>msg.payload</code> will be converted to a number, the <b>min</b> value will be used if conversion fails,
and it will update the user interface. If the value changes, it will also be passed to the output.</p>
<p>The <b>Value Format</b> field can be used to change the displayed format. For example, a <b>Value Format</b>
of <code>{{value}} %</code>
with a value of <b>23</b> will show <b>23 %</b> on the user interface. The <b>Value Format</b> field can contain
HTML or Angular filters to format the output (eg: <code>&amp;deg;</code> will show the degree symbol).</p>
<p>Setting the Value Format field to <code>{{msg.payload}}</code> will make the input field editable so you can type in a number.</p>
<p>The label can also be set by a message property by setting
the field to the name of the property, for example <code>{{msg.topic}}</code>.</p>
<p>Setting <code>msg.enabled</code> to <code>false</code> will disable the widget output.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,15 @@
<script type="text/html" data-help-name="ui_slider">
<p>Adds a slider widget to the user interface.</p>
<p>The user can change its value between the limits (<b>min</b> and <b>max</b>). Each value change
will generate a message with the value set as <b>payload</b>.</p>
<p>A vertical slider can be created by setting the size so that the height is greater than the width.</p>
<p>The slider can be reversed by setting the min value larger than the max value. e.g. min 100, max 0.</p>
<p>If a <b>Topic</b> is specified, it will be added as <code>msg.topic</code>.</p>
<p>An input <code>msg.payload</code> will be converted to a number, and used to preset a value.
The <b>min</b> value will be used if conversion fails,
and it will update the user interface. If the value changes, it will also be passed to the output.</p>
<p>The label can also be set by a message property by setting
the field to the name of the property, for example <code>{{msg.topic}}</code>.</p>
<p>Setting <code>msg.enabled</code> to <code>false</code> will disable the slider output.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,3 @@
<script type="text/html" data-help-name="ui_spacer">
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,19 @@
<script type="text/html" data-help-name="ui_switch">
<p>Adds a switch to the user interface.</p>
<p>Each change in the state of the switch will generate
a <code>msg.payload</code> with the specified <b>On</b> and <b>Off</b> values.</p>
<p>The <b>On/Off Color</b> and <b>On/Off Icon</b> are optional fields. If they are all present, the default
toggle switch will be replaced with the relevant icons and their respective colors.</p>
<p>The <b>On/Off Icon</b> field can be either a <a href="https://klarsys.github.io/angular-material-icons/" target="_blank">Material Design icon</a>
<i>(e.g. 'check', 'close')</i> or a <a href="https://fontawesome.com/v4.7.0/icons/" target="_blank">Font Awesome icon</a>
<i>(e.g. 'fa-fire')</i>, or a <a href="https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md">Weather icon</a>.
You can use the full set of google material icons if you add 'mi-' to the icon name. e.g. 'mi-videogame_asset'.</p>
<p>In pass through mode the switch state can be updated by an incoming <code>msg.payload</code> with the specified values,
that must also match the specified type (number, string, etc). When not in passthrough mode then the icon can either
track the state of the output - or the input msg.payload, in order to provide a closed loop feedback.</p>
<p>The label can also be set by a message property by setting
the field to the name of the property, for example <code>{{msg.topic}}</code>.</p>
<p>If a <b>Topic</b> is specified, it will be added to the output as <code>msg.topic</code>.</p>
<p>Setting <code>msg.enabled</code> to <code>false</code> will disable the switch widget.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,13 @@
<script type="text/html" data-help-name="ui_tab">
<p>Tab configuration for Dashboard</p>
<p><b>Disabled</b> pages are not included in the Dashboard app, and are therefore not functional.
The tab name still appears in the Navigation Menu (unless it is also hidden).
</p>
<p><b>Hidden</b> pages are not listed in the Left-hand Navigation Menu.
However, they are still active in the Dashboard, and can be shown by using a `ui_control` msg.
</p>
<p>The <b>Icon</b> field can be either a <a href="https://klarsys.github.io/angular-material-icons/" target="_blank">Material Design icon</a>
<i>(e.g. 'check', 'close')</i> or a <a href="https://fontawesome.com/v4.7.0/icons/" target="_blank">Font Awesome icon</a>
<i>(e.g. 'fa-fire')</i>, or a <a href="https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md">Weather icon</a>.
You can use the full set of google material icons if you add 'mi-' to the icon name. e.g. 'mi-videogame_asset'.</p>
</script>

View File

@@ -0,0 +1,21 @@
{
"ui_tab" : {
"label" : {
"home" : "Home",
"tab" : "Tab",
"name" : "Name",
"icon" : "Icon",
"state" : "State",
"navmenu" : "Nav. Menu",
"enabled" : "Enabled",
"disabled" : "Disabled",
"visible" : "Visible",
"hidden" : "Hidden"
},
"info": {
"disabled": " Tab is inactive in Dashboard.",
"hidden": " Tab is not shown in Navigation Menu."
},
"tip" : "The <b>Icon</b> field can be either a <a href=\"https://klarsys.github.io/angular-material-icons/\" target=\"_blank\">Material Design icon</a> <i>(e.g. 'check', 'close')</i> or a <a href=\"https://fontawesome.com/v4.7.0/icons/\" target=\"_blank\">Font Awesome icon</a> <i>(e.g. 'fa-fire')</i>, or a <a href=\"https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md\" target=\"_blank\">Weather icon</a> <i>(e.g. 'wi-wu-sunny')</i>.</p><p>You can use the full set of google material icons if you add 'mi-' to the icon name. e.g. 'mi-videogame_asset'.</p>"
}
}

View File

@@ -0,0 +1,49 @@
<script type="text/html" data-help-name="ui_template">
<p>The template widget can contain any valid html and Angular/Angular-Material directives.</p>
<p>This node can be used to create a dynamic user interface element that changes its appearance
based on the input message and can send back messages to Node-RED.</p>
<p><b>For example:</b><br>
<pre style="font-size:smaller;">&lt;div layout=&quot;row&quot; layout-align=&quot;space-between&quot;&gt;
&lt;p&gt;The number is&lt;/p&gt;
&lt;font color=&quot;{{((msg.payload || 0) % 2 === 0) ? 'green' : 'red'}}&quot;&gt;
{{(msg.payload || 0) % 2 === 0 ? 'even' : 'odd'}}
&lt;/font&gt;
&lt;/div&gt;</pre>
Will display if the number received as <code>msg.payload</code> is even or odd. It will also
change the color of the text to green if the number is even or red if odd.<br/>
The next example shows how to set a unique id for your template, pick up the default theme colour,
and watch for any incoming message.</p>
<pre style="font-size:smaller;">
&lt;div id="{{'my_'+$id}}" style="{{'color:'+theme.base_color}}"&gt;Some text&lt;/div&gt;
&lt;script&gt;
(function(scope) {
scope.$watch('msg', function(msg) {
if (msg) {
// Do something when msg arrives
$("#my_"+scope.$id).html(msg.payload);
}
});
})(scope);
&lt;/script&gt;</pre>
<p>Templates made in this way can be copied and remain independent of each other.</p>
<p><b>Sending a message:</b><br>
<pre style="font-size:smaller;">
&lt;script&gt;
var value = "hello world";
// or overwrite value in your callback function ...
this.scope.action = function() { return value; }
&lt;/script&gt;
&lt;md-button ng-click=&quot;send({payload:action()})&quot;&gt;
Click me to send a hello world
&lt;/md-button&gt;</pre>
Will display a button that when clicked will send a message with the payload <code>'Hello world'</code>.</p>
<p><b>Using <code>msg.template</code>:</b><br>
You can also define the template content via <code>msg.template</code>, so you can use external files for example.<br>
Template will be reloaded on input if it has changed.<br>
Code written in the Template field will be ignored when <code>msg.template</code> is present.</p>
<p>The following icon fonts are available: <a href="https://klarsys.github.io/angular-material-icons/" target="_blank">Material Design icon</a>
<i>(e.g. 'check', 'close')</i> or a <a href="https://fontawesome.com/v4.7.0/icons/" target="_blank">Font Awesome icon</a>
<i>(e.g. 'fa-fire')</i>, or a <a href="https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md">Weather icon</a>.
You can use the full set of Google material icons if you add 'mi-' to the icon name. e.g. 'mi-videogame_asset'.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,19 @@
{
"ui_template": {
"label": {
"type": "Template type",
"local": "Widget in group",
"global": "Added to site <head> section",
"group": "Group",
"size": "Size",
"name": "Name",
"pass-through": "Pass through messages from input.",
"store-state": "Add output messages to stored state.",
"template": "Template",
"expand": "Expand",
"resend": "Reload last value on refresh.",
"className": "Class",
"classNamePlaceholder": "Optional CSS class name(s) for widget"
}
}
}

View File

@@ -0,0 +1,16 @@
<script type="text/html" data-help-name="ui_text">
<p>Will display a non-editable text field on the user interface.</p>
<p>Each received <code>msg.payload</code> will update the text based on the provided <b>Value Format</b>.</p>
<p>The <b>Value Format</b> field can be used to change the displayed format and can contain valid HTML and
<a href="https://scotch.io/tutorials/all-about-the-built-in-angularjs-filters" target="_blank">Angular filters</a>.</p>
<p>For example: <code>{{value | uppercase}} &amp;deg;</code> will uppercase the payload text and add the degree symbol.</p>
<p>The label can also be set by a message property by setting
the field to the name of the property, for example <code>{{msg.topic}}</code>.</p>
<p>The following icon fonts are also available: <a href="https://klarsys.github.io/angular-material-icons/" target="_blank">Material Design icon</a>
<i>(e.g. 'check', 'close')</i> or a <a href="https://fontawesome.com/v4.7.0/icons/" target="_blank">Font Awesome icon</a>
<i>(e.g. 'fa-fire')</i>, or a <a href="https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md">Weather icon</a>.
You can use the full set of google material icons if you add 'mi-' to the icon name. e.g. 'mi-videogame_asset'.</p>
<p>The widget also has a class of <code>nr-dashboard-widget-{the_widget_label_with_underscores}</code> which can be used for additional
styling if required. You may need to use the <i>!important</i> flag to override the theme.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,16 @@
<script type="text/html" data-help-name="ui_text_input">
<p>Adds a text input field to the user interface. Mode can be regular text, email or color picker.</p>
<p>Any input is sent as <code>msg.payload</code>. If set to pass through mode an arriving <code>msg.payload</code>
will be used if it is different from the existing text in the input field. This allows you to preset
the text of the input field.</p>
<p>The <b>Delay</b> <i>(default : 300ms)</i> sets the amount of time in milliseconds before the output is sent.
Setting to <code>0</code> waits for "Enter" or "Tab" key to send. Enter will send payload but remain in focus.
Tab will send payload and move to next field. Clicking elsewhere will also send the payload.</p>
<p>The email mode will color in red if it is not a valid address and will return undefined.</p>
<p>The time input type returns a number of milliseconds from midnight.</p>
<p>Not all browsers support the <i>week</i> and <i>month</i> input types, and may return <i>undefined</i>.
Please test your target browser(s) before use.</p>
<p>If a <b>Topic</b> is specified, it will be added as <code>msg.topic</code>.</p>
<p>Setting <code>msg.enabled</code> to <code>false</code> will disable the input.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent card. This way you can style the card and the elements inside it with custom CSS. The Class can be set at runtime by setting a <code>msg.className</code> string property.</p>
</script>

View File

@@ -0,0 +1,16 @@
<script type="text/html" data-help-name="ui_toast">
<p>Shows <code>msg.payload</code> as a popup notification or OK / Cancel dialog
message on the user interface.</p>
<p>If a <code>msg.topic</code> is available it will be used as the title.</p>
<p>If you do not set an optional border highlight colour, then it can be set dynamically by <code>msg.highlight</code>.</p>
<p>You may also configure the position and duration of the toast notifications. If you leave the timeout blank
it can be set by <code>msg.timeout</code>. This does not apply to OK/Cancel dialogs.
<p>The dialog returns a <code>msg.payload</code> string of whatever you configure
the button label(s) to be. The second (cancel) button is optional, as is the return
value of <code>msg.topic</code>.</p>
<p>If you select 'OK, Cancel and Input' mode then <code>msg.payload</code> will contain
any text input by the user, rather than the OK button text.</p>
<p>The OK and Cancel button labels can be replaced by using <code>msg.ok</code> and <code>msg.cancel</code></p>
<p>Sending a blank payload will remove any active dialog without sending any data.</p>
<p>If a <b>Class</b> is specified, it will be added to the parent element. This way you can style the card and the elements inside it with custom CSS.</p>
</script>

View File

@@ -0,0 +1,28 @@
<script type="text/html" data-help-name="ui_ui_control">
<p>Allows dynamic control of the Dashboard.</p>
<p>The default function is to change the currently displayed tab. <code>msg.payload</code>
should either be an object of the form <code>{"tab":"my_tab_name"}</code>, or just be the <b>tab name</b>
or <b>numeric index</b> (from 0) of the tab or link to be displayed.</p>
<p>Sending a blank tab name "" will refresh the current page.
You can also send "+1" for next tab and "-1" for previous tab.</p>
<p>Dashboard pages (i.e. "tabs") can be controlled by sending a <code>msg.payload</code> object with the format
<pre>{"tabs": {"hide": "tab_name_to_hide", "disable": ["secret_tab", "unused_stuff"]}}</pre>.
There are 2 toggle states available: <b>show</b>/<b>hide</b> and <b>enable</b>/<b>disable</b></p>
<p>Visibility of individual groups of widgets can controlled by a payload like
<pre>{"group": {"hide": ["tab_name_group_name_with_underscores"], "show": ["reveal_another_group"], "focus": true}}</pre>
where <b>focus</b> is optional and will cause the screen to scroll to show that group if required.
You can also use properties `open` and `close` to set the state of a group that can be controlled by the user. The group
names are the IDs of the groups and are typically formed from the <i>tab name</i> plus <i>group name</i> joined with
underscores replacing all spaces.</p>
<p>When any browser client connects or loses connection, changes tab, or expands or collapses a group this node will emit a <code>msg</code> containing:</p>
<ul>
<li><code>payload</code> - <i>connect</i>, <i>lost</i>, <i>change</i>, or <i>group</i>.
<li><code>socketid</code> - the ID of the socket (this will change every time the browser reloads the page).
<li><code>socketip</code> - the ip address from where the connection originated.
<li><code>tab</code> - the number of the tab. (only for 'change' event).
<li><code>name</code> - the name of the tab. (only for 'change' event).
<li><code>group</code> - the name of the group. (only for 'group' event).
<li><code>open</code> - the state of the group. (only for 'group' event).
</ul>
<p>Optional - report only connect events - useful to use to trigger a resend of data to a new client without needing to filter out other events.</p>
</script>

View File

@@ -0,0 +1,16 @@
{
"ui_ui_control": {
"label": {
"name": "Name",
"output": "Output"
},
"placeholder": {
"name": "Name"
},
"events": {
"all": "Connect, lost, change tab or group events",
"change": "Change tab or group events only",
"connect": "Connect event only"
}
}
}

View File

@@ -0,0 +1,99 @@
{
"ui_base" : {
"label" : {
"dashboard" : "ダッシュボード",
"title" : "タイトル",
"options" : "オプション",
"date-format" : "日付形式",
"sizes" : "サイズ",
"horizontal" : "横",
"vertical" : "縦",
"widget-size" : "最小Widgetサイズ",
"widget-spacing" : "Widget間隔",
"group-padding" : "グループパディング",
"group-spacing" : "グループ間隔",
"layout" : "配置",
"angular": "Angular",
"theme" : "テーマ",
"site" : "サイト"
},
"auto" : "自動",
"title" : "Node-REDダッシュボード",
"layout" : {
"tab-and-link" : "タブ & リンク",
"tab" : "タブ",
"link" : "リンク",
"group" : "グループ",
"edit" : "編集",
"spacer": "スペーサ",
"layout" : "レイアウト",
"layout-editor" : "ダッシュボードレイアウトエディタ",
"width" : "幅",
"auto": "自動サイズ調整",
"manual": "手動サイズ変更"
},
"theme" : {
"style" : "スタイル",
"custom-profile" : "カスタムプロファイル",
"custom-profile-name" : "名称未設定テーマ 1",
"base-settings" : "基本設定",
"page-settings" : "ページ設定",
"page" : {
"title" : "タイトルバー背景色",
"page" : "ページ背景色",
"side" : "サイドバー背景色"
},
"group-settings" : "グループ設定",
"group" : {
"text" : "グループ文字色",
"border" : "グループボーダー色",
"background" : "グループ背景色"
},
"widget-settings" : "Widget設定",
"widget" : {
"text" : "Widget文字色",
"colour" : "Widget色",
"background" : "Widget背景色"
}
},
"style" : {
"light" : "ライト (デフォルト)",
"dark" : "ダーク",
"custom" : "カスタム",
"primary" : "プライマリ",
"accents" : "アクセント",
"background" : "背景",
"warnings" : "警告",
"palette": "ライト/ダーク"
},
"base" : {
"colour" : "色",
"font" : "フォント"
},
"font" : {
"system" : "システムフォント (デフォルト)"
},
"site" : {
"title" : "Node-RED ダッシュボード",
"date-format" : "YYYY/MM/DD"
},
"title-bar" : {
"show" : "タイトルバー表示",
"hide" : "タイトルバー非表示"
},
"swipe" : {
"no-swipe" : "スワイプによるタブ切り替えをしない",
"allow-swipe" : "スワイプによるタブ切り替えをする"
},
"lock" : {
"clicked" : "サイドメニューをクリックで表示",
"locked" : "サイドメニューを表示したままにする",
"locked-icon": "常にアイコンのみを表示"
},
"temp" : {
"no-theme" : "ui_templateでテーマ設定を許可しない",
"allow-theme" : "ui_templateでテーマ設定を許可する",
"none" : "Angularテーマを全ての箇所で使用"
}
}
}

View File

@@ -0,0 +1,13 @@
<script type="text/html" data-help-name="ui_chart">
<p>入力値をグラフに出力しますこのグラフは時系列の折れ線グラフ棒グラフ(縦や横)円グラフのいずれかです</p>
<p>入力値の<code>msg.payload</code></p>
<p><b>Y</b>調</p>
<p>入力メッセージ毎に異なる<code>msg.topic</code>1<code>msg.label</code></p>
<p><b>X</b>軸には、表示する時間、または最大のポイント数を定義します。古くなったデータは、自動的にグラフから削除されます。軸のラベルは、<a href="https://momentjs.com/docs/#/displaying/format/" target="_blank">Moment.jsの時間フォーマット</a>の文字列を用いて表示できます。</p>
<p><code>msg.payload</code>に空の配列<code>[]</code>を含む入力によってグラフを初期化できます。</p>
<p>全てのグラフに渡すデータの作成方法については、<b><a href="https://github.com/node-red/node-red-dashboard/blob/master/Charts.md" target="_new">本情報</a></b>を参照してください。</p>
<p><b>初期ラベル</b></p>
<p>プロパティ名(例えば<code>{{msg.topic}}</code>)</p>
<p>本ノードの出力には必要に応じて保存されたグラフの状態を持つ配列が含まれています本データをchartードに渡すことで保存されたデータを再表示することもできます</p>
<p>クラスが指定されている場合そのクラスは親要素に追加されます このようにしてカスタムCSSを使用してカードとその中の要素のスタイルを設定できます クラスは<code> msg.className </code></p>
</script>

View File

@@ -0,0 +1,56 @@
{
"ui_chart": {
"label": {
"group": "グループ",
"size": "サイズ",
"label": "ラベル",
"optionalChartTitle": "任意のグラフタイトル",
"type": "種類",
"lineChart": " &#xf201; 折れ線グラフ",
"barChart": " &#xf080; 棒グラフ",
"barChartH": " &#xf080; 棒グラフ(横)",
"pieChart": " &#xf200; 円グラフ",
"polarAreaChart": " &#xf200; 鶏頭図",
"radarChart": " &#xf200; レーダーチャート",
"enlargePoints": "ポイントを表示",
"xAxis": "X軸",
"last": "直近",
"seconds": "秒",
"minutes": "分",
"hours": "時間",
"days": "日",
"weeks": "週",
"or": "又は",
"points": "ポイント",
"xAxisLabel": "X軸ラベル",
"HHmmss": "HH:mm:ss",
"HHmm": "HH:mm",
"yearMonthDate": "年-月-日",
"dateMonth": "日/月",
"dayHHmm": "曜日 HH:mm",
"custom": "カスタム",
"automatic": "自動",
"asUTC": "UTCを使用",
"yAxis": "Y軸",
"min": "最小",
"max": "最大",
"legend": "凡例",
"none": "非表示",
"show": "表示",
"interpolate": "補完",
"linear": "直線",
"step": "段階",
"bezier": "ベジェ",
"cubic": "3次補間",
"cubicMono": "単調3次補間",
"cutout": "中心の切抜き率",
"useFirstColourForAllBars": "最初の色を全グラフで使用",
"seriesColours": "配色",
"blankLabel": "初期ラベル",
"displayThisTextBeforeValidDataArrives": "有効なデータが届く前に本文字列を表示",
"useDifferentColor": "シリーズに別の色を使用",
"className": "種類",
"classNamePlaceholder": "ウィジェット用のCSSクラス名オプション"
}
}
}

View File

@@ -0,0 +1,17 @@
<script type="text/html" data-help-name="ui_form">
<p>ユーザインターフェイスにフォームを追加します</p>
<p>ユーザから複数の値を受け付け送信ボタンのクリックによって<code>msg.payload</code></p>
<p>要素ボタンで追加することで複数の入力向けの要素を追加できます</p>
<p>各要素は以下の部品を持っています:</p>
<ul>
<li> <b>ラベル</b> : </li>
<li> <b>名前</b> : <code>msg.payload</code>()</li>
<li> <b>種類</b> : </li>
<li> <b>必須</b> : </li>
<li> <b>行数</b> : UI</li>
<li> <b>削除</b> : </li>
</ul>
<p><b>Topic</b><code>msg.topic</code></p>
<p>キャンセルボタンは値に空白""を設定することで非表示にできます</p>
<p>クラスが指定されている場合そのクラスは親要素に追加されます このようにしてカスタムCSSを使用してカードとその中の要素のスタイルを設定できます クラスは<code> msg.className </code></p>
</script>

View File

@@ -0,0 +1,35 @@
{
"ui_form": {
"label": {
"group": "グループ",
"size": "サイズ",
"label": "ラベル",
"optionalLabel": "任意のラベル",
"formElements": "フォームの要素",
"type": "種類",
"required": "必須",
"rows": "行数",
"remove": "削除",
"egName": "例) 名前",
"egName2": "例) name",
"text": "文字列",
"multiline": "複数行",
"number": "数値",
"email": "E-メール",
"password": "パスワード",
"checkbox": "チェックボックス",
"switch": "スイッチ",
"date": "日付",
"time": "時間",
"element": "要素",
"buttons": "ボタン",
"submitButtonText": "送信ボタンの文字列",
"cancelButtonText": "キャンセルボタンの文字列",
"topic": "トピック",
"optionalMsgTopic": "任意のmsg.topic",
"splitLayout": "フォーム要素を2列に配置",
"className": "種類",
"classNamePlaceholder": "ウィジェット用のCSSクラス名オプション"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"ui_group" : {
"label" : {
"name" : "名前",
"tab" : "タブ",
"width" : "幅",
"default" : "デフォルト",
"group" : "グループ",
"unassigned" : "未設定",
"className": "種類",
"classNamePlaceholder": "ウィジェット用のCSSクラス名オプション"
},
"display-name" : "グループ名を表示する",
"collapse-name" : "グループの折りたたみを有効にする"
}
}

View File

@@ -0,0 +1,16 @@
{
"ui_link" : {
"label" : {
"name" : "名前",
"link" : "リンク",
"icon" : "アイコン",
"open-in" : "開く方法",
"new-tab" : "新規タブ",
"this-tab" : "このタブ",
"iframe" : "iframe",
"className": "種類",
"classNamePlaceholder": "ウィジェット用のCSSクラス名オプション"
},
"tip" : "<b>アイコン</b>フィールドには <a href=\"https://klarsys.github.io/angular-material-icons/\" target=\"_blank\">Material Design icon</a> <i>(例: 'check', 'close')</i>、<a href=\"https://fontawesome.com/v4.7.0/icons/\" target=\"_blank\">Font Awesome icon</a> <i>(例: 'fa-fire')</i>、もしくは <a href=\"https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md\" target=\"_blank\">Weather icon</a> <i>(例: 'wi-wu-sunny')</i>を指定できます。</p>"
}
}

View File

@@ -0,0 +1,21 @@
{
"ui_tab" : {
"label" : {
"home" : "ホーム",
"tab" : "タブ",
"name" : "名前",
"icon" : "アイコン",
"state" : "状態",
"navmenu" : "メニュー",
"enabled" : "有効",
"disabled" : "無効",
"visible" : "表示",
"hidden" : "非表示"
},
"info": {
"disabled": " タブを無効化します",
"hidden": " タブを移動メニューに表示しません"
},
"tip" : "<b>アイコン</b>フィールドには <a href=\"https://klarsys.github.io/angular-material-icons/\" target=\"_blank\">Material Design icon</a> <i>(例: 'check', 'close')</i>、<a href=\"https://fontawesome.com/v4.7.0/icons/\" target=\"_blank\">Font Awesome icon</a> <i>(例: 'fa-fire')</i>、もしくは <a href=\"https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md\" target=\"_blank\">Weather icon</a> <i>(例: 'wi-wu-sunny')</i>を指定できます。</p>"
}
}

View File

@@ -0,0 +1,44 @@
<script type="text/html" data-help-name="ui_template">
<p>Template WidgetにはHTMLコードおよびAngular/Angular-Materialディレクティブを指定できます</p>
<p>このノードで動的なユーザインターフェイス要素を作成し入力によって見た目を変更したりメッセージをNode-REDに送り返したりできます</p>
<p><b>:</b><br>
<pre style="font-size:smaller;">&lt;div layout=&quot;row&quot; layout-align=&quot;space-between&quot;&gt;
&lt;p&gt;数値は&lt;/p&gt;
&lt;font color=&quot;{{((msg.payload || 0) % 2 === 0) ? 'green' : 'red'}}&quot;&gt;
{{(msg.payload || 0) % 2 === 0 ? '偶数' : '奇数'}}
&lt;/font&gt;
&lt;/div&gt;</pre>
このコードは<code>msg.payload</code>で受け取った数値が偶数か奇数かを表示します。同時に、偶数であれば緑に、奇数であれば赤にテキストの色を変更します。<br/>
次は一意なIDをテンプレートに設定デフォルトのテーマカラーを設定入力メッセージの到着を監視する例です</p>
<pre style="font-size:smaller;">
&lt;div id="{{'my_'+$id}}" style="{{'color:'+theme.base_color}}"&gt;何らかのテキスト&lt;/div&gt;
&lt;script&gt;
(function(scope) {
scope.$watch('msg', function(msg) {
if (msg) {
// メッセージ同着時に適当な処理を実行
$("#my_"+scope.$id).html(msg.payload);
}
});
})(scope);
&lt;/script&gt;</pre>
<p>この方法で作成したテンプレートはコピー可能ですコピーはそれぞれ独立して利用できます</p>
<p><b>メッセージ送信:</b><br>
<pre style="font-size:smaller;">
&lt;script&gt;
var value = "こんにちは世界";
// もしくは、コールバック関数で値を書き換え
this.scope.action = function() { return value; }
&lt;/script&gt;
&lt;md-button ng-click=&quot;send({payload:action()})&quot;&gt;
クリックするとこんにちは世界を送信します
&lt;/md-button&gt;</pre>
この例はクリックするとペイロードに<code>'こんにちは世界'</code></p>
<p><b><code>msg.template</code>使:</b><br>
<code>msg.template</code><br>
テンプレートは入力が変化した場合に再ロードされます<br>
HTMLコードフィールドに記述したコードは<code>msg.template</code></p>
<p>以下のアイコンフォントの利用も可能です: <a href="https://klarsys.github.io/angular-material-icons/" target="_blank">マテリアルデザインアイコン</a><i>(例:'check'、'close')</i><a href="https://fontawesome.com/v4.7.0/icons/" target="_blank">Font Awesomeアイコン</a><i>(例:'fa-fire')</i><a href="https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md">天気アイコン</a>
アイコン名に'mi-に追加することでGoogleマテリアルアイコン一式を利用できます。例:'mi-videogame_asset'</p>
<p>クラスが指定されている場合そのクラスは親要素に追加されます このようにしてカスタムCSSを使用してカードとその中の要素のスタイルを設定できます クラスは<code> msg.className </code></p>
</script>

View File

@@ -0,0 +1,19 @@
{
"ui_template" : {
"label" : {
"type" : "コード種別",
"local" : "グループ内のWidget",
"global" : "<head>ヘッドセクションへ追加",
"group" : "グループ",
"size" : "サイズ",
"name" : "名前",
"pass-through" : "入力メッセージをそのまま渡す",
"store-state" : "出力メッセージを状態として保存",
"template" : "HTMLコード",
"expand": "展開する",
"resend": "更新時に最後の値を再度読み込む",
"className": "種類",
"classNamePlaceholder": "ウィジェット用のCSSクラス名オプション"
}
}
}

View File

@@ -0,0 +1,25 @@
<script type="text/html" data-help-name="ui_ui_control">
<p>ダッシュボードの動的制御を行います</p>
<p>表示されているタブの切り替えが可能です<code>msg.payload</code>
The default function is to change the currently displayed tab. <code>msg.payload</code><code>{tab:""}</code><b></b><b></b> (0)</p>
<p>空のタブ名("")を送信すると表示されているページを更新しますまた"+1"を送ると次のタブ"-1"を送ると前のタブに切り替えられます</p>
<p>次の形式のオブジェクトを<code>msg.payload</code>
ダッシュボードページ(タブ)を制御できます<pre>{"tabs": {"hide": "非表示タブ", "disable": ["秘密タブ", "未使用"]}}</pre>
ここでは次の2つの状態が利用できます: <b>show</b>/<b>hide</b> <b>enable</b>/<b>disable</b></p>
<p>widgetグループの表示を次のようなペイロードで制御できます<br/>
<pre>{group:{hide:["タブ名_グループ名"], show:["他_グループ_表示"], focus:true}}</pre>
<b>focus</b>
また`open`および`close`プロパティを用いてグループの状態をユーザが制御できます
グループ名の指定は<i>タブ名</i>と<i>グループ名</i>を下線(_)で結合したものを用います空白は下線で置き換えます</p>
<p>クライアントのブラウザが接続もしくは切断した場合タブが変更された場合グループを拡大もしくは縮小した場合次のプロパティを持つメッセージを送信します:</p>
<ul>
<li><code>payload</code> - <i>connect</i><i>lost</i>、<i>change</i>もしくは<i>group</i>
<li><code>socketid</code> - ID()
<li><code>socketip</code> - IP,
<li><code>tab</code> - ('change')
<li><code>name</code> - ('change')
<li><code>group</code> - ('group')
<li><code>open</code> - ('group')
</ul>
<p>追加 - Connectイベントのみは新しいクライアントへのデータの再送を他のイベントをフィルタしないで行う場合に有用です</p>
</script>

View File

@@ -0,0 +1,16 @@
{
"ui_ui_control" : {
"label" : {
"name" : "名前",
"output": "出力"
},
"placeholder" : {
"name" : "名前"
},
"events": {
"all": "connect, lost, タブ変更もしくはグループイベント",
"change": "タブ変更もしくはグループイベントのみ",
"connect": "Connectイベントのみ"
}
}
}

69
node_modules/node-red-dashboard/nodes/ui_audio.html generated vendored Normal file
View File

@@ -0,0 +1,69 @@
<!DOCTYPE html>
<script type="text/javascript">
(function() {
var myvoice = 0;
var voices;
RED.nodes.registerType('ui_audio',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
paletteLabel: 'audio out',
color: 'rgb(119, 198, 204)',
defaults: {
name: {value:""},
group: {type: 'ui_group', required: true},
voice: {value:""},
always: {value:""}
},
inputs:1,
outputs:0,
icon: "feed.png",
align: "right",
label: function() { return this.name||"audio out"; },
labelStyle: function() { return this.name?"node_label_italic":""; },
onpaletteadd: function() {
if ('speechSynthesis' in window) { voices = window.speechSynthesis.getVoices(); }
},
oneditprepare: function() {
if ('speechSynthesis' in window) {
voices = window.speechSynthesis.getVoices();
for (i = 0; i < voices.length ; i++) {
//console.log(i,voices[i].name,voices[i].lang,voices[i].voiceURI,voices[i].default);
var option = document.createElement('option');
option.textContent = i + " : " + voices[i].name + ' (' + voices[i].lang + ')';
if (voices[i].default) { option.textContent += ' -- DEFAULT'; }
option.setAttribute('value', voices[i].voiceURI);
document.getElementById("node-input-voice").appendChild(option);
}
$('#node-input-voice').val(this.voice || 0);
}
else {
$('#voice-input-row').hide();
}
$("#node-input-voice").on("change", function() {
myvoice = this.voice = $("#node-input-voice").val();
});
}
});
})();
</script>
<script type="text/html" data-template-name="ui_audio">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row" id="voice-input-row">
<label for="node-input-voice"><i class="fa fa-language"></i> TTS Voice</label>
<select id="node-input-voice" style="width:70%"></select>
</div>
<div class="form-row">
<label for="node-input-always"></label>
<input type="checkbox" checked id="node-input-always" style="display:inline-block; width:auto; vertical-align:top;">
Play audio when window not in focus.
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>

49
node_modules/node-red-dashboard/nodes/ui_audio.js generated vendored Normal file
View File

@@ -0,0 +1,49 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function uiAudioNode(config) {
RED.nodes.createNode(this,config);
this.voice = config.voice;
this.group = config.group;
this.always = config.always || false;
if (this.group && RED.nodes.getNode(this.group).hasOwnProperty("config")) {
this.tabname = RED.nodes.getNode(RED.nodes.getNode(this.group).config.tab).name;
}
var node = this;
node.status({});
this.on('input', function(msg) {
if (msg.hasOwnProperty("level") && (isNaN(msg.level) || msg.level > 300 || msg.level < 0)) {
delete msg.level;
}
if (msg.reset == true) {
ui.emitSocket('ui-audio', { reset:true, tabname:node.tabname, always:node.always, socketid:msg.socketid });
}
else if (Buffer.isBuffer(msg.payload)) {
ui.emitSocket('ui-audio', { audio:msg.payload, tabname:node.tabname, always:node.always, vol:msg.level, socketid:msg.socketid });
}
else if (typeof msg.payload === "string") {
ui.emitSocket('ui-audio', { tts:msg.payload, voice:(node.voice || msg.voice || 0), tabname:node.tabname, always:node.always, vol:msg.level, socketid:msg.socketid });
}
});
var updateStatus = function(audioStatus) {
if (audioStatus === "complete") {
// When the audio or speech has played completely, clear the node status
node.status({});
}
else if (audioStatus.indexOf("error") === 0) {
node.status({shape:"ring",fill:"red",text:audioStatus});
}
else {
node.status({shape:"dot",fill:"blue",text:audioStatus});
}
};
ui.ev.on('audiostatus', updateStatus);
this.on('close', function() {
ui.ev.removeListener('audiostatus', updateStatus);
})
}
RED.nodes.registerType("ui_audio", uiAudioNode);
}

3401
node_modules/node-red-dashboard/nodes/ui_base.html generated vendored Normal file

File diff suppressed because it is too large Load Diff

125
node_modules/node-red-dashboard/nodes/ui_base.js generated vendored Normal file
View File

@@ -0,0 +1,125 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
var path= require('path');
var gsp = require.resolve('gridstack');
var node;
var uiset = RED.settings.ui || "{}";
function BaseNode(config) {
RED.nodes.createNode(this, config);
node = this;
var baseFontName = "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif";
var defaultLightTheme = {
baseColor: '#0094CE',
baseFont: baseFontName
}
var defaultDarkTheme = {
baseColor: '#097479',
baseFont: baseFontName
}
var defaultCustomTheme = {
name: 'Untitled Theme 1',
baseColor: defaultLightTheme.baseColor,
baseFont: baseFontName
}
var defaultAngularTheme = {
primary:'indigo',
accents:'teal',
warn: "red",
background:'grey',
palette:'light'
};
// Setup theme name
// First try old format (for upgrading with old flow file)
// Then try new format
// Else fallback to theme-light
var themeName;
if (typeof(config.theme) === 'string') { themeName = config.theme; }
else { themeName = config.theme.name || "theme-light"; }
// Setup other styles
var defaultThemeState = {}
if (themeName === 'theme-light') {
defaultThemeState["base-font"] = {value: baseFontName};
defaultThemeState["base-color"] = {value: "#0094CE"};
defaultThemeState["page-backgroundColor"] = {value: "#fafafa"};
defaultThemeState["page-titlebar-backgroundColor"] = {value: "#0094CE"};
defaultThemeState["page-sidebar-backgroundColor"] = {value: "#ffffff"};
defaultThemeState["group-backgroundColor"] = {value: "#ffffff"};
defaultThemeState["group-textColor"] = {value: "#000000"};
defaultThemeState["group-borderColor"] = {value: "#ffffff"};
defaultThemeState["widget-textColor"] = {value: "#111111"};
defaultThemeState["widget-backgroundColor"] = {value: "#0094CE"};
}
else {
defaultThemeState["base-font"] = {value: baseFontName};
defaultThemeState["base-color"] = {value: "#097479"};
defaultThemeState["page-backgroundColor"] = {value: "#111111"};
defaultThemeState["page-titlebar-backgroundColor"] = {value: "#097479"};
defaultThemeState["page-sidebar-backgroundColor"] = {value: "#333333"};
defaultThemeState["group-backgroundColor"] = {value: "#333333"};
defaultThemeState["group-textColor"] = {value: "#10cfd8"};
defaultThemeState["group-borderColor"] = {value: "#555555"};
defaultThemeState["widget-textColor"] = {value: "#eeeeee"};
defaultThemeState["widget-backgroundColor"] = {value: "#097479"};
}
var defaultThemeObject = {
name: themeName,
lightTheme: config.theme.lightTheme || defaultLightTheme,
darkTheme: config.theme.darkTheme || defaultDarkTheme,
customTheme: config.theme.customTheme || defaultCustomTheme,
angularTheme: config.theme.angularTheme || defaultAngularTheme,
themeState: config.theme.themeState || defaultThemeState
}
this.config = {
theme: defaultThemeObject,
site: config.site
}
ui.addBaseConfig(this.config);
}
RED.nodes.registerType("ui_base", BaseNode);
RED.library.register("themes");
RED.httpAdmin.get('/uisettings', function(req, res) {
res.json(uiset);
});
const optsjs = { root: path.join(__dirname , '../dist/js'), dotfiles: 'deny' };
const optscss = { root: path.join(__dirname , '../dist/css'), dotfiles: 'deny' };
const optsgs = { root: path.dirname(gsp), dotfiles: 'deny' };
RED.httpAdmin.get('/ui_base/js/*', function(req, res) {
res.sendFile(req.params[0], optsjs, function (err) {
if (err) {
res.sendStatus(404);
if (node) { node.warn("JS File not found."); }
else { console.log("ui_base - error:",err); }
}
});
});
RED.httpAdmin.get('/ui_base/css/*', function(req, res) {
res.sendFile(req.params[0], optscss, function (err) {
if (err) {
res.sendStatus(404);
if (node) { node.warn("CSS File not found."); }
else { console.log("ui_base - error:",err); }
}
});
});
RED.httpAdmin.get('/ui_base/gs/*', function(req, res) {
res.sendFile(req.params[0], optsgs, function (err) {
if (err) {
res.sendStatus(404);
if (node) { node.warn("Gridstack file not found."); }
else { console.log("ui_base - error:",err); }
}
});
});
};

116
node_modules/node-red-dashboard/nodes/ui_button.html generated vendored Normal file
View File

@@ -0,0 +1,116 @@
<script type="text/javascript">
RED.nodes.registerType('ui_button',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(176, 223, 227)',
defaults: {
name: {value: ''},
group: {type: 'ui_group', required: true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
passthru: {value: false},
label: {value: 'button'},
tooltip: {value: ''},
color: {value: ''},
bgcolor: {value: ''},
className: {value: ''},
icon: {value: ''},
payload: {value: '',validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('payloadType'):function(v) { return true})},
payloadType: { value: 'str'},
topic: {value: 'topic', validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('topicType'):function(v) { return true})},
topicType: {value: 'msg'}
},
inputs:1,
outputs:1,
outputLabels: function() { if (this.payloadType === "str") {
return this.payload;
} else {return this.payloadType; } },
icon: "ui_button.png",
paletteLabel: 'button',
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'button'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
$('#node-input-payload').typedInput({
default: 'str',
typeField: $("#node-input-payloadType"),
types: ['str','num','bool','json','bin','date','flow','global']
});
$('#node-input-topic').typedInput({
default: 'str',
typeField: $("#node-input-topicType"),
types: ['str','msg','flow','global']
});
}
});
</script>
<script type="text/html" data-template-name="ui_button">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> <span data-i18n="ui_button.label.group"></label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> <span data-i18n="ui_button.label.size"></label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-icon"><i class="fa fa-picture-o"></i> <span data-i18n="ui_button.label.icon"></label>
<input type="text" id="node-input-icon" data-i18n="[placeholder]ui_button.label.optionalIcon">
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> <span data-i18n="ui_button.label.label"></label>
<input type="text" id="node-input-label" data-i18n="[placeholder]ui_button.label.optionalLabel">
</div>
<div class="form-row">
<label for="node-input-tooltip"><i class="fa fa-info-circle"></i> <span data-i18n="ui_button.label.tooltip"></label>
<input type="text" id="node-input-tooltip" data-i18n="[placeholder]ui_button.label.optionalTooltip">
</div>
<div class="form-row">
<label for="node-input-color"><i class="fa fa-tint"></i> <span data-i18n="ui_button.label.color"></label>
<input type="text" id="node-input-color" data-i18n="[placeholder]ui_button.label.optionalColor">
</div>
<div class="form-row">
<label for="node-input-bgcolor"><i class="fa fa-tint"></i> <span data-i18n="ui_button.label.background"></label>
<input type="text" id="node-input-bgcolor" data-i18n="[placeholder]ui_button.label.optionalBackgroundColor">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-payload"><i class="fa fa-envelope-o"></i> <span data-i18n="ui_button.label.whenClicked"></label>
</div>
<div class="form-row">
<label for="node-input-payload" style="padding-left: 25px; margin-right: -25px"><span data-i18n="ui_button.label.payload"></label>
<input type="text" id="node-input-payload" style="width:70%">
<input type="hidden" id="node-input-payloadType">
</div>
<div class="form-row">
<label for="node-input-topic" style="padding-left: 25px; margin-right: -25px"><span data-i18n="ui_button.label.topic"></label>
<input type="text" id="node-input-topic">
<input type="hidden" id="node-input-topicType">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> <span data-i18n="ui_button.label.emulateClick"></label>
<input type="checkbox" id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> <span data-i18n="ui_button.label.className"></label>
<input type="text" id="node-input-className" data-i18n="[placeholder]ui_button.label.classNamePlaceholder"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>

91
node_modules/node-red-dashboard/nodes/ui_button.js generated vendored Normal file
View File

@@ -0,0 +1,91 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function ButtonNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
var payloadType = config.payloadType;
var payload = config.payload;
if (payloadType === 'flow' || payloadType === 'global') {
try {
var parts = RED.util.normalisePropertyExpression(payload);
if (parts.length === 0) {
throw new Error();
}
}
catch(err) {
node.warn("Invalid payload property expression - defaulting to node id")
payload = node.id;
payloadType = 'str';
}
}
else {
payload = payload || node.id;
}
node.on("input", function(msg) {
try {
node.topi = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg);
}
catch(e) { }
});
var done = ui.add({
node: node,
tab: tab,
group: group,
emitOnlyNewValues: false,
forwardInputMessages: config.passthru || false,
storeFrontEndInputAsState: false,
control: {
type: 'button',
label: config.label,
tooltip: config.tooltip,
color: config.color,
bgcolor: config.bgcolor,
className: config.className,
icon: config.icon,
order: config.order,
value: payload,
format: config.bgcolor,
width: config.width || group.config.width || 3,
height: config.height || 1
},
beforeSend: function (msg,m2) {
var t = undefined;
try {
t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg)
}
catch(e) { }
if (typeof t === "undefined") { t = node.topi; }
if (t !== undefined) { msg.topic = t; }
if (((config.topicType || "str") === "str") && t == "") { delete msg.topic; }
if (m2 !== undefined) { msg.event = m2.event; }
},
convertBack: function (value) {
if (payloadType === "date") {
value = Date.now();
}
else {
try {
value = RED.util.evaluateNodeProperty(payload,payloadType,node);
}
catch(e) {
if (payloadType === "bin") { node.error("Badly formatted buffer"); }
else { node.error(e,payload); }
}
}
return value;
}
});
node.on("close", done);
}
RED.nodes.registerType("ui_button", ButtonNode);
};

295
node_modules/node-red-dashboard/nodes/ui_chart.html generated vendored Normal file
View File

@@ -0,0 +1,295 @@
<style>
input.series-color {
width: 100px;
text-align: center;
}
input.series-color::-webkit-color-swatch {
border: none;
}
</style>
<script type="text/javascript">
RED.nodes.registerType('ui_chart',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(119, 198, 204)',
defaults: {
name: {value: ''},
group: {type: 'ui_group', required: true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}},
height: {value: 0},
label: {value: 'chart'},
chartType: {value: 'line'},
legend: {value: 'false'},
xformat: {value: 'HH:mm:ss'},
interpolate: {value: 'linear', required:true},
nodata: {value: ''},
dot: {value: false},
ymin: {value: '', validate:function(value) { return value === '' || RED.validators.number(); }},
ymax: {value: '', validate:function(value) { return value === '' || RED.validators.number(); }},
removeOlder: {value: 1, validate:RED.validators.number(), required:true},
removeOlderPoints: {value: '', validate:function(value) { return value === '' || RED.validators.number(); }},
removeOlderUnit: {value: '3600', required:true},
cutout: {value: 0},
useOneColor: {value: false},
useUTC: {value: false},
colors: {value: ['#1F77B4', '#AEC7E8', '#FF7F0E', '#2CA02C', '#98DF8A', '#D62728', '#FF9896', '#9467BD', '#C5B0D5']},
outputs: {value: 1},
useDifferentColor: {value: false},
className: {value: ''}
},
inputs:1,
outputs:1,
inputLabels: function() { return this.chartType; },
outputLabels: ["chart state"],
align: "right",
icon: "ui_chart.png",
paletteLabel: 'chart',
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'chart'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
var oldouts = this.outputs;
if (RED.nodes.filterLinks({source:{id:this.id},sourcePort:1}).length > 0) { this.outputs = 2; }
else { this.outputs = 1; }
if (this.outputs !== oldouts) { this.changed = true; }
if (!$("#node-input-chartType").val()) {
$("#node-input-chartType").val("line");
}
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
$("#node-input-chartType").on("change", function() {
$("#legend-show").hide();
$("#show-useDifferentColor").hide();
if ($(this).val() === "horizontalBar") {
$("#y-label-show").hide();
$("#x-label-show").show();
}
else {
$("#y-label-show").show();
$("#x-label-show").hide();
}
if ($(this).val() === "line") {
$("#x-axis-show").show();
$("#x-axis-label-show").show();
$("#interpolate-show").show();
$("#legend-show").show();
$("#y-axis-show").show();
$("#hole-size-show").hide();
$("#show-dot-field").show();
$("#show-useOneColor").hide();
}
else {
$("#x-axis-show").hide();
$("#x-axis-label-show").hide();
$("#interpolate-show").hide();
$("#show-dot-field").hide();
if (($(this).val() === "bar")||($(this).val() === "horizontalBar")) {
$("#show-useOneColor").show();
$("#legend-show").show();
}
else {
$("#show-useOneColor").hide();
}
if ($(this).val() === "pie") {
$("#y-axis-show").hide();
$("#legend-show").show();
$("#hole-size-show").show();
}
else if ($(this).val() === "polar-area") {
$("#y-axis-show").show();
$("#legend-show").show();
$("#hole-size-show").hide();
$("#show-useDifferentColor").show();
}
else if ($(this).val() === "radar") {
$("#y-axis-show").show();
$("#legend-show").show();
$("#hole-size-show").hide();
}
else {
$("#y-axis-show").show();
$("#hole-size-show").hide();
}
}
});
var setColour = function(id, value) {
$(id).val(value);
$(id).css("background-color", value);
var rgb = tinycolor(value).toRgb();
var level = ((rgb.r*299) + (rgb.g*587) + (rgb.b*114))/1000;
var textColor = (level >= 128) ? '#111111' : '#eeeeee';
$(id).css("color", textColor);
}
$(".series-color").on("change", function() {
setColour("#"+$(this).attr("id"), $(this).val());
});
var oval = $("#node-input-xformat").val();
if (!oval) { $("#node-input-xformat").val("HH:mm:ss"); }
var odef = 'custom';
if (oval === "HH:mm:ss") { odef = oval; }
if (oval === "HH:mm") { odef = oval; }
if (oval === "Y-M-D") { odef = oval; }
if (oval === "D/M") { odef = oval; }
if (oval === "dd HH:mm") { odef = oval; }
if (oval === "auto") { odef = oval; }
var ohms = {value: "HH:mm:ss", label: RED._("node-red-dashboard/ui_chart:ui_chart.label.HHmmss"), hasValue: false};
var ohm = {value: "HH:mm", label: RED._("node-red-dashboard/ui_chart:ui_chart.label.HHmm"), hasValue: false};
var oymd = {value: "Y-M-D", label: RED._("node-red-dashboard/ui_chart:ui_chart.label.yearMonthDate"), hasValue: false};
var odm = {value: "D/M", label: RED._("node-red-dashboard/ui_chart:ui_chart.label.dateMonth"), hasValue: false};
var oahm = {value: "dd HH:mm", label: RED._("node-red-dashboard/ui_chart:ui_chart.label.dayHHmm"), hasValue: false};
var ocus = {value: "custom", label: RED._("node-red-dashboard/ui_chart:ui_chart.label.custom"), icon: "red/images/typedInput/az.png"};
var oaut = {value: "auto", label: RED._("node-red-dashboard/ui_chart:ui_chart.label.automatic"), hasValue: false};
$("#node-input-xformat").typedInput({
default: odef,
types:[ ohms, ohm, oahm, odm, oymd, ocus, oaut ]
});
var defaultColors = ['#1F77B4', '#AEC7E8', '#FF7F0E', '#2CA02C', '#98DF8A', '#D62728', '#FF9896', '#9467BD', '#C5B0D5'];
if (this.colors) {
for (var i=0; i<this.colors.length; i++) {
var value = this.colors[i] || defaultColors[i];
setColour("#node-input-color"+(i+1), value);
}
}
else {
for (var c=0; c<defaultColors.length; c++) {
setColour("#node-input-color"+(c+1), defaultColors[c]);
}
}
},
oneditsave: function() {
if ($("#node-input-xformat").typedInput('type') !== 'custom') {
$("#node-input-xformat").val($("#node-input-xformat").typedInput('type'));
}
this.colors = [$("#node-input-color1").val(),$("#node-input-color2").val(),$("#node-input-color3").val(),
$("#node-input-color4").val(),$("#node-input-color5").val(),$("#node-input-color6").val(),
$("#node-input-color7").val(),$("#node-input-color8").val(),$("#node-input-color9").val()];
}
});
</script>
<script type="text/html" data-template-name="ui_chart">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> <span data-i18n="ui_chart.label.group"></label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> <span data-i18n="ui_chart.label.size"></label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> <span data-i18n="ui_chart.label.label"></label>
<input type="text" id="node-input-label" data-i18n="[placeholder]ui_chart.label.optionalChartTitle">
</div>
<div class="form-row">
<label for="node-input-removeOlder"><i class="fa fa-line-chart"></i> <span data-i18n="ui_chart.label.type"></label>
<select id="node-input-chartType" style="width:159px; font-family:'FontAwesome','Helvetica Neue', Helvetica, Arial, sans-serif">
<option value="line" data-i18n="[html]ui_chart.label.lineChart"></option>
<option value="bar" data-i18n="[html]ui_chart.label.barChart"></option>
<option value="horizontalBar" data-i18n="[html]ui_chart.label.barChartH"></option>
<option value="pie" data-i18n="[html]ui_chart.label.pieChart"></option>
<option value="polar-area" data-i18n="[html]ui_chart.label.polarAreaChart"></option>
<option value="radar" data-i18n="[html]ui_chart.label.radarChart"></option>
</select>
<div id="show-dot-field" style="display:inline-block;">
<input type="checkbox" id="node-input-dot" style="display:inline-block; width:auto; vertical-align:baseline; margin-left:40px; margin-right:5px;"><span data-i18n="ui_chart.label.enlargePoints">
</div>
</div>
<div class="form-row" id="x-axis-show">
<label for="node-input-removeOlder" data-i18n="ui_chart.label.xAxis"></label>
<label for="node-input-removeOlder" style="width:auto" data-i18n="ui_chart.label.last"></label>
<input type="text" id="node-input-removeOlder" style="width:50px;">
<select id="node-input-removeOlderUnit" style="width:80px;">
<option value="1" data-i18n="ui_chart.label.seconds"></option>
<option value="60" data-i18n="ui_chart.label.minutes"></option>
<option value="3600" data-i18n="ui_chart.label.hours"></option>
<option value="86400" data-i18n="ui_chart.label.days"></option>
<option value="604800" data-i18n="ui_chart.label.weeks"></option>
</select>
<label for="node-input-removeOlderPoints" style="width:auto; margin-left:10px; margin-right:10px;" data-i18n="ui_chart.label.or"></label>
<input type="text" id="node-input-removeOlderPoints" style="width:60px;" placeholder="1000">
<span style="margin-left:5px;" data-i18n="ui_chart.label.points"></span>
</div>
<div class="form-row" id="x-axis-label-show">
<label for="node-input-xformat" data-i18n="ui_chart.label.xAxisLabel"></label>
<input type="text" id="node-input-xformat" style="width:250px;">
<input type="checkbox" id="node-input-useUTC" style="display:inline-block; width:auto; vertical-align:baseline; margin-left:8px; margin-right:4px;"> <span data-i18n="ui_chart.label.asUTC"></span>
</div>
<div class="form-row" id="y-axis-show">
<label id="y-label-show" for="node-input-ymin" data-i18n="ui_chart.label.yAxis"></label>
<label id="x-label-show" for="node-input-ymin" data-i18n="ui_chart.label.xAxis"></label>
<label for="node-input-ymin" style="width:auto" data-i18n="ui_chart.label.min"></label>
<input type="text" id="node-input-ymin" style="width:92px">
<label for="node-input-ymax" style="width:auto; margin-left:20px;" data-i18n="ui_chart.label.max"></label>
<input type="text" id="node-input-ymax" style="width:92px">
</div>
<div class="form-row" id="legend-show">
<label for="node-input-legend" data-i18n="ui_chart.label.legend"></label>
<select id="node-input-legend" style="width:120px;">
<option value="false" data-i18n="ui_chart.label.none"></option>
<option value="true" data-i18n="ui_chart.label.show"></option>
</select>
<span id="interpolate-show">&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="ui_chart.label.interpolate"></span>
<select id="node-input-interpolate" style="width:120px;">
<option value="linear" data-i18n="ui_chart.label.linear"></option>
<option value="step" data-i18n="ui_chart.label.step"></option>
<option value="bezier" data-i18n="ui_chart.label.bezier"></option>
<option value="cubic" data-i18n="ui_chart.label.cubic"></option>
<option value="monotone" data-i18n="ui_chart.label.cubicMono"></option>
</select>
</span>
<span id="hole-size-show">&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="ui_chart.label.cutout"></span>
<input type="text" id="node-input-cutout" style="width:35px"> %
</span>
</div>
<div id="show-useOneColor" style="display:none; height:24px;">
<input type="checkbox" id="node-input-useOneColor" style="display:inline-block; width:auto; vertical-align:baseline; margin-left:105px; margin-right:5px;"><span data-i18n="ui_chart.label.useFirstColourForAllBars"></span>
</div>
<div class="form-row" id="ui-chart-colours">
<label for="node-input-color1" data-i18n="ui_chart.label.seriesColours"></label>
<input type="color" id="node-input-color1" class="series-color" style="width:100px;"/>
<input type="color" id="node-input-color2" class="series-color" style="width:100px;"/>
<input type="color" id="node-input-color3" class="series-color" style="width:100px;"/>
<div style="margin-top:5px; margin-left:104px;">
<input type="color" id="node-input-color4" class="series-color" style="width:100px;"/>
<input type="color" id="node-input-color5" class="series-color" style="width:100px;"/>
<input type="color" id="node-input-color6" class="series-color" style="width:100px;"/>
</div>
<div style="margin-top:5px; margin-left:104px;">
<input type="color" id="node-input-color7" class="series-color" style="width:100px;"/>
<input type="color" id="node-input-color8" class="series-color" style="width:100px;"/>
<input type="color" id="node-input-color9" class="series-color" style="width:100px;"/>
</div>
</div>
<div id="show-useDifferentColor" class="form-row">
<label></label>
<input type="checkbox" id="node-input-useDifferentColor" style="display:inline-block; width:auto; vertical-align:top;">
<span data-i18n="ui_chart.label.useDifferentColor"></span>
</input>
</div>
<div class="form-row">
<label for="node-input-nodata" data-i18n="ui_chart.label.blankLabel"></label>
<input type="text" id="node-input-nodata" data-i18n="[placeholder]ui_chart.label.displayThisTextBeforeValidDataArrives">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> <span data-i18n="ui_chart.label.className"></span></label>
<input type="text" id="node-input-className" data-i18n="[placeholder]ui_chart.label.classNamePlaceholder"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>

234
node_modules/node-red-dashboard/nodes/ui_chart.js generated vendored Normal file
View File

@@ -0,0 +1,234 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
var ChartIdList = {};
function ChartNode(config) {
RED.nodes.createNode(this, config);
this.chartType = config.chartType || "line";
var node = this;
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
if (config.width === "0") { delete config.width; }
if (config.height === "0") { delete config.height; }
// number of pixels wide the chart will be... 43 = sizes.sx - sizes.px
//var pixelsWide = ((config.width || group.config.width || 6) - 1) * 43 - 15;
if (!tab || !group) { return; }
var dnow = Date.now();
var options = {
emitOnlyNewValues: true,
node: node,
tab: tab,
group: group,
control: {
type: 'chart',
look: node.chartType,
order: config.order,
label: config.label,
legend: config.legend || false,
interpolate: config.interpolate,
nodata: config.nodata,
width: parseInt(config.width || group.config.width || 6),
height: parseInt(config.height || group.config.width/2+1 || 4),
ymin: config.ymin,
ymax: config.ymax,
dot: config.dot || false,
xformat : config.xformat || "HH:mm:ss",
cutout: parseInt(config.cutout || 0),
colors: config.colors,
useOneColor: config.useOneColor || false,
useUTC: config.useUTC || false,
animation: false,
spanGaps: false,
useDifferentColor: config.useDifferentColor || false,
options: {},
className: config.className || '',
},
convertBack: function(data) {
if (data) {
if (data[0] && data[0].hasOwnProperty("values")) {
return [data[0].values];
}
if (data.length == 0) {
return [];
}
}
},
convert: function(value, oldValue, msg) {
var converted = {};
if (ChartIdList.hasOwnProperty(node.id) && ChartIdList[node.id] !== node.chartType) {
value = [];
}
if (this.control.look !== node.chartType) {
if ((this.control.look === "line") || (node.chartType === "line")) { value = []; }
node.chartType = this.control.look;
}
ChartIdList[node.id] = node.chartType;
if (Array.isArray(value)) {
if (value.length === 0) { // reset chart
converted.update = false;
converted.updatedValues = [];
return converted;
}
if (value[0].hasOwnProperty("series") && value[0].hasOwnProperty("data")) {
if (!Array.isArray(value[0].series)) { node.error("series not array",msg); return; }
if (!Array.isArray(value[0].data)) { node.error("Data not array",msg); return; }
var flag = true;
for (var dd = 0; dd < value[0].data.length; dd++ ) {
if (!isNaN(value[0].data[dd][0])) { flag = false; }
}
if (node.chartType === "line") {
if (flag) { delete value[0].labels; }
if (config.removeOlderPoints) {
for (var dl=0; dl < value[0].data.length; dl++ ) {
if (value[0].data[dl].length > config.removeOlderPoints) {
value[0].data[dl] = value[0].data[dl].slice(-config.removeOlderPoints);
}
}
}
}
else if (node.chartType === "bar" || node.chartType === "horizontalBar") {
if (flag) {
var tmp = [];
for (var d=0; d<value[0].data.length; d++) {
tmp.push([value[0].data[d]]);
}
value[0].data = tmp;
var tmp2 = value[0].series;
value[0].series = value[0].labels;
value[0].labels = tmp2;
}
}
value = [{ key:node.id, values:(value[0] || {series:[], data:[], labels:[]}) }];
}
else {
node.warn("Bad data inject");
value = oldValue;
}
converted.update = false;
converted.updatedValues = value;
}
else {
if (value === false) { value = null; } // let false also create gaps in chart
if (value !== null) { // let null object through for gaps
value = parseFloat(value); // only handle numbers
if (isNaN(value)) { return; } // return if not a number
}
converted.newPoint = true;
var label = msg.label || " ";
var series = msg.series || msg.topic || "";
//if (node.chartType === "bar" || node.chartType === "horizontalBar" || node.chartType === "pie") {
if (node.chartType !== "line") {
if (!msg.series) {
label = msg.topic || msg.label || " ";
series = msg.series || "";
}
}
if ((!oldValue) || (oldValue.length === 0)) {
oldValue = [{ key:node.id, values:{ series:[], data:[], labels:[] } }];
}
//if (node.chartType === "line" || node.chartType === "pie" || node.chartType === "bar" || node.chartType === "horizontalBar" || node.chartType === "radar") { // Line, Bar and Radar
var refill = false;
if (node.chartType === "line") { label = ""; }
var s = oldValue[0].values.series.indexOf(series);
if (!oldValue[0].values.hasOwnProperty("labels")) { oldValue[0].values.labels = []; }
var l = oldValue[0].values.labels.indexOf(label);
if (s === -1) {
oldValue[0].values.series.push(series);
s = oldValue[0].values.series.length - 1;
oldValue[0].values.data[s] = [];
if (l > 0) { refill = true; }
}
if (l === -1) {
oldValue[0].values.labels.push(label);
l = oldValue[0].values.labels.length - 1;
if (l > 0) { refill = true; }
}
if (node.chartType === "line") {
var time;
if (msg.timestamp !== undefined) { time = new Date(msg.timestamp).getTime(); }
else { time = new Date().getTime(); }
var limitOffsetSec = parseInt(config.removeOlder) * parseInt(config.removeOlderUnit);
var limitTime = time - limitOffsetSec * 1000;
if (time < limitTime) { return oldValue; } // ignore if too old for window
var point = { "x":time, "y":value };
oldValue[0].values.data[s].push(point);
converted.newPoint = [{ key:node.id, update:true, values:{ series:series, data:point, labels:label } }];
var rc = 0;
for (var u = 0; u < oldValue[0].values.data[s].length; u++) {
if (oldValue[0].values.data[s][u].x >= limitTime) { break; } // stop as soon as we are in time window.
else { rc += 1; }
}
if (rc > 0) { oldValue[0].values.data[s].splice(0,rc); }
if (config.removeOlderPoints) {
var rc2 = oldValue[0].values.data[s].length-config.removeOlderPoints;
if (rc2 > 0) { oldValue[0].values.data[s].splice(0,rc2); rc = rc2;}
}
if (rc > 0) { converted.newPoint[0].remove = rc; }
var swap; // insert correctly if a timestamp was earlier.
for (var t = oldValue[0].values.data[s].length-2; t>=0; t--) {
if (oldValue[0].values.data[s][t].x <= time) {
break; // stop if we are in the right place
}
else {
swap = oldValue[0].values.data[s][t];
oldValue[0].values.data[s][t] = oldValue[0].values.data[s][t+1];
oldValue[0].values.data[s][t+1] = swap;
}
}
if (swap) { converted.newPoint = true; } // if inserted then update whole chart
if (Date.now() > (dnow + 60000)) {
dnow = Date.now();
for (var x = 0; x < oldValue[0].values.data.length; x++) {
for (var y = 0; y < oldValue[0].values.data[x].length; y++) {
if (oldValue[0].values.data[x][y].x >= limitTime) {
break; // stop as soon as we are in time window.
}
else {
oldValue[0].values.data[x].splice(0,1);
converted.newPoint = true;
y = y - 1;
}
}
}
}
}
else {
oldValue[0].values.data[s][l] = value;
if (refill) {
for (var i = 0; i < oldValue[0].values.series.length; i++) {
for (var k = 0; k < oldValue[0].values.labels.length; k++) {
oldValue[0].values.data[i][k] = oldValue[0].values.data[i][k] || null;
}
}
}
}
converted.update = true;
converted.updatedValues = oldValue;
}
return converted;
}
};
var chgtab = function() {
node.receive({payload:"R"});
};
ui.ev.on('changetab', chgtab);
var done = ui.add(options);
var st = setTimeout(function() {
node.emit("input",{payload:"start"}); // trigger a redraw at start to flush out old data.
}, 100);
node.on("close", function() {
if (st) { clearTimeout(st); }
ui.ev.removeListener('changetab', chgtab);
done();
})
}
RED.nodes.registerType("ui_chart", ChartNode);
};

View File

@@ -0,0 +1,163 @@
<script type="text/javascript">
RED.nodes.registerType('ui_colour_picker',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(176, 223, 227)',
defaults: {
name: {value: ''},
label: {value: ''},
group: {type: 'ui_group', required: true},
format: {value: 'hex'},
outformat: {value: 'string'},
showSwatch: {value: true},
showPicker: {value: false},
showValue: {value: false},
showHue: {value: false},
showAlpha: {value: false},
showLightness: {value: true},
square: {value: "false"},
dynOutput: {value: "false"},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
passthru: {value: true},
topic: {value: 'topic', validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('topicType'):function(v) { return true})},
topicType: {value: 'msg'},
className: {value: ''}
},
inputs:1,
outputs:1,
outputLabels: function() { return this.format; },
icon: "ui_colour_picker.png",
paletteLabel: 'colour picker',
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'colour picker'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
if (this.square === undefined) {
this.square = "false";
$("#node-input-square").val("false");
}
$("#node-input-square").on("change", function() {
if ($("#node-input-square").val() === "false") {
$("#node-input-showLightness").prop('checked', true);
$("#node-input-showHue").prop('checked', false);
}
else {
$("#node-input-showLightness").prop('checked', false);
$("#node-input-showHue").prop('checked', true);
}
});
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
$("#node-input-format").on("change", function() {
if ($(this).val() === "hex") {
$("#node-alpha-control").hide();
}
else {
$("#node-alpha-control").show();
}
});
$('#node-input-topic').typedInput({
default: 'str',
typeField: $("#node-input-topicType"),
types: ['str','msg','flow','global']
});
},
oneditsave: function() {
if (!$("#node-input-showPicker").is(':checked') && !$("#node-input-showValue").is(':checked')) {
$("#node-input-showSwatch").prop('checked', true);
this.showSwatch = true;
}
}
});
</script>
<script type="text/html" data-template-name="ui_colour_picker">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> Label</label>
<input type="text" id="node-input-label">
</div>
<div class="form-row">
<label for="node-input-format"><i class="fa fa-keyboard-o"></i> Format</label>
<select id="node-input-format" style="width:156px;">
<option value="hex">hex</option>
<option value="hex8">hex8</option>
<option value="hsl">hsl</option>
<option value="hsv">hsv</option>
<option value="rgb">rgb</option>
</select>
<select id="node-input-square" style="width:130px; margin-left:30px">
<option value="false">round</option>
<option value="true">square</option>
</select>
</div>
<div class="form-row">
<label>&nbsp;</label> Show hue slider : <input type="checkbox" id="node-input-showHue" style="display:inline-block; width:auto; vertical-align:baseline;">
<br/>
<label>&nbsp;</label> Show lightness slider : <input type="checkbox" id="node-input-showLightness" style="display:inline-block; width:auto; vertical-align:baseline;">
<br/>
<span id="node-alpha-control"><label>&nbsp;</label> Show transparency slider : <input type="checkbox" id="node-input-showAlpha" style="display:inline-block; width:auto; vertical-align:baseline;"></span>
</div>
<div class="form-row">
If width is 4 or greater:<br/>
<label>&nbsp;</label>
Always show swatch : <input type="checkbox" checked id="node-input-showSwatch" style="display:inline-block; width:auto; vertical-align:top;">
<br/>
<label>&nbsp;</label>
Always show picker : <input type="checkbox" checked id="node-input-showPicker" style="display:inline-block; width:auto; vertical-align:top;">
<br/>
<label>&nbsp;</label>
Always show value field : <input type="checkbox" checked id="node-input-showValue" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> If <code>msg</code> arrives on input, pass through to output: </label>
<input type="checkbox" checked id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label for="node-input-dynOutput"><i class="fa fa-envelope-o"></i> Send</label>
<select id="node-input-dynOutput" style="width:60%">
<option value="false">one value when released/closed</option>
<option value="true">multiple values during editing</option>
</select>
</div>
<div class="form-row">
<label for="node-input-outformat" style="padding-left: 25px; margin-right: -25px">Payload</label>
<select id="node-input-outformat" style="width:60%">
<option value="string">current value as a string</option>
<option value="object">current value as an object</option>
</select>
</div>
<div class="form-row">
<label for="node-input-topic" style="padding-left: 25px; margin-right: -25px">Topic</label>
<input type="text" id="node-input-topic" placeholder="optional topic">
<input type="hidden" id="node-input-topicType">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
<input type="text" id="node-input-className" placeholder="Optional CSS class name(s) for widget"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name">
</div>
</script>

View File

@@ -0,0 +1,67 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
var tc = require('../dist/js/tinycolor-min');
function ColourPickerNode(config) {
RED.nodes.createNode(this, config);
this.format = config.format;
this.outformat = config.outformat;
var node = this;
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
node.on("input", function(msg) {
node.topi = msg.topic;
});
var done = ui.add({
node: node,
tab: tab,
group: group,
forwardInputMessages: config.passthru,
control: {
type: 'colour-picker',
label: config.label,
format: config.format,
showPicker: config.showPicker,
showSwatch: config.showSwatch,
showValue: config.showValue,
showHue: config.showHue,
showAlpha: config.showAlpha,
showLightness: config.showLightness,
square: (config.square == 'true') || false,
dynOutput: config.dynOutput,
allowEmpty: true,
order: config.order,
value: '',
width: config.width || group.config.width || 6,
height: config.height || 1,
className: config.className || '',
},
beforeSend: function (msg) {
if (node.outformat === 'object') {
var pay = tc(msg.payload);
if (node.format === 'rgb') { msg.payload = pay.toRgb(); }
if (node.format === 'hsl') { msg.payload = pay.toHsl(); }
if (node.format === 'hsv') { msg.payload = pay.toHsv(); }
}
var t = undefined;
try {
t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg) || node.topi;
}
catch(e) { }
if (t !== undefined) { msg.topic = t; }
},
convert: function(p,o,m) {
if (m.payload === undefined || m.payload === null) { return; }
var colour = tc(m.payload);
return colour.toString(config.format);
}
});
node.on("close", done);
}
RED.nodes.registerType("ui_colour_picker", ColourPickerNode);
};

View File

@@ -0,0 +1,86 @@
<script type="text/javascript">
RED.nodes.registerType('ui_date_picker',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(176, 223, 227)',
defaults: {
name: {value: ''},
label: {value: 'date'},
group: {type: 'ui_group', required: true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
passthru: {value: true},
topic: {value: 'topic', validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('topicType'):function(v) { return true})},
topicType: {value: 'msg'},
className: {value: ''}
},
inputs:1,
outputs:1,
outputLabels: ["epoch mS"],
icon: "ui_date_picker.png",
paletteLabel: 'date picker',
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'date picker'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
$('#node-input-topic').typedInput({
default: 'str',
typeField: $("#node-input-topicType"),
types: ['str','msg','flow','global']
});
}
});
</script>
<script type="text/html" data-template-name="ui_date_picker">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> Label</label>
<input type="text" id="node-input-label">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> If <code>msg</code> arrives on input, pass through to output: </label>
<input type="checkbox" checked id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-payload"><i class="fa fa-envelope-o"></i> When changed, send:</label>
</div>
<div class="form-row">
<label style="padding-left:25px; margin-right:-25px">Payload</label>
<label style="width:auto">Current value</label>
</div>
<div class="form-row">
<label for="node-input-topic" style="padding-left:25px; margin-right:-25px">Topic</label>
<input type="text" id="node-input-topic">
<input type="hidden" id="node-input-topicType">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
<input type="text" id="node-input-className" placeholder="Optional CSS class name(s) for widget"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name">
</div>
</script>

View File

@@ -0,0 +1,54 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function DatePickerNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
node.on("input", function(msg) {
node.topi = msg.topic;
});
var done = ui.add({
node: node,
tab: tab,
group: group,
forwardInputMessages: config.passthru,
emitOnlyNewValues: false,
control: {
type: 'date-picker',
label: config.label,
order: config.order,
ddd : new Date().setUTCHours(0,0,0,0),
width: config.width || group.config.width || 6,
height: config.height || 1,
className: config.className || '',
},
convert: function (p,o,m) {
var d = new Date(m.payload);
this.control.ddd = d;
return m.payload;
},
beforeEmit: function (msg, value) {
if (value === undefined) { return; }
value = new Date(value);
return { msg:msg, value:value };
},
convertBack: function (value) {
var d = new Date(value).valueOf();
return d;
},
beforeSend: function (msg) {
var t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg) || node.topi;
if (t !== undefined) { msg.topic = t; }
}
});
node.on("close", done);
}
RED.nodes.registerType("ui_date_picker", DatePickerNode);
};

179
node_modules/node-red-dashboard/nodes/ui_dropdown.html generated vendored Normal file
View File

@@ -0,0 +1,179 @@
<script type="text/javascript">
RED.nodes.registerType('ui_dropdown',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(176, 223, 227)',
defaults: {
name: {value: ''},
label: {value: ''},
tooltip: {value: ''},
place: {value: 'Select option'},
group: {type: 'ui_group', required:true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
passthru: {value: true},
multiple: {value: false},
options: {value:[{value: '', label : ''}],
validate:function(v) {
var unique = new Set(v.map(function(o) {return o.value;}));
return v.length == unique.size;
}},
payload: {value: ''},
topic: {value: 'topic', validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('topicType'):function(v) { return true})},
topicType: {value: 'msg'},
className: {value: ''}
},
inputs:1,
outputs:1,
icon: "ui_dropdown.png",
paletteLabel: 'dropdown',
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'dropdown'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
if (this.multiple === undefined) {
$("#node-input-multiple").prop('checked', false);;
}
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
var un = new Set(this.options.map(function(o) {return o.value}));
if (this.options.length == un.size) { $("#valWarning").hide(); }
else { $("#valWarning").show(); }
function generateOption(i, option) {
var container = $('<li/>',{style:"background: var(--red-ui-secondary-background, #fff); margin:0; padding:8px 0px 0px; border-bottom: 1px solid var(--red-ui-form-input-border-color, #ccc);"});
var row = $('<div/>').appendTo(container);
var row2 = $('<div/>',{style:"padding-top:5px; padding-left:175px;"}).appendTo(container);
var row3 = $('<div/>',{style:"padding-top:5px; padding-left:120px;"}).appendTo(container);
$('<i style="color: var(--red-ui-form-text-color, #eee); cursor:move; margin-left:3px;" class="node-input-option-handle fa fa-bars"></i>').appendTo(row);
var valueField = $('<input/>',{class:"node-input-option-value",type:"text",style:"margin-left:7px; width:calc(50% - 32px);", placeholder: 'Value',value:option.value}).appendTo(row).typedInput({default:option.type||'str',types:['str','num','bool']});
var labelField = $('<input/>',{class:"node-input-option-label",type:"text",style:"margin-left:7px; width:calc(50% - 32px);", placeholder: 'Label', value:option.label}).appendTo(row);
var finalspan = $('<span/>',{style:"float:right; margin-right:8px;"}).appendTo(row);
var deleteButton = $('<a/>',{href:"#",class:"editor-button editor-button-small", style:"margin-top:7px; margin-left:5px;"}).appendTo(finalspan);
$('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton);
deleteButton.click(function() {
container.css({"background":"var(--red-ui-secondary-background-inactive, #fee)"});
container.fadeOut(300, function() {
$(this).remove();
});
});
$("#node-input-option-container").append(container);
}
$("#node-input-add-option").click(function() {
generateOption($("#node-input-option-container").children().length+1, {});
$("#node-input-option-container-div").scrollTop($("#node-input-option-container-div").get(0).scrollHeight);
});
for (var i=0; i<this.options.length; i++) {
var option = this.options[i];
generateOption(i+1,option);
}
$( "#node-input-option-container" ).sortable({
axis: "y",
handle:".node-input-option-handle",
cursor: "move"
});
$('#node-input-topic').typedInput({
default: 'str',
typeField: $("#node-input-topicType"),
types: ['str','msg','flow','global']
});
},
oneditsave: function() {
var options = $("#node-input-option-container").children();
var node = this;
node.options = [];
options.each(function(i) {
var option = $(this);
var o = {
label: option.find(".node-input-option-label").val(),
value: option.find(".node-input-option-value").typedInput('value'),
type: option.find(".node-input-option-value").typedInput('type')
};
if (option.find(".node-input-option-value").typedInput('type') === "num") {
o.value = Number(o.value);
}
if (option.find(".node-input-option-value").typedInput('type') === "bool") {
o.value = (o.value == "true");
}
node.options.push(o);
});
},
oneditresize: function() {
}
});
</script>
<script type="text/html" data-template-name="ui_dropdown">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-tag"></i> Label</label>
<input type="text" id="node-input-label" placeholder="optional label">
</div>
<div class="form-row">
<label for="node-input-tooltip"><i class="fa fa-info-circle"></i> Tooltip</label>
<input type="text" id="node-input-tooltip" placeholder="optional tooltip">
</div>
<div class="form-row">
<label for="node-input-place"><i class="fa fa-tag"></i> Placeholder</label>
<input type="text" id="node-input-place" placeholder="optional placeholder">
</div>
<div class="form-row node-input-option-container-row" style="margin-bottom: 0px;width: 100%">
<label for="node-input-width" style="vertical-align:top"><i class="fa fa-list-alt"></i> Options</label>
<div id="node-input-option-container-div" style="box-sizing:border-box; border-radius:5px; height:257px; padding:5px; border:1px solid var(--red-ui-form-input-border-color, #ccc); overflow-y:scroll; display:inline-block; width:calc(70% + 15px);">
<span id="valWarning" style="color: var(--red-ui-text-color-error, #910000)"><b>All Values must be unique.</b></span>
<ol id="node-input-option-container" style="list-style-type:none; margin:0;"></ol>
</div>
</div>
<div class="form-row">
<a href="#" class="editor-button editor-button-small" id="node-input-add-option" style="margin-top:4px; margin-left:103px;"><i class="fa fa-plus"></i> <span>option</span></a>
</div>
<div class="form-row">
<label style="width:auto" for="node-input-multiple"><i class="fa fa-th-list"></i> Allow multiple selections from list: </label>
<input type="checkbox" checked id="node-input-multiple" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> If <code>msg</code> arrives on input, pass through to output: </label>
<input type="checkbox" checked id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="optional msg.topic">
<input type="hidden" id="node-input-topicType">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
<input type="text" id="node-input-className" placeholder="Optional CSS class name(s) for widget"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name">
</div>
</script>

221
node_modules/node-red-dashboard/nodes/ui_dropdown.js generated vendored Normal file
View File

@@ -0,0 +1,221 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function DropdownNode(config) {
RED.nodes.createNode(this, config);
this.pt = config.passthru;
this.multiple = config.multiple || false;
this.state = [" "," "];
var node = this;
node.status({});
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
var control = {
type: 'dropdown',
multiple: config.multiple,
label: config.label,
tooltip: config.tooltip,
place: config.place,
order: config.order,
value: config.payload || node.id,
width: config.width || group.config.width || 6,
height: config.height || 1,
className: config.className || '',
};
for (var o=0; o<config.options.length; o++) {
config.options[o].label = config.options[o].label || config.options[o].value;
}
control.options = config.options;
var emitOptions = { value:undefined };
node.on("input", function(msg) {
node.topi = msg.topic;
});
var done = ui.add({
node: node,
tab: tab,
group: group,
forwardInputMessages: config.passthru,
control: control,
convert: function (payload, oldValue, msg) {
// convert msg
// as of now, only allow a full replacement of options
// beforeEmit is only called when a node linked to us sends a msg
// we are expecting to receive an "update options" msg
// which we expect to be an array of new options
// for convenience, we pass an indication to the node connected to this dropdown
// that this is an "update options" message coming from the input sender
// 'beforeEmit' is called before 'beforeSend', so we may pass in that info
// otherwise that convenience info would not be sent (would not cause any problems)...
emitOptions = {isOptionsValid:false, value:undefined, newOptions:undefined};
do {
if (!msg.options) { break; }
if (typeof msg.options === "string" ) { msg.options = [ msg.options ]; }
if (!Array.isArray(msg.options)) { break; }
emitOptions.newOptions = [];
if (msg.options.length === 0) {
emitOptions.isOptionsValid = true;
break;
}
// could check whether or not all members have same type
for (var i = 0; i < msg.options.length; i++) {
var opt = msg.options[i];
if (opt === undefined || opt === null) { continue; }
switch (typeof opt) {
case 'number': {
opt = "" + opt;
emitOptions.newOptions.push({label:opt, value:opt, type:"number"});
break;
}
case 'string': {
emitOptions.newOptions.push({label:opt, value:opt, type:"string"});
break;
}
case 'object': {
// assuming object of {label:value}
for (var m in opt) {
if (opt.hasOwnProperty(m)) {
emitOptions.newOptions.push({label:m, value:opt[m], type:typeof(opt[m])});
}
}
break;
}
default:
// do nothing, just continue with next option
}
}
// send null object on change of menu list
if (emitOptions.newOptions.length > 0) { emitOptions.value = null; }
// or send the preselected value
if (msg.payload) { emitOptions.value = msg.payload; }
emitOptions.isOptionsValid = true;
} while (false);
// finally adjust msg to reflect the input
msg._dontSend = true;
if (emitOptions.isOptionsValid) {
control.options = emitOptions.newOptions;
control.value = emitOptions.value;
}
else {
if (msg.options) {
node.error("ERR: Invalid Options", msg);
}
}
if (msg.hasOwnProperty("resetSearch") && msg.resetSearch) {
emitOptions.resetSearch = true;
}
if (msg.hasOwnProperty("payload")) {
if (node.multiple) {
if (typeof msg.payload === "string") {
msg.payload = msg.payload.split(',');
}
}
emitOptions.value = msg.payload;
control.value = emitOptions.value;
delete msg._dontSend;
return emitOptions;
}
// we do not overide payload here due to 'opt.emitOnlyNewValues' in ui.js
// when undefined is returned, msg will not be forwarded
return emitOptions.isOptionsValid ? emitOptions : undefined; // always pass entire object (newValue == oldValue)
},
beforeEmit: function (msg, newValue) {
if (msg.socketid) { emitOptions.socketid = msg.socketid; }
return emitOptions;
},
convertBack: function (msg) {
var val = node.multiple ? [] : "";
var m = RED.util.cloneMessage(msg);
var mm = (m.hasOwnProperty("id") && m.hasOwnProperty("value")) ? m.value : m;
for (var i=0; i<control.options.length; i++) {
if (!node.multiple) {
delete m["$$mdSelectId"];
if (JSON.stringify(control.options[i].value) == JSON.stringify(mm)) {
val = control.options[i].value;
if (typeof val === "string" && control.options[i].type.indexOf("str") !== 0) {
try { val = JSON.parse(val); }
catch(e) {}
}
break;
}
}
else if (node.multiple && mm !== null) {
if (!Array.isArray(mm)) {
if (mm.hasOwnProperty("value")) { mm = mm.value; }
// if (typeof m === "string") { m = [ m ]; }
if (mm == null) { mm = []; }
else { mm = [ mm ]; }
}
mm.map(x => delete x["$$mdSelectId"])
for (var j = 0; j < mm.length; j++) {
if (JSON.stringify(control.options[i].value) === JSON.stringify(mm[j])) {
var v = control.options[i].value;
if (typeof v === "string" && control.options[i].type !== "string") {
try { v = JSON.parse(v); }
catch(e) {}
}
val.push(v);
break;
}
}
}
}
return val;
},
beforeSend: function (msg) {
if (msg.payload === undefined) { msg.payload = []; }
if (msg.payload === "") { msg._dontSend = true; }
if (msg._dontSend) {
delete msg.options;
msg.payload = emitOptions.value;
}
var t = undefined;
try {
t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg) || node.topi;
}
catch(e) { }
if (t !== undefined) { msg.topic = t; }
if (msg.payload === null || msg._dontSend) { node.status({}); }
else {
var stat = "";
if (Array.isArray(msg.payload)) { stat = msg.payload.length + " items"; }
else {
if (typeof msg.payload === "object") { stat = JSON.stringify(msg.payload); }
else { stat = msg.payload.toString(); }
if (stat.length > 32) { stat = stat.substr(0,31)+"..."; }
}
if (node.pt) {
node.status({shape:"dot",fill:"grey",text:stat});
}
else {
node.state[1] = stat;
node.status({shape:"dot",fill:"grey",text:node.state[1] + " | " + node.state[1]});
}
}
}
});
if (!node.pt) {
node.on("input", function(msg) {
node.state[0] = msg.payload;
node.status({shape:"dot",fill:"grey",text:node.state[0] + " | " + node.state[1]});
});
}
node.on("close", done);
}
RED.nodes.registerType("ui_dropdown", DropdownNode);
};

317
node_modules/node-red-dashboard/nodes/ui_form.html generated vendored Normal file
View File

@@ -0,0 +1,317 @@
<script type="text/javascript">
RED.nodes.registerType('ui_form',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(176, 223, 227)',
defaults: {
name: {value: ''},
label: {value: ''},
group: {type: 'ui_group', required: true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
options: {value:[{value:'', label :'', type:'', required:true}], validate:function(value) {
if (value.length ) {
for (var i = 0; i < value.length; i++) {
if (!value[i].value) {
return false;
}
}
}
else {
return false;
}
return true;
}, required:true},
formValue:{value:{}},
payload: {value: ''},
submit: {value: "submit"},
cancel: {value: "cancel"},
topic: {value: 'topic', validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('topicType'):function(v) { return true})},
topicType: {value: 'msg'},
splitLayout: {value:''},
className: {value: ''}
},
inputs:1,
outputs:1,
icon: "ui_form.png",
paletteLabel: 'form',
label: function() { return this.name || this.label || 'form'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
if ($("#node-input-submit").val() === null) { $("#node-input-submit").val("submit"); }
if ($("#node-input-cancel").val() === null) { $("#node-input-cancel").val("cancel"); }
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
this.resizeRule = function(option,newWidth) {
//option.find(".node-input-option-type").width(newWidth);
// option.find(".node-input-option-label").width(newWidth);
// option.find(".node-input-option-value").width(newWidth);
}
function generateOption(i, option) {
var container = $('<li/>',{style:"margin:0; padding:8px 0px 0px; border-bottom:1px solid var(--red-ui-form-input-border-color, #ccc);"});
var row = $('<div/>').appendTo(container);
var row2 = $('<div/>',{style:"padding-top:5px; padding-left:175px;"}).appendTo(container);
var row3 = $('<div/>',{style:"padding-top:5px; padding-left:120px;"}).appendTo(container);
$('<i style="cursor:move; margin-left:3px;" class="node-input-option-handle fa fa-bars"></i>').appendTo(row);
var labelField = $('<input/>',{class:"node-input-option-label", type:"text", style:"margin-left:7px; width:20%;", placeholder: RED._("node-red-dashboard/ui_form:ui_form.label.egName"), value:option.label}).appendTo(row);//.typedInput({default:'str',types:['str', 'num']});
var valueClass ="node-input-option-value"
if (!option.value) { valueClass ="node-input-option-value input-error"; }
var valueField = $('<input/>',{class:valueClass, type:"text", style:"margin-left:7px; width:20%;", placeholder: RED._("node-red-dashboard/ui_form:ui_form.label.egName2"), value:option.value}).appendTo(row);//.typedInput({default:'str',types:['str','num','bool']});
valueField.keyup(function() {
if ($(this).val() && $(this).hasClass('input-error')) {
$(this).removeClass('input-error')
}
else {
if (!$(this).val()) {
$(this).addClass('input-error')
}
}
});
// var typeField = $('<input/>',{class:"node-input-option-type",type:"text",style:"margin-left: 7px; width: 135px;", placeholder: 'Type', value:option.type}).appendTo(row).typedInput({default:'str',types:['str', 'num']});
var typeField = $('<select/>',{class:"node-input-option-type",type:"text",style:"margin-left:7px; width:16%"}).appendTo(row);//.typedInput({default:'str',types:['str', 'num']});
var arr = [
{val : "text", text: RED._("node-red-dashboard/ui_form:ui_form.label.text")},
{val : "multiline", text: RED._("node-red-dashboard/ui_form:ui_form.label.multiline")},
{val : "number", text: RED._("node-red-dashboard/ui_form:ui_form.label.number")},
{val : "email", text: RED._("node-red-dashboard/ui_form:ui_form.label.email")},
{val : "password", text: RED._("node-red-dashboard/ui_form:ui_form.label.password")},
{val : "checkbox", text: RED._("node-red-dashboard/ui_form:ui_form.label.checkbox")},
{val : "switch", text: RED._("node-red-dashboard/ui_form:ui_form.label.switch")},
{val : "date", text: RED._("node-red-dashboard/ui_form:ui_form.label.date")},
{val : "time", text: RED._("node-red-dashboard/ui_form:ui_form.label.time")}
];
//var sel = $('<select>').appendTo('body');
$(arr).each(function() {
var isSelected= false;
if (option.type == this.val) {
isSelected = true;
}
typeField.append($("<option>").attr('value',this.val).text(this.text).prop('selected',isSelected));
});
//var labelForRequried = $('<span/>',{style:"margin: 10px;"}).text('Required').appendTo(row);
var requiredContainer= $('<div/>',{style:"display:inline-block; height:34px; width:13%; vertical-align: middle"}).appendTo(row);
var requiredInnerContainer= $('<div/>',{style:"left:35%; position:relative; width:30px"}).appendTo(requiredContainer);
var reqRow=$("<label />",{class:"switch",style:"top:10px; width:30px;"}).appendTo(requiredInnerContainer);
//var required = $('<input/>',{class:"node-input-option-required",style:"margin: 5px;width:19%",type:"checkbox", checked:option.required}).appendTo(row);//labelForRequried);//.typedInput({default:'str',types:['str', 'num']});
var required = $('<input/>',{class:"node-input-option-required", type:"checkbox", checked:option.required, style:"vertical-align:top;"}).appendTo(reqRow);//labelForRequried);//.typedInput({default:'str',types:['str', 'num']});
var reqDiv=$("<div />",{class:"slider round"}).appendTo(reqRow);
var vis = option.rows ? 'visible' : 'hidden';
var rowsField = $('<input/>',{class:"node-input-option-rows", type:"number", style:"width:10%;", placeholder:'Rows', value:option.rows }).css('visibility',vis).appendTo(row);
var finalspan = $('<div/>',{style:"display:inline-block; width:5%;"}).appendTo(row);
var deleteButton = $('<a/>',{href:"#",class:"editor-button", style:"font-size:1.3em; left:45%; position:relative;"}).appendTo(finalspan);
$('<i/>',{class:"fa fa-trash-o"}).appendTo(deleteButton);
typeField.change(function(e){
if (e.target.value != 'multiline') {
rowsField.val(undefined)
option.rows = null;
rowsField.css('visibility','hidden')
} else {
rowsField.css('visibility','visible')
if (!rowsField[0].value) rowsField[0].value = 3;
}
})
deleteButton.click(function() {
container.find(".node-input-option-value").removeAttr('required')
container.css({"background":"var(--red-ui-secondary-background-inactive, #fee)"});
container.fadeOut(300, function() {
$(this).remove();
});
});
$("#node-input-option-container").append(container);
}
$("#node-input-add-option").click(function() {
generateOption($("#node-input-option-container").children().length+1, {});
$("#node-input-option-container-div").scrollTop($("#node-input-option-container-div").get(0).scrollHeight);
});
for (var i=0; i<this.options.length; i++) {
var option = this.options[i];
generateOption(i+1,option);
}
$('#node-input-topic').typedInput({
default: 'str',
typeField: $("#node-input-topicType"),
types: ['str','msg','flow','global']
});
$( "#node-input-option-container" ).sortable({
axis: "y",
handle:".node-input-option-handle",
cursor: "move"
});
},
oneditsave: function() {
var options = $("#node-input-option-container").children();
var node = this;
node.options = [];
node.formValue = {};
options.each(function(i) {
var option = $(this);
var o = {
label: option.find(".node-input-option-label").val(),//typedInput('value'),
value: option.find(".node-input-option-value").val(),//typedInput('value'),
type: option.find(".node-input-option-type").val(),//typedInput('value')
required: option.find(".node-input-option-required").is(':checked'),
rows: parseInt(option.find(".node-input-option-rows").val())
};
// o.value= o.value||o.label||(o.type+"_"+i);
node.formValue[o.value]= o.type == "checkbox" || o.type == "switch" ? false : "";
node.options.push(o);
});
},
oneditresize: function() {
var options = $("#node-input-option-container").children();
var newWidth = ($("#node-input-option-container").width() - 175)/2;
var node = this;
options.each(function(i) {
node.resizeRule($(this),newWidth);
});
}
});
</script>
<script type="text/html" data-template-name="ui_form">
<style>
.switch {
position: relative;
display: inline-block;
width: 30px;
height: 18px;
}
.switch input {display:none;}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--red-ui-tertiary-border-color, #ccc);
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 15px;
width: 15px;
left: 2px;
bottom: 2px;
background-color: var(--red-ui-secondary-background, white);
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #910000;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(11px);
-ms-transform: translateX(11px);
transform: translateX(11px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
</style>
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> <span data-i18n="ui_form.label.group"></label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> <span data-i18n="ui_form.label.size"></label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-tag"></i> <span data-i18n="ui_form.label.label"></label>
<input type="text" id="node-input-label" data-i18n="[placeholder]ui_form.label.optionalLabel">
</div>
<div class="form-row node-input-option-container-row" style="margin-bottom:0px; width:100%; min-width:520px">
<label style="vertical-align:top;"><i class="fa fa-list-alt"></i> <span data-i18n="ui_form.label.formElements"></label>
<div style="display:inline-block; width:78%; border:1px solid var(--red-ui-form-input-border-color, #ccc); border-radius:5px; box-sizing:border-box;">
<div class="red-ui-tray-header" style="width:100%; display: inline-block; padding-top:10px; padding-bottom:10px; border-top:0px solid; border-radius:5px 5px 0 0; border-bottom:1px solid var(--red-ui-form-input-border-color, #ccc);">
<div style="width:94%; display:inline-block; margin-left:27px">
<div style="width:20%; text-align:center; float:left;" data-i18n="ui_form.label.label"></span></div>
<div style="width:20%; text-align:center; float:left; margin-left:9px" data-i18n="node-red:common.label.name"></div>
<div style="margin-left:7px; width:16%; text-align:center; float:left; margin-left:9px" data-i18n="ui_form.label.type"></div>
<div style="width:16%; text-align:center; float:left;" data-i18n="ui_form.label.required"></div>
<div style="width:10%; text-align:center; float:left;" data-i18n="ui_form.label.rows"></div>
<div style="width:12%; text-align:center; float:left;" data-i18n="ui_form.label.remove"></div>
</div>
</div>
<div id="node-input-option-container-div" style=" height: 257px; padding: 5px; overflow-y:scroll;">
<ol id="node-input-option-container" style=" list-style-type:none; margin: 0;"></ol>
</div>
</div>
</div>
<div class="form-row">
<a href="#" class="editor-button editor-button-small" id="node-input-add-option" style="margin-top: 4px; margin-left: 103px;"><i class="fa fa-plus"></i> <span data-i18n="ui_form.label.element"></span></a>
</div>
<div class="form-row">
<label for="node-input-submit"><i class="fa fa-square"></i> <span data-i18n="ui_form.label.buttons"></label>
<i class="fa fa-thumbs-o-up"></i> <input type="text" id="node-input-submit" data-i18n="[placeholder]ui_form.label.submitButtonText" style="width:35%;">
<span style="margin-left:16px"><i class="fa fa-thumbs-o-down"></i></span>
<input type="text" id="node-input-cancel" data-i18n="[placeholder]ui_form.label.cancelButtonText" style="width:35%;">
</div>
<div class="form-row">
<label for="node-input-splitLayout"></label>
<input type="checkbox" id="node-input-splitLayout" style="display:inline-block; width:auto; vertical-align:top;">
<span data-i18n="ui_form.label.splitLayout">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="ui_form.label.topic"></label>
<input type="text" id="node-input-topic" data-i18n="[placeholder]ui_form.label.optionalMsgTopic">
<input type="hidden" id="node-input-topicType">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> <span data-i18n="ui_form.label.className"></label>
<input type="text" id="node-input-className" data-i18n="[placeholder]ui_form.label.classNamePlaceholder"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>

50
node_modules/node-red-dashboard/nodes/ui_form.js generated vendored Normal file
View File

@@ -0,0 +1,50 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function FormNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
node.on("input", function(msg) {
node.topi = msg.topic;
});
var done = ui.add({
node: node,
tab: tab,
group: group,
forwardInputMessages: false,
storeFrontEndInputAsState: false,
control: {
type: 'form',
label: config.label,
order: config.order,
value: config.payload || node.id,
width: config.width || group.config.width || 6,
height: config.height || config.splitLayout == true ? Math.ceil(config.options.length/2) : config.options.length,
options: config.options,
formValue: config.formValue,
submit: config.submit,
cancel: config.cancel,
splitLayout: config.splitLayout || false,
sy: ui.getSizes().sy,
cy: ui.getSizes().cy,
className: config.className || '',
},
beforeSend: function (msg) {
var t = undefined;
try {
t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg) || node.topi;
}
catch(e) { }
if (t !== undefined) { msg.topic = t; }
}
});
node.on("close", done);
}
RED.nodes.registerType("ui_form", FormNode);
};

176
node_modules/node-red-dashboard/nodes/ui_gauge.html generated vendored Normal file
View File

@@ -0,0 +1,176 @@
<style>
input.gauge-color {
width: 100px;
text-align: center;
}
input.gauge-color::-webkit-color-swatch {
border: none;
}
</style>
<script type="text/javascript">
RED.nodes.registerType('ui_gauge',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(119, 198, 204)',
defaults: {
name: {value: ''},
group: {type: 'ui_group', required: true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v || 0;
var currentGroup = $('#node-input-group').val() || this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
gtype: {value: 'gage'},
title: {value: 'gauge'},
label: {value: 'units'},
format: {value: '{{value}}'},
min: {value: 0, required: true, validate: RED.validators.number()},
max: {value: 10, required: true, validate: RED.validators.number()},
colors: {value: ["#00B500","#E6E600","#CA3838"]},
seg1: {value: ""},
seg2: {value: ""},
diff: {value: false},
className: {value: ''}
},
inputs:1,
outputs:0,
inputLabels: function() { return this.min+" - "+this.max; },
align: "right",
icon: "ui_gauge.png",
paletteLabel: 'gauge',
label: function() { return this.name || (~this.title.indexOf("{{") ? null : this.title) || ((this.gtype === "gage") ? "gauge" : this.gtype) || 'gauge'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
var setColour = function(id, value) {
$(id).val(value);
$(id).css("background-color", value);
var rgb = tinycolor(value).toRgb();
var level = ((rgb.r*299) + (rgb.g*587) + (rgb.b*114))/1000;
var textColor = (level >= 128) ? '#111111' : '#eeeeee';
$(id).css("color", textColor);
}
$(".gauge-color").on("change", function() {
setColour("#"+$(this).attr("id"), $(this).val());
});
var defaultColors = ['#00B500', '#E6E600', '#CA3838'];
if (this.colors) {
for (var i=0; i<this.colors.length; i++) {
var value = this.colors[i] || defaultColors[i];
setColour("#node-input-color"+(i+1), value);
}
}
else {
for (var j=0; j<defaultColors.length; j++) {
setColour("#node-input-color"+(j+1), defaultColors[j]);
}
}
if (this.gtype === undefined) {
this.gtype = "gage";
$("#node-input-gtype").val("gage");
}
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
$("#node-input-gtype").on("change", function() {
if (($(this).val() === "compass") || ($(this).val() === "wave")) {
$("#ui-gauge-colours").hide();
$("#ui-gauge-segments").hide();
}
else {
$("#ui-gauge-colours").show();
$("#ui-gauge-segments").show();
}
if ($(this).val() === "gage") {
$("#ui-gauge-diff").show();
}
else { $("#ui-gauge-diff").hide(); }
});
$("#node-input-min").on("change", function() {
$("#seg-min").text($(this).val());
});
$("#node-input-max").on("change", function() {
$("#seg-max").text($(this).val());
});
},
oneditsave: function() {
this.colors = [$("#node-input-color1").val(),$("#node-input-color2").val(),$("#node-input-color3").val()];
}
});
</script>
<script type="text/html" data-template-name="ui_gauge">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-gtype"><i class="fa fa-list"></i> Type</label>
<select id="node-input-gtype" style="width:150px">
<option value="gage">Gauge</option>
<option value="donut">Donut</option>
<option value="compass">Compass</option>
<option value="wave">Level</option>
</select>
</div>
<div id="ui-gauge-labels">
<div class="form-row">
<label for="node-input-title"><i class="fa fa-i-cursor"></i> Label</label>
<input type="text" id="node-input-title">
</div>
<div class="form-row" id="ui-gauge-format">
<label for="node-input-format"><i class="fa fa-i-cursor"></i> Value format</label>
<input type="text" id="node-input-format" placeholder="{{value}}">
</div>
<div class="form-row" id="ui-gauge-units">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> Units</label>
<input type="text" id="node-input-label" placeholder="optional sub-label">
</div>
</div>
<div class="form-row">
<label for="node-input-min">Range</label>
<span for="node-input-min">min</span>
<input type="text" id="node-input-min" style="width:80px">
<span for="node-input-max" style="margin-left:20px;">max</span>
<input type="text" id="node-input-max" style="width:80px">
</div>
<div class="form-row" id="ui-gauge-colours">
<label for="node-input-color1">Colour gradient</label>
<input type="color" id="node-input-color1" class="gauge-color" style="width:100px;"/>
<input type="color" id="node-input-color2" class="gauge-color" style="width:100px;"/>
<input type="color" id="node-input-color3" class="gauge-color" style="width:100px;"/>
</div>
<div class="form-row" id="ui-gauge-segments">
<label>Sectors</label>
<span id="seg-min" style="display:inline-block; width:40px;">0</span>...
<input type="text" id="node-input-seg1" style="text-align:center; width:87px;" placeholder="optional"> ...
<input type="text" id="node-input-seg2" style="text-align:center; width:87px;" placeholder="optional"> ...
<span id="seg-max" style="display:inline-block; width:40px; text-align:right">10</span>
</div>
<div class="form-row" id="ui-gauge-diff">
<label></label> Fill gauge from centre.
<input type="checkbox" id="node-input-diff" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
<input type="text" id="node-input-className" placeholder="Optional CSS class name(s) for widget"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name">
</div>
</script>

90
node_modules/node-red-dashboard/nodes/ui_gauge.js generated vendored Normal file
View File

@@ -0,0 +1,90 @@
module.exports = function (RED) {
var ui = require('../ui')(RED);
function GaugeNode(config) {
RED.nodes.createNode(this, config);
this.colors = config.colors || ["#00B500","#E6E600","#CA3838"];
var node = this;
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
if (config.width === "0") { delete config.width; }
if (config.height === "0") { delete config.height; }
if (config.height === "1") { config.hideMinMax = true; }
node.autoheight = parseInt(group.config.width*0.5+1.5) || 4;
if (config.gtype && config.gtype === "wave") { node.autoheight = parseInt(group.config.width*0.75+0.5); }
if (config.gtype && config.gtype === "donut") { node.autoheight = parseInt(group.config.width -1); }
if (config.gtype && config.gtype === "compass") { node.autoheight = parseInt(group.config.width -1); }
var sizes = ui.getSizes();
var theme = ui.getTheme();
if (theme === undefined) {
theme = {"group-textColor":{value:"#000"}};
theme["widget-textColor"] = {value:"#000"};
theme["widget-backgroundColor"] = {value:'#1784be'};
}
var gageoptions = {};
gageoptions.lineWidth = {'theme-dark':0.75};
gageoptions.pointerOptions = {'theme-dark':{color:'#8e8e93'}, 'theme-custom':theme["group-textColor"].value};
gageoptions.backgroundColor = {'theme-dark':'#515151', 'theme-custom':theme["widget-textColor"].value };
gageoptions.compassColor = {'theme-dark':theme["widget-backgroundColor"].value, 'theme-light':theme["widget-backgroundColor"].value, 'theme-custom':theme["widget-backgroundColor"].value};
gageoptions.valueFontColor = {'theme-dark':'#eee', 'theme-light':'#111', 'theme-custom':theme["widget-textColor"].value};
var waveoptions = {};
waveoptions.circleColor = {'theme-dark':theme["widget-backgroundColor"].value, 'theme-light':theme["widget-backgroundColor"].value, 'theme-custom':theme["widget-backgroundColor"].value};
waveoptions.waveColor = {'theme-dark':theme["widget-backgroundColor"].value, 'theme-light':theme["widget-backgroundColor"].value, 'theme-custom':theme["widget-backgroundColor"].value};
waveoptions.textColor = {'theme-dark':theme["widget-textColor"].value, 'theme-light':theme["widget-textColor"].value, 'theme-custom':theme["widget-textColor"].value};
waveoptions.waveTextColor = {'theme-dark':theme["widget-textColor"].value, 'theme-light':theme["widget-textColor"].value, 'theme-custom':theme["widget-textColor"].value};
var done = ui.add({
node: node,
tab: tab,
group: group,
emitOnlyNewValues: false,
control: {
type: 'gauge',
name: config.name,
label: config.title,
units: config.label,
order: config.order,
value: config.min,
format: config.format,
gtype: config.gtype || 'gage',
min: (parseFloat(config.min) < parseFloat(config.max)) ? parseFloat(config.min) : parseFloat(config.max),
seg1: (parseFloat(config.seg1) < parseFloat(config.seg2)) ? parseFloat(config.seg1) : parseFloat(config.seg2),
seg2: (parseFloat(config.seg1) < parseFloat(config.seg2)) ? parseFloat(config.seg2) : parseFloat(config.seg1),
max: (parseFloat(config.min) < parseFloat(config.max)) ? parseFloat(config.max) : parseFloat(config.min),
reverse: (parseFloat(config.max) < parseFloat(config.min)) ? true : false,
sizes: sizes,
hideMinMax: config.hideMinMax,
width: config.width || group.config.width || 6,
height: config.height || node.autoheight,
colors: node.colors,
diff: config.diff || false,
gageoptions: gageoptions,
waveoptions: waveoptions,
options: null,
className: config.className || '',
},
convert: function(p,o,m) {
var form = config.format.replace(/{{/g,"").replace(/}}/g,"").replace(/\s/g,"") || "_zzz_zzz_zzz_";
form = form.split('|')[0];
var value = RED.util.getMessageProperty(m,form);
if (value !== undefined) {
if (!isNaN(parseFloat(value))) { value = parseFloat(value); }
return value;
}
if (!isNaN(parseFloat(p))) { p = parseFloat(p); }
return p;
//return ui.toFloat.bind(this, config);
}
});
node.on("close", done);
}
RED.nodes.registerType("ui_gauge", GaugeNode);
};

87
node_modules/node-red-dashboard/nodes/ui_group.html generated vendored Normal file
View File

@@ -0,0 +1,87 @@
<script type="text/javascript">
// convert to i18 text
function c_ui_group(x) {
return RED._("node-red-dashboard/ui_group:ui_group."+x);
}
RED.nodes.registerType('ui_group',{
category: 'config',
defaults: {
name: {value: c_ui_group("label.default")},
tab: {type:"ui_tab", required: true },
order: {value: 0},
disp: {value: true},
width: {value: 6},
collapse: {value: false},
disabled: {value: false},
hidden: {value: false},
className: {value: ''},
},
sort: function(A,B) {
if (A.tab !== B.tab) {
var tabA = RED.nodes.node(A.tab);
var tabB = RED.nodes.node(B.tab);
if (!tabA && tabB) {
return -1;
}
else if (tabA && !tabB) {
return 1;
}
else {
return tabA.order - tabB.order;
}
}
return A.order - B.order;
},
paletteLabel: 'dashboard group',
label: function() {
var tabNode = RED.nodes.node(this.tab);
if (tabNode) {
return "["+(tabNode.name||c_ui_group("label.tab"))+"] " + (this.name || c_ui_group("label.group"));
}
return "["+c_ui_group("label.unassigned")+"] " + (this.name || c_ui_group("label.group"));
},
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
$("#node-input-size").elementSizer({
width: "#node-config-input-width",
auto: false
});
$("#node-config-input-disp").on("change", function() {
if ($("#node-config-input-disp").is(":checked")) {
$("#group-collapse-flag").show();
}
else {
$("#group-collapse-flag").hide();
$("#node-config-input-collapse").prop("checked",false);
}
});
}
});
</script>
<script type="text/html" data-template-name="ui_group">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="ui_group.label.name"></span></label>
<input type="text" id="node-config-input-name">
</div>
<div class="form-row">
<label for="node-config-input-tab"><i class="fa fa-table"></i> <span data-i18n="ui_group.label.tab"></span></label>
<input type="text" id="node-config-input-tab">
</div>
<div class="form-row">
<label for="node-config-input-className"><i class="fa fa-code"></i> <span data-i18n="ui_group.label.className"></label>
<input type="text" id="node-config-input-className" data-i18n="[placeholder]ui_group.label.classNamePlaceholder"/>
</div>
<div class="form-row">
<label for="node-config-input-width"><i class="fa fa-arrows-h"></i> <span data-i18n="ui_group.label.width"></span></label>
<input type="hidden" id="node-config-input-width">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<input style="margin:8px 0 10px 102px; width:20px;" type="checkbox" checked id="node-config-input-disp"> <label style="width:auto" for="node-config-input-disp"><span data-i18n="ui_group.display-name"></span></label>
</div>
<div class="form-row" id="group-collapse-flag">
<input style="margin:8px 0 10px 102px; width:20px;" type="checkbox" id="node-config-input-collapse"> <label style="width:auto" for="node-config-input-collapse"><span data-i18n="ui_group.collapse-name"></span></label>
</div>
</script>

20
node_modules/node-red-dashboard/nodes/ui_group.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
module.exports = function(RED) {
function GroupNode(config) {
RED.nodes.createNode(this, config);
this.config = {
name: config.name,
disp: config.disp,
width: config.width,
order: config.order,
tab: config.tab,
collapse: config.collapse || false,
className: config.className || ''
};
if (!this.config.hasOwnProperty("disp")) { this.config.disp = true; }
if (this.config.disp !== false) { this.config.disp = true; }
if (!this.config.hasOwnProperty("collapse")) { this.config.collapse = false; }
}
RED.nodes.registerType("ui_group", GroupNode);
};

65
node_modules/node-red-dashboard/nodes/ui_link.html generated vendored Normal file
View File

@@ -0,0 +1,65 @@
<script type="text/javascript">
RED.nodes.registerType('ui_link',{
category: 'config',
color: 'rgb( 63, 173, 181)',
defaults: {
name: {value: 'Google'},
link: {value: 'https://www.google.com'},
icon: {value: 'open_in_browser'},
target: {value: 'newtab', validate :function() { return true; }},
order: {value: 0},
className: {value: ''}
},
inputs:0,
outputs:0,
hasUsers: false,
align: "right",
icon: "ui_link.png",
paletteLabel: 'link',
label: function() { return this.name || 'link'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
document.getElementById('node-config-input-target-opentab').checked = (this.target === 'newtab');
document.getElementById('node-config-input-target-openiframe').checked = (this.target === 'iframe');
document.getElementById('node-config-input-target-openthis').checked = (this.target === 'thistab');
},
oneditsave : function () {
var t = 'iframe';
if (document.getElementById('node-config-input-target-opentab').checked) { t = 'newtab'; }
if (document.getElementById('node-config-input-target-openthis').checked) { t = 'thistab'; }
this.target = t;
},
onadd: function() {
//console.log("PING");
}
});
</script>
<script type="text/html" data-template-name="ui_link">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="ui_link.label.name"></span></label>
<input type="text" id="node-config-input-name">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> <span data-i18n="ui_link.label.className"></label>
<input type="text" id="node-input-className" data-i18n="[placeholder]ui_link.label.classNamePlaceholder"/>
</div>
<div class="form-row">
<label for="node-config-input-link"><i class="fa fa-link"></i> <span data-i18n="ui_link.label.link"></span></label>
<input type="text" id="node-config-input-link">
</div>
<div class="form-row">
<label for="node-config-input-icon"><i class="fa fa-image"></i> <span data-i18n="ui_link.label.icon"></span></label>
<input type="text" id="node-config-input-icon">
</div>
<div class="form-row">
<label><i class="fa fa-link"></i> <span data-i18n="ui_link.label.open-in"></span></label>
<input type="radio" id="node-config-input-target-opentab" name="open-link-method" style="width:20px; margin-top:0px; margin-bottom:5px" checked>
<label for="node-config-input-target-opentab" data-i18n="ui_link.label.new-tab"></label><br/>
<input type="radio" id="node-config-input-target-openthis" name="open-link-method" style="width:20px; margin-left:104px; margin-top:0px; margin-bottom:5px">
<label for="node-config-input-target-openthis" data-i18n="ui_link.label.this-tab"></label><br/>
<input type="radio" id="node-config-input-target-openiframe" name="open-link-method" style="width:20px; margin-left:104px; margin-top:0px; margin-bottom:5px">
<label for="node-config-input-target-openiframe" data-i18n="ui_link.label.iframe"></label>
</div>
<div class="form-tips" data-i18n="[html]ui_link.tip"></div>
</script>

12
node_modules/node-red-dashboard/nodes/ui_link.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function LinkNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var done = ui.addLink(config.name, config.link, config.icon, config.order, config.target, config.className);
node.on("close", done);
}
RED.nodes.registerType("ui_link", LinkNode);
};

113
node_modules/node-red-dashboard/nodes/ui_numeric.html generated vendored Normal file
View File

@@ -0,0 +1,113 @@
<script type="text/javascript">
RED.nodes.registerType('ui_numeric',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(176, 223, 227)',
defaults: {
name: {value: ''},
label: {value: 'numeric'},
tooltip: {value: ''},
group: {type: 'ui_group', required: true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
wrap: {value: false},
passthru: {value: true},
topic: {value: 'topic', validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('topicType'):function(v) { return true})},
topicType: {value: 'msg'},
format: {value: '{{value}}'},
min: {value: 0, required: true, validate: RED.validators.number()},
max: {value: 10, required: true, validate: RED.validators.number()},
step: {value: 1},
className: {value: ''}
},
inputs:1,
outputs:1,
outputLabels: function() { return this.min+" - "+this.max; },
icon: "ui_numeric.png",
paletteLabel: 'numeric',
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'numeric'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
$('#node-input-topic').typedInput({
default: 'str',
typeField: $("#node-input-topicType"),
types: ['str','msg','flow','global']
});
}
});
</script>
<script type="text/html" data-template-name="ui_numeric">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> Label</label>
<input type="text" id="node-input-label">
</div>
<div class="form-row">
<label for="node-input-tooltip"><i class="fa fa-info-circle"></i> Tooltip</label>
<input type="text" id="node-input-tooltip" placeholder="optional tooltip">
</div>
<div class="form-row">
<label for="node-input-format"><i class="fa fa-i-cursor"></i> Value Format</label>
<input type="text" id="node-input-format" placeholder="{{value}}">
</div>
<div class="form-row">
<label for="node-input-min"><i class="fa fa-arrows-h"></i> Range</label>
<span for="node-input-min">min</span>
<input type="text" id="node-input-min" style="width:60px">
<span for="not-input-max" style="margin-left:22px;">max</span>
<input type="text" id="node-input-max" style="width:60px">
<span for="not-input-step" style="margin-left:22px;">step</span>
<input type="text" id="node-input-step" style="width:60px">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-wrap"><i class="fa fa-refresh"></i> Wrap value from max to min and min to max.</label>
<input type="checkbox" id="node-input-wrap" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> If <code>msg</code> arrives on input, pass through to output: </label>
<input type="checkbox" checked id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-payload"><i class="fa fa-envelope-o"></i> When changed, send:</label>
</div>
<div class="form-row">
<label style="padding-left:25px; margin-right:-25px">Payload</label>
<label style="width:auto">Current value</label>
</div>
<div class="form-row">
<label for="node-input-topic" style="padding-left:25px; margin-right:-25px">Topic</label>
<input type="text" id="node-input-topic">
<input type="hidden" id="node-input-topicType">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
<input type="text" id="node-input-className" placeholder="Optional CSS class name(s) for widget"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name">
</div>
</script>

70
node_modules/node-red-dashboard/nodes/ui_numeric.js generated vendored Normal file
View File

@@ -0,0 +1,70 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function NumericNode(config) {
RED.nodes.createNode(this, config);
this.pt = config.passthru;
this.state = [" "," "];
var node = this;
node.status({});
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
node.on("input", function(msg) {
node.topi = msg.topic;
});
var done = ui.add({
node: node,
tab: tab,
group: group,
forwardInputMessages: config.passthru,
control: {
type: 'numeric',
label: config.label,
tooltip: config.tooltip,
order: config.order,
format: config.format,
pre: config.format.split('{{')[0] || "",
post: config.format.split('}}')[1] || "",
value: Number(config.min),
min: Number(config.min),
max: Number(config.max),
step: Number(config.step || 1),
wrap: config.wrap || false,
width: config.width || group.config.width || 6,
height: config.height || 1,
ed: (config.format.includes("value") ? false : true),
className: config.className || '',
},
beforeSend: function (msg) {
msg.payload = parseFloat(msg.payload);
var t = undefined;
try {
t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg) || node.topi;
}
catch(e) { }
if (t !== undefined) { msg.topic = t; }
if (node.pt) {
node.status({shape:"dot",fill:"grey",text:msg.payload});
}
else {
node.state[1] = msg.payload;
node.status({shape:"dot",fill:"grey",text:node.state[1] + " | " + node.state[1]});
}
},
convert: ui.toFloat.bind(this, config)
});
if (!node.pt) {
node.on("input", function(msg) {
node.state[0] = msg.payload;
node.status({shape:"dot",fill:"grey",text:node.state[0] + " | " + node.state[1]});
});
}
node.on("close", done);
}
RED.nodes.registerType("ui_numeric", NumericNode);
};

112
node_modules/node-red-dashboard/nodes/ui_slider.html generated vendored Normal file
View File

@@ -0,0 +1,112 @@
<script type="text/javascript">
RED.nodes.registerType('ui_slider',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(176, 223, 227)',
defaults: {
name: {value: ''},
label: {value: 'slider'},
tooltip: {value: ''},
group: {type: 'ui_group', required: true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
passthru: {value: true},
outs: {value: 'all'},
topic: {value: 'topic', validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('topicType'):function(v) { return true})},
topicType: {value: 'msg'},
min: {value: 0, required:true, validate:RED.validators.number()},
max: {value: 10, required:true, validate:RED.validators.number()},
step: {value: 1},
className: {value: ''}
},
inputs:1,
outputs:1,
outputLabels: function() { return this.min+" - "+this.max; },
icon: "ui_slider.png",
paletteLabel: 'slider',
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'slider'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
$('#node-input-topic').typedInput({
default: 'str',
typeField: $("#node-input-topicType"),
types: ['str','msg','flow','global']
});
if (!$("#node-input-outs").val()) { $("#node-input-outs").val("all") }
}
});
</script>
<script type="text/html" data-template-name="ui_slider">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> Label</label>
<input type="text" id="node-input-label">
</div>
<div class="form-row">
<label for="node-input-tooltip"><i class="fa fa-info-circle"></i> Tooltip</label>
<input type="text" id="node-input-tooltip" placeholder="optional tooltip">
</div>
<div class="form-row">
<label for="node-input-min"><i class="fa fa-arrows-h"></i> Range</label>
<span for="node-input-min">min</span>
<input type="text" id="node-input-min" style="width:60px">
<span for="not-input-max" style="margin-left:22px;">max</span>
<input type="text" id="node-input-max" style="width:60px">
<span for="not-input-step" style="margin-left:22px;">step</span>
<input type="text" id="node-input-step" style="width:60px">
</div>
<div class="form-row">
<label for="node-input-outs"><i class="fa fa-sign-out"></i> Output</label>
<select id="node-input-outs" style="width:204px">
<option value="all">continuously while sliding</option>
<option value="end">only on release</option>
</select>
</div>
<div class="form-row">
<label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> If <code>msg</code> arrives on input, pass through to output: </label>
<input type="checkbox" checked id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-payload"><i class="fa fa-envelope-o"></i> When changed, send:</label>
</div>
<div class="form-row">
<label style="padding-left:25px; margin-right:-25px">Payload</label>
<label style="width:auto">Current value</label>
</div>
<div class="form-row">
<label for="node-input-topic" style="padding-left:25px; margin-right:-25px">Topic</label>
<input type="text" id="node-input-topic">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
<input type="text" id="node-input-className" placeholder="Optional CSS class name(s) for widget"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name">
<input type="hidden" id="node-input-topicType">
</div>
</script>

71
node_modules/node-red-dashboard/nodes/ui_slider.js generated vendored Normal file
View File

@@ -0,0 +1,71 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function SliderNode(config) {
RED.nodes.createNode(this, config);
this.pt = config.passthru;
this.state = [" "," "];
var node = this;
node.status({});
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
node.on("input", function(msg) {
node.topi = msg.topic;
});
var done = ui.add({
node: node,
tab: tab,
group: group,
forwardInputMessages: config.passthru,
control: {
type: 'slider',
label: config.label,
tooltip: config.tooltip,
order: config.order,
value: config.min,
min: Math.min(config.min, config.max),
max: Math.max(config.max, config.min),
invert: (parseFloat(config.min) > parseFloat(config.max)) ? true : undefined,
step: Math.abs(config.step) || 1,
outs: config.outs || "all",
width: config.width || group.config.width || 6,
height: config.height || 1,
className: config.className || '',
},
beforeSend: function (msg) {
var t = undefined;
try {
t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg) || node.topi;
}
catch(e) { }
if (t !== undefined) { msg.topic = t; }
if (node.pt) {
node.status({shape:"dot",fill:"grey",text:msg.payload});
}
else {
node.state[1] = msg.payload;
node.status({shape:"dot",fill:"grey",text:node.state[1] + " | " + node.state[1]});
}
},
convert: ui.toFloat.bind(this, config)
});
if (!node.pt) {
node.on("input", function(msg) {
node.state[0] = msg.payload;
node.status({shape:"dot",fill:"grey",text:node.state[0] + " | " + node.state[1]});
});
}
else if (node._wireCount === 0) {
node.on("input", function(msg) {
node.status({shape:"dot",fill:"grey",text:msg.payload});
});
}
node.on("close", done);
}
RED.nodes.registerType("ui_slider", SliderNode);
};

58
node_modules/node-red-dashboard/nodes/ui_spacer.html generated vendored Normal file
View File

@@ -0,0 +1,58 @@
<script type="text/javascript">
RED.nodes.registerType('ui_spacer', {
category: 'config',
color: '#D4F0F8',
defaults: {
name: {value: "spacer"},
group: {type: 'ui_group', required:true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
className: {value: ''}
},
z: RED.workspaces.active(),
inputs:0,
outputs:0,
hasUsers: false,
icon: "ui_spacer.png",
paletteLabel: 'spacer',
label: function() { return this.name + " " + this.width + "x" + this.height; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
},
oneditsave: function() {
this.width = $("#node-input-width").val();
this.height = $("#node-input-height").val();
}
});
</script>
<script type="text/html" data-template-name="ui_spacer">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
<input type="text" id="node-input-className" placeholder="Optional CSS class name(s) for widget"/>
</div>
</script>

28
node_modules/node-red-dashboard/nodes/ui_spacer.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function SpacerNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
var done = ui.add({
node: node,
tab: tab,
group: group,
control: {
type: 'spacer',
order: config.order,
width: config.width || group.config.width || 6,
height: config.height || 1,
className: config.className || ''
}
});
node.on("close", done);
}
RED.nodes.registerType("ui_spacer", SpacerNode);
};

182
node_modules/node-red-dashboard/nodes/ui_switch.html generated vendored Normal file
View File

@@ -0,0 +1,182 @@
<script type="text/javascript">
RED.nodes.registerType('ui_switch',{
category: RED._("node-red-dashboard/ui_base:ui_base.label.category"),
color: 'rgb(176, 223, 227)',
defaults: {
name: {value: ''},
label: {value: 'switch'},
tooltip: {value: ''},
group: {type: 'ui_group', required: true},
order: {value: 0},
width: {value: 0, validate: function(v) {
var width = v||0;
var currentGroup = $('#node-input-group').val()||this.group;
var groupNode = RED.nodes.node(currentGroup);
var valid = !groupNode || +width <= +groupNode.width;
$("#node-input-size").toggleClass("input-error",!valid);
return valid;
}
},
height: {value: 0},
passthru: {value: true},
decouple: {value: "false"},
topic: {value: 'topic', validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('topicType'):function(v) { return true})},
topicType: {value: 'msg'},
style: {value: ''},
onvalue: {value: true, required:true, validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('onvalueType'):function(v) { return true})},
onvalueType: {value: 'bool'},
onicon: {value: '' },
oncolor: {value: ''},
offvalue: {value: false, required:true, validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('offvalueType'):function(v) { return true})},
offvalueType: {value: 'bool'},
officon: {value: ''},
offcolor: {value: ''},
animate: {value: false},
className: {value: ''}
},
inputs:1,
outputs:1,
icon: "ui_switch.png",
paletteLabel: 'switch',
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'switch'; },
labelStyle: function() { return this.name?"node_label_italic":""; },
oneditprepare: function() {
$("#node-input-size").elementSizer({
width: "#node-input-width",
height: "#node-input-height",
group: "#node-input-group"
});
$('#node-input-custom-icons').on("change", function() {
if ($('#node-input-custom-icons').val() === "default") {
$(".form-row-custom-icons").hide();
}
else {
$(".form-row-custom-icons").show();
}
});
if (this.onicon !== "" || this.oncolor !== "" || this.officon !=="" || this.offcolor !== "") {
$('#node-input-custom-icons').val('custom');
}
else {
$(".form-row-custom-icons").hide();
$('#node-input-custom-icons').change();
}
$('#node-input-onvalue').typedInput({
default: 'str',
typeField: $("#node-input-onvalueType"),
types: ['str','num','bool','json','bin','date','flow','global']
});
$('#node-input-offvalue').typedInput({
default: 'str',
typeField: $("#node-input-offvalueType"),
types: ['str','num','bool','json','bin','date','flow','global']
});
$('#node-input-topic').typedInput({
default: 'str',
typeField: $("#node-input-topicType"),
types: ['str','msg','flow','global']
});
$('#node-input-passthru').on("change", function() {
if (this.checked) {
$('.form-row-decouple').hide();
$('#node-input-decouple').val("false");
}
else {
$('.form-row-decouple').show();
}
});
},
oneditsave: function() {
if ($('#node-input-custom-icons').val() === 'default') {
$('#node-input-onicon').val('');
$('#node-input-officon').val('');
$('#node-input-oncolor').val('');
$('#node-input-offcolor').val('');
}
}
});
</script>
<script type="text/html" data-template-name="ui_switch">
<div class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
<input type="hidden" id="node-input-height">
<button class="editor-button" id="node-input-size"></button>
</div>
<div class="form-row">
<label for="node-input-label"><i class="fa fa-i-cursor"></i> Label</label>
<input type="text" id="node-input-label">
</div>
<div class="form-row">
<label for="node-input-tooltip"><i class="fa fa-info-circle"></i> Tooltip</label>
<input type="text" id="node-input-tooltip" placeholder="optional tooltip">
</div>
<div class="form-row">
<label for="node-input-custom-icons"><i class="fa fa-picture-o"></i> Icon</label>
<select id="node-input-custom-icons" style="width:35%">
<option value="default">Default</option>
<option value="custom">Custom</option>
</select>
<span style="width:auto; padding-left:20px" class="form-row-custom-icons" for="node-input-animate"> Animate
<input type="checkbox" checked id="node-input-animate" style="display:inline-block; width:auto; vertical-align:baseline;"></span>
</div>
<div class="form-row form-row-custom-icons">
<label for="node-input-onicon" style="text-align:right;"><i class="fa fa-toggle-on"></i> On Icon</label>
<input type="text" id="node-input-onicon" style="width:120px">
<label for="node-input-oncolor" style="width:50px; text-align:right;">Colour</label>
<input type="text" id="node-input-oncolor" style="width:120px">
</div>
<div class="form-row form-row-custom-icons">
<label for="node-input-officon" style="text-align:right;"><i class="fa fa-toggle-off"></i> Off Icon</label>
<input type="text" id="node-input-officon" style="width:120px">
<label for="node-input-offcolor" style="width:50px; text-align:right;">Colour</label>
<input type="text" id="node-input-offcolor" style="width:120px">
</div>
<div class="form-row">
<label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> Pass through <code>msg</code> if payload matches valid state: </label>
<input type="checkbox" checked id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row form-row-decouple">
<label for="node-input-decouple"><i class="fa fa-toggle-on"></i> Indicator</label>
<select id="node-input-decouple" style="display: inline-block; vertical-align: middle; width:70%;">
<option value="false">Switch icon shows state of the output</option>
<option value="true">Switch icon shows state of the input</option>
</select>
</div>
<div class="form-row">
<label style="width:auto" for="node-input-onvalue"><i class="fa fa-envelope-o"></i> When clicked, send:</label>
</div>
<div class="form-row">
<label for="node-input-onvalue" style="padding-left:25px; margin-right:-25px">On Payload</label>
<input type="text" id="node-input-onvalue" style="width:70%">
<input type="hidden" id="node-input-onvalueType">
</div>
<div class="form-row">
<label for="node-input-offvalue" style="padding-left:25px; margin-right:-25px">Off Payload</label>
<input type="text" id="node-input-offvalue" style="width:70%">
<input type="hidden" id="node-input-offvalueType">
</div>
<div class="form-row">
<label for="node-input-topic" style="padding-left:25px; margin-right:-25px">Topic</label>
<input type="text" id="node-input-topic">
<input type="hidden" id="node-input-topicType">
</div>
<div class="form-row">
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
<input type="text" id="node-input-className" placeholder="Optional CSS class name(s) for widget"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name">
</div>
</script>

158
node_modules/node-red-dashboard/nodes/ui_switch.js generated vendored Normal file
View File

@@ -0,0 +1,158 @@
module.exports = function(RED) {
var ui = require('../ui')(RED);
function validateSwitchValue(node,property,type,payload) {
if (payloadType === 'flow' || payloadType === 'global') {
try {
var parts = RED.util.normalisePropertyExpression(payload);
if (parts.length === '') {
throw new Error();
}
} catch(err) {
node.warn("Invalid payload property expression - defaulting to node id")
payload = node.id;
payloadType = 'str';
}
}
else {
payload = payload || node.id;
}
}
function SwitchNode(config) {
RED.nodes.createNode(this, config);
this.pt = config.passthru;
this.state = ["off"," "];
this.decouple = (config.decouple === "true") ? false : true;
var node = this;
node.status({});
var group = RED.nodes.getNode(config.group);
if (!group) { return; }
var tab = RED.nodes.getNode(group.config.tab);
if (!tab) { return; }
var parts;
var onvalue = config.onvalue;
var onvalueType = config.onvalueType;
if (onvalueType === 'flow' || onvalueType === 'global') {
try {
parts = RED.util.normalisePropertyExpression(onvalue);
if (parts.length === 0) {
throw new Error();
}
} catch(err) {
node.warn("Invalid onvalue property expression - defaulting to true")
onvalue = true;
onvalueType = 'bool';
}
}
var offvalue = config.offvalue;
var offvalueType = config.offvalueType;
if (offvalueType === 'flow' || offvalueType === 'global') {
try {
parts = RED.util.normalisePropertyExpression(offvalue);
if (parts.length === 0) {
throw new Error();
}
} catch(err) {
node.warn("Invalid offvalue property expression - defaulting to false")
offvalue = false;
offvalueType = 'bool';
}
}
node.on("input", function(msg) {
node.topi = msg.topic;
});
var done = ui.add({
node: node,
tab: tab,
group: group,
emitOnlyNewValues: false,
forwardInputMessages: config.passthru,
storeFrontEndInputAsState: (config.decouple === "true") ? false : true, //config.passthru,
state: false,
control: {
type: 'switch' + (config.style ? '-' + config.style : ''),
label: config.label,
tooltip: config.tooltip,
order: config.order,
value: false,
onicon: config.onicon,
officon: config.officon,
oncolor: config.oncolor,
offcolor: config.offcolor,
animate: config.animate?"flip-icon":"",
width: config.width || group.config.width || 6,
height: config.height || 1,
className: config.className || '',
},
convert: function (payload, oldval, msg) {
var myOnValue,myOffValue;
if (onvalueType === "date") { myOnValue = Date.now(); }
else { myOnValue = RED.util.evaluateNodeProperty(onvalue,onvalueType,node); }
if (offvalueType === "date") { myOffValue = Date.now(); }
else { myOffValue = RED.util.evaluateNodeProperty(offvalue,offvalueType,node); }
if (!this.forwardInputMessages && this.storeFrontEndInputAsState) {
if (myOnValue === oldval) { return true; }
if (oldval === true) { return true; }
else { return false; }
}
if (RED.util.compareObjects(myOnValue,msg.payload)) { node.state[0] = "on"; return true; }
else if (RED.util.compareObjects(myOffValue,msg.payload)) { node.state[0] = "off"; return false; }
else { return oldval; }
},
convertBack: function (value) {
node.state[1] = value?"on":"off";
if (node.pt) {
node.status({fill:(value?"green":"red"),shape:(value?"dot":"ring"),text:value?"on":"off"});
}
else {
var col = (node.decouple) ? ((node.state[1]=="on")?"green":"red") : ((node.state[0]=="on")?"green":"red");
var shp = (node.decouple) ? ((node.state[1]=="on")?"dot":"ring") : ((node.state[0]=="on")?"dot":"ring");
var txt = (node.decouple) ? (node.state[0] +" | "+node.state[1].toUpperCase()) : (node.state[0].toUpperCase() +" | "+node.state[1])
node.status({fill:col, shape:shp, text:txt});
}
var payload = value ? onvalue : offvalue;
var payloadType = value ? onvalueType : offvalueType;
if (payloadType === "date") { value = Date.now(); }
else {
try {
value = RED.util.evaluateNodeProperty(payload,payloadType,node);
}
catch(e) {
if (payloadType === "bin") { node.error("Badly formatted buffer"); }
else { node.error(e,payload); }
}
}
return value;
},
beforeSend: function (msg) {
var t = undefined;
try {
t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg) || node.topi;
}
catch(e) { }
if (t !== undefined) { msg.topic = t; }
}
});
if (!node.pt) {
node.on("input", function() {
var col = (node.state[0]=="on") ? "green" : "red";
var shp = (node.state[0]=="on") ? "dot" : "ring";
var txt = (node.decouple) ? (node.state[0] +" | "+node.state[1].toUpperCase()) : (node.state[0].toUpperCase() +" | "+node.state[1])
node.status({fill:col, shape:shp, text:txt});
});
}
node.on("close", done);
}
RED.nodes.registerType("ui_switch", SwitchNode);
};

91
node_modules/node-red-dashboard/nodes/ui_tab.html generated vendored Normal file
View File

@@ -0,0 +1,91 @@
<script type="text/javascript">
// convert to i18 text
function c_ui_tab(x) {
return RED._("node-red-dashboard/ui_tab:ui_tab."+x);
}
RED.nodes.registerType('ui_tab',{
category: 'config',
defaults: {
name: {value: c_ui_tab("label.home")},
icon: {value: 'dashboard'},
order: {value: 0},
disabled: {value: false},
hidden: {value: false}
},
paletteLabel: 'dashboard tab',
label: function() { return this.name || c_ui_tab("label.tab"); },
sort: function(A,B) {
return A.order - B.order;
},
oneditprepare: function() {
$("#node-config-input-disabled-btn").on("click", function(e) {
var i = $(this).find("i");
var active = i.hasClass("fa-toggle-on");
var newCls = "fa fa-toggle-" + (active ? "off" : "on");
i.attr("class", newCls);
$("#node-config-input-disabled").prop('checked',active);
var newTxt = c_ui_tab(active ? "label.disabled" : "label.enabled");
$("#node-config-input-disabled-label").text(newTxt);
var info = $("#node-config-input-disabled-info");
var done = active ? info.show() : info.hide();
});
if (this.disabled) {
$("#node-config-input-disabled-btn").click();
}
else {
$("#node-config-input-disabled-label").text(c_ui_tab("label.enabled"));
}
$("#node-config-input-hidden-btn").on("click", function(e) {
var i = $(this).find("i");
var active = i.hasClass("fa-toggle-on");
var newCls = "fa fa-toggle-" + (active ? "off" : "on");
i.attr("class", newCls);
$("#node-config-input-hidden").prop('checked',active);
var newTxt = c_ui_tab(active ? "label.hidden" : "label.visible");
$("#node-config-input-hidden-label").text(newTxt);
var info = $("#node-config-input-hidden-info");
var done = active ? info.show() : info.hide();
});
if (this.hidden) {
$("#node-config-input-hidden-btn").click();
}
else {
$("#node-config-input-hidden-label").text(c_ui_tab("label.visible"));
}
},
oneditsave: function() {
this.disabled = $("#node-config-input-disabled").prop("checked");
this.hidden = $("#node-config-input-hidden").prop("checked");
}
});
</script>
<script type="text/html" data-template-name="ui_tab">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="ui_tab.label.name"></span></label>
<input type="text" id="node-config-input-name">
</div>
<div class="form-row">
<label for="node-config-input-icon"><i class="fa fa-file-image-o"></i> <span data-i18n="ui_tab.label.icon"></span></label>
<input type="text" id="node-config-input-icon">
</div>
<div class="form-row">
<label for="node-config-input-disabled-btn"><i class="fa fa-ban"></i> <span data-i18n="ui_tab.label.state"></span></label>
<button id="node-config-input-disabled-btn" class="editor-button" style="width:100px; margin-right:6px;"><i class="fa fa-toggle-on"></i> <span id="node-config-input-disabled-label"></span></button>
<input type="checkbox" id="node-config-input-disabled" style="display:none;"/>
<span id="node-config-input-disabled-info" data-i18n="[html]ui_tab.info.disabled" style="display:none;"></span>
</div>
<div class="form-row">
<label for="node-config-input-hidden-btn"><i class="fa fa-eye-slash"></i> <span data-i18n="ui_tab.label.navmenu"></span></label>
<button id="node-config-input-hidden-btn" class="editor-button" style="width:100px; margin-right:6px;"><i class="fa fa-toggle-on"></i> <span id="node-config-input-hidden-label"></span></button>
<input type="checkbox" id="node-config-input-hidden" style="display:none;"/>
<span id="node-config-input-hidden-info" data-i18n="[html]ui_tab.info.hidden" style="display:none;"></span>
</div>
<div class="form-tips" data-i18n="[html]ui_tab.tip"></div>
</script>

15
node_modules/node-red-dashboard/nodes/ui_tab.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
module.exports = function(RED) {
function TabNode(config) {
RED.nodes.createNode(this, config);
this.config = {
name: config.name,
order: config.order || 0,
icon: config.icon || '',
disabled: config.disabled || false,
hidden: config.hidden || false
};
}
RED.nodes.registerType("ui_tab", TabNode);
};

Some files were not shown because too many files have changed in this diff Show More