Intial Commit
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_button.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 336 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_chart.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 344 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_colour_picker.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 650 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_date_picker.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 153 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_dropdown.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 236 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_form.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 232 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_gauge.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 507 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_numeric.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 407 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_slider.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 216 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_switch.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 396 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_template.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 413 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_text.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 409 B |
BIN
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/icons/ui_toast.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 353 B |
96
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/en-US/ui_base.json
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"ui_base" : {
|
||||
"label" : {
|
||||
"dashboard" : "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"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"lock" : {
|
||||
"clicked" : "Click to show side menu",
|
||||
"locked" : "Always show side menu"
|
||||
},
|
||||
"temp" : {
|
||||
"allow-theme" : "Node-RED theme everywhere",
|
||||
"no-theme" : "Use Angular theme in ui_template",
|
||||
"none" : "Angular theme everywhere"
|
||||
}
|
||||
}
|
||||
}
|
||||
14
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/en-US/ui_group.json
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ui_group" : {
|
||||
"label" : {
|
||||
"name" : "Name",
|
||||
"tab" : "Tab",
|
||||
"width" : "Width",
|
||||
"default" : "Default",
|
||||
"group" : "Group",
|
||||
"unassigned" : "unassigned"
|
||||
},
|
||||
"display-name" : "Display group name",
|
||||
"collapse-name" : "Allow group to be collapsed"
|
||||
}
|
||||
}
|
||||
14
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/en-US/ui_link.json
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ui_link" : {
|
||||
"label" : {
|
||||
"name" : "Name",
|
||||
"link" : "Link",
|
||||
"icon" : "Icon",
|
||||
"open-in" : "Open in",
|
||||
"new-tab" : "New Tab",
|
||||
"this-tab" : "This Tab",
|
||||
"iframe" : "iframe"
|
||||
},
|
||||
"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>"
|
||||
}
|
||||
}
|
||||
21
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/en-US/ui_tab.json
generated
vendored
Normal 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>"
|
||||
}
|
||||
}
|
||||
17
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/en-US/ui_template.json
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"ui_template" : {
|
||||
"label" : {
|
||||
"category" : "dashboard",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
11
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/en-US/ui_ui_control.json
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ui_ui_control" : {
|
||||
"label" : {
|
||||
"category" : "dashboard",
|
||||
"name" : "Name"
|
||||
},
|
||||
"placeholder" : {
|
||||
"name" : "Name"
|
||||
}
|
||||
}
|
||||
}
|
||||
96
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/ja/ui_base.json
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"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" : "幅"
|
||||
},
|
||||
"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" : "サイドメニューを表示したままにする"
|
||||
},
|
||||
"temp" : {
|
||||
"no-theme" : "ui_templateでテーマ設定を許可しない",
|
||||
"allow-theme" : "ui_templateでテーマ設定を許可する",
|
||||
"none" : "Angularテーマを全ての箇所で使用"
|
||||
}
|
||||
}
|
||||
}
|
||||
14
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/ja/ui_group.json
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ui_group" : {
|
||||
"label" : {
|
||||
"name" : "名前",
|
||||
"tab" : "タブ",
|
||||
"width" : "幅",
|
||||
"default" : "デフォルト",
|
||||
"group" : "グループ",
|
||||
"unassigned" : "未設定"
|
||||
},
|
||||
"display-name" : "グループ名を表示する",
|
||||
"collapse-name" : "グループの折りたたみを有効にする"
|
||||
}
|
||||
}
|
||||
14
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/ja/ui_link.json
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ui_link" : {
|
||||
"label" : {
|
||||
"name" : "名前",
|
||||
"link" : "リンク",
|
||||
"icon" : "アイコン",
|
||||
"open-in" : "開く方法",
|
||||
"new-tab" : "新規タブ",
|
||||
"this-tab" : "このタブ",
|
||||
"iframe" : "iframe"
|
||||
},
|
||||
"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>"
|
||||
}
|
||||
}
|
||||
21
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/ja/ui_tab.json
generated
vendored
Normal 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>"
|
||||
}
|
||||
}
|
||||
44
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/ja/ui_template.html
generated
vendored
Normal 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;"><div layout="row" layout-align="space-between">
|
||||
<p>数値は</p>
|
||||
<p ng-style="{color: (msg.payload || 0) % 2 === 0 ? 'green' : 'red'}">
|
||||
{{(msg.payload || 0) % 2 === 0 ? '偶数' : '奇数'}}
|
||||
</p>
|
||||
</div></pre>
|
||||
このコードは<code>msg.payload</code>で受け取った数値が偶数か奇数かを表示します。同時に、偶数であれば緑に、奇数であれば赤にテキストの色を変更します。<br/>
|
||||
次は、一意なIDをテンプレートに設定、デフォルトのテーマカラーを設定、入力メッセージの到着を監視する例です。</p>
|
||||
<pre style="font-size:smaller;">
|
||||
<div id="{{'my_'+$id}}" style="{{'color:'+theme.base_color}}">何らかのテキスト</div>
|
||||
<script>
|
||||
(function(scope) {
|
||||
scope.$watch('msg', function(msg) {
|
||||
if (msg) {
|
||||
// メッセージ同着時に適当な処理を実行
|
||||
$("#my_"+scope.$id).html(msg.payload);
|
||||
}
|
||||
});
|
||||
})(scope);
|
||||
</script></pre>
|
||||
<p>この方法で作成したテンプレートはコピー可能です。コピーはそれぞれ独立して利用できます。</p>
|
||||
<p><b>メッセージ送信:</b><br>
|
||||
<pre style="font-size:smaller;">
|
||||
<script>
|
||||
var value = "こんにちは世界";
|
||||
// もしくは、コールバック関数で値を書き換え
|
||||
this.scope.action = function() { return value; }
|
||||
</script>
|
||||
<md-button ng-click="send({payload:action()})">
|
||||
クリックすると「こんにちは世界」を送信します
|
||||
</md-button></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">Material Design icons</a>,
|
||||
<a href="https://fontawesome.com/v4.7.0/icons/" target="_blank">Font Awesome icons</a>,
|
||||
<a href="https://github.com/Paul-Reed/weather-icons-lite/blob/master/css_mappings.md" target="_blank">Weather icons</a></p>
|
||||
</script>
|
||||
17
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/ja/ui_template.json
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"ui_template" : {
|
||||
"label" : {
|
||||
"category" : "dashboard",
|
||||
"type" : "コード種別",
|
||||
"local" : "グループ内のWidget",
|
||||
"global" : "<head>ヘッドセクションへ追加",
|
||||
"group" : "グループ",
|
||||
"size" : "サイズ",
|
||||
"name" : "名前",
|
||||
"pass-through" : "入力メッセージをそのまま渡す",
|
||||
"store-state" : "出力メッセージを状態として保存",
|
||||
"template" : "HTMLコード",
|
||||
"expand": "展開する"
|
||||
}
|
||||
}
|
||||
}
|
||||
15
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/ja/ui_ui_control.html
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<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>widgetグループを表示を次のようなペイロードで制御できます。<br/>
|
||||
<code>{group:{hide:["タブ名_グループ名"], show:["他_グループ_表示"], focus:true}}</code><br/>
|
||||
<b>focus</b>は、グループが表示されるよう必要に応じて画面をスクロールするためのパラメータで、省略可能です。グループの指定は<i>タブ名</i>と<i>グループ名</i>を下線(_)で結合したものを用います。空白は下線で置き換えます。</p>
|
||||
<p>クライアントのブラウザが接続もしくは切断した場合、次のプロパティを持つメッセージを送信します:</p>
|
||||
<ul>
|
||||
<li><code>payload</code> - <i>connect</i>もしくは<i>lost</i>,
|
||||
<li><code>socketid</code> - ソケットID(ブラウザがページを再ロードする毎に変化します),
|
||||
<li><code>socketip</code> - 接続元のIPアドレス。
|
||||
</ul>
|
||||
</script>
|
||||
11
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/locales/ja/ui_ui_control.json
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ui_ui_control" : {
|
||||
"label" : {
|
||||
"category" : "dashboard",
|
||||
"name" : "名前"
|
||||
},
|
||||
"placeholder" : {
|
||||
"name" : "名前"
|
||||
}
|
||||
}
|
||||
}
|
||||
84
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_audio.html
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var myvoice = 0;
|
||||
var voices;
|
||||
RED.nodes.registerType('ui_audio',{
|
||||
category: 'dashboard',
|
||||
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].lang);
|
||||
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>
|
||||
|
||||
<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>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>
|
||||
46
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_audio.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
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.reset == true) {
|
||||
ui.emit('ui-audio', { reset:true, tabname:node.tabname, always:node.always });
|
||||
}
|
||||
else if (Buffer.isBuffer(msg.payload)) {
|
||||
ui.emit('ui-audio', { audio:msg.payload, tabname:node.tabname, always:node.always });
|
||||
}
|
||||
else if (typeof msg.payload === "string") {
|
||||
ui.emit('ui-audio', { tts:msg.payload, voice:(node.voice || msg.voice || 0), tabname:node.tabname, always:node.always });
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
3406
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_base.html
generated
vendored
Normal file
@@ -0,0 +1,3406 @@
|
||||
<style>
|
||||
:root {
|
||||
--nr-db-dark-text: #444;
|
||||
--nr-db-light-text: #eee;
|
||||
--nr-db-disabled-text: #999;
|
||||
}
|
||||
.nr-db-sb {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
bottom: 2px;
|
||||
left: 1px;
|
||||
right: 1px;
|
||||
overflow-y: scroll;
|
||||
padding: 10px;
|
||||
}
|
||||
.nr-db-sb .form-row label {
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
.nr-db-sb .form-row input,
|
||||
.nr-db-sb .form-row select {
|
||||
width: calc(100% - 100px);
|
||||
margin-bottom:0;
|
||||
}
|
||||
.nr-db-sb .compact {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
.nr-db-sb .red-ui-editableList-container {
|
||||
padding: 0;
|
||||
min-height: 250px;
|
||||
height: auto;
|
||||
}
|
||||
.nr-db-sb-tab-list {
|
||||
min-height: 250px;
|
||||
height: auto;
|
||||
}
|
||||
.nr-db-sb-tab-list li {
|
||||
padding: 0;
|
||||
}
|
||||
.nr-db-sb-tab-list-item {
|
||||
border-radius: 4px;
|
||||
color: var(--nr-db-dark-text);
|
||||
}
|
||||
.nr-db-sb-list-header {
|
||||
cursor: pointer;
|
||||
position:relative;
|
||||
color: var(--nr-db-dark-text);
|
||||
padding:3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.nr-db-sb-list-header:hover {
|
||||
color: var(--nr-db-dark-text);
|
||||
}
|
||||
.nr-db-sb-title-hidden {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.nr-db-sb-title-disabled {
|
||||
color: var(--nr-db-disabled-text);
|
||||
}
|
||||
.nr-db-sb-tab-list-header {
|
||||
background: var(--nr-db-light-text);
|
||||
padding:5px;
|
||||
}
|
||||
.nr-db-sb-group-list-header:hover,
|
||||
.nr-db-sb-widget-list-header:hover {
|
||||
background: var(--nr-db-light-text);
|
||||
}
|
||||
.nr-db-sb-list-chevron {
|
||||
width: 15px;
|
||||
text-align: center;
|
||||
margin: 3px 5px 3px 5px;
|
||||
}
|
||||
.nr-db-sb-tab-list-item .red-ui-editableList-container {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
height: auto !important;
|
||||
min-height: unset;
|
||||
}
|
||||
.nr-db-sb-list-handle {
|
||||
vertical-align: top;
|
||||
opacity: 0;
|
||||
cursor: move;
|
||||
}
|
||||
.nr-db-sb-list-header:hover>.nr-db-sb-list-handle,
|
||||
.nr-db-sb-list-header:hover>.nr-db-sb-list-header-button-group {
|
||||
opacity: 1;
|
||||
}
|
||||
.nr-db-sb-list-header-button-group {
|
||||
opacity: 0;
|
||||
}
|
||||
.nr-db-sb-list-handle {
|
||||
color: var(--nr-db-light-text);
|
||||
padding:5px;
|
||||
}
|
||||
.nr-db-sb-tab-list-header>.nr-db-sb-list-chevron {
|
||||
margin-left: 0px;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
.nr-db-sb-group-list-header>.nr-db-sb-list-chevron {
|
||||
margin-left: 20px;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
.nr-db-sb-group-list {
|
||||
min-height: 10px;
|
||||
}
|
||||
.nr-db-sb-group-list li {
|
||||
border-bottom-color: var(--nr-db-light-text);
|
||||
}
|
||||
.nr-db-sb-group-list>li.ui-sortable-helper {
|
||||
border-top: 1px solid var(--nr-db-light-text);
|
||||
}
|
||||
.nr-db-sb-group-list>li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.nr-db-sb-widget-list>li {
|
||||
border: none !important;
|
||||
}
|
||||
.nr-db-sb-group-list>li>.red-ui-editableList-item-handle {
|
||||
left: 10px;
|
||||
}
|
||||
.nr-db-sb-list-button-group {
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 0px;
|
||||
z-index: 2;
|
||||
}
|
||||
.nr-db-sb-list-header-button-group {
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 4px;
|
||||
}
|
||||
.nr-db-sb-list-header-button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.nr-db-sb li.ui-sortable-helper {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.nr-db-sb-widget-icon {
|
||||
margin-left: 56px;
|
||||
}
|
||||
.nr-db-sb-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.nr-db-sb-link {
|
||||
display: inline-block;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.nr-db-sb-link-name-container .fa-external-link {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.nr-db-sb-link-url {
|
||||
font-size: 0.8em;
|
||||
color: var(--nr-db-mid-grey);
|
||||
}
|
||||
span.nr-db-color-pick-container {
|
||||
max-width: 50px;
|
||||
border-radius: 3px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
input.nr-db-field-themeColor[type="color"] {
|
||||
width: 60px !important;
|
||||
padding: 0px;
|
||||
height: 20px;
|
||||
box-shadow: none;
|
||||
position: absolute;
|
||||
right: 36px;
|
||||
border-radius: 3px !important;
|
||||
border: solid 1px #ccc;
|
||||
-webkit-appearance: none;
|
||||
font-size: smaller;
|
||||
text-align: center;
|
||||
}
|
||||
input.nr-db-field-themeColor::-webkit-color-swatch {
|
||||
border: none;
|
||||
}
|
||||
.red-ui-tabs {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.red-ui-tab.hidden {
|
||||
display: none;
|
||||
}
|
||||
#dashboard-tabs-list li a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
#dash-link-button {
|
||||
background: none;
|
||||
border: none;
|
||||
margin-top: 3px;
|
||||
display: inline-block;
|
||||
margin: 3px 0px 0px 3px;
|
||||
height: 32px;
|
||||
line-height: 29px;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
padding: 0px 7px 0px 7px;
|
||||
}
|
||||
ul.red-ui-dashboard-theme-styles {
|
||||
list-style: none;
|
||||
}
|
||||
ul.red-ui-dashboard-theme-styles li {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.nr-db-resetIcon {
|
||||
margin: 3px 6px 0px 6px;
|
||||
float: right;
|
||||
color: var(--nr-db-mid-grey);
|
||||
opacity: 0.8;
|
||||
display: block;
|
||||
}
|
||||
.nr-db-resetIcon:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
#nr-db-field-font {
|
||||
margin-left: 2em;
|
||||
width: calc(100% - 81px);
|
||||
}
|
||||
.nr-db-theme-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
#custom-theme-library-container .btn-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- Dashboard layout tool -->
|
||||
<link rel="stylesheet" href="./ui_base/css/gridstack.min.css">
|
||||
<link rel="stylesheet" href="./ui_base/css/gridstack-extra.min.css">
|
||||
<style>
|
||||
.grid-stack {
|
||||
background-color: #f8f8f8;
|
||||
border: solid 2px #C0C0C0;
|
||||
margin: auto;
|
||||
min-height: 46px;
|
||||
display: table-cell;
|
||||
background-image: linear-gradient(#C0C0C0 1px, transparent 0),
|
||||
linear-gradient(90deg, #C0C0C0 1px, transparent 0);
|
||||
background-size: 40px 47px;
|
||||
}
|
||||
.grid-stack>.grid-stack-item>.grid-stack-item-content {
|
||||
top: 3px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
bottom: 3px;
|
||||
}
|
||||
.grid-stack-item-content {
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
background-color: #b0dfe3;
|
||||
border-radius: 2px;
|
||||
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.grid-stack-item {
|
||||
cursor: move;
|
||||
}
|
||||
.nr-dashboard-layout-container-fluid {
|
||||
width: 100%;
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
margin-right: 0px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
.nr-dashboard-layout-row {
|
||||
width: 100%;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
margin-right: 0px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
.nr-dashboard-layout-span12 {
|
||||
width: 98.4%;
|
||||
padding: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.nr-dashboard-layout-span6 {
|
||||
width: 49.2%;
|
||||
padding: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.nr-dashboard-layout-span4 {
|
||||
width: 32.7%;
|
||||
padding: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.nr-dashboard-layout-span3 {
|
||||
width: 24.3%;
|
||||
padding: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.nr-dashboard-layout-span2 {
|
||||
width: 16.0%;
|
||||
padding: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.nr-dashboard-layout-resize-disable {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
position: relative;
|
||||
z-index: 90;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.nr-dashboard-layout-resize-enable {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
position: relative;
|
||||
z-index: 90;
|
||||
margin-right: 1px;
|
||||
}
|
||||
.grid-stack>.ui-state-disabled {
|
||||
opacity: 1;
|
||||
background-image: none;
|
||||
}
|
||||
.grid-stack>.grid-stack-item>.ui-resizable-handle {
|
||||
z-index: 90;
|
||||
margin-right: -7px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
(function($) {
|
||||
var editSaveEventHandler;
|
||||
var nodesAddEventHandler;
|
||||
var nodesRemoveEventHandler;
|
||||
var layoutUpdateEventHandler; // Dashboard layout tool
|
||||
var uip = 'ui';
|
||||
var attemptedVendorLoad = false;
|
||||
var loadTinyColor = function(path) {
|
||||
$.ajax({ url: path,
|
||||
success: function (data) {
|
||||
var jsScript = document.createElement("script");
|
||||
jsScript.type = "application/javascript";
|
||||
jsScript.src = path;
|
||||
document.body.appendChild(jsScript);
|
||||
//console.log('Tiny Color Loaded:',path);
|
||||
},
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
if (xhr.status === 404 && !attemptedVendorLoad) {
|
||||
loadTinyColor('/'+uip+'/vendor/tinycolor2/dist/tinycolor-min.js');
|
||||
attemptedVendorLoad = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// convert to i18 text
|
||||
function c_(x) {
|
||||
return RED._("node-red-dashboard/ui_base:ui_base."+x);
|
||||
}
|
||||
|
||||
// Try to load dist version first
|
||||
// then if fails, load non dist version
|
||||
loadTinyColor('ui_base/js/tinycolor-min.js');
|
||||
//loadTinyColor('ui_base/tinycolor2/dist/tinycolor-min.js');
|
||||
|
||||
|
||||
// Dashboard layout tool
|
||||
// Load gridstack library
|
||||
var loadGsLib = function(path, callback) {
|
||||
$.ajax({ url: path,
|
||||
success: function (data) {
|
||||
var jsScript = document.createElement("script");
|
||||
jsScript.type = "application/javascript";
|
||||
jsScript.src = path;
|
||||
document.body.appendChild(jsScript);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
// TODO
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadGsLib('ui_base/js/lodash.min.js', function() {
|
||||
loadGsLib('ui_base/js/gridstack.min.js', function() {
|
||||
loadGsLib('ui_base/js/gridstack.jQueryUI.min.js', null)
|
||||
});
|
||||
});
|
||||
|
||||
var tabDatas; // Layout editing tab data
|
||||
var oldSpacer; // Spacer not needed after editing
|
||||
|
||||
var widthChange; // Group width change
|
||||
var widgetMove; // Move widget event
|
||||
var widgetResize; // Change widget event
|
||||
var widgetDrag; // Drag wiget eevnt
|
||||
|
||||
var MAX_GROUP_WIDTH = 30; // The maximum width is 30
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Get widget under specified tab from node information
|
||||
/////////////////////////////////////////////////////////
|
||||
function getTabDataFromNodes(tabID) {
|
||||
var nodes = RED.nodes.createCompleteNodeSet(false);
|
||||
var tab = {};
|
||||
// Tab information
|
||||
for (var cnt = 0; cnt < nodes.length; cnt++) {
|
||||
if (nodes[cnt].type == "ui_tab" && nodes[cnt].id == tabID) {
|
||||
tab = {
|
||||
id: nodes[cnt].id,
|
||||
name: nodes[cnt].name,
|
||||
type: nodes[cnt].type,
|
||||
order: nodes[cnt].order,
|
||||
groups: []
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Group information
|
||||
for (var cnt = 0; cnt < nodes.length; cnt++) {
|
||||
if (nodes[cnt].type == "ui_group" && nodes[cnt].tab == tabID) {
|
||||
var group = {
|
||||
id: nodes[cnt].id,
|
||||
name: nodes[cnt].name,
|
||||
type: nodes[cnt].type,
|
||||
order: nodes[cnt].order,
|
||||
width: nodes[cnt].width,
|
||||
widgets: []
|
||||
};
|
||||
tab.groups.push(group);
|
||||
}
|
||||
}
|
||||
// Widget information
|
||||
var groupsIdx = {};
|
||||
for (var cnt = 0; cnt < tab.groups.length; cnt++) {
|
||||
groupsIdx[tab.groups[cnt].id] = tab.groups[cnt];
|
||||
}
|
||||
for (var cnt = 0; cnt < nodes.length; cnt++) {
|
||||
var group = groupsIdx[nodes[cnt].group];
|
||||
if (group != null && (/^ui_/.test(nodes[cnt].type) && nodes[cnt].type !== 'ui_link' && nodes[cnt].type !== 'ui_toast' && nodes[cnt].type !== 'ui_ui_control' && nodes[cnt].type !== 'ui_audio' && nodes[cnt].type !== 'ui_base' && nodes[cnt].type !== 'ui_group' && nodes[cnt].type !== 'ui_tab')) {
|
||||
var widget = {
|
||||
id: nodes[cnt].id,
|
||||
type: nodes[cnt].type,
|
||||
order: nodes[cnt].order,
|
||||
width: nodes[cnt].width,
|
||||
height: nodes[cnt].height,
|
||||
auto: nodes[cnt].width == 0 ? true : false
|
||||
};
|
||||
group.widgets.push(widget);
|
||||
|
||||
if (!isLayoutToolSupported(nodes[cnt].type)){
|
||||
console.log("LayoutTool warning: Unsupported widget. Widget="+JSON.stringify(widget));
|
||||
}
|
||||
}
|
||||
}
|
||||
return tab;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Update node information in the edited widget
|
||||
////////////////////////////////////////////////////
|
||||
function putTabDataToNodes() {
|
||||
// Delete old flow spacer node
|
||||
for (var cnt = 0; cnt < oldSpacer.length; cnt++) {
|
||||
RED.nodes.remove(oldSpacer[cnt]);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
|
||||
var t_groups = tabDatas.groups;
|
||||
for (var cnt1 = 0; cnt1 < t_groups.length; cnt1++){
|
||||
var n_group = RED.nodes.node(t_groups[cnt1].id);
|
||||
n_group.width = t_groups[cnt1].width;
|
||||
var t_widgets = t_groups[cnt1].widgets;
|
||||
for (var cnt2 = 0; cnt2 < t_widgets.length; cnt2++) {
|
||||
var n_widget = RED.nodes.node(t_widgets[cnt2].id);
|
||||
if (n_widget != null) {
|
||||
if (n_widget.group !== n_group.id) {
|
||||
var oldGroupNode = RED.nodes.node(n_widget.group);
|
||||
if (oldGroupNode) {
|
||||
var index = oldGroupNode.users.indexOf(n_widget);
|
||||
oldGroupNode.users.splice(index,1);
|
||||
}
|
||||
n_widget.group = n_group.id;
|
||||
n_group.users.push(n_widget);
|
||||
}
|
||||
n_widget.order = t_widgets[cnt2].order;
|
||||
if (t_widgets[cnt2].auto === true ) {
|
||||
n_widget.width = 0;
|
||||
n_widget.height = 0;
|
||||
} else {
|
||||
n_widget.width = t_widgets[cnt2].width;
|
||||
n_widget.height = t_widgets[cnt2].height;
|
||||
}
|
||||
|
||||
n_widget.changed = true;
|
||||
n_widget.dirty = true;
|
||||
RED.editor.validateNode(n_widget);
|
||||
RED.events.emit("layout:update",n_widget);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
} else {
|
||||
// Add a spacer node
|
||||
if (t_widgets[cnt2].type === 'ui_spacer') {
|
||||
var spaceNode = {
|
||||
_def: RED.nodes.getType("ui_spacer"),
|
||||
type: "ui_spacer",
|
||||
hasUsers: false,
|
||||
users: [],
|
||||
id: RED.nodes.id(),
|
||||
tab: tabDatas.id,
|
||||
group: n_group.id,
|
||||
order: t_widgets[cnt2].order,
|
||||
name: "spacer",
|
||||
width: t_widgets[cnt2].width,
|
||||
height: t_widgets[cnt2].height,
|
||||
label: function() { return this.name + " " + this.width + "x" + this.height; }
|
||||
};
|
||||
RED.nodes.add(spaceNode);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
RED.sidebar.info.refresh();
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// Sort by order
|
||||
////////////////////////////////////////
|
||||
function compareOrder(a, b) {
|
||||
var r = 0;
|
||||
if (a.order < b.order) { r = -1; }
|
||||
else if (a.order > b.order) { r = 1; }
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// Sort by XY
|
||||
////////////////////////////////////////
|
||||
function compareXY(a, b) {
|
||||
var r = 0;
|
||||
if (a.y < b.y) { r = -1; }
|
||||
else if (a.y > b.y) { r = 1; }
|
||||
else if (a.x < b.x) { r = -1; }
|
||||
else if (a.x > b.x) { r = 1; }
|
||||
return r;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Placeable location search (placed in the upper left)
|
||||
///////////////////////////////////////////////////////
|
||||
function search_point(width, height, maxWidth, maxHeight, tbl){
|
||||
for (var y=0; y < maxHeight; y++) {
|
||||
for (var x=0; x < maxWidth; x++) {
|
||||
if (check_matrix(x, y, width, height, maxWidth, tbl)) {
|
||||
fill_matrix(x, y, width, height, maxWidth, tbl);
|
||||
return {x:x, y:y};
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Check placement position
|
||||
function check_matrix(px, py, width, height, maxWidth, tbl){
|
||||
if (px+width > maxWidth) return false;
|
||||
for (var y=py; y < py+height; y++) {
|
||||
for (var x=px; x<px+width; x++) {
|
||||
if (tbl[maxWidth*y+x]) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Mark the placement position
|
||||
function fill_matrix(px, py, width, height, maxWidth, tbl){
|
||||
for (var y=py; y < py+height; y++) {
|
||||
for (var x=px; x < px+width; x++) {
|
||||
tbl[maxWidth*y+x] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Apply edit result to tab information for editing
|
||||
////////////////////////////////////////////////////
|
||||
function saveGridDatas(){
|
||||
var groups = tabDatas.groups;
|
||||
for (var cnt = 0; cnt < groups.length; cnt++) {
|
||||
// Get layout editing results
|
||||
var gridID = '#grid'+cnt;
|
||||
var serializedData = _.map($(gridID+'.grid-stack > .grid-stack-item:visible'), function (el) {
|
||||
el = $(el);
|
||||
var node = el.data('_gridstack_node');
|
||||
return {
|
||||
id: el[0].dataset.noderedid,
|
||||
type: el[0].dataset.noderedtype,
|
||||
group: groups[cnt].id,
|
||||
width: Number(node.width),
|
||||
height: Number(node.height),
|
||||
x: node.x,
|
||||
y: node.y,
|
||||
auto: (el[0].dataset.noderedsizeauto == 'true') ? true : false
|
||||
};
|
||||
}, this);
|
||||
|
||||
var width = Number(groups[cnt].width);
|
||||
var height = 0;
|
||||
|
||||
// Search group height
|
||||
for (var i=0; i < serializedData.length; i++){
|
||||
var wd = serializedData[i];
|
||||
if (height < wd.y + wd.height) {
|
||||
height = wd.y + wd.height;
|
||||
}
|
||||
}
|
||||
|
||||
// Place widget on table
|
||||
var tbl = new Array(width * height);
|
||||
for (var i = 0; i< tbl.length; i++){
|
||||
tbl[i]=0;
|
||||
}
|
||||
for (var i = 0; i < serializedData.length; i++){
|
||||
var wd = serializedData[i];
|
||||
for (var y = wd.y; y < wd.y+wd.height; y++){
|
||||
for (var x = wd.x; x < wd.x+wd.width; x++){
|
||||
tbl[width*y+x]=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add Spacer to Blank
|
||||
for (var y = 0; y < height; y++){
|
||||
var spacerAdded = false;
|
||||
for (var x = 0; x < width; x++){
|
||||
if (tbl[width*y+x]===0) {
|
||||
if (!spacerAdded) {
|
||||
// Add 1x1 spacer
|
||||
serializedData.push({
|
||||
x: x,
|
||||
y: y,
|
||||
width: 1,
|
||||
height: 1,
|
||||
name: 'spacer',
|
||||
type: 'ui_spacer'
|
||||
});
|
||||
spacerAdded = true;
|
||||
} else {
|
||||
// Extend the spacer width by 1
|
||||
serializedData[serializedData.length-1].width += 1;
|
||||
}
|
||||
} else {
|
||||
spacerAdded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort Gridstack objects by x, y information
|
||||
serializedData.sort(compareXY);
|
||||
|
||||
// Delete x and y elements as information for sorting, and give order
|
||||
var order = 0;
|
||||
for (i in serializedData) {
|
||||
order++;
|
||||
delete serializedData[i].x;
|
||||
delete serializedData[i].y;
|
||||
serializedData[i].order = order;
|
||||
}
|
||||
|
||||
// Update widget information in group with edited data
|
||||
var group = groups[cnt];
|
||||
delete group.widgets;
|
||||
group.widgets = serializedData;
|
||||
}
|
||||
|
||||
// Save process call
|
||||
putTabDataToNodes();
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Get default height for automatic settings
|
||||
////////////////////////////////////////////////////
|
||||
function getDefaultHeight(nodeID, groupWidth) {
|
||||
var redNode = RED.nodes.node(nodeID);
|
||||
var height = 1;
|
||||
|
||||
if (redNode.type === 'ui_gauge') {
|
||||
if (redNode.gtype === 'gage') {
|
||||
height = Math.round(groupWidth/2)+1;
|
||||
} else if (redNode.gtype === 'wave') {
|
||||
if (groupWidth < 3) {
|
||||
height = 1;
|
||||
} else {
|
||||
height = Math.round(groupWidth*0.75);
|
||||
}
|
||||
} else { // donut or compass
|
||||
if (groupWidth < 3) {
|
||||
height = 1;
|
||||
} else if (groupWidth < 11) {
|
||||
height = groupWidth - 1;
|
||||
} else {
|
||||
height = Math.round(groupWidth*0.95);
|
||||
}
|
||||
}
|
||||
} else if (redNode.type === 'ui_chart') {
|
||||
height = Math.floor(groupWidth/2)+1;
|
||||
} else if (redNode.type === 'ui_form') {
|
||||
var optNum = redNode.options.length; // Sub widget number
|
||||
if (redNode.label) {
|
||||
height = optNum + 2; // Label and Button
|
||||
} else {
|
||||
height = optNum + 1; // Button only
|
||||
}
|
||||
} else if (redNode.type === 'ui_lineargauge') {
|
||||
if (redNode.unit && redNode.name) {
|
||||
height = 5;
|
||||
} else {
|
||||
height = 4;
|
||||
}
|
||||
} else if (redNode.type === 'ui_list') {
|
||||
height = 5;
|
||||
} else if (redNode.type === 'ui_vega') {
|
||||
height = 5;
|
||||
}
|
||||
return height;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Grid width change
|
||||
////////////////////////////
|
||||
var changeGroupWidth = function(id) {
|
||||
var widthID = '#change-width'+id;
|
||||
var gridID = '#grid'+id;
|
||||
$(widthID).spinner( {
|
||||
min: 1,
|
||||
max: MAX_GROUP_WIDTH,
|
||||
spin: function(event, ui){
|
||||
// Search current maximum width
|
||||
serializedData = _.map($(gridID+'.grid-stack > .grid-stack-item:visible'), function (el) {
|
||||
el = $(el);
|
||||
var node = el.data('_gridstack_node');
|
||||
return {
|
||||
width: Number(node.width),
|
||||
x: node.x,
|
||||
auto: (el[0].dataset.noderedsizeauto == 'true') ? true : false
|
||||
};
|
||||
}, this);
|
||||
var maxWidth = 0;
|
||||
for (var i=0; i < serializedData.length; i++){
|
||||
var wd = serializedData[i];
|
||||
if (wd.auto == false) {
|
||||
if (maxWidth < wd.x + wd.width) {
|
||||
maxWidth = wd.x + wd.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
var width = ui.value;
|
||||
if (width < maxWidth) {
|
||||
width = maxWidth;
|
||||
}
|
||||
|
||||
var grid = $(gridID+'.grid-stack').data('gridstack');
|
||||
$(gridID+'.grid-stack').css("width", width * 40);
|
||||
$(gridID+'.grid-stack').css("background-size", 100/width+"% 47px");
|
||||
grid.setGridWidth(tabDatas.groups[id].width, true);
|
||||
grid.setGridWidth(width, true);
|
||||
tabDatas.groups[id].width = width;
|
||||
|
||||
$(gridID+'.grid-stack > .grid-stack-item:visible').each( function(idx, el) {
|
||||
el = $(el);
|
||||
var node = el.data('_gridstack_node');
|
||||
var auto = (el[0].dataset.noderedsizeauto == 'true') ? true : false;
|
||||
var type = el[0].dataset.noderedtype;
|
||||
grid.resizable(el, !auto);
|
||||
if (auto === true) {
|
||||
grid.resize(el, width, getDefaultHeight(node.id, width));
|
||||
}
|
||||
});
|
||||
|
||||
if (width !== ui.value) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
// Move between groups of widgets
|
||||
//////////////////////////////////
|
||||
var moveGroupWidget = function(id) {
|
||||
|
||||
var toGridID = '#grid'+id;
|
||||
var toGrid = $(toGridID+'.grid-stack').data('gridstack');
|
||||
|
||||
toGrid.container.droppable({
|
||||
acccept: '.grid-stack-item',
|
||||
tolerance: 'pointer',
|
||||
drop: function( event, ui ) {
|
||||
|
||||
var fromGridID = '#' + ui.draggable[0].offsetParent.id;
|
||||
|
||||
// Move the widget
|
||||
if (fromGridID !== toGridID) {
|
||||
|
||||
var toGridOffset = $(toGridID).offset();
|
||||
var groupWidth = tabDatas.groups[id].width;
|
||||
|
||||
// Reset group width before moving (Grid stack measures)
|
||||
toGrid.setGridWidth(groupWidth, true);
|
||||
|
||||
// Current widget width factor for x position
|
||||
var curWidth = ($('#grid1').offset().left - $('#grid0').offset().left) * 0.9;
|
||||
var xfactor = curWidth / (groupWidth * 40);
|
||||
xfactor = xfactor > 1.0 ? 1.0 : xfactor;
|
||||
var x = Math.floor((ui.offset.left - toGridOffset.left)/(40 * xfactor));
|
||||
var y = Math.floor((ui.offset.top - toGridOffset.top)/47);
|
||||
var node = {
|
||||
type: ui.draggable[0].dataset.noderedtype,
|
||||
id: ui.draggable[0].dataset.noderedid,
|
||||
x: x,
|
||||
y: y,
|
||||
width: ui.draggable[0].dataset.gsWidth,
|
||||
height: ui.draggable[0].dataset.gsHeight,
|
||||
auto: (ui.draggable[0].dataset.noderedsizeauto == 'true') ? true : false
|
||||
};
|
||||
|
||||
var dispType = ui.draggable[0].dataset.nodereddisptype;
|
||||
var dispLabel = ui.draggable[0].dataset.nodereddisplabel;
|
||||
|
||||
var item = $('<div></div>', {
|
||||
'data-noderedtype': node.type,
|
||||
'data-noderedid': node.id,
|
||||
'data-nodereddisptype': dispType,
|
||||
'data-nodereddisplabel': dispLabel,
|
||||
'data-noderedsizeauto': node.auto
|
||||
});
|
||||
|
||||
if (node.auto === true) {
|
||||
node.x = 0;
|
||||
node.width = groupWidth;
|
||||
node.height = getDefaultHeight(node.id, groupWidth);
|
||||
}
|
||||
|
||||
var itemContent = $('<div></div>', {
|
||||
addClass: 'grid-stack-item-content',
|
||||
title: dispLabel + ':' + dispType
|
||||
});
|
||||
|
||||
if (node.auto === true) {
|
||||
itemContent.append('<i class="fa fa-unlock nr-dashboard-layout-resize-enable"></i>');
|
||||
itemContent.find('.nr-dashboard-layout-resize-enable').on('click',layoutResizeEnable);
|
||||
} else {
|
||||
itemContent.append('<i class="fa fa-lock nr-dashboard-layout-resize-disable"</i>');
|
||||
itemContent.find('.nr-dashboard-layout-resize-disable').on('click',layoutResizeDisable);
|
||||
}
|
||||
|
||||
itemContent.append('<b>'+ dispLabel +'</b><br/>'+ dispType);
|
||||
itemContent.appendTo(item);
|
||||
var newWidget = toGrid.addWidget(
|
||||
item,
|
||||
node.x, node.y, node.width, node.height, false, null, null,
|
||||
ui.draggable[0].dataset.gsMinHeight,
|
||||
ui.draggable[0].dataset.gsMaxHeight,
|
||||
node.id);
|
||||
|
||||
var fromWidget = ui.draggable[0];
|
||||
if (node.auto === true) {
|
||||
$(fromWidget).find('.nr-dashboard-layout-resize-enable').off('click');
|
||||
} else {
|
||||
$(fromWidget).find('.nr-dashboard-layout-resize-disable').off('click');
|
||||
}
|
||||
var fromGrid = $(fromGridID+'.grid-stack').data('gridstack');
|
||||
fromGrid.removeWidget(fromWidget);
|
||||
toGrid.resizable(newWidget, !node.auto);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Widget size change (start event)
|
||||
//////////////////////////////////////////
|
||||
var resizeGroupWidget = function(id) {
|
||||
var gridID = '#grid'+id;
|
||||
var grid = $(gridID+'.grid-stack').data('gridstack');
|
||||
$(gridID+'.grid-stack').on('resizestart', function(event, ui) {
|
||||
// Reset group width
|
||||
grid.setGridWidth(tabDatas.groups[id].width, true);
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Widget drag (start event)
|
||||
//////////////////////////////////////////
|
||||
var dragGroupWidget = function(id) {
|
||||
var gridID = '#grid'+id;
|
||||
var grid = $(gridID+'.grid-stack').data('gridstack');
|
||||
$(gridID+'.grid-stack').on('dragstart', function(event, ui) {
|
||||
// Reset group width
|
||||
grid.setGridWidth(tabDatas.groups[id].width, true);
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Layout resize Disable (Auto:false)
|
||||
//////////////////////////////////////////
|
||||
var layoutResizeDisable = function(e) {
|
||||
var target = $(e.target);
|
||||
var el = target.parents('.grid-stack-item:visible');
|
||||
var grid = target.parents('.grid-stack').data('gridstack');
|
||||
var node = el.data('_gridstack_node');
|
||||
|
||||
var id = Number(target.parents('.grid-stack').attr('id').slice(4));
|
||||
var width = Number(tabDatas.groups[id].width);
|
||||
|
||||
var nodeID = el[0].dataset.noderedid;
|
||||
var height = getDefaultHeight(nodeID, width);
|
||||
grid.move(el, 0, node.y);
|
||||
grid.resize(el, width, height);
|
||||
|
||||
grid.resizable(el, false);
|
||||
el.find('.nr-dashboard-layout-resize-disable').off('click');
|
||||
el.attr({'data-noderedsizeauto':'true'});
|
||||
target.removeClass().addClass('fa fa-unlock nr-dashboard-layout-resize-enable');
|
||||
el.find('.nr-dashboard-layout-resize-enable').on('click',layoutResizeEnable);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Layout resize Enable (Auto:true)
|
||||
//////////////////////////////////////////
|
||||
var layoutResizeEnable = function(e){
|
||||
var target = $(e.target);
|
||||
var el = target.parents('.grid-stack-item:visible');
|
||||
var grid = target.parents('.grid-stack').data('gridstack');
|
||||
|
||||
grid.resizable(el, true);
|
||||
el.find('.nr-dashboard-layout-resize-enable').off('click');
|
||||
el.attr({'data-noderedsizeauto':'false'});
|
||||
target.removeClass().addClass('fa fa-lock nr-dashboard-layout-resize-disable');
|
||||
el.find('.nr-dashboard-layout-resize-disable').on('click',layoutResizeDisable);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Check dashboard layout tool supported
|
||||
//////////////////////////////////////////
|
||||
function isLayoutToolSupported(nodeType){
|
||||
if (nodeType !== "ui_spacer" &&
|
||||
nodeType !== "ui_button" &&
|
||||
nodeType !== "ui_dropdown" &&
|
||||
nodeType !== "ui_switch" &&
|
||||
nodeType !== "ui_slider" &&
|
||||
nodeType !== "ui_numeric" &&
|
||||
nodeType !== "ui_text_input" &&
|
||||
nodeType !== "ui_date_picker" &&
|
||||
nodeType !== "ui_colour_picker" &&
|
||||
nodeType !== "ui_form" &&
|
||||
nodeType !== "ui_text" &&
|
||||
nodeType !== "ui_gauge" &&
|
||||
nodeType !== "ui_chart" &&
|
||||
nodeType !== "ui_template" &&
|
||||
nodeType !== "ui_list" &&
|
||||
nodeType !== "ui_table" &&
|
||||
nodeType !== "ui_lineargauge" &&
|
||||
nodeType !== "ui_my-little-ui-node" &&
|
||||
nodeType !== "ui_vega") {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RED.nodes.registerType('ui_base',{
|
||||
category: 'config',
|
||||
defaults: {
|
||||
name: {},
|
||||
theme: {},
|
||||
site: {}
|
||||
},
|
||||
hasUsers: false,
|
||||
paletteLabel: 'Dashboard',
|
||||
label: function() { return this.name || 'Node-RED Dashboard'; },
|
||||
labelStyle: function() { return this.name ? "node_label_italic" : ""; },
|
||||
onpaletteremove: function() {
|
||||
RED.sidebar.removeTab("dashboard");
|
||||
RED.events.off("editor:save",editSaveEventHandler);
|
||||
RED.events.off("nodes:add",nodesAddEventHandler);
|
||||
RED.events.off("nodes:remove",nodesRemoveEventHandler);
|
||||
RED.events.off("layout:update",layoutUpdateEventHandler); // Dashboard layout tool
|
||||
},
|
||||
onpaletteadd: function() {
|
||||
var globalDashboardNode = null;
|
||||
var editor;
|
||||
var baseStyles = ['base-color'];
|
||||
var configurableStyles = ['page-titlebar-backgroundColor', 'page-backgroundColor', 'page-sidebar-backgroundColor',
|
||||
'group-textColor', 'group-borderColor', 'group-backgroundColor',
|
||||
'widget-textColor', 'widget-backgroundColor','widget-borderColor'];
|
||||
var baseFontName = "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif";
|
||||
var aTheme = {primary:"indigo", accents:"blue", warn:"red", background:"grey"};
|
||||
// tiny colour implementation
|
||||
var colours = {
|
||||
leastReadable: function(base, colours) {
|
||||
var least = tinycolor.readability(base, colours[0]);
|
||||
var leastColor = colours[0];
|
||||
for (var i=1; i<colours.length; i++) {
|
||||
var readability = tinycolor.readability(base, colours[i]);
|
||||
if (readability < least) {
|
||||
least = readability;
|
||||
leastColor = colours[i];
|
||||
}
|
||||
}
|
||||
return leastColor;
|
||||
},
|
||||
whiteGreyMostReadable: function (base) {
|
||||
var rgb = tinycolor(base).toRgb();
|
||||
var level = ((rgb.r*299) + (rgb.g*587) + (rgb.b*114))/1000;
|
||||
var readable = (level >= 128) ? '#111111' : '#eeeeee';
|
||||
return readable;
|
||||
},
|
||||
whiteBlackLeastReadable: function(base) {
|
||||
return this.leastReadable(base, ["#000000", "#ffffff"]);
|
||||
},
|
||||
calculate_page_backgroundColor: function(base) {
|
||||
var pageBackground = "#fafafa";
|
||||
var theme = "light";
|
||||
if (globalDashboardNode && globalDashboardNode.hasOwnProperty("theme") && globalDashboardNode.theme.hasOwnProperty("name")) {
|
||||
theme = globalDashboardNode.theme.name.split('-')[1];
|
||||
}
|
||||
if (theme === "dark") {
|
||||
pageBackground = "#111111";
|
||||
}
|
||||
else if (theme === "custom") {
|
||||
var whiteOrBlack = this.whiteBlackLeastReadable(base);
|
||||
if (whiteOrBlack === "#000000") { pageBackground = "#111111"; }
|
||||
}
|
||||
return pageBackground;
|
||||
},
|
||||
calculate_page_sidebar_backgroundColor: function(base) {
|
||||
return this.whiteBlackLeastReadable(base);
|
||||
},
|
||||
calculate_page_titlebar_backgroundColor: function(base) {
|
||||
return base;
|
||||
},
|
||||
calculate_group_textColor: function(base) {
|
||||
var groupTextColour = tinycolor(base).lighten(15).toHexString();
|
||||
//if (this.whiteBlackLeastReadable(base) === "#ffffff") { groupTextColour = "#000000"; }
|
||||
return groupTextColour;
|
||||
},
|
||||
calculate_group_backgroundColor: function(base) {
|
||||
var groupBackground = "#ffffff";
|
||||
var theme = "light";
|
||||
if (globalDashboardNode && globalDashboardNode.hasOwnProperty("theme") && globalDashboardNode.theme.hasOwnProperty("name")) {
|
||||
theme = globalDashboardNode.theme.name.split('-')[1];
|
||||
}
|
||||
if (theme === "dark") {
|
||||
groupBackground = "#333333";
|
||||
}
|
||||
else if (theme === "custom") {
|
||||
var whiteOrBlack = this.whiteBlackLeastReadable(base);
|
||||
if (whiteOrBlack === "#000000") { groupBackground = "#333333"; }
|
||||
}
|
||||
return groupBackground;
|
||||
},
|
||||
calculate_group_borderColor: function(base) {
|
||||
var groupBackground = this.calculate_group_backgroundColor(base);
|
||||
return this.leastReadable(groupBackground, ["#ffffff", "#555555"]);
|
||||
},
|
||||
calculate_widget_textColor: function(base) {
|
||||
//most readable against group background
|
||||
var groupBackground = this.calculate_group_backgroundColor(base);
|
||||
return tinycolor.mostReadable(groupBackground, ["#111111", "#eeeeee"]).toHexString();
|
||||
},
|
||||
calculate_widget_backgroundColor: function(base) {
|
||||
//return tinycolor(base).darken(5).toHexString()
|
||||
return tinycolor(base).toHexString();
|
||||
},
|
||||
calculate_widget_borderColor: function(base) {
|
||||
var widgetBorder = "#ffffff";
|
||||
var theme = "light";
|
||||
if (globalDashboardNode && globalDashboardNode.hasOwnProperty("theme") && globalDashboardNode.theme.hasOwnProperty("name")) {
|
||||
theme = globalDashboardNode.theme.name.split('-')[1];
|
||||
}
|
||||
if (theme === "dark") {
|
||||
widgetBorder = "#333333";
|
||||
}
|
||||
else if (theme === "custom") {
|
||||
var whiteOrBlack = this.whiteBlackLeastReadable(base);
|
||||
if (whiteOrBlack === "#000000") { widgetBorder = "#333333"; }
|
||||
}
|
||||
return widgetBorder;
|
||||
},
|
||||
calculate_base_font: function(base) {
|
||||
return baseFontName;
|
||||
}
|
||||
}
|
||||
var sizes = {
|
||||
sx: 48, // width of <1> grid square
|
||||
sy: 48, // height of <1> grid square
|
||||
gx: 6, // gap between groups
|
||||
gy: 6, // gap between groups
|
||||
cx: 6, // gap between components
|
||||
cy: 6, // gap between components
|
||||
px: 0, // padding of group's cards
|
||||
py: 0 // padding of group's cards
|
||||
};
|
||||
|
||||
function ensureDashboardNode(createMissing) {
|
||||
if (globalDashboardNode !== null) {
|
||||
// Check if it has been deleted beneath us
|
||||
var n = RED.nodes.node(globalDashboardNode.id);
|
||||
if (n === null) { globalDashboardNode = null; }
|
||||
}
|
||||
|
||||
// Find the old dashboard node
|
||||
if (globalDashboardNode === null) {
|
||||
var bases = [];
|
||||
RED.nodes.eachConfig(function(n) {
|
||||
if (n.type === 'ui_base') { bases.push(n); }
|
||||
});
|
||||
|
||||
// make sure we only have one ui_base node
|
||||
// at the moment this will just use our existing one - deleting any new base node and theme
|
||||
// at some point we may want to make this an option to select one or the other.
|
||||
while (bases.length > 1) {
|
||||
var n = bases.pop();
|
||||
RED.nodes.remove(n.id);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
|
||||
if (bases.length === 1) { globalDashboardNode = bases[0]; }
|
||||
|
||||
// If there is no dashboard node, ensure we create it after
|
||||
// initialising
|
||||
var noDashboardNode = (globalDashboardNode === null);
|
||||
|
||||
// set up theme state
|
||||
var themeState = {};
|
||||
var baseColor = "#0094CE"
|
||||
for (var i=0; i<baseStyles.length; i++) {
|
||||
themeState[baseStyles[i]] = { default:baseColor, value:baseColor, edited:false };
|
||||
}
|
||||
for (var j = 0; j < configurableStyles.length; j++) {
|
||||
var underscore = configurableStyles[j].split("-").join("_");
|
||||
var colour = colours['calculate_'+underscore](baseColor);
|
||||
themeState[configurableStyles[j]] = {value:colour, edited:false};
|
||||
}
|
||||
themeState["base-font"] = {value:baseFontName};
|
||||
|
||||
var missingFields = (!globalDashboardNode || !globalDashboardNode.theme || !globalDashboardNode.site || !globalDashboardNode.site.sizes );
|
||||
|
||||
if (missingFields && createMissing) {
|
||||
var lightTheme = {
|
||||
default: baseColor,
|
||||
baseColor: baseColor,
|
||||
baseFont: baseFontName,
|
||||
edited: false
|
||||
}
|
||||
var darkTheme = {
|
||||
default: "#097479",
|
||||
baseColor: "#097479",
|
||||
baseFont: baseFontName,
|
||||
edited: false
|
||||
}
|
||||
var customTheme = {
|
||||
name: 'Untitled Theme 1',
|
||||
default: "#4B7930",
|
||||
baseColor: "#4B7930",
|
||||
baseFont: baseFontName
|
||||
}
|
||||
var oldThemeName;
|
||||
if (globalDashboardNode && typeof(globalDashboardNode.theme === 'string')) { oldThemeName = globalDashboardNode.theme; }
|
||||
|
||||
var theme = {
|
||||
name: oldThemeName || "theme-light",
|
||||
lightTheme: lightTheme,
|
||||
darkTheme: darkTheme,
|
||||
customTheme: customTheme,
|
||||
themeState: themeState,
|
||||
angularTheme: aTheme
|
||||
}
|
||||
|
||||
var site_name = c_("site.title");
|
||||
var site_date_format = c_("site.date-format");
|
||||
var site = { name:site_name, hideToolbar:"false", allowSwipe:"false", lockMenu:"false", allowTempTheme:"true", dateFormat:site_date_format, sizes:sizes };
|
||||
if (globalDashboardNode !== null) {
|
||||
if (typeof globalDashboardNode.site !== "undefined") {
|
||||
site = {
|
||||
name: globalDashboardNode.site.name || globalDashboardNode.name,
|
||||
hideToolbar: globalDashboardNode.site.hideToolbar,
|
||||
lockMenu: globalDashboardNode.site.lockMenu,
|
||||
allowSwipe: globalDashboardNode.site.allowSwipe,
|
||||
allowTempTheme: globalDashboardNode.site.allowTempTheme,
|
||||
dateFormat: globalDashboardNode.site.dateFormat,
|
||||
sizes: globalDashboardNode.site.sizes
|
||||
}
|
||||
}
|
||||
if (globalDashboardNode.theme.hasOwnProperty("angularTheme")) {
|
||||
aTheme = globalDashboardNode.theme.angularTheme;
|
||||
}
|
||||
else { globalDashboardNode.theme.angularTheme = aTheme; }
|
||||
}
|
||||
|
||||
if (noDashboardNode) {
|
||||
globalDashboardNode = {
|
||||
id: RED.nodes.id(),
|
||||
_def: RED.nodes.getType("ui_base"),
|
||||
type: "ui_base",
|
||||
site: site,
|
||||
theme: theme,
|
||||
users: []
|
||||
}
|
||||
RED.nodes.add(globalDashboardNode);
|
||||
}
|
||||
else {
|
||||
globalDashboardNode["_def"] = RED.nodes.getType("ui_base");
|
||||
globalDashboardNode.site = site;
|
||||
globalDashboardNode.theme = theme;
|
||||
delete globalDashboardNode.name;
|
||||
}
|
||||
$("#nr-db-field-font").val(baseFontName);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var content = $("<div>").css({"position":"relative","height":"100%"});
|
||||
var mainContent = $("<div>",{class:"nr-db-sb"}).appendTo(content);
|
||||
var form = $('<form class="dialog-form">').appendTo(mainContent);
|
||||
|
||||
// Dashboard Tabs markup
|
||||
var divTab = $('<div class="red-ui-tabs">').appendTo(form);
|
||||
var ulDashboardTabs = $('<ul id="dashboard-tabs-list"></ul>').appendTo(divTab);
|
||||
var layout_label = c_("label.layout");
|
||||
var site_label = c_("label.site");
|
||||
var theme_label = c_("label.theme");
|
||||
var angular_label = c_("label.angular");
|
||||
var liLayoutTab = $('<li class="red-ui-tab" style="width:70px;"><a class="red-ui-tab-label" title="Layout"><span>'+layout_label+'</span></a></li>').appendTo(ulDashboardTabs);
|
||||
var liSiteTab = $('<li class="red-ui-tab" style="width:70px;"><a class="red-ui-tab-label" title="Site" style="width:60px;"><span>'+site_label+'</span></a></li>').appendTo(ulDashboardTabs);
|
||||
var liThemeTab = $('<li class="red-ui-tab" style="width:70px;"><a class="red-ui-tab-label" title="Theme" style="width:80px;"><span>'+theme_label+'</span></a></li>').appendTo(ulDashboardTabs);
|
||||
var liAngularTab = $('<li class="red-ui-tab" style="width:70px;"><a class="red-ui-tab-label" title="Angular" style="width:80px;"><span>'+angular_label+'</span></a></li>').appendTo(ulDashboardTabs);
|
||||
|
||||
// Link out to dashboard
|
||||
$.getJSON('uisettings',function(data) {
|
||||
if (data.hasOwnProperty("path")) { uip = data.path; }
|
||||
var lnk = document.location.host+RED.settings.httpNodeRoot+"/"+uip;
|
||||
var re = new RegExp('\/{1,}','g');
|
||||
lnk = lnk.replace(re,'/');
|
||||
if (!RED.hasOwnProperty("actions")) {
|
||||
RED.keyboard.add("*",/* d */ 68,{ctrl:true, shift:true},function() { window.open(document.location.protocol+"//"+lnk, "nr-dashboard") });
|
||||
}
|
||||
else {
|
||||
RED.keyboard.add("*","ctrl-shift-d","dashboard:show-dashboard");
|
||||
RED.actions.add("dashboard:show-dashboard",function() { window.open(document.location.protocol+"//"+lnk, "nr-dashboard") });
|
||||
}
|
||||
$('<span id="dash-link-button" class="editor-button" style="position:absolute; right:0px;"><i class="fa fa-external-link"></i></span>')
|
||||
.click(function(evt) {
|
||||
window.open(document.location.protocol+"//"+lnk);
|
||||
evt.preventDefault();
|
||||
})
|
||||
.appendTo(ulDashboardTabs);
|
||||
});
|
||||
|
||||
// Dashboard Tab containers
|
||||
var layoutTab = $('<div id="dashboard-layout" style="height:calc(100% - 48px)">').appendTo(form);
|
||||
var siteTab = $('<div id="dashboard-site" style="display:none;">').appendTo(form);
|
||||
var themeTab = $('<div id="dashboard-theme" style="display:none;">').appendTo(form);
|
||||
var angularTab = $('<div id="dashboard-angular" style="display:none;">').appendTo(form);
|
||||
|
||||
ulDashboardTabs.children().first().addClass("active");
|
||||
|
||||
// Tab logic
|
||||
var onTabClick = function() {
|
||||
//Toggle tabs
|
||||
ulDashboardTabs.children().removeClass("active");
|
||||
ulDashboardTabs.children().css({"transition": "width 100ms"});
|
||||
$(this).parent().addClass("active");
|
||||
|
||||
var selectedTab = $(this)[0].title;
|
||||
if (selectedTab === 'Layout') {
|
||||
themeTab.hide();
|
||||
siteTab.hide();
|
||||
angularTab.hide();
|
||||
layoutTab.show();
|
||||
}
|
||||
else if (selectedTab === 'Angular') {
|
||||
themeTab.hide();
|
||||
siteTab.hide();
|
||||
angularTab.show();
|
||||
layoutTab.hide();
|
||||
}
|
||||
else if (selectedTab === 'Theme') {
|
||||
layoutTab.hide();
|
||||
siteTab.hide();
|
||||
angularTab.hide();
|
||||
themeTab.show();
|
||||
if ($("#nr-db-field-theme option:selected").val() === 'theme-custom') { themeSettingsContainer.show(); }
|
||||
else { themeSettingsContainer.hide(); }
|
||||
}
|
||||
else {
|
||||
layoutTab.hide();
|
||||
themeTab.hide();
|
||||
angularTab.hide();
|
||||
siteTab.show();
|
||||
}
|
||||
}
|
||||
|
||||
ulDashboardTabs.find("li.red-ui-tab a").on("click",onTabClick)
|
||||
|
||||
// Site Tab
|
||||
var divTitle = $('<div>',{class:"form-row compact"}).appendTo(siteTab);
|
||||
$('<div>').html('<b>'+c_("label.title")+'</b>').appendTo(divTitle);
|
||||
$('<input type="text" id="nr-db-field-title">').val(site_name).css("width","100%")
|
||||
.on("change", function() {
|
||||
if (!globalDashboardNode || globalDashboardNode.site.name !== $(this).val()) {
|
||||
ensureDashboardNode(true);
|
||||
globalDashboardNode.site.name = $(this).val();
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
})
|
||||
.appendTo(divTitle);
|
||||
|
||||
var divHideToolbar = $('<div>',{class:"form-row compact"}).appendTo(siteTab);
|
||||
$('<div>').html('<b>'+c_("label.options")+'</b>').appendTo(divHideToolbar);
|
||||
$('<select id="nr-db-field-hideToolbar">')
|
||||
.css("width","100%")
|
||||
.append($('<option>', { value:"false", text:c_("title-bar.show"), selected:true }))
|
||||
.append($('<option>', { value:"true", text:c_("title-bar.hide") }))
|
||||
.val("false")
|
||||
.on("change", function() {
|
||||
if (!globalDashboardNode || globalDashboardNode.site.hideToolbar !== $(this).val()) {
|
||||
ensureDashboardNode(true);
|
||||
globalDashboardNode.site.hideToolbar = $(this).val();
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
})
|
||||
.appendTo(divHideToolbar);
|
||||
|
||||
var divLockMenu = $('<div>',{class:"form-row compact"}).appendTo(siteTab);
|
||||
$('<select id="nr-db-field-lockMenu">')
|
||||
.css("width","100%")
|
||||
.append($('<option>', { value:"false", text:c_("lock.clicked"), selected:true }))
|
||||
.append($('<option>', { value:"true", text:c_("lock.locked") }))
|
||||
.val("false")
|
||||
.on("change", function() {
|
||||
if (!globalDashboardNode || globalDashboardNode.site.lockMenu !== $(this).val()) {
|
||||
ensureDashboardNode(true);
|
||||
globalDashboardNode.site.lockMenu = $(this).val();
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
})
|
||||
.appendTo(divLockMenu);
|
||||
|
||||
var divAllowSwipe = $('<div>',{class:"form-row compact"}).appendTo(siteTab);
|
||||
$('<select id="nr-db-field-allowSwipe">')
|
||||
.css("width","100%")
|
||||
.append($('<option>', { value:"false", text:c_("swipe.no-swipe"), selected:true }))
|
||||
.append($('<option>', { value:"true", text:c_("swipe.allow-swipe") }))
|
||||
.val("false")
|
||||
.on("change", function() {
|
||||
if (!globalDashboardNode || globalDashboardNode.site.allowSwipe !== $(this).val()) {
|
||||
ensureDashboardNode(true);
|
||||
globalDashboardNode.site.allowSwipe = $(this).val();
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
})
|
||||
.appendTo(divAllowSwipe);
|
||||
|
||||
var divAllowTempTheme = $('<div>',{class:"form-row compact"}).appendTo(siteTab);
|
||||
$('<select id="nr-db-field-allowTempTheme">')
|
||||
.css("width","100%")
|
||||
.append($('<option>', { value:"true", text:c_("temp.allow-theme"), selected:true }))
|
||||
.append($('<option>', { value:"false", text:c_("temp.no-theme") }))
|
||||
.append($('<option>', { value:"none", text:c_("temp.none") }))
|
||||
.val("true")
|
||||
.on("change", function() {
|
||||
if (!globalDashboardNode || globalDashboardNode.site.allowTempTheme !== $(this).val()) {
|
||||
ensureDashboardNode(true);
|
||||
globalDashboardNode.site.allowTempTheme = $(this).val();
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
if ($('#nr-db-field-allowTempTheme').val() === "none") {
|
||||
ulDashboardTabs.children().eq(2).addClass("hidden");
|
||||
ulDashboardTabs.children().eq(3).removeClass("hidden");
|
||||
}
|
||||
else {
|
||||
ulDashboardTabs.children().eq(2).removeClass("hidden");
|
||||
ulDashboardTabs.children().eq(3).addClass("hidden");
|
||||
}
|
||||
})
|
||||
.appendTo(divAllowTempTheme);
|
||||
|
||||
var site_name = c_("site.title");
|
||||
var site_date_format = c_("site.date-format");
|
||||
var divDateFormat = $('<div>',{class:"form-row"}).appendTo(siteTab);
|
||||
$('<div>').html('<b>'+c_("label.date-format")+'</b>')
|
||||
.css("width","80%")
|
||||
.css("display","inline-block")
|
||||
.appendTo(divDateFormat);
|
||||
$('<div>').html("<a href='https://momentjs.com/docs/#/displaying/format/' target='_new'><i class='fa fa-info-circle' style='color:grey;'></i></a>")
|
||||
.css("display","inline-block")
|
||||
.css("margin-right","6px")
|
||||
.css("float","right")
|
||||
.appendTo(divDateFormat);
|
||||
$('<input type="text" id="nr-db-field-dateFormat">').val(site_date_format).css("width","100%")
|
||||
.on("change", function() {
|
||||
if (!globalDashboardNode || globalDashboardNode.site.dateFormat !== $(this).val()) {
|
||||
ensureDashboardNode(true);
|
||||
globalDashboardNode.site.dateFormat = $(this).val();
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
})
|
||||
.appendTo(divDateFormat);
|
||||
|
||||
var divSetSizes = $('<div>',{class:"form-row"}).appendTo(siteTab);
|
||||
$('<span style="width:45%; display:inline-block">').html('<b>'+c_("label.sizes")+'</b>').appendTo(divSetSizes);
|
||||
$('<span style="width:25%; display:inline-block; font-size:smaller">').text(c_("label.horizontal")).appendTo(divSetSizes);
|
||||
$('<span style="width:20%; display:inline-block; font-size:smaller">').text(c_("label.vertical")).appendTo(divSetSizes);
|
||||
$('<i id="sizes-reset" class="fa fa-undo nr-db-resetIcon"></i>')
|
||||
.css({opacity:1.0})
|
||||
.click(function(e) {
|
||||
$("#nr-db-field-sx").val(sizes.sx); globalDashboardNode.site.sizes.sx = sizes.sx;
|
||||
$("#nr-db-field-sy").val(sizes.sy); globalDashboardNode.site.sizes.sy = sizes.sy;
|
||||
$("#nr-db-field-px").val(sizes.px); globalDashboardNode.site.sizes.px = sizes.px;
|
||||
$("#nr-db-field-py").val(sizes.py); globalDashboardNode.site.sizes.py = sizes.py;
|
||||
$("#nr-db-field-gx").val(sizes.gx); globalDashboardNode.site.sizes.gx = sizes.gx;
|
||||
$("#nr-db-field-gy").val(sizes.gy); globalDashboardNode.site.sizes.gy = sizes.gy;
|
||||
$("#nr-db-field-cx").val(sizes.cx); globalDashboardNode.site.sizes.cx = sizes.cx;
|
||||
$("#nr-db-field-cy").val(sizes.cy); globalDashboardNode.site.sizes.cy = sizes.cy;
|
||||
RED.nodes.dirty(true);
|
||||
})
|
||||
.appendTo(divSetSizes);
|
||||
|
||||
$('<br/><span style="width:45%; display:inline-block">').text(c_("label.widget-size")).appendTo(divSetSizes);
|
||||
$('<input type="number" name="sx" min="24" id="nr-db-field-sx">').val(48).css("width","20%")
|
||||
.on("change", function() { ensureDashboardNode(true); globalDashboardNode.site.sizes.sx=Number($(this).val()); RED.nodes.dirty(true); } )
|
||||
.appendTo(divSetSizes);
|
||||
$('<span style="width:5%; display:inline-block">').text(' ').appendTo(divSetSizes);
|
||||
$('<input type="number" name="sy" min="24" id="nr-db-field-sy">').val(48).css("width","20%")
|
||||
.on("change", function() { ensureDashboardNode(true); globalDashboardNode.site.sizes.sy=Number($(this).val()); RED.nodes.dirty(true); } )
|
||||
.appendTo(divSetSizes);
|
||||
|
||||
$('<br/><span style="width:45%; display:inline-block">').text(c_("label.widget-spacing")).appendTo(divSetSizes);
|
||||
$('<input type="number" name="cx" min="0" id="nr-db-field-cx">').val(6).css("width","20%")
|
||||
.on("change", function() { ensureDashboardNode(true); globalDashboardNode.site.sizes.cx=Number($(this).val()); RED.nodes.dirty(true); } )
|
||||
.appendTo(divSetSizes);
|
||||
$('<span style="width:5%; display:inline-block">').text(' ').appendTo(divSetSizes);
|
||||
$('<input type="number" name="cy" min="0" id="nr-db-field-cy">').val(6).css("width","20%")
|
||||
.on("change", function() { ensureDashboardNode(true); globalDashboardNode.site.sizes.cy=Number($(this).val()); RED.nodes.dirty(true); } )
|
||||
.appendTo(divSetSizes);
|
||||
|
||||
$('<br/><span style="width:45%; display:inline-block">').text(c_("label.group-padding")).appendTo(divSetSizes);
|
||||
$('<input type="number" name="px" min="0" id="nr-db-field-px">').val(0).css("width","20%")
|
||||
.on("change", function() { ensureDashboardNode(true); globalDashboardNode.site.sizes.px=Number($(this).val()); RED.nodes.dirty(true); } )
|
||||
.appendTo(divSetSizes);
|
||||
$('<span style="width:5%; display:inline-block">').text(' ').appendTo(divSetSizes);
|
||||
$('<input type="number" name="py" min="0" id="nr-db-field-py">').val(0).css("width","20%")
|
||||
.on("change", function() { ensureDashboardNode(true); globalDashboardNode.site.sizes.py=Number($(this).val()); RED.nodes.dirty(true); } )
|
||||
.appendTo(divSetSizes);
|
||||
|
||||
$('<br/><span style="width:45%; display:inline-block">').text(c_("label.group-spacing")).appendTo(divSetSizes);
|
||||
$('<input type="number" name="gx" min="0" id="nr-db-field-gx">').val(6).css("width","20%")
|
||||
.on("change", function() { ensureDashboardNode(true); globalDashboardNode.site.sizes.gx=Number($(this).val()); RED.nodes.dirty(true); } )
|
||||
.appendTo(divSetSizes);
|
||||
$('<span style="width:5%; display:inline-block">').text(' ').appendTo(divSetSizes);
|
||||
$('<input type="number" name="gy" min="0" id="nr-db-field-gy">').val(6).css("width","20%")
|
||||
.on("change", function() { ensureDashboardNode(true); globalDashboardNode.site.sizes.gy=Number($(this).val()); RED.nodes.dirty(true); } )
|
||||
.appendTo(divSetSizes);
|
||||
|
||||
|
||||
// Angular Theme Tab
|
||||
var changed = function() {
|
||||
ensureDashboardNode(true);
|
||||
globalDashboardNode.theme.angularTheme = aTheme;
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
|
||||
var angColorList = ["red", "pink", "purple", "deep-purple", "indigo", "blue", "light-blue", "cyan", "teal", "green", "light-green", "lime", "yellow", "amber", "orange", "deep-orange", "brown", "grey", "blue-grey"];
|
||||
var angColors = "";
|
||||
angColorList.forEach(function(c) { angColors += '<option value="' + c + '">' + c + '</option>'; });
|
||||
|
||||
var divPrimStyle = $('<div>',{class:"form-row"}).appendTo(angularTab);
|
||||
$('<span style="width:45%; display:inline-block">')
|
||||
.html('<b>'+c_("style.primary")+'</b>')
|
||||
.appendTo(divPrimStyle);
|
||||
$('<i id="ang-reset" class="fa fa-undo nr-db-resetIcon"></i>')
|
||||
.css({opacity:1.0})
|
||||
.click(function(e) {
|
||||
$("#nr-db-field-angPrimary").val("indigo");
|
||||
globalDashboardNode.theme.angularTheme.primary = "indigo";
|
||||
RED.nodes.dirty(true);
|
||||
})
|
||||
.appendTo(divPrimStyle);
|
||||
$('<select id="nr-db-field-angPrimary">'+angColors+'</select>')
|
||||
.css("width","100%")
|
||||
.val(aTheme.primary)
|
||||
.on("change", function() { aTheme.primary = $(this).val(); changed(); })
|
||||
.appendTo(divPrimStyle);
|
||||
|
||||
var divAccStyle = $('<div>',{class:"form-row"}).appendTo(angularTab);
|
||||
$('<span style="width:45%; display:inline-block">')
|
||||
.html('<b>'+c_("style.accents")+'</b>')
|
||||
.appendTo(divAccStyle);
|
||||
$('<i id="ang-reset" class="fa fa-undo nr-db-resetIcon"></i>')
|
||||
.css({opacity:1.0})
|
||||
.click(function(e) {
|
||||
$("#nr-db-field-angAccents").val("blue");
|
||||
globalDashboardNode.theme.angularTheme.accents = "blue";
|
||||
RED.nodes.dirty(true);
|
||||
})
|
||||
.appendTo(divAccStyle);
|
||||
$('<select id="nr-db-field-angAccents">'+angColors+'</select>')
|
||||
.css("width","100%")
|
||||
.val(aTheme.accents)
|
||||
.on("change", function() { aTheme.accents = $(this).val(); changed(); })
|
||||
.appendTo(divAccStyle);
|
||||
|
||||
var divWarnStyle = $('<div>',{class:"form-row"}).appendTo(angularTab);
|
||||
$('<span style="width:45%; display:inline-block">')
|
||||
.html('<b>'+c_("style.warnings")+'</b>')
|
||||
.appendTo(divWarnStyle);
|
||||
$('<i id="ang-reset" class="fa fa-undo nr-db-resetIcon"></i>')
|
||||
.css({opacity:1.0})
|
||||
.click(function(e) {
|
||||
$("#nr-db-field-angWarn").val("red");
|
||||
globalDashboardNode.theme.angularTheme.warn = "red";
|
||||
RED.nodes.dirty(true);
|
||||
})
|
||||
.appendTo(divWarnStyle);
|
||||
$('<select id="nr-db-field-angWarn">'+angColors+'</select>')
|
||||
.css("width","100%")
|
||||
.val(aTheme.warn)
|
||||
.on("change", function() { aTheme.warn = $(this).val(); changed(); })
|
||||
.appendTo(divWarnStyle);
|
||||
|
||||
var divBackStyle = $('<div>',{class:"form-row"}).appendTo(angularTab);
|
||||
$('<span style="width:45%; display:inline-block">')
|
||||
.html('<b>'+c_("style.background")+'</b>')
|
||||
.appendTo(divBackStyle);
|
||||
$('<i id="ang-reset" class="fa fa-undo nr-db-resetIcon"></i>')
|
||||
.css({opacity:1.0})
|
||||
.click(function(e) {
|
||||
$("#nr-db-field-angBackground").val("grey");
|
||||
globalDashboardNode.theme.angularTheme.background = "grey";
|
||||
RED.nodes.dirty(true);
|
||||
})
|
||||
.appendTo(divBackStyle);
|
||||
$('<select id="nr-db-field-angBackground">'+angColors+'</select>')
|
||||
.css("width","100%")
|
||||
.val(aTheme.background)
|
||||
.on("change", function() { aTheme.background = $(this).val(); changed(); })
|
||||
.appendTo(divBackStyle);
|
||||
|
||||
var divPalStyle = $('<div>',{class:"form-row"}).appendTo(angularTab);
|
||||
$('<span style="width:45%; display:inline-block">')
|
||||
.html('<b>'+c_("style.palette")+'</b>')
|
||||
.appendTo(divPalStyle);
|
||||
var lightdark = '<option value="light">' +c_("style.light")+ '</option>';
|
||||
lightdark += '<option value="dark">' +c_("style.dark")+ '</option>';
|
||||
$('<select id="nr-db-field-angLook">'+lightdark+'</select>')
|
||||
.css("width","100%")
|
||||
.val(aTheme.palette || "light")
|
||||
.on("change", function() { aTheme.palette = $(this).val(); changed(); })
|
||||
.appendTo(divPalStyle);
|
||||
|
||||
// Theme Tab
|
||||
// For all customisable styles, generate and apply the css
|
||||
var generateColours = function(base) {
|
||||
var theme = globalDashboardNode.theme.name.split('-')[1];
|
||||
if (!globalDashboardNode.theme.themeState.hasOwnProperty["base-font"]) {
|
||||
if (globalDashboardNode.theme[theme+"Theme"].baseFont === "Helvetica Neue") {
|
||||
globalDashboardNode.theme[theme+"Theme"].baseFont = baseFontName;
|
||||
}
|
||||
globalDashboardNode.theme.themeState["base-font"] = {value:globalDashboardNode.theme[theme+"Theme"].baseFont};
|
||||
$("#nr-db-field-font").val(globalDashboardNode.theme[theme+"Theme"].baseFont);
|
||||
}
|
||||
for (var i=0; i<configurableStyles.length; i++) {
|
||||
var styleID = configurableStyles[i];
|
||||
var underscore = styleID.split("-").join("_");
|
||||
if (!globalDashboardNode.theme.themeState.hasOwnProperty(styleID)) {
|
||||
globalDashboardNode.theme.themeState[styleID] = {value:"#fff",edited:false};
|
||||
}
|
||||
if (!globalDashboardNode.theme.themeState[styleID].edited || globalDashboardNode.theme[theme+'Theme'].reset) {
|
||||
var colour = colours['calculate_'+underscore](base);
|
||||
globalDashboardNode.theme.themeState[styleID].value = colour;
|
||||
}
|
||||
setColourPickerColour(styleID, globalDashboardNode.theme.themeState[styleID].value, globalDashboardNode.theme.themeState[styleID].edited);
|
||||
}
|
||||
globalDashboardNode.theme[theme+'Theme'].reset = false;
|
||||
}
|
||||
|
||||
var divThemeStyle = $('<div>',{class:"form-row"}).appendTo(themeTab);
|
||||
$('<label class="nr-db-theme-label">').text(c_("theme.style")).appendTo(divThemeStyle);
|
||||
var themeSelection = $('<select id="nr-db-field-theme">'+
|
||||
'<option value="theme-light">'+c_("style.light")+'</option>'+
|
||||
'<option value="theme-dark">'+c_("style.dark")+'</option>'+
|
||||
'<option value="theme-custom">'+c_("style.custom")+'</option>'+
|
||||
'</select>')
|
||||
.css("width","100%")
|
||||
.on("change", function() {
|
||||
if (!globalDashboardNode || globalDashboardNode.theme.name !== $(this).val()) {
|
||||
ensureDashboardNode(true);
|
||||
var theme = globalDashboardNode.theme.name.split('-')[1];
|
||||
var baseColour = globalDashboardNode.theme[theme+'Theme'].baseColor;
|
||||
var baseFont = globalDashboardNode.theme[theme+'Theme'].baseFont;
|
||||
globalDashboardNode.theme.name = $(this).val();
|
||||
theme = globalDashboardNode.theme.name.split('-')[1];
|
||||
if (theme !== "custom") {
|
||||
baseColour = globalDashboardNode.theme[theme+'Theme'].default;
|
||||
}
|
||||
else { baseColour = globalDashboardNode.theme[theme+'Theme'].baseColor; }
|
||||
setColourPickerColour("base-color", baseColour);
|
||||
globalDashboardNode.theme.themeState['base-color'].value = baseColour;
|
||||
globalDashboardNode.theme.themeState['base-color'].default = baseColour;
|
||||
globalDashboardNode.theme.themeState['base-font'] = {value:baseFont};
|
||||
$("#nr-db-field-font").val(baseFont);
|
||||
globalDashboardNode.theme[theme+'Theme'].reset = true;
|
||||
//generate colours for all colour settings from base colour
|
||||
generateColours(baseColour);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
$('#base-color-reset').remove();
|
||||
if ($(this).val() === 'theme-custom') {
|
||||
$("#custom-theme-library-container").show(); //TODO undo this at some point
|
||||
$("#custom-theme-settings").show();
|
||||
//addResetButton('base-color', baseSettingsUl.children());
|
||||
}
|
||||
else {
|
||||
$("#custom-theme-library-container").hide();
|
||||
$("#custom-theme-settings").hide();
|
||||
addLightAndDarkResetButton('base-color', baseSettingsUl.children().first());
|
||||
}
|
||||
})
|
||||
.appendTo(divThemeStyle);
|
||||
|
||||
var customThemeLibraryContainer = $('<div id="custom-theme-library-container">').appendTo(themeTab);
|
||||
$('<label class="nr-db-theme-label">').text(c_("theme.custom-profile")).appendTo(customThemeLibraryContainer);
|
||||
$('<input type="text" id="ui-sidebar-name" style="vertical-align:top;" placeholder="profile name (not blank)">')
|
||||
.val(c_("theme.custom-profile-name"))
|
||||
.on("change", function() {
|
||||
if (!globalDashboardNode || globalDashboardNode.theme.customTheme.name !== $(this).val()) {
|
||||
ensureDashboardNode(true);
|
||||
globalDashboardNode.theme.customTheme.name = $(this).val();
|
||||
if (editor) {
|
||||
editor.setValue(JSON.stringify({theme:globalDashboardNode.theme.themeState, site:globalDashboardNode.site}),1);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
})
|
||||
.keyup(function() {
|
||||
if ($(this).val().length === 0) {
|
||||
$("#custom-theme-library-container div").css("pointer-events","none");
|
||||
}
|
||||
else { $("#custom-theme-library-container div").css("pointer-events","inherit"); }
|
||||
})
|
||||
.appendTo(customThemeLibraryContainer);
|
||||
$('<input type="hidden" id="nr-db-field-format">').appendTo(customThemeLibraryContainer);
|
||||
$('<div style="display:none;" class="node-text-editor" id="nr-db-field-format-editor"></div>').appendTo(customThemeLibraryContainer);
|
||||
|
||||
var baseThemeSettingsContainer = $('<div id="base-theme-settings">').appendTo(themeTab);
|
||||
|
||||
var baseSettings = $('<div>',{class:"form-row"}).appendTo(baseThemeSettingsContainer);
|
||||
$('<label class="nr-db-theme-label">').text(c_("theme.base-settings")).appendTo(baseSettings);
|
||||
var baseSettingsUl = $('<ul id="base-settings-ul" class="red-ui-dashboard-theme-styles"></ul>').appendTo(baseSettings);
|
||||
|
||||
var baseColourItem = $('<li class="red-ui-dashboard-theme-item"><span>'+c_("base.colour")+'</span></li>').appendTo(baseSettingsUl);
|
||||
var spanColorContainer = $('<span class="nr-db-color-pick-container"></span>').appendTo(baseColourItem);
|
||||
|
||||
$('<input id="base-color" class="nr-db-field-themeColor" type="color" value="#ffffff"/>')
|
||||
.on("change", function() {
|
||||
ensureDashboardNode(true);
|
||||
var value = $(this).val();
|
||||
var lightThemeMatch = globalDashboardNode.theme.lightTheme.baseColor === value;
|
||||
var darkThemeMatch = globalDashboardNode.theme.darkTheme.baseColor === value;
|
||||
var customThemeMatch = globalDashboardNode.theme.customTheme.baseColor === value;
|
||||
if (!globalDashboardNode || !lightThemeMatch || !darkThemeMatch || !customThemeMatch) {
|
||||
var theme = globalDashboardNode.theme.name.split('-')[1];
|
||||
globalDashboardNode.theme[theme+'Theme'].baseColor = value;
|
||||
if (globalDashboardNode.theme.name === 'theme-light' || globalDashboardNode.theme.name === 'theme-dark') {
|
||||
//for light and dark themes, reset the colours
|
||||
globalDashboardNode.theme[theme+'Theme'].reset = true;
|
||||
}
|
||||
generateColours(value);
|
||||
editor.setValue(JSON.stringify({theme:globalDashboardNode.theme.themeState, site:globalDashboardNode.site}),1);
|
||||
colourPickerChangeHandler($(this).attr('id'), value);
|
||||
}
|
||||
})
|
||||
.appendTo(spanColorContainer);
|
||||
|
||||
var baseFontItem = $('<li class="red-ui-dashboard-theme-item"><span>'+c_("base.font")+'</span></li>').appendTo(baseSettingsUl);
|
||||
var fontSelector = $('<select id="nr-db-field-font">'+
|
||||
'<option value="'+baseFontName+'" style="font-family:'+baseFontName+'">'+c_("font.system")+'</option>'+
|
||||
'<option value="Arial,Arial,Helvetica,sans-serif" style="font-family:Arial,Arial,Helvetica,sans-serif">Arial</option>'+
|
||||
'<option value="Arial Black,Arial Black,Gadget,sans-serif" style="font-family:Arial Black,Arial Black,Gadget,sans-serif">Arial Black</option>'+
|
||||
'<option value="Arial Narrow,Nimbus Sans L,sans-serif" style="font-family:Arial Narrow,Nimbus Sans L,sans-serif">Arial Narrow</option>'+
|
||||
'<option value="Century Gothic,CenturyGothic,AppleGothic,sans-serif" style="font-family:Century Gothic,CenturyGothic,AppleGothic,sans-serif">Century Gothic</option>'+
|
||||
'<option value="Copperplate,Copperplate Gothic Light,fantasy" style="font-family:Copperplate,Copperplate Gothic Light,fantasy;">Copperplate</option>'+
|
||||
'<option value="Courier,monospace" style="font-family:Courier,monospace;">Courier</option>'+
|
||||
'<option value="Georgia,Georgia,serif" style="font-family:Georgia,Georgia,serif">Georgia</option>'+
|
||||
'<option value="Gill Sans,Geneva,sans-serif" style="font-family:Gill Sans,Geneva,sans-serif;">Gill Sans</option>'+
|
||||
//'<option value="Helvetica Neue,Helvetica,sans-serif" style="font-family:Helvetica Neue,Helvetica,sans-serif">Helvetica Neue</option>'+
|
||||
'<option value="Impact,Impact,Charcoal,sans-serif" style="font-family:Impact,Impact,Charcoal,sans-serif">Impact</option>'+
|
||||
'<option value="Lucida Sans Typewriter,Lucida Console,Monaco,monospace" style="font-family:Lucida Console,Monaco,monospace">Lucida Console</option>'+
|
||||
'<option value="Lucida Sans Unicode,Lucida Grande,sans-serif" style="font-family:Lucida Sans Unicode,Lucida Grande,sans-serif">Lucida Sans</option>'+
|
||||
'<option value="Palatino Linotype,Palatino,Book Antiqua,serif" style="font-family:Palatino Linotype,Palatino,Book Antiqua,serif">Palatino Linotype</option>'+
|
||||
'<option value="Tahoma,Geneva,sans-serif" style="font-family:Tahoma,Geneva,sans-serif">Tahoma</optionstyle="font-family:>'+
|
||||
'<option value="Times New Roman,Times,serif" style="font-family:Times New Roman,Times,serif">Times New Roman</option>'+
|
||||
'<option value="Trebuchet MS,Helvetica,sans-serif" style="font-family:Trebuchet MS,Helvetica,sans-serif">Trebuchet MS</option>'+
|
||||
'<option value="Verdana,Verdana,Geneva,sans-serif" style="font-family:Verdana,Verdana,Geneva,sans-serif">Verdana</option>'+
|
||||
'</select>')
|
||||
.on("change", function() {
|
||||
ensureDashboardNode(true);
|
||||
var theme = globalDashboardNode.theme.name.split('-')[1];
|
||||
globalDashboardNode.theme[theme+'Theme'].baseFont = $(this).val();
|
||||
globalDashboardNode.theme.themeState['base-font'] = {value:$(this).val()};
|
||||
RED.nodes.dirty(true);
|
||||
})
|
||||
.appendTo(baseFontItem);
|
||||
|
||||
var themeSettingsContainer = $('<div id="custom-theme-settings">').appendTo(themeTab);
|
||||
|
||||
// Markup
|
||||
// Page styles
|
||||
var divPageStyle = $('<div>',{class:"form-row"}).appendTo(themeSettingsContainer);
|
||||
$('<label class="nr-db-theme-label">').text(c_("theme.page-settings")).appendTo(divPageStyle);
|
||||
var pageStyles = $('<ul class="red-ui-dashboard-theme-styles"></ul>').appendTo(themeSettingsContainer);
|
||||
addCustomisableStyle('page-titlebar-backgroundColor', c_("theme.page.title"), pageStyles);
|
||||
addCustomisableStyle('page-backgroundColor', c_("theme.page.page"), pageStyles);
|
||||
addCustomisableStyle('page-sidebar-backgroundColor', c_("theme.page.side"), pageStyles);
|
||||
|
||||
// Group styles
|
||||
var divGroupStyle = $('<div>',{class:"form-row"}).appendTo(themeSettingsContainer);
|
||||
$('<label class="nr-db-theme-label">').text(c_("theme.group-settings")).appendTo(divGroupStyle);
|
||||
var groupStyles = $('<ul class="red-ui-dashboard-theme-styles"></ul>').appendTo(themeSettingsContainer);
|
||||
addCustomisableStyle('group-textColor', c_("theme.group.text"), groupStyles);
|
||||
addCustomisableStyle('group-borderColor', c_("theme.group.border"), groupStyles);
|
||||
addCustomisableStyle('group-backgroundColor', c_("theme.group.background"), groupStyles);
|
||||
|
||||
// Widget styles
|
||||
var divWidgetStyle = $('<div>',{class:"form-row"}).appendTo(themeSettingsContainer);
|
||||
$('<label class="nr-db-theme-label">').text(c_("theme.widget-settings")).appendTo(divWidgetStyle);
|
||||
var widgetStyles = $('<ul class="red-ui-dashboard-theme-styles"></ul>').appendTo(themeSettingsContainer);
|
||||
addCustomisableStyle('widget-textColor', c_("theme.widget.text"), widgetStyles);
|
||||
addCustomisableStyle('widget-backgroundColor', c_("theme.widget.colour"), widgetStyles);
|
||||
addCustomisableStyle('widget-borderColor', c_("theme.widget.background"), widgetStyles);
|
||||
|
||||
function addCustomisableStyle(id, name, parentUl) {
|
||||
var styleLi = $('<li class="red-ui-dashboard-theme-item"><span>'+name+'</span></li>').appendTo(parentUl);
|
||||
var spanColorContainer = $('<span class="nr-db-color-pick-container"></span>').appendTo(styleLi);
|
||||
$('<input id="'+id+'" class="nr-db-field-themeColor" type="color" value="#ffffff"/>')
|
||||
.on("change", function() {
|
||||
colourPickerChangeHandler($(this).attr('id'), $(this).val());
|
||||
})
|
||||
.appendTo(spanColorContainer);
|
||||
addResetButton(id, styleLi);
|
||||
}
|
||||
|
||||
function colourPickerChangeHandler(id, value) {
|
||||
$("#"+id).css("background-color", value);
|
||||
$("#"+id+"-reset").css({opacity:1});
|
||||
globalDashboardNode.theme.themeState[id].edited = true;
|
||||
globalDashboardNode.theme.themeState[id].value = value;
|
||||
if (editor) {
|
||||
editor.setValue(JSON.stringify({theme:globalDashboardNode.theme.themeState, site:globalDashboardNode.site}),1);
|
||||
}
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
|
||||
function addResetButton(id, parent) {
|
||||
var resetToDefault = $('<i id="'+id+'-reset" class="fa fa-undo nr-db-resetIcon"></i>')
|
||||
.css({opacity:0.2})
|
||||
.click(function(e) { resetClick(e); })
|
||||
.appendTo(parent);
|
||||
}
|
||||
|
||||
function addLightAndDarkResetButton(id, parent) {
|
||||
if ($("#" + id + "-reset").length === 0) {
|
||||
var resetToDefault = $('<i id="'+id+'-reset" class="fa fa-undo nr-db-resetIcon"></i>')
|
||||
.css({opacity:1})
|
||||
.click(function(e) { lightAndDarkResetClick(e); })
|
||||
.appendTo(parent);
|
||||
globalDashboardNode.theme[globalDashboardNode.theme.name.split('-')[1] + 'Theme'].edited = true;
|
||||
}
|
||||
}
|
||||
|
||||
function lightAndDarkResetClick(e) {
|
||||
var elementID = e.target.id.split('-reset')[0];
|
||||
var key = globalDashboardNode.theme.name.split('-')[1] + 'Theme';
|
||||
//sanity check - light and dark only allow base-color-reset
|
||||
if (elementID === 'base-color') { // && globalDashboardNode.theme[key].edited) {
|
||||
var defaultColor = globalDashboardNode.theme[key].default;
|
||||
globalDashboardNode.theme[key].reset = true;
|
||||
generateColours(defaultColor);
|
||||
setColourPickerColour(elementID, defaultColor);
|
||||
$("#"+elementID+"-reset").css({opacity:0.2});
|
||||
globalDashboardNode.theme.themeState[elementID].value = defaultColor;
|
||||
globalDashboardNode.theme[key].baseColor = defaultColor;
|
||||
globalDashboardNode.theme[key].edited = false;
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
function resetClick(e) {
|
||||
//take off -reset
|
||||
var elementID = e.target.id.split('-reset')[0];
|
||||
if (globalDashboardNode.theme.themeState[elementID].edited) {
|
||||
var defaultColor = globalDashboardNode.theme.themeState['base-color'].value;
|
||||
var colour;
|
||||
//set colour
|
||||
if (elementID === 'base-color') {
|
||||
colour = defaultColor;
|
||||
generateColours(colour);
|
||||
}
|
||||
else {
|
||||
var underscore = elementID.split('-').join('_');
|
||||
colour = colours['calculate_'+underscore](defaultColor);
|
||||
}
|
||||
setColourPickerColour(elementID, colour);
|
||||
$("#"+elementID+"-reset").css({opacity:0.2});
|
||||
globalDashboardNode.theme.themeState[elementID].edited = false;
|
||||
globalDashboardNode.theme.themeState[elementID].value = colour;
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
function setColourPickerColour(id, val, ed) {
|
||||
$("#"+id).val(val);
|
||||
$("#"+id).css("background-color", val);
|
||||
//call mostReadableGreyWhite to set text colour
|
||||
var textColor = colours.whiteGreyMostReadable(val);
|
||||
$("#"+id).css("color", textColor);
|
||||
if (ed === true) { $("#"+id+"-reset").css({opacity:1}); }
|
||||
else { $("#"+id+"-reset").css({opacity:0.2}); }
|
||||
}
|
||||
|
||||
//Layout Tab
|
||||
var divTabs = $('<div>',{class:"form-row",style:"position:relative"}).appendTo(layoutTab);
|
||||
$('<label>').html('<b>'+c_("layout.tab-and-link")+'</b>').appendTo(divTabs);
|
||||
|
||||
var buttonGroup = $('<div>',{class:"nr-db-sb-list-button-group"}).appendTo(divTabs);
|
||||
|
||||
//Toggle expand buttons
|
||||
$('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-angle-double-up"></i></a>')
|
||||
.click(function(evt) {
|
||||
tabContainer.find(".nr-db-sb-group-list-container").slideUp().addClass('nr-db-sb-collapsed');
|
||||
tabContainer.find(".nr-db-sb-tab-list-header>.nr-db-sb-list-chevron").css({"transform":"rotate(-90deg)"});
|
||||
evt.preventDefault();
|
||||
})
|
||||
.appendTo(buttonGroup);
|
||||
$('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-angle-double-down"></i></a>')
|
||||
.click(function(evt) {
|
||||
tabContainer.find(".nr-db-sb-group-list-container").slideDown().removeClass('nr-db-sb-collapsed');
|
||||
tabContainer.find(".nr-db-sb-tab-list-header>.nr-db-sb-list-chevron").css({"transform":""});
|
||||
evt.preventDefault();
|
||||
})
|
||||
.appendTo(buttonGroup);
|
||||
|
||||
//Add item button
|
||||
$('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-plus"></i> '+c_("layout.tab")+'</a>')
|
||||
.click(function(evt) {
|
||||
tabContainer.editableList('addItem',{type: 'ui_tab'});
|
||||
evt.preventDefault();
|
||||
})
|
||||
.appendTo(buttonGroup);
|
||||
$('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-plus"></i> '+c_("layout.link")+'</a>')
|
||||
.click(function(evt) {
|
||||
tabContainer.editableList('addItem',{type: 'ui_link'});
|
||||
evt.preventDefault();
|
||||
})
|
||||
.appendTo(buttonGroup);
|
||||
|
||||
var tabLists = {};
|
||||
var groupLists = {};
|
||||
|
||||
// toggle slide tab group content
|
||||
var titleToggle = function (id,content,chevron) {
|
||||
return function(evt) {
|
||||
if (content.is(":visible")) {
|
||||
content.slideUp();
|
||||
chevron.css({"transform":"rotate(-90deg)"});
|
||||
content.addClass('nr-db-sb-collapsed');
|
||||
listStates[id] = false;
|
||||
}
|
||||
else {
|
||||
content.slideDown();
|
||||
chevron.css({"transform":""});
|
||||
content.removeClass('nr-db-sb-collapsed');
|
||||
listStates[id] = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var addTabOrLinkItem = function(container,i,item) {
|
||||
// create node if needed
|
||||
if (!item.node) {
|
||||
var defaultItem = {
|
||||
'ui_tab': {
|
||||
_def: RED.nodes.getType('ui_tab'),
|
||||
type: 'ui_tab',
|
||||
users: [],
|
||||
icon: 'dashboard',
|
||||
name: 'Tab'
|
||||
},
|
||||
'ui_link': {
|
||||
_def: RED.nodes.getType('ui_link'),
|
||||
type: 'ui_link',
|
||||
users: [],
|
||||
icon: 'open_in_browser',
|
||||
name: 'Link',
|
||||
target: 'newtab'
|
||||
}
|
||||
}
|
||||
item.node = defaultItem[item.type]
|
||||
item.node.id = RED.nodes.id()
|
||||
item.node.order = i+1
|
||||
item.node.name += ' '+item.node.order
|
||||
listElements[item.node.id] = container;
|
||||
if (item.type === 'ui_tab') {
|
||||
item.groups = [];
|
||||
}
|
||||
RED.nodes.add(item.node);
|
||||
RED.history.push({
|
||||
t:'add',
|
||||
nodes:[item.node.id],
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
else if (item.type === undefined) {
|
||||
item.type = item.node.type
|
||||
}
|
||||
|
||||
listElements[item.node.id] = container;
|
||||
if (RED.nodes.hasOwnProperty('updateConfigNodeUsers')) {
|
||||
RED.nodes.updateConfigNodeUsers(item.node);
|
||||
}
|
||||
|
||||
// title
|
||||
var titleRow = $('<div>',{class:"nr-db-sb-list-header nr-db-sb-tab-list-header"}).appendTo(container);
|
||||
switch (item.type) {
|
||||
case 'ui_tab': {
|
||||
container.addClass("nr-db-sb-tab-list-item");
|
||||
$('<i class="nr-db-sb-list-handle nr-db-sb-tab-list-handle fa fa-bars"></i>').appendTo(titleRow);
|
||||
var chevron = $('<i class="fa fa-angle-down nr-db-sb-list-chevron">',{style:"width:10px;"}).appendTo(titleRow);
|
||||
var tabicon = "fa-object-group";
|
||||
//var tabicon = item.node.disabled ? "fa-window-close-o" : item.node.hidden ? "fa-eye-slash" : "fa-object-group";
|
||||
$('<i>',{class:"nr-db-sb-icon nr-db-sb-tab-icon fa "+tabicon}).appendTo(titleRow);
|
||||
var tabhide = item.node.hidden ? " nr-db-sb-title-hidden" : "";
|
||||
var tabable = item.node.disabled ? " nr-db-sb-title-disabled" : "";
|
||||
$('<span>',{class:"nr-db-sb-title"+tabhide+tabable}).text(item.node.name||"").appendTo(titleRow);
|
||||
break;
|
||||
}
|
||||
case 'ui_link': {
|
||||
$('<i class="nr-db-sb-list-handle fa fa-bars"></i>').appendTo(titleRow);
|
||||
var title = $('<div class="nr-db-sb-link">').appendTo(titleRow);
|
||||
var nameContainer = $('<div class="nr-db-sb-link-name-container">').appendTo(title);
|
||||
$('<i class="fa fa-external-link"></i>').appendTo(nameContainer);
|
||||
$('<span class="nr-db-sb-link-name">').text(item.node.name||"untitled").appendTo(nameContainer);
|
||||
$('<div class="nr-db-sb-link-url">').text(item.node.link||"http://").appendTo(title);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// buttons
|
||||
var buttonGroup = $('<div>',{class:"nr-db-sb-list-header-button-group"}).appendTo(titleRow);
|
||||
if (item.type === 'ui_tab') {
|
||||
var addGroupButton = $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-plus"></i> '+c_("layout.group")+'</a>').appendTo(buttonGroup);
|
||||
}
|
||||
var editButton = $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-pencil"></i> '+c_("layout.edit")+'</a>').appendTo(buttonGroup);
|
||||
editButton.on('click',function(evt) {
|
||||
RED.editor.editConfig("", item.type, item.node.id);
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
});
|
||||
|
||||
// Dashboard layout tool
|
||||
if (item.type === 'ui_tab') {
|
||||
var layoutButton = $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-pencil"></i> '+c_("layout.layout")+'</a>').appendTo(buttonGroup);
|
||||
layoutButton.on('click',function(evt) {
|
||||
var editTabName = item.node.name ? item.node.name : item.node.id;
|
||||
var trayOptions = {
|
||||
title: c_("layout.layout-editor") + " : " + editTabName,
|
||||
width: Infinity,
|
||||
buttons: [
|
||||
{
|
||||
id: "node-dialog-cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
// clean editor
|
||||
RED.tray.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "node-dialog-ok",
|
||||
text: RED._("common.label.done"),
|
||||
class: "primary",
|
||||
click: function() {
|
||||
// Save data after editing
|
||||
saveGridDatas();
|
||||
RED.tray.close();
|
||||
}
|
||||
}
|
||||
],
|
||||
resize: function(dimensions) {
|
||||
//
|
||||
},
|
||||
open: function(tray) {
|
||||
// Get widget of specified tab from node information
|
||||
tabDatas = getTabDataFromNodes(item.node.id);
|
||||
// The width that can be handled by Layout is up to MAX_GROUP_WIDTH
|
||||
// Groups exceeding the maximum width are not supported.
|
||||
var tmpGroups = tabDatas.groups;
|
||||
tmpGroups.sort(compareOrder);
|
||||
var groups = [];
|
||||
for (var cnt = 0; cnt < tmpGroups.length; cnt++) {
|
||||
if (tmpGroups[cnt].width <= MAX_GROUP_WIDTH) {
|
||||
groups.push(tmpGroups[cnt]);
|
||||
}
|
||||
}
|
||||
tabDatas.groups = groups;
|
||||
|
||||
var editor = $('<div></div>',{addClass: 'nr-dashboard-layout-container-fluid'});
|
||||
var row = $('<div></div>',{addClass: 'nr-dashboard-layout-row'});
|
||||
var span_num = Math.floor(12 / groups.length); // bootstrap grid 12 splits
|
||||
span_num = span_num < 2 ? 2 : span_num; // max 6 groups per row
|
||||
for (var cnt = 0; cnt < groups.length; cnt++) {
|
||||
if (cnt !=0 && (cnt % 6) == 0) {
|
||||
editor.append(row);
|
||||
editor.append('<div><br></div>');
|
||||
row = $('<div></div>',{addClass: 'nr-dashboard-layout-row'});
|
||||
}
|
||||
var span = $('<div></div>',{addClass: 'nr-dashboard-layout-span' + span_num});
|
||||
var groupName = groups[cnt].name ? groups[cnt].name : groups[cnt].id;
|
||||
var title = $('<div></div>', {
|
||||
style: "margin-top:2px; margin-bottom:2px;"
|
||||
});
|
||||
var title_group = $('<div></div>', {
|
||||
title: groupName,
|
||||
style: "margin-left:4px; margin-right:8px; overflow:hidden;"
|
||||
}).appendTo(title);
|
||||
$("<b/>").text(groupName).appendTo(title_group);
|
||||
var title_width = $('<div></div>', {
|
||||
style: "text-align:right; margin-right:8px;"
|
||||
}).appendTo(title);
|
||||
$("<span/>", {
|
||||
style: "margin_right: 8px;"
|
||||
}).text(c_("layout.width")+': ').appendTo(title_width);
|
||||
var changeWidth = $('<input>', {
|
||||
id: 'change-width' + cnt,
|
||||
value: groups[cnt].width,
|
||||
style: 'width:30px;',
|
||||
readonly: true
|
||||
});
|
||||
title_width.append(changeWidth);
|
||||
|
||||
title.css('white-space','nowrap');
|
||||
title.css('overflow','hidden');
|
||||
var gridstack = $('<div></div>', {
|
||||
id: 'grid'+cnt,
|
||||
addClass: 'grid-stack'
|
||||
});
|
||||
span.append(title);
|
||||
span.append(gridstack);
|
||||
row.append(span);
|
||||
}
|
||||
if (groups.length != 0) {
|
||||
editor.append(row);
|
||||
}
|
||||
|
||||
// Show layout editor in tray
|
||||
var trayBody = tray.find('.red-ui-tray-body, .editor-tray-body');
|
||||
trayBody.css('overflow','auto');
|
||||
trayBody.append(editor);
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Gridstack.js option
|
||||
////////////////////////////////////////
|
||||
var options = {
|
||||
cellHeight: 46,
|
||||
verticalMargin: 1,
|
||||
float: true,
|
||||
alwaysShowResizeHandle: true,
|
||||
disableOneColumnMode : true
|
||||
};
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Editor screen generation
|
||||
/////////////////////////////////////////
|
||||
oldSpacer = [];
|
||||
widthChange = [];
|
||||
widgetMove = [];
|
||||
widgetResize = [];
|
||||
widgetDrag = [];
|
||||
for (var cnt=0; cnt < groups.length; cnt++) {
|
||||
var gridID='#grid' + cnt;
|
||||
// gridstack generation
|
||||
$(gridID).gridstack(options);
|
||||
|
||||
// Clear the contents of Grind
|
||||
var grid = $(gridID+'.grid-stack').data('gridstack');
|
||||
grid.removeAll();
|
||||
|
||||
// Set the width of the display area of gridstack
|
||||
var groupWidth = Number(groups[cnt].width);
|
||||
$(gridID+'.grid-stack').css("width", groupWidth * 40);
|
||||
$(gridID+'.grid-stack').css("background-size", 100/groupWidth+"% 47px");
|
||||
grid.setGridWidth(groupWidth, true);
|
||||
|
||||
// Determination of placement position of widget of Grid
|
||||
var widgets = groups[cnt].widgets;
|
||||
widgets.sort(compareOrder);
|
||||
|
||||
var tbl = {};
|
||||
for (var cnt2 = 0; cnt2 < widgets.length; cnt2++) {
|
||||
// Set default value when there is auto width
|
||||
if (widgets[cnt2].auto == true) {
|
||||
widgets[cnt2].width = groupWidth;
|
||||
// Adjust to the group width
|
||||
} else if (widgets[cnt2].width > groupWidth) {
|
||||
widgets[cnt2].width = groupWidth;
|
||||
}
|
||||
// Auto support
|
||||
if (widgets[cnt2].auto === true || widgets[cnt2].type === 'ui_form') {
|
||||
widgets[cnt2].height = getDefaultHeight(widgets[cnt2].id, groupWidth);
|
||||
}
|
||||
// Calculate coordinates to be placed
|
||||
var point = search_point(Number(widgets[cnt2].width), Number(widgets[cnt2].height), groupWidth, 256, tbl);
|
||||
if (point) {
|
||||
widgets[cnt2].x = point.x;
|
||||
widgets[cnt2].y = point.y;
|
||||
}
|
||||
}
|
||||
|
||||
var items = GridStackUI.Utils.sort(widgets);
|
||||
_.each(items, function (node) {
|
||||
var minHeight = null;
|
||||
var maxHeight = null;
|
||||
// ui_form is fixed to height 2
|
||||
if (node.type === 'ui_form') {
|
||||
minHeight = node.height;
|
||||
maxHeight = node.height;
|
||||
}
|
||||
if (node.type !== 'ui_spacer') {
|
||||
var dispNode = RED.nodes.node(node.id);
|
||||
var dispType = dispNode._def.paletteLabel;
|
||||
var dispLabel = dispNode._def.label;
|
||||
try {
|
||||
dispLabel = (typeof dispLabel === "function" ? dispLabel.call(dispNode) : dispLabel)||"";
|
||||
}
|
||||
catch(err) {
|
||||
console.log("Definition error: " + node.type + ".label",err);
|
||||
dispLabel = dispType;
|
||||
}
|
||||
|
||||
var item = $('<div></div>', {
|
||||
'data-noderedtype': node.type,
|
||||
'data-noderedid': node.id,
|
||||
'data-nodereddisptype': dispType,
|
||||
'data-nodereddisplabel': dispLabel,
|
||||
'data-noderedsizeauto': node.auto
|
||||
});
|
||||
var itemContent = $('<div></div>', {
|
||||
addClass: 'grid-stack-item-content',
|
||||
title: dispLabel + ':' + dispType
|
||||
});
|
||||
|
||||
if (node.auto === true) {
|
||||
itemContent.append('<i class="fa fa-unlock nr-dashboard-layout-resize-enable"></i>');
|
||||
} else {
|
||||
itemContent.append('<i class="fa fa-lock nr-dashboard-layout-resize-disable"></i>');
|
||||
}
|
||||
itemContent.append('<b>'+ dispLabel +'</b><br/>'+ dispType);
|
||||
item.append(itemContent);
|
||||
grid.addWidget(
|
||||
item,
|
||||
node.x, node.y, node.width, node.height, false, null, null,
|
||||
minHeight, maxHeight, node.id);
|
||||
} else {
|
||||
// Record the spacer node ID to be deleted
|
||||
oldSpacer.push(node.id);
|
||||
}
|
||||
});
|
||||
|
||||
$(gridID+'.grid-stack > .grid-stack-item:visible').each( function(idx, el) {
|
||||
el = $(el);
|
||||
var node = el.data('_gridstack_node');
|
||||
var auto = (el[0].dataset.noderedsizeauto == 'true') ? true : false;
|
||||
grid.resizable(el, !auto);
|
||||
});
|
||||
|
||||
// Group width change
|
||||
widthChange.push(new changeGroupWidth(cnt));
|
||||
// Move widgets between groups (drag and drop)
|
||||
widgetMove.push(new moveGroupWidget(cnt));
|
||||
// Resize widget in group (start event)
|
||||
widgetResize.push(new resizeGroupWidget(cnt));
|
||||
// Dragging widgets in a group (start event)
|
||||
widgetDrag.push(new dragGroupWidget(cnt));
|
||||
}
|
||||
$('.grid-stack>.grid-stack-item>.grid-stack-item-content>.nr-dashboard-layout-resize-disable').on('click',layoutResizeDisable);
|
||||
$('.grid-stack>.grid-stack-item>.grid-stack-item-content>.nr-dashboard-layout-resize-enable').on('click',layoutResizeEnable);
|
||||
},
|
||||
close: function() {
|
||||
//
|
||||
},
|
||||
show: function() {}
|
||||
}
|
||||
|
||||
RED.tray.show(trayOptions);
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
if (item.type === 'ui_tab') {
|
||||
var content = $('<div>',{class:"nr-db-sb-group-list-container"}).appendTo(container);
|
||||
|
||||
// ui_tab group chevron
|
||||
if (listStates.hasOwnProperty(item.node.id) && !listStates[item.node.id]) {
|
||||
content.hide();
|
||||
chevron.css({"transform":"rotate(-90deg)"});
|
||||
content.addClass('nr-db-sb-collapsed');
|
||||
listStates[item.node.id] = false;
|
||||
}
|
||||
else {
|
||||
listStates[item.node.id] = true;
|
||||
}
|
||||
titleRow.click(titleToggle(item.node.id,content,chevron));
|
||||
|
||||
// ui_tab group list
|
||||
var ol = $('<ol>',{class:"nr-db-sb-group-list"}).appendTo(content).editableList({
|
||||
sortable:".nr-db-sb-group-list-header",
|
||||
addButton: false,
|
||||
height: 'auto',
|
||||
connectWith: ".nr-db-sb-group-list",
|
||||
addItem: function(container,i,group) {
|
||||
if (!group.node) {
|
||||
group.node = {
|
||||
id: RED.nodes.id(),
|
||||
_def: RED.nodes.getType("ui_group"),
|
||||
type: "ui_group",
|
||||
users: [],
|
||||
tab: item.node.id,
|
||||
order: i+1,
|
||||
name: "Group "+(i+1),
|
||||
width: 6,
|
||||
disp: true
|
||||
};
|
||||
listElements[group.node.id] = container;
|
||||
RED.nodes.add(group.node);
|
||||
group.widgets = [];
|
||||
RED.history.push({
|
||||
t:'add',
|
||||
nodes:[group.node.id],
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.nodes.dirty(true);
|
||||
if (RED.nodes.hasOwnProperty('updateConfigNodeUsers')) {
|
||||
RED.nodes.updateConfigNodeUsers(group.node);
|
||||
}
|
||||
}
|
||||
var groupNode = group.node;
|
||||
elementParents[groupNode] = item.node.id;
|
||||
var titleRow = $('<div>',{class:"nr-db-sb-list-header nr-db-sb-group-list-header"}).appendTo(container);
|
||||
$('<i class="nr-db-sb-list-handle nr-db-sb-group-list-handle fa fa-bars"></i>').appendTo(titleRow);
|
||||
var chevron = $('<i class="fa fa-angle-down nr-db-sb-list-chevron">',{style:"width:10px;"}).appendTo(titleRow);
|
||||
$('<i class="nr-db-sb-icon nr-db-sb-group-icon fa fa-table"></i>').appendTo(titleRow);
|
||||
var title = $('<span class="nr-db-sb-title">').text(groupNode.name||groupNode.id||"").appendTo(titleRow);
|
||||
listElements[groupNode.id] = container;
|
||||
var buttonGroup = $('<div>',{class:"nr-db-sb-list-header-button-group"}).appendTo(titleRow);
|
||||
var spacerButton = $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-plus"></i> '+c_("layout.spacer")+'</a>').appendTo(buttonGroup);
|
||||
spacerButton.on('click',function(evt) {
|
||||
var spaceNode = {
|
||||
_def: RED.nodes.getType("ui_spacer"),
|
||||
type: "ui_spacer",
|
||||
hasUsers: false,
|
||||
users: [],
|
||||
id: RED.nodes.id(),
|
||||
tab: item.node.name,
|
||||
group: group.node.id,
|
||||
order: i+1,
|
||||
name: "spacer",
|
||||
width: 1,
|
||||
height:1,
|
||||
label: function() { return "spacer " + this.width + "x" + this.height; }
|
||||
};
|
||||
RED.nodes.add(spaceNode);
|
||||
RED.editor.validateNode(spaceNode);
|
||||
RED.history.push({
|
||||
t:'add',
|
||||
nodes:[spaceNode.id],
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
});
|
||||
var editButton = $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-pencil"></i> '+c_("layout.edit")+'</a>').appendTo(buttonGroup);
|
||||
var content = $('<div>',{class:"nr-db-sb-widget-list-container"}).appendTo(container);
|
||||
if (!listStates.hasOwnProperty(groupNode.id) || !listStates[groupNode.id]) {
|
||||
content.hide();
|
||||
chevron.css({"transform":"rotate(-90deg)"});
|
||||
content.addClass('nr-db-sb-collapsed');
|
||||
listStates[groupNode.id] = false;
|
||||
}
|
||||
else {
|
||||
listStates[groupNode.id] = true;
|
||||
}
|
||||
|
||||
var ol = $('<ol>',{class:"nr-db-sb-widget-list"}).appendTo(content).editableList({
|
||||
sortable:".nr-db-sb-widget-list-header",
|
||||
addButton: false,
|
||||
height: 'auto',
|
||||
connectWith: ".nr-db-sb-widget-list",
|
||||
addItem: function(container,i,widgetNode) {
|
||||
elementParents[widgetNode.id] = groupNode.id;
|
||||
var titleRow = $('<div>',{class:"nr-db-sb-list-header nr-db-sb-widget-list-header"}).appendTo(container);
|
||||
$('<i class="nr-db-sb-list-handle nr-db-sb-widget-list-handle fa fa-bars"></i>').appendTo(titleRow);
|
||||
$('<i class="nr-db-sb-icon nr-db-sb-widget-icon fa fa-picture-o"></i>').click(function(e) { e.preventDefault(); RED.search.show(widgetNode.id); }).appendTo(titleRow);
|
||||
var l = widgetNode._def.label;
|
||||
try {
|
||||
l = (typeof l === "function" ? l.call(widgetNode) : l)||"";
|
||||
}
|
||||
catch(err) {
|
||||
console.log("Definition error: "+d.type+".label",err);
|
||||
l = d.type;
|
||||
}
|
||||
var title = $('<span class="nr-db-sb-title">').text(l).appendTo(titleRow);
|
||||
listElements[widgetNode.id] = container;
|
||||
var buttonGroup = $('<div>',{class:"nr-db-sb-list-header-button-group"}).appendTo(titleRow);
|
||||
var editButton = $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-pencil"></i> '+c_("layout.edit")+'</a>').appendTo(buttonGroup);
|
||||
container.on('mouseover',function() {
|
||||
widgetNode.highlighted = true;
|
||||
widgetNode.dirty = true;
|
||||
RED.view.redraw();
|
||||
});
|
||||
container.on('mouseout',function() {
|
||||
widgetNode.highlighted = false;
|
||||
widgetNode.dirty = true;
|
||||
RED.view.redraw();
|
||||
});
|
||||
editButton.on('click',function(evt) {
|
||||
RED.editor.edit(widgetNode);
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
});
|
||||
},
|
||||
sortItems: function(items) {
|
||||
var historyEvents = [];
|
||||
items.each(function(i,el) {
|
||||
var node = el.data('data');
|
||||
var hev = {
|
||||
t:'edit',
|
||||
node:node,
|
||||
changes:{
|
||||
order:node.order,
|
||||
group:node.group
|
||||
},
|
||||
dirty:node.dirty,
|
||||
changed:node.changed
|
||||
};
|
||||
historyEvents.push(hev);
|
||||
var changed = false;
|
||||
if (node.order !== i+1) {
|
||||
node.order = i+1;
|
||||
changed = true;
|
||||
}
|
||||
if (node.group !== group.node.id) {
|
||||
var oldGroupNode = RED.nodes.node(node.group);
|
||||
if (oldGroupNode) {
|
||||
var index = oldGroupNode.users.indexOf(node);
|
||||
oldGroupNode.users.splice(index,1);
|
||||
}
|
||||
node.group = group.node.id;
|
||||
group.node.users.push(node);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
node.dirty = true;
|
||||
node.changed = true;
|
||||
}
|
||||
})
|
||||
RED.history.push({
|
||||
t:'multi',
|
||||
events: historyEvents
|
||||
});
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
}
|
||||
});
|
||||
ol.css("min-height","5px");
|
||||
if (groupNode.id) {
|
||||
groupLists[groupNode.id] = ol;
|
||||
}
|
||||
titleRow.click(titleToggle(groupNode.id,content,chevron));
|
||||
editButton.on('click',function(evt) {
|
||||
RED.editor.editConfig("", groupNode.type, groupNode.id);
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
});
|
||||
group.widgets.forEach(function(widget) {
|
||||
ol.editableList('addItem',widget);
|
||||
})
|
||||
},
|
||||
sortItems: function(items) {
|
||||
var historyEvents = [];
|
||||
items.each(function(i,el) {
|
||||
var groupData = el.data('data');
|
||||
var node = groupData.node;
|
||||
var hev = {
|
||||
t:'edit',
|
||||
node:node,
|
||||
changes:{
|
||||
order:node.order,
|
||||
tab:node.tab
|
||||
},
|
||||
dirty:node.dirty,
|
||||
changed:node.changed
|
||||
};
|
||||
historyEvents.push(hev);
|
||||
var changed = false;
|
||||
if (node.order !== i+1) {
|
||||
node.order = i+1;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
node.dirty = true;
|
||||
node.changed = true;
|
||||
}
|
||||
if (node.tab !== item.node.id) {
|
||||
var oldTabNode = RED.nodes.node(node.tab);
|
||||
if (oldTabNode) {
|
||||
var index = oldTabNode.users.indexOf(node);
|
||||
oldTabNode.users.splice(index,1);
|
||||
}
|
||||
node.tab = item.node.id;
|
||||
item.node.users.push(node);
|
||||
changed = true;
|
||||
}
|
||||
})
|
||||
RED.history.push({
|
||||
t:'multi',
|
||||
events: historyEvents
|
||||
});
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
}
|
||||
})
|
||||
tabLists[item.node.id] = ol;
|
||||
|
||||
addGroupButton.click(function(evt) {
|
||||
ol.editableList('addItem',{});
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
});
|
||||
item.groups.forEach(function(group) {
|
||||
ol.editableList('addItem',group);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var tabContainer = $('<ol>',{class:"nr-db-sb-tab-list"}).appendTo(divTabs).editableList({
|
||||
sortable:".nr-db-sb-tab-list-header",
|
||||
addButton: false,
|
||||
addItem: addTabOrLinkItem,
|
||||
sortItems: function(items) {
|
||||
var historyEvents = [];
|
||||
items.each(function(i,el) {
|
||||
var itemData = el.data('data');
|
||||
var node = itemData.node;
|
||||
var hev = {
|
||||
t:'edit',
|
||||
node:node,
|
||||
changes:{
|
||||
order:node.order
|
||||
},
|
||||
dirty:node.dirty,
|
||||
changed:node.changed
|
||||
}
|
||||
historyEvents.push(hev);
|
||||
var changed = false;
|
||||
if (node.order !== i+1) {
|
||||
node.order = i+1;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
node.dirty = true;
|
||||
node.changed = true;
|
||||
}
|
||||
})
|
||||
RED.history.push({
|
||||
t:'multi',
|
||||
events: historyEvents
|
||||
});
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
}
|
||||
});
|
||||
|
||||
var orphanedWidgets = $('<div>',{class:"form-row"}).appendTo(layoutTab);
|
||||
$('<span><i class="fa fa-info-circle"></i> There <span id="nr-db-missing-group-count"></span> not in a group. Click <a id="nr-db-add-missing-groups" href="#">here</a> to create the missing groups</span>').appendTo(orphanedWidgets);
|
||||
orphanedWidgets.find('a').click(function(event) {
|
||||
var unknownGroups = {};
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if (/^ui_/.test(node.type) && node.type !== 'ui_link' && node.type !== 'ui_toast' && node.type !== 'ui_ui_control') {
|
||||
if (!RED.nodes.node(node.group)) {
|
||||
var g = node.group || "_BLANK_";
|
||||
unknownGroups[g] = unknownGroups[g] || [];
|
||||
unknownGroups[g].push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
var tab = null;
|
||||
var tabs = tabContainer.editableList('items');
|
||||
tabs.first().each(function(i,el) {
|
||||
var tabData = el.data('data');
|
||||
tab = tabData.node;
|
||||
});
|
||||
|
||||
var hev = [];
|
||||
if (tab === null) {
|
||||
tab = {
|
||||
id: RED.nodes.id(),
|
||||
_def: RED.nodes.getType("ui_tab"),
|
||||
type: "ui_tab",
|
||||
users: [],
|
||||
order: 0,
|
||||
name: "Tab",
|
||||
icon: "dashboard"
|
||||
};
|
||||
RED.nodes.add(tab);
|
||||
hev.push(tab.id);
|
||||
}
|
||||
for (var groupId in unknownGroups) {
|
||||
if (unknownGroups.hasOwnProperty(groupId)) {
|
||||
var groupNode = {
|
||||
id: RED.nodes.id(),
|
||||
_def: RED.nodes.getType("ui_group"),
|
||||
type: "ui_group",
|
||||
users: [],
|
||||
tab: tab.id,
|
||||
order: i+1,
|
||||
name: (groupId==="_BLANK_"?"Group":groupId),
|
||||
width: 6,
|
||||
disp: true
|
||||
};
|
||||
hev.push(groupNode.id);
|
||||
RED.nodes.add(groupNode);
|
||||
RED.editor.validateNode(groupNode);
|
||||
if (RED.nodes.hasOwnProperty('updateConfigNodeUsers')) {
|
||||
RED.nodes.updateConfigNodeUsers(groupNode);
|
||||
}
|
||||
var widgets = unknownGroups[groupId];
|
||||
for (var i=0; i<widgets.length; i++) {
|
||||
widgets[i].group = groupNode.id;
|
||||
widgets[i].changed = true;
|
||||
widgets[i].dirty = true;
|
||||
if (RED.nodes.hasOwnProperty('updateConfigNodeUsers')) {
|
||||
RED.nodes.updateConfigNodeUsers(widgets[i]);
|
||||
}
|
||||
RED.editor.validateNode(widgets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
RED.history.push({
|
||||
t:'add',
|
||||
nodes: hev,
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.nodes.dirty(true);
|
||||
refresh();
|
||||
refreshOrphanedWidgets();
|
||||
RED.view.redraw();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
var listElements = {};
|
||||
var dashboard = [];
|
||||
var listStates = {};
|
||||
var elementParents = {};
|
||||
var awaitingGroups = {};
|
||||
var awaitingTabs = {};
|
||||
|
||||
function getCurrentList() {
|
||||
var currentList = [];
|
||||
var tabs = tabContainer.editableList('items');
|
||||
var open = false;
|
||||
tabs.each(function(i,el) {
|
||||
var tabData = el.data('data');
|
||||
var tab = [];
|
||||
var groups = el.find('.nr-db-sb-group-list').editableList('items');
|
||||
groups.each(function(j,el) {
|
||||
var group = [];
|
||||
var groupData = el.data('data');
|
||||
var widgets = el.find('.nr-db-sb-widget-list').editableList('items');
|
||||
widgets.each(function(k,el) {
|
||||
var widgetData = el.data('data');
|
||||
group.push(widgetData.id);
|
||||
})
|
||||
tab.push({id:groupData.node.id, widgets:group});
|
||||
});
|
||||
currentList.push({id:tabData.node.id,groups:tab});
|
||||
});
|
||||
return currentList;
|
||||
}
|
||||
|
||||
function refreshOrphanedWidgets() {
|
||||
var unknownGroups = {};
|
||||
var count = 0;
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if (/^ui_/.test(node.type) && node.type !== 'ui_link' && node.type !== 'ui_toast' && node.type !== 'ui_ui_control' && (node.type === 'ui_template' && node.templateScope !== 'global')) {
|
||||
if (!RED.nodes.node(node.group)) {
|
||||
var g = node.group || "_BLANK_";
|
||||
unknownGroups[g] = unknownGroups[g] || [];
|
||||
unknownGroups[g].push(node);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (count > 0) {
|
||||
orphanedWidgets.show();
|
||||
$("#nr-db-missing-group-count").text((count===1?"is ":"are ")+count+" widget"+(count === 1?"":"s"))
|
||||
}
|
||||
else {
|
||||
orphanedWidgets.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
var currentList = getCurrentList();
|
||||
dashboard = [];
|
||||
var tabs = {};
|
||||
var groups = {};
|
||||
var elements = [];
|
||||
var groupElements = {};
|
||||
var tabGroups = {};
|
||||
var groupId;
|
||||
var group;
|
||||
var tabId;
|
||||
var tab;
|
||||
var unknownGroups = 0;
|
||||
// Find all the tabs and groups
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
switch (node.type) {
|
||||
case 'ui_tab':
|
||||
case 'ui_link': {
|
||||
tabs[node.id] = node;
|
||||
//tabContainer.editableList('addItem',node);
|
||||
break;
|
||||
}
|
||||
case 'ui_group': {
|
||||
groups[node.id] = node;
|
||||
break;
|
||||
}
|
||||
case 'ui_spacer': {
|
||||
if (groups.hasOwnProperty(node.group)) {
|
||||
groupElements[node.group] = groupElements[node.group]||[];
|
||||
groupElements[node.group].push(node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
for (groupId in groups) {
|
||||
if (groups.hasOwnProperty(groupId)) {
|
||||
group = groups[groupId];
|
||||
if (tabs.hasOwnProperty(group.tab)) {
|
||||
// This group belongs to a tab
|
||||
tabGroups[group.tab] = tabGroups[group.tab]||[];
|
||||
tabGroups[group.tab].push(group);
|
||||
}
|
||||
else {
|
||||
unknownGroups++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Find all ui widgets - list them by their group id
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if (/^ui_/.test(node.type)) {
|
||||
if (groups.hasOwnProperty(node.group)) {
|
||||
groupElements[node.group] = groupElements[node.group]||[];
|
||||
groupElements[node.group].push(node);
|
||||
}
|
||||
else if ((node.type !== 'ui_toast')&&(node.type !== 'ui_ui_control')&&(node.type === 'ui_template' && node.templateScope !== 'global')) {
|
||||
unknownGroups++;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (unknownGroups > 0) {
|
||||
$("#nr-db-missing-group-count").text((unknownGroups===1?"is ":"are ")+unknownGroups+" widget"+(unknownGroups === 1?"":"s"))
|
||||
orphanedWidgets.show();
|
||||
}
|
||||
else {
|
||||
orphanedWidgets.hide();
|
||||
}
|
||||
// Sort each group's array of widgets
|
||||
for (groupId in groupElements) {
|
||||
if (groupElements.hasOwnProperty(groupId)) {
|
||||
group = groupElements[groupId];
|
||||
groupElements[groupId] = group.map(function(v,i) { return {n:v,i:i} }).sort(function(A,B) {
|
||||
if (A.n.order < B.n.order) { return A.n.order!==0?-1:1;}
|
||||
if (A.n.order > B.n.order) { return B.n.order!==0?1:-1;}
|
||||
return A.i - B.i;
|
||||
}).map(function(v) { return v.n})
|
||||
}
|
||||
}
|
||||
// Sort each tabs's array of groups
|
||||
for (tabId in tabGroups) {
|
||||
if (tabGroups.hasOwnProperty(tabId)) {
|
||||
tab = tabGroups[tabId];
|
||||
tabGroups[tabId] = tab.map(function(v,i) { return {n:v,i:i} }).sort(function(A,B) {
|
||||
if (A.n.order < B.n.order) { return -1;}
|
||||
if (A.n.order > B.n.order) { return 1;}
|
||||
return A.i - B.i;
|
||||
}).map(function(v) { return v.n})
|
||||
}
|
||||
}
|
||||
var tabIds = Object.keys(tabs).map(function(v,i) { return {n:tabs[v],i:i} }).sort(function(A,B) {
|
||||
if (A.n.order < B.n.order) { return -1;}
|
||||
if (A.n.order > B.n.order) { return 1;}
|
||||
return A.i - B.i;
|
||||
}).map(function(v) { return v.n.id});
|
||||
tabIds.forEach(function(tabId) {
|
||||
var tab = {node:tabs[tabId],groups:[]};
|
||||
if (tabGroups[tabId]) {
|
||||
tabGroups[tabId].forEach(function(groupNode) {
|
||||
var group = {node:groupNode,widgets:[]};
|
||||
if (groupElements[groupNode.id]) {
|
||||
group.widgets = groupElements[groupNode.id];
|
||||
}
|
||||
tab.groups.push(group);
|
||||
});
|
||||
}
|
||||
dashboard.push(tab);
|
||||
});
|
||||
var newList = dashboard.map(function(t) {
|
||||
return {
|
||||
id: t.node.id,
|
||||
groups: t.groups.map(function(g) {
|
||||
return {
|
||||
id: g.node.id,
|
||||
widgets: g.widgets.map(function(w) {
|
||||
return w.id;
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
if (JSON.stringify(newList)!=JSON.stringify(currentList)) {
|
||||
listElements = {};
|
||||
groupLists = {};
|
||||
tabLists = {};
|
||||
tabs = {};
|
||||
groups = {};
|
||||
elementParents = {};
|
||||
tabContainer.empty();
|
||||
dashboard.forEach(function(tab) {
|
||||
tabContainer.editableList('addItem',tab);
|
||||
});
|
||||
}
|
||||
ensureDashboardNode(true);
|
||||
if (globalDashboardNode) {
|
||||
$("#nr-db-field-title").val(globalDashboardNode.site.name);
|
||||
$("#nr-db-field-allowSwipe").val(globalDashboardNode.site.allowSwipe || "false");
|
||||
$("#nr-db-field-allowTempTheme").val(globalDashboardNode.site.allowTempTheme || "true");
|
||||
$("#nr-db-field-hideToolbar").val(globalDashboardNode.site.hideToolbar || "false");
|
||||
$("#nr-db-field-dateFormat").val(globalDashboardNode.site.dateFormat);
|
||||
|
||||
if (typeof globalDashboardNode.site.sizes !== "object") {
|
||||
globalDashboardNode.site.sizes = sizes;
|
||||
}
|
||||
$("#nr-db-field-sx").val(globalDashboardNode.site.sizes.sx);
|
||||
$("#nr-db-field-sy").val(globalDashboardNode.site.sizes.sy);
|
||||
$("#nr-db-field-px").val(globalDashboardNode.site.sizes.px);
|
||||
$("#nr-db-field-py").val(globalDashboardNode.site.sizes.py);
|
||||
$("#nr-db-field-cx").val(globalDashboardNode.site.sizes.cx);
|
||||
$("#nr-db-field-cy").val(globalDashboardNode.site.sizes.cy);
|
||||
$("#nr-db-field-gx").val(globalDashboardNode.site.sizes.gx);
|
||||
$("#nr-db-field-gy").val(globalDashboardNode.site.sizes.gy);
|
||||
|
||||
if (typeof globalDashboardNode.theme.angularTheme !== "object") {
|
||||
globalDashboardNode.theme.angularTheme = aTheme;
|
||||
}
|
||||
$("#nr-db-field-angPrimary").val(globalDashboardNode.theme.angularTheme.primary || "indigo");
|
||||
$("#nr-db-field-angAccents").val(globalDashboardNode.theme.angularTheme.accents || "blue");
|
||||
$("#nr-db-field-angWarn").val(globalDashboardNode.theme.angularTheme.warn || "red");
|
||||
$("#nr-db-field-angBackground").val(globalDashboardNode.theme.angularTheme.background || "grey");
|
||||
$("#nr-db-field-angLook").val(globalDashboardNode.theme.angularTheme.palette || "light");
|
||||
|
||||
$("#nr-db-field-theme").val(globalDashboardNode.theme.name);
|
||||
$("#ui-sidebar-name").val(globalDashboardNode.theme.customTheme.name);
|
||||
if (globalDashboardNode.theme.name === 'theme-custom') {
|
||||
$("#custom-theme-library-container").show();
|
||||
$("#custom-theme-settings").show();
|
||||
}
|
||||
else {
|
||||
$("#custom-theme-library-container").hide();
|
||||
$("#custom-theme-settings").hide();
|
||||
}
|
||||
if ($('#nr-db-field-allowTempTheme').val() === "none") {
|
||||
ulDashboardTabs.children().eq(2).addClass("hidden");
|
||||
ulDashboardTabs.children().eq(3).removeClass("hidden");
|
||||
}
|
||||
else {
|
||||
ulDashboardTabs.children().eq(2).removeClass("hidden");
|
||||
ulDashboardTabs.children().eq(3).addClass("hidden");
|
||||
}
|
||||
|
||||
//set colour start
|
||||
if (typeof globalDashboardNode.theme.name !== "string") {
|
||||
globalDashboardNode.theme.name = "theme-light";
|
||||
}
|
||||
var currentTheme = globalDashboardNode.theme.name.split("-")[1];
|
||||
var startingValue = globalDashboardNode.theme[currentTheme+"Theme"].baseColor;
|
||||
setColourPickerColour("base-color", startingValue);
|
||||
$("#nr-db-field-font").val(globalDashboardNode.theme[currentTheme+"Theme"].baseFont);
|
||||
generateColours(startingValue);
|
||||
if (globalDashboardNode.theme.name === 'theme-light' || globalDashboardNode.theme.name === 'theme-dark') {
|
||||
addLightAndDarkResetButton('base-color', $('#base-settings-ul').children().first());
|
||||
}
|
||||
|
||||
if (editor === undefined) {
|
||||
editor = RED.editor.createEditor({
|
||||
id: 'nr-db-field-format-editor',
|
||||
mode: 'ace/mode/javascript',
|
||||
value: JSON.stringify({theme:globalDashboardNode.theme.themeState, site:globalDashboardNode.site})
|
||||
});
|
||||
|
||||
RED.library.create({
|
||||
url:"themes", // where to get the data from
|
||||
type:"theme", // the type of object the library is for
|
||||
editor: editor, // the field name the main text body goes to
|
||||
mode:"ace/mode/javascript",
|
||||
fields:['name'],
|
||||
elementPrefix:"ui-sidebar-"
|
||||
});
|
||||
}
|
||||
|
||||
editor.on('input', function() {
|
||||
// Check for any changes on the editor object
|
||||
// i.e. has the theme been customised compared
|
||||
// to what is stored
|
||||
var editorObject = JSON.parse(editor.getValue());
|
||||
// var objectsEqual = true;
|
||||
// var keysEqual = true;
|
||||
// var editorKeys = Object.keys(editorObject);
|
||||
// var themeKeys = Object.keys(editorObject.theme);
|
||||
// var siteKeys = Object.keys(editorObject.site);
|
||||
// var dashNodeKeys = Object.keys(globalDashboardNode.theme.themeState);
|
||||
|
||||
// // Test equality
|
||||
// for (var i=0; i<themeKeys.length; i++) {
|
||||
// if (themeKeys[i] !== dashNodeKeys[i]) {
|
||||
// keysEqual = false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (keysEqual) {
|
||||
// for (var j=0; j<themeKeys.length; j++) {
|
||||
// var key = themeKeys[j];
|
||||
// if (editorObject.theme[key] !== globalDashboardNode.theme.themeState[key]) {
|
||||
// objectsEqual = false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!objectsEqual) {
|
||||
// globalDashboardNode.theme.themeState = editorObject.theme;
|
||||
// }
|
||||
//
|
||||
// //If custom theme is chosen, update the elements and current state
|
||||
// if (globalDashboardNode.theme.name === 'theme-custom') {
|
||||
// //globalDashboardNode.theme.customTheme.baseColor = globalDashboardNode.theme.themeState['base-color'].default;
|
||||
// generateColours(globalDashboardNode.theme.customTheme.baseColor);
|
||||
// //setColourPickerColour("base-color", globalDashboardNode.theme.customTheme.baseColor);
|
||||
// if (!objectsEqual) { RED.nodes.dirty(true); }
|
||||
// }
|
||||
|
||||
//Update theme object if necessary
|
||||
if (JSON.stringify(editorObject.theme) !== JSON.stringify(globalDashboardNode.theme.themeState)) {
|
||||
globalDashboardNode.theme.themeState = editorObject.theme;
|
||||
if ($("#ui-sidebar-name").val() !== globalDashboardNode.theme.customTheme.name) {
|
||||
globalDashboardNode.theme.customTheme.name = $("#ui-sidebar-name").val();
|
||||
globalDashboardNode.theme.customTheme.baseColor = globalDashboardNode.theme.themeState["base-color"].value;
|
||||
setColourPickerColour("base-color", globalDashboardNode.theme.customTheme.baseColor);
|
||||
generateColours(globalDashboardNode.theme.themeState["base-color"].value);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
if (JSON.stringify(aTheme) !== JSON.stringify(globalDashboardNode.theme.angularTheme)) {
|
||||
globalDashboardNode.theme.angularTheme = aTheme;
|
||||
}
|
||||
|
||||
//Update site object if necessary
|
||||
if (JSON.stringify(editorObject.site) !== JSON.stringify(globalDashboardNode.site)) {
|
||||
globalDashboardNode.site = editorObject.site;
|
||||
$("#nr-db-field-title").val(globalDashboardNode.site.name);
|
||||
$("#nr-db-field-hideToolbar").val(globalDashboardNode.site.hideToolbar);
|
||||
$("#nr-db-field-allowSwipe").val(globalDashboardNode.site.allowSwipe);
|
||||
$("#nr-db-field-allowTempTheme").val(globalDashboardNode.site.allowTempTheme);
|
||||
$("#nr-db-field-dateFormat").val(globalDashboardNode.site.dateFormat);
|
||||
$("#nr-db-field-sx").val(globalDashboardNode.site.sizes.sx);
|
||||
$("#nr-db-field-sy").val(globalDashboardNode.site.sizes.sy);
|
||||
$("#nr-db-field-px").val(globalDashboardNode.site.sizes.px);
|
||||
$("#nr-db-field-py").val(globalDashboardNode.site.sizes.py);
|
||||
$("#nr-db-field-gx").val(globalDashboardNode.site.sizes.gx);
|
||||
$("#nr-db-field-gy").val(globalDashboardNode.site.sizes.gy);
|
||||
$("#nr-db-field-cx").val(globalDashboardNode.site.sizes.cx);
|
||||
$("#nr-db-field-cy").val(globalDashboardNode.site.sizes.cy);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
awaitingGroups = {};
|
||||
awaitingTabs = {};
|
||||
}
|
||||
|
||||
RED.sidebar.addTab({
|
||||
id: "dashboard",
|
||||
label: c_("label.dashboard"),
|
||||
name: "Dashboard",
|
||||
content: content,
|
||||
closeable: true,
|
||||
pinned: true,
|
||||
iconClass: "fa fa-bar-chart",
|
||||
disableOnEdit: true,
|
||||
onchange: function() { refresh(); }
|
||||
});
|
||||
|
||||
editSaveEventHandler = function(node) {
|
||||
if (/^ui_/.test(node.type)) {
|
||||
if (node.type === "ui_tab" || node.type === "ui_group") {
|
||||
if (listElements[node.id]) {
|
||||
// Existing element
|
||||
listElements[node.id].children(".nr-db-sb-list-header").find(".nr-db-sb-title").text(node.name||node.id);
|
||||
if (node.type === "ui_group") {
|
||||
refresh();
|
||||
}
|
||||
else {
|
||||
if (node.hidden === true) { listElements[node.id].children(".nr-db-sb-list-header").find(".nr-db-sb-title").addClass('nr-db-sb-title-hidden'); }
|
||||
else { listElements[node.id].children(".nr-db-sb-list-header").find(".nr-db-sb-title").removeClass('nr-db-sb-title-hidden'); }
|
||||
if (node.disabled === true) { listElements[node.id].children(".nr-db-sb-list-header").find(".nr-db-sb-title").addClass('nr-db-sb-title-disabled'); }
|
||||
else { listElements[node.id].children(".nr-db-sb-list-header").find(".nr-db-sb-title").removeClass('nr-db-sb-title-disabled'); }
|
||||
}
|
||||
}
|
||||
else if (node.type === "ui_tab") {
|
||||
// Adding a tab
|
||||
tabContainer.editableList('addItem',{node:node,groups:[]})
|
||||
}
|
||||
else {
|
||||
// Adding a group
|
||||
if (tabLists[node.tab]) {
|
||||
tabLists[node.tab].editableList('addItem',{node:node,widgets:[]})
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node.type === "ui_link") {
|
||||
if (listElements[node.id]) {
|
||||
var container = listElements[node.id];
|
||||
container.find(".nr-db-sb-link-name").text(node.name||"untitled");
|
||||
container.find(".nr-db-sb-link-url").text(node.link);
|
||||
}
|
||||
}
|
||||
else {
|
||||
refreshOrphanedWidgets();
|
||||
if (listElements[node.id]) {
|
||||
if (node.group != elementParents[node.id]) {
|
||||
// Moved to a different group
|
||||
if (groupLists[elementParents[node.id]]) {
|
||||
groupLists[elementParents[node.id]].editableList('removeItem',listElements[node.id].data('data'))
|
||||
}
|
||||
if (groupLists[node.group]) {
|
||||
groupLists[node.group].editableList('removeItem',node)
|
||||
groupLists[node.group].editableList('addItem',node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var l = node._def.label;
|
||||
try {
|
||||
l = (typeof l === "function" ? l.call(node) : l)||"";
|
||||
}
|
||||
catch(err) {
|
||||
console.log("Definition error: "+d.type+".label",err);
|
||||
l = d.type;
|
||||
}
|
||||
listElements[node.id].children(".nr-db-sb-list-header").find(".nr-db-sb-title").text(l);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (groupLists[node.group]) {
|
||||
if (node.order === 0) { node.order = groupLists[node.group].editableList('length'); }
|
||||
groupLists[node.group].editableList('addItem',node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
RED.events.on("editor:save",editSaveEventHandler);
|
||||
|
||||
// Dashboard layout tool
|
||||
layoutUpdateEventHandler = function(node) {
|
||||
if (/^ui_/.test(node.type) && node.type !== 'ui_link' && node.type !== 'ui_toast' && node.type !== 'ui_ui_control' && node.type !== 'ui_audio' && node.type !== 'ui_base' && node.type !== 'ui_group' && node.type !== 'ui_tab') {
|
||||
if (listElements[node.id]) {
|
||||
if (node.group != elementParents[node.id]) {
|
||||
// Moved to a different group
|
||||
if (groupLists[elementParents[node.id]]) {
|
||||
groupLists[elementParents[node.id]].editableList('removeItem',listElements[node.id].data('data'))
|
||||
}
|
||||
if (groupLists[node.group]) {
|
||||
groupLists[node.group].editableList('removeItem',node)
|
||||
groupLists[node.group].editableList('addItem',node);
|
||||
groupLists[node.group].editableList('sort',function(a,b){return a.order-b.order;});
|
||||
}
|
||||
}
|
||||
else {
|
||||
groupLists[node.group].editableList('sort',function(a,b){return a.order-b.order;});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
RED.events.on("layout:update",layoutUpdateEventHandler);
|
||||
|
||||
|
||||
var pendingAdd = [];
|
||||
var pendingAddTimer = null;
|
||||
|
||||
function handlePendingAdds() {
|
||||
var hasTabs = false;
|
||||
var hasGroups = false;
|
||||
pendingAdd.sort(function(A,B) {
|
||||
hasTabs = hasTabs || A.type === "ui_tab" || B.type === "ui_tab";
|
||||
hasGroups = hasGroups || A.type === "ui_group" || B.type === "ui_group";
|
||||
if (A.type === B.type) {
|
||||
return 0;
|
||||
}
|
||||
if (A.type === "ui_tab") {
|
||||
return -1;
|
||||
}
|
||||
else if (B.type === "ui_tab") {
|
||||
return 1;
|
||||
}
|
||||
else if (A.type === "ui_group") {
|
||||
return -1;
|
||||
}
|
||||
else if (B.type === "ui_group") {
|
||||
return 1;
|
||||
}
|
||||
return 0
|
||||
});
|
||||
for (var i=0; i<pendingAdd.length; i++) {
|
||||
var node = pendingAdd[i];
|
||||
if (node.type === "ui_tab") {
|
||||
tabContainer.editableList('addItem',{node:node,groups:[]});
|
||||
}
|
||||
else {
|
||||
if (hasTabs) {
|
||||
// We've added some tabs, need to give jquery time to add the lists
|
||||
pendingAdd = pendingAdd.slice(i);
|
||||
pendingAddTimer = setTimeout(handlePendingAdds,50);
|
||||
return;
|
||||
}
|
||||
if (node.type === "ui_group") {
|
||||
if (tabLists[node.tab]) {
|
||||
tabLists[node.tab].editableList('addItem',{node:node,widgets:[]});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hasGroups) {
|
||||
// We've added some tabs, need to give jquery time to add the lists
|
||||
pendingAdd = pendingAdd.slice(i);
|
||||
pendingAddTimer = setTimeout(handlePendingAdds,50);
|
||||
return;
|
||||
}
|
||||
if (groupLists[node.group]) {
|
||||
groupLists[node.group].editableList('addItem',node)
|
||||
}
|
||||
else {
|
||||
refreshOrphanedWidgets();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pendingAdd = [];
|
||||
}
|
||||
|
||||
nodesAddEventHandler = function(node) {
|
||||
if (/^ui_/.test(node.type) && !listElements[node.id]) {
|
||||
pendingAdd.push(node);
|
||||
clearTimeout(pendingAddTimer);
|
||||
pendingAddTimer = setTimeout(handlePendingAdds,100);
|
||||
}
|
||||
};
|
||||
RED.events.on("nodes:add", nodesAddEventHandler);
|
||||
|
||||
nodesRemoveEventHandler = function(node) {
|
||||
if (/^ui_/.test(node.type)) {
|
||||
if (node.type === "ui_tab" || node.type === "ui_link") {
|
||||
if (listElements[node.id]) {
|
||||
tabContainer.editableList('removeItem',listElements[node.id].data('data'));
|
||||
delete tabLists[node.id];
|
||||
}
|
||||
}
|
||||
else if (node.type === "ui_group") {
|
||||
if (tabLists[node.tab] && listElements[node.id]) {
|
||||
tabLists[node.tab].editableList('removeItem',listElements[node.id].data('data'));
|
||||
}
|
||||
delete groupLists[node.id];
|
||||
}
|
||||
else {
|
||||
if (groupLists[node.group]) {
|
||||
groupLists[node.group].editableList('removeItem',node)
|
||||
}
|
||||
}
|
||||
refreshOrphanedWidgets();
|
||||
delete listElements[node.id];
|
||||
}
|
||||
};
|
||||
RED.events.on("nodes:remove", nodesRemoveEventHandler);
|
||||
}
|
||||
});
|
||||
|
||||
$.widget("nodereddashboard.elementSizerByNum", {
|
||||
_create: function() {
|
||||
var that = this;
|
||||
var has_height = this.options.has_height;
|
||||
var pos = this.options.pos;
|
||||
var c_width = has_height ? '15%' : '6%';
|
||||
var container = $('<div>').css({
|
||||
position: 'absolute',
|
||||
background: 'white',
|
||||
padding: '10px 10px 10px 10px',
|
||||
border: '1px solid grey',
|
||||
zIndex: '20',
|
||||
borderRadius: "4px",
|
||||
display:"none",
|
||||
width: c_width
|
||||
}).appendTo(document.body);
|
||||
var box0 = $("<div>").css({
|
||||
fontSize: '13px',
|
||||
color: '#aaa',
|
||||
float: 'left',
|
||||
paddingTop: '1px'
|
||||
}).appendTo(container);
|
||||
|
||||
var width = $(this.options.width).val();
|
||||
var height = has_height ? $(this.options.height).val() : undefined;
|
||||
var max_w = '';
|
||||
var groupNode = this.options.groupNode;
|
||||
if(groupNode) {
|
||||
max_w = 'max="'+groupNode.width+'"';
|
||||
}
|
||||
width = (width > 0) ? width : 1;
|
||||
height = (height > 0) ? height : 1;
|
||||
var in0 = $('<input type="number" min="1" '+max_w+'>')
|
||||
.css("width", has_height ? "45%" : "100%")
|
||||
.val(width)
|
||||
.appendTo(box0);
|
||||
if(has_height) {
|
||||
var pad = $('<span>')
|
||||
.text(" x ")
|
||||
.appendTo(box0);
|
||||
var in1 = $('<input type="number" min="1">')
|
||||
.css("width", "45%")
|
||||
.val(height)
|
||||
.appendTo(box0);
|
||||
}
|
||||
var closeTimer;
|
||||
var closeFunc = function() {
|
||||
var w = in0.val();
|
||||
var h = has_height ? in1.val() : undefined;
|
||||
var label = that.options.label;
|
||||
label.text(w+(has_height ? (' x '+h) : ''));
|
||||
$(that.options.width).val(w).change();
|
||||
if(has_height) {
|
||||
$(that.options.height).val(h).change();
|
||||
}
|
||||
that.destroy();
|
||||
};
|
||||
container.keypress(function(e) {
|
||||
if(e.which === 13) { // pressed ENTER
|
||||
container.fadeOut(100, closeFunc);
|
||||
}
|
||||
});
|
||||
container.on('mouseleave', function(e) {
|
||||
closeTimer = setTimeout(function() {
|
||||
container.fadeOut(200, closeFunc);
|
||||
}, 100);
|
||||
});
|
||||
container.on('mouseenter', function(e) {
|
||||
clearTimeout(closeTimer);
|
||||
});
|
||||
container.css({
|
||||
top: (pos.top -10)+"px",
|
||||
left: (pos.left +10)+"px"
|
||||
});
|
||||
container.fadeIn(200);
|
||||
}
|
||||
});
|
||||
|
||||
$.widget( "nodereddashboard.elementSizer", {
|
||||
_create: function() {
|
||||
var that = this;
|
||||
var gridWidth = 6;
|
||||
var width = parseInt($(this.options.width).val()||0);
|
||||
var height = parseInt(this.options.hasOwnProperty('height')?$(this.options.height).val():"1")||0;
|
||||
var hasAuto = (!this.options.hasOwnProperty('auto') || this.options.auto);
|
||||
|
||||
this.element.css({
|
||||
minWidth: this.element.height()+4
|
||||
});
|
||||
var auto_text = c_("auto");
|
||||
var sizeLabel = (width === 0 && height === 0)?auto_text:width+(this.options.hasOwnProperty('height')?" x "+height:"");
|
||||
this.element.text(sizeLabel).on('mousedown',function(evt) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
||||
var width = parseInt($(that.options.width).val()||0);
|
||||
var height = parseInt(that.options.hasOwnProperty('height')?$(that.options.height).val():"1")||0;
|
||||
var maxWidth = 0;
|
||||
var maxHeight;
|
||||
var fixedWidth = false;
|
||||
var fixedHeight = false;
|
||||
var group = $(that.options.group).val();
|
||||
if (group) {
|
||||
var groupNode = RED.nodes.node(group);
|
||||
if (groupNode) {
|
||||
gridWidth = Math.max(6,groupNode.width,+width);
|
||||
maxWidth = groupNode.width || gridWidth;
|
||||
fixedWidth = true;
|
||||
}
|
||||
maxHeight = Math.max(6,+height+1);
|
||||
}
|
||||
else {
|
||||
gridWidth = Math.max(12,+width);
|
||||
maxWidth = gridWidth;
|
||||
maxHeight = 1;
|
||||
fixedHeight = true;
|
||||
}
|
||||
|
||||
var pos = $(this).offset();
|
||||
var container = $('<div>').css({
|
||||
position: 'absolute',
|
||||
background: 'white',
|
||||
padding: '5px 10px 10px 10px',
|
||||
border: '1px solid grey',
|
||||
zIndex: '20',
|
||||
borderRadius: "4px",
|
||||
display:"none"
|
||||
}).appendTo(document.body);
|
||||
var closeTimer;
|
||||
|
||||
container.on('mouseleave',function(evt) {
|
||||
closeTimer = setTimeout(function() {
|
||||
container.fadeOut(200, function() { $(this).remove(); });
|
||||
},100)
|
||||
});
|
||||
container.on('mouseenter',function() {
|
||||
clearTimeout(closeTimer);
|
||||
})
|
||||
|
||||
var label = $("<div>").css({
|
||||
fontSize: '13px',
|
||||
color: '#aaa',
|
||||
float: 'left',
|
||||
paddingTop: '1px'
|
||||
}).appendTo(container).text((width === 0 && height === 0)?auto_text:(width+(that.options.hasOwnProperty('height')?" x "+height:"")));
|
||||
label.hover(function() {
|
||||
$(this).css('text-decoration', 'underline');
|
||||
}, function() {
|
||||
$(this).css('text-decoration', 'none');
|
||||
});
|
||||
label.click(function(e) {
|
||||
var group = $(that.options.group).val();
|
||||
var groupNode = null;
|
||||
if(group) {
|
||||
groupNode = RED.nodes.node(group);
|
||||
if(groupNode === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(that).elementSizerByNum({
|
||||
width: that.options.width,
|
||||
height: that.options.height,
|
||||
groupNode: groupNode,
|
||||
pos: pos,
|
||||
label: that.element,
|
||||
has_height: that.options.hasOwnProperty('height')
|
||||
});
|
||||
closeTimer = setTimeout(function() {
|
||||
container.fadeOut(200, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
},100)
|
||||
});
|
||||
|
||||
var buttonRow = $('<div>',{style:"text-align:right; height:25px;"}).appendTo(container);
|
||||
|
||||
if (hasAuto) {
|
||||
var button = $('<a>',{href:"#",class:"editor-button editor-button-small",style:"margin-bottom:5px"})
|
||||
.text(auto_text)
|
||||
.appendTo(buttonRow)
|
||||
.on('mouseup',function(evt) {
|
||||
that.element.text(auto_text)
|
||||
$(that.options.width).val(0).change();
|
||||
$(that.options.height).val(0).change();
|
||||
evt.preventDefault();
|
||||
container.fadeOut(200, function() { $(this).remove(); });
|
||||
});
|
||||
}
|
||||
|
||||
var cellBorder = "1px dashed lightGray";
|
||||
var cellBorderExisting = "1px solid gray";
|
||||
var cellBorderHighlight = "1px dashed black";
|
||||
var rows = [];
|
||||
function addRow(i) {
|
||||
var row = $('<div>').css({padding:0,margin:0,height:"25px","box-sizing":"border-box"}).appendTo(container);
|
||||
rows.push(row);
|
||||
cells.push([])
|
||||
for (var j=0; j<gridWidth; j++) {
|
||||
addCell(i,j);
|
||||
}
|
||||
}
|
||||
function addCell(i,j) {
|
||||
var row = rows[i];
|
||||
var cell = $('<div>').css({
|
||||
display:"inline-block",
|
||||
width: "25px",
|
||||
height: "25px",
|
||||
borderRight: (j===(width-1)&&i<height)?cellBorderExisting:cellBorder,
|
||||
borderBottom: (i===(height-1)&&j<width)?cellBorderExisting:cellBorder,
|
||||
boxSizing: "border-box",
|
||||
cursor:"pointer",
|
||||
background: (j<maxWidth)?"#fff":"#eee"
|
||||
}).appendTo(row);
|
||||
cells[i].push(cell);
|
||||
if (j===0) {
|
||||
cell.css({borderLeft:((i<=height-1)?cellBorderExisting:cellBorder)});
|
||||
}
|
||||
if (i===0) {
|
||||
cell.css({borderTop:((j<=width-1)?cellBorderExisting:cellBorder)});
|
||||
}
|
||||
if (j<maxWidth) {
|
||||
cell.data("w",j);
|
||||
cell.data("h",i);
|
||||
cell.on("mouseup",function() {
|
||||
that.element.text(($(this).data("w")+1)+(that.options.hasOwnProperty('height')?" x "+($(this).data("h")+1):""))
|
||||
$(that.options.width).val($(this).data("w")+1).change();
|
||||
$(that.options.height).val($(this).data("h")+1).change();
|
||||
container.fadeOut(200, function() { $(this).remove(); });
|
||||
});
|
||||
cell.on("mouseover",function() {
|
||||
var w = $(this).data("w");
|
||||
var h = $(this).data("h");
|
||||
label.text((w+1)+(that.options.hasOwnProperty('height')?" x "+(h+1):""));
|
||||
for (var y = 0; y<maxHeight; y++) {
|
||||
for (var x = 0; x<maxWidth; x++) {
|
||||
cells[y][x].css({
|
||||
background: (y<=h && x<=w)?'#ddd':'#fff',
|
||||
borderLeft: (x===0&&y<=h)?cellBorderHighlight:(x===0)?((y<=height-1)?cellBorderExisting:cellBorder):'',
|
||||
borderTop: (y===0&&x<=w)?cellBorderHighlight:(y===0)?((x<=width-1)?cellBorderExisting:cellBorder):'',
|
||||
borderRight: (x===w&&y<=h)?cellBorderHighlight:((x===width-1&&y<=height-1)?cellBorderExisting:cellBorder),
|
||||
borderBottom: (y===h&&x<=w)?cellBorderHighlight:((y===height-1&&x<=width-1)?cellBorderExisting:cellBorder)
|
||||
})
|
||||
}
|
||||
}
|
||||
if (!fixedHeight && h === maxHeight-1) {
|
||||
addRow(maxHeight++)
|
||||
}
|
||||
if (!fixedWidth && w === maxWidth-1) {
|
||||
maxWidth++;
|
||||
gridWidth++;
|
||||
for (var r=0; r<maxHeight; r++) {
|
||||
addCell(r,maxWidth-1);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
var cells = [];
|
||||
for (var i=0; i<maxHeight; i++) {
|
||||
addRow(i);
|
||||
}
|
||||
container.css({
|
||||
top:(pos.top)+"px",
|
||||
left:(pos.left)+"px"
|
||||
});
|
||||
container.fadeIn(200);
|
||||
})
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="ui_base">
|
||||
<div class='form-row'>
|
||||
This <i>ui_base</i> node is the main node that all<br/>other dashboard widget nodes communicate to.<br/>
|
||||
<br/>One instance is required to support the dashboard.<br/>
|
||||
<br/>If you have no dashboard you can delete this node.<br/>
|
||||
It will be re-created automatically if required.<br/>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="ui_base">
|
||||
</script>
|
||||
117
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_base.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
module.exports = function(RED) {
|
||||
var ui = require('../ui')(RED);
|
||||
var path= require('path');
|
||||
var node;
|
||||
var set = 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'
|
||||
};
|
||||
|
||||
// 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: "#000000"};
|
||||
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(set);
|
||||
});
|
||||
|
||||
RED.httpAdmin.get('/ui_base/js/*', function(req, res) {
|
||||
var filename = path.join(__dirname , '../dist/js', req.params[0]);
|
||||
res.sendFile(filename, function (err) {
|
||||
if (err) {
|
||||
if (node) {
|
||||
node.warn(filename + " not found. Maybe running in dev mode.");
|
||||
}
|
||||
else {
|
||||
console.log("ui_base - error:",err);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
RED.httpAdmin.get('/ui_base/css/*', function(req, res) {
|
||||
var filename = path.join(__dirname , '../dist/css', req.params[0]);
|
||||
res.sendFile(filename, function (err) {
|
||||
if (err) {
|
||||
if (node) {
|
||||
node.warn(filename + " not found. Maybe running in dev mode.");
|
||||
}
|
||||
else {
|
||||
console.log("ui_base - error:",err);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
123
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_button.html
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_button',{
|
||||
category: 'dashboard',
|
||||
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: ''},
|
||||
icon: {value: ''},
|
||||
payload: {value: '',validate: (RED.validators.hasOwnProperty('typedInput')?RED.validators.typedInput('payloadType'):function(v) { return true})},
|
||||
payloadType: { value: 'str'},
|
||||
topic: {value: ''}
|
||||
},
|
||||
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']
|
||||
})
|
||||
}
|
||||
});
|
||||
</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> 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-icon"><i class="fa fa-picture-o"></i> Icon</label>
|
||||
<input type="text" id="node-input-icon" placeholder="optional icon ">
|
||||
</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" 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-color"><i class="fa fa-tint"></i> Colour</label>
|
||||
<input type="text" id="node-input-color" placeholder="optional text/icon color">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-bgcolor"><i class="fa fa-tint"></i> Background</label>
|
||||
<input type="text" id="node-input-bgcolor" placeholder="optional background color">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label style="width:auto" for="node-input-payload"><i class="fa fa-envelope-o"></i> When clicked, send:</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-payload" style="padding-left: 25px; margin-right: -25px">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">Topic</label>
|
||||
<input type="text" id="node-input-topic">
|
||||
</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, emulate a button click: </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-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<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>{{msg.background}}</code>.</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 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 <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>
|
||||
</script>
|
||||
69
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_button.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
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) {
|
||||
msg.topic = config.topic || msg.topic;
|
||||
},
|
||||
convertBack: function (value) {
|
||||
if (payloadType === "date") {
|
||||
value = Date.now();
|
||||
}
|
||||
else {
|
||||
value = RED.util.evaluateNodeProperty(payload,payloadType,node);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
});
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_button", ButtonNode);
|
||||
};
|
||||
295
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_chart.html
generated
vendored
Normal 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: 'dashboard',
|
||||
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},
|
||||
colors: {value: ['#1F77B4', '#AEC7E8', '#FF7F0E', '#2CA02C', '#98DF8A', '#D62728', '#FF9896', '#9467BD', '#C5B0D5']},
|
||||
useOldStyle: {value: false},
|
||||
outputs: {value: 1}
|
||||
},
|
||||
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");
|
||||
}
|
||||
if (this.useOldStyle === undefined) {
|
||||
$("#node-input-useOldStyle").prop('checked', true);
|
||||
}
|
||||
$("#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();
|
||||
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 {
|
||||
$("#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:"HH:mm:ss", hasValue:false};
|
||||
var ohm = {value:"HH:mm", label:"HH:mm", hasValue:false};
|
||||
var oymd = {value:"Y-M-D", label:"Year-Month-Date", hasValue:false};
|
||||
var odm = {value:"D/M", label:"Date/Month", hasValue:false};
|
||||
var oahm = {value:"dd HH:mm", label:"Day HH:mm", hasValue:false};
|
||||
var ocus = {value:"custom", label:"custom", icon:"red/images/typedInput/az.png"};
|
||||
var oaut = {value:"auto", 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> 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" placeholder="optional chart title">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-removeOlder"><i class="fa fa-line-chart"></i> Type</label>
|
||||
<select id="node-input-chartType" style="width:159px; font-family:'FontAwesome','Helvetica Neue', Helvetica, Arial, sans-serif">
|
||||
<option value="line">  Line chart</option>
|
||||
<option value="bar">  Bar chart</option>
|
||||
<option value="horizontalBar">  Bar chart (H)</option>
|
||||
<option value="pie">  Pie chart</option>
|
||||
<option value="polar-area">  Polar area chart</option>
|
||||
<option value="radar">  Radar chart</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;">enlarge points
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" id="x-axis-show">
|
||||
<label for="node-input-removeOlder">X-axis</label>
|
||||
<label for="node-input-removeOlder" style="width:auto">last</label>
|
||||
<input type="text" id="node-input-removeOlder" style="width:50px;">
|
||||
<select id="node-input-removeOlderUnit" style="width:80px;">
|
||||
<option value="1">seconds</option>
|
||||
<option value="60">minutes</option>
|
||||
<option value="3600">hours</option>
|
||||
<option value="86400">days</option>
|
||||
<option value="604800">weeks</option>
|
||||
</select>
|
||||
<label for="node-input-removeOlderPoints" style="width:auto; margin-left:10px; margin-right:10px;">OR</label>
|
||||
<input type="text" id="node-input-removeOlderPoints" style="width:60px;" placeholder="1000">
|
||||
<span style="margin-left:5px;">points</span>
|
||||
</div>
|
||||
<div class="form-row" id="x-axis-label-show">
|
||||
<label for="node-input-xformat">X-axis Label</label>
|
||||
<input type="text" id="node-input-xformat" style="width:268px;">
|
||||
</div>
|
||||
<div class="form-row" id="y-axis-show">
|
||||
<label id="y-label-show" for="node-input-ymin">Y-axis</label>
|
||||
<label id="x-label-show" for="node-input-ymin">X-axis</label>
|
||||
<label for="node-input-ymin" style="width:auto">min</label>
|
||||
<input type="text" id="node-input-ymin" style="width:92px">
|
||||
<label for="not-input-ymax" style="width:auto; margin-left:20px;">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">Legend</label>
|
||||
<select id="node-input-legend" style="width:120px;">
|
||||
<option value="false">None</option>
|
||||
<option value="true">Show</option>
|
||||
</select>
|
||||
<span id="interpolate-show"> Interpolate
|
||||
<select id="node-input-interpolate" style="width:120px;">
|
||||
<option value="linear">linear</option>
|
||||
<option value="step">step</option>
|
||||
<option value="bezier">bezier</option>
|
||||
</select>
|
||||
</span>
|
||||
<span id="hole-size-show"> Cutout
|
||||
<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;">Use first colour for all bars
|
||||
</div>
|
||||
<div class="form-row" id="ui-chart-colours">
|
||||
<label for="node-input-color1">Series Colours</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 class="form-row">
|
||||
<label for="node-input-nodata">Blank label</label>
|
||||
<input type="text" id="node-input-nodata" placeholder="display this text before valid data arrives">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-input-useOldStyle" style="display:inline-block; width:auto; vertical-align:baseline; margin-left:105px; margin-right:5px;">Use deprecated (pre 2.5.0) data format.
|
||||
</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>
|
||||
|
||||
<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>
|
||||
</script>
|
||||
280
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_chart.js
generated
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
module.exports = function(RED) {
|
||||
var ui = require('../ui')(RED);
|
||||
var ChartIdList = {};
|
||||
|
||||
function ChartNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
this.chartType = config.chartType || "line";
|
||||
this.newStyle = (!config.hasOwnProperty("useOldStyle") || (config.useOldStyle === true)) ? false : true;
|
||||
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,
|
||||
animation: false,
|
||||
spanGaps: false,
|
||||
options: {},
|
||||
},
|
||||
convertBack: function(data) {
|
||||
if (node.newStyle) {
|
||||
if (data && data[0] && data[0].hasOwnProperty("values")) {
|
||||
return [data[0].values];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (data && data[0]) {
|
||||
if (data[0] && data[0].hasOwnProperty("values") && data[0].values.hasOwnProperty("series") ) {
|
||||
var o = [];
|
||||
for (var i=0; i<data[0].values.series.length; i++) {
|
||||
if (data[0].values.data[i] !== undefined) {
|
||||
if (node.chartType !== "line") {
|
||||
o.push({ key:data[0].values.series[i], values:data[0].values.data[i][0] });
|
||||
}
|
||||
else {
|
||||
var d = data[0].values.data[i].map(function(i) { return [i.x, i.y]; });
|
||||
o.push({ key:data[0].values.series[i], values:d });
|
||||
}
|
||||
}
|
||||
}
|
||||
data = o;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
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;
|
||||
}
|
||||
// New style
|
||||
if (!value[0].hasOwnProperty("key")) {
|
||||
if (value[0].hasOwnProperty("series") && value[0].hasOwnProperty("data")) {
|
||||
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; }
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Old style
|
||||
else {
|
||||
if (node.chartType !== "line") {
|
||||
var nb = { series:[], data:[], labels:[] };
|
||||
for (var v in value) {
|
||||
if (value.hasOwnProperty(v)) {
|
||||
nb.data.push([ value[v].values ]);
|
||||
nb.series.push(value[v].key);
|
||||
}
|
||||
}
|
||||
value = [{key:node.id, values:nb}];
|
||||
}
|
||||
else {
|
||||
if (value[0] && value[0].hasOwnProperty("values")) {
|
||||
if (Array.isArray(value[0].values)) { // Handle "old" style data array
|
||||
var na = {series:[], data:[]};
|
||||
for (var n=0; n<value.length; n++) {
|
||||
na.series.push(value[n].key);
|
||||
na.data.push(value[n].values.map(function(i) {
|
||||
return {x:i[0], y:i[1]};
|
||||
}));
|
||||
}
|
||||
value = [{ key:node.id, values:na }];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//console.log("RETURN",JSON.stringify(value));
|
||||
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 (!node.newStyle || !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 {
|
||||
oldValue[0].values.data[s].shift();
|
||||
rc += 1;
|
||||
}
|
||||
}
|
||||
if (config.removeOlderPoints) {
|
||||
while (oldValue[0].values.data[s].length > config.removeOlderPoints) {
|
||||
oldValue[0].values.data[s].shift();
|
||||
rc += 1;
|
||||
}
|
||||
}
|
||||
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].shift();
|
||||
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.
|
||||
if (node.wires.length === 2) { // if it's an old version of the node honour it
|
||||
node.send([null, {payload:"restore", for:node.id}]);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
node.on("close", function() {
|
||||
if (st) { clearTimeout(st); }
|
||||
ui.ev.removeListener('changetab', chgtab);
|
||||
done();
|
||||
})
|
||||
}
|
||||
RED.nodes.registerType("ui_chart", ChartNode);
|
||||
};
|
||||
151
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_colour_picker.html
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_colour_picker',{
|
||||
category: 'dashboard',
|
||||
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: ''}
|
||||
},
|
||||
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-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();
|
||||
}
|
||||
});
|
||||
},
|
||||
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> </label> Show hue slider : <input type="checkbox" id="node-input-showHue" style="display:inline-block; width:auto; vertical-align:baseline;">
|
||||
<br/>
|
||||
<label> </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> </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> </label>
|
||||
Always show swatch : <input type="checkbox" checked id="node-input-showSwatch" style="display:inline-block; width:auto; vertical-align:top;">
|
||||
<br/>
|
||||
<label> </label>
|
||||
Always show picker : <input type="checkbox" checked id="node-input-showPicker" style="display:inline-block; width:auto; vertical-align:top;">
|
||||
<br/>
|
||||
<label> </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">
|
||||
</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>
|
||||
|
||||
<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>
|
||||
</script>
|
||||
57
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_colour_picker.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
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; }
|
||||
|
||||
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
|
||||
},
|
||||
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(); }
|
||||
}
|
||||
msg.topic = config.topic || msg.topic;
|
||||
},
|
||||
convert: function(p,o,m) {
|
||||
if (m.payload === undefined) { return; }
|
||||
var colour = tc(m.payload);
|
||||
return colour.toString(config.format);
|
||||
}
|
||||
});
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_colour_picker", ColourPickerNode);
|
||||
};
|
||||
81
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_date_picker.html
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_date_picker',{
|
||||
category: 'dashboard',
|
||||
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: ''}
|
||||
},
|
||||
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"
|
||||
});
|
||||
}
|
||||
});
|
||||
</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">
|
||||
</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>
|
||||
|
||||
<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>
|
||||
</script>
|
||||
47
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_date_picker.js
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
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; }
|
||||
|
||||
var done = ui.add({
|
||||
node: node,
|
||||
tab: tab,
|
||||
group: group,
|
||||
forwardInputMessages: config.passthru,
|
||||
control: {
|
||||
type: 'date-picker',
|
||||
label: config.label,
|
||||
order: config.order,
|
||||
ddd : new Date(),
|
||||
width: config.width || group.config.width || 6,
|
||||
height: config.height || 1
|
||||
},
|
||||
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) {
|
||||
msg.topic = config.topic || msg.topic;
|
||||
}
|
||||
});
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_date_picker", DatePickerNode);
|
||||
};
|
||||
163
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_dropdown.html
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_dropdown',{
|
||||
category: 'dashboard',
|
||||
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},
|
||||
options: {value:[{value: '', label : ''}]},
|
||||
payload: {value: ''},
|
||||
topic: {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() {
|
||||
$("#node-input-size").elementSizer({
|
||||
width: "#node-input-width",
|
||||
height: "#node-input-height",
|
||||
group: "#node-input-group"
|
||||
});
|
||||
|
||||
function generateOption(i, option) {
|
||||
var container = $('<li/>',{style:"background: #fff; margin:0; padding:8px 0px 0px; border-bottom: 1px solid #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:#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":"#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"
|
||||
});
|
||||
},
|
||||
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 #ccc; overflow-y:scroll;display: inline-block; width: calc(70% + 15px);">
|
||||
<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-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">
|
||||
</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>
|
||||
|
||||
<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 the value will set the pre-selected choice in the dropdown.</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>
|
||||
</script>
|
||||
150
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_dropdown.js
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
module.exports = function(RED) {
|
||||
var ui = require('../ui')(RED);
|
||||
|
||||
function DropdownNode(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; }
|
||||
|
||||
var control = {
|
||||
type: 'dropdown',
|
||||
label: config.label,
|
||||
tooltip: config.tooltip,
|
||||
place: config.place || "Select option",
|
||||
order: config.order,
|
||||
value: config.payload || node.id,
|
||||
width: config.width || group.config.width || 6,
|
||||
height: config.height || 1
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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 || !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});
|
||||
break;
|
||||
}
|
||||
case 'string': {
|
||||
emitOptions.newOptions.push({label:opt, value:opt});
|
||||
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]});
|
||||
}
|
||||
}
|
||||
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._fromInput = 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("payload")) {
|
||||
emitOptions.value = msg.payload;
|
||||
control.value = emitOptions.value;
|
||||
emitOptions._fromInput = true;
|
||||
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) {
|
||||
return emitOptions;
|
||||
},
|
||||
|
||||
beforeSend: function (msg) {
|
||||
var val = "";
|
||||
if (msg._fromInput) {
|
||||
delete msg.options;
|
||||
msg.payload = emitOptions.value;
|
||||
}
|
||||
for (var i=0; i<control.options.length; i++) {
|
||||
if (control.options[i].value === msg.payload) { val = control.options[i].label; }
|
||||
}
|
||||
msg.topic = config.topic || msg.topic;
|
||||
if (node.pt) {
|
||||
node.status({shape:"dot",fill:"grey",text:val});
|
||||
}
|
||||
else {
|
||||
node.state[1] = val;
|
||||
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);
|
||||
};
|
||||
314
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_form.html
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_form',{
|
||||
category: 'dashboard',
|
||||
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: ''}
|
||||
},
|
||||
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:"background: #fff; margin:0; padding:8px 0px 0px; border-bottom: 1px solid #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:#eee; 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: 'e.g. Name', 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: 'e.g. name',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: 'Text'},
|
||||
{val : "multiline", text: 'Multiline'},
|
||||
{val : "number", text: 'Number'},
|
||||
{val : "email", text: 'E-mail'},
|
||||
{val : "password", text: 'Password'},
|
||||
{val : "checkbox", text: 'Checkbox'},
|
||||
{val : "switch", text: 'Switch'},
|
||||
{val : "date", text: 'Date'}
|
||||
];
|
||||
|
||||
//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":"#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"
|
||||
});
|
||||
},
|
||||
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: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background-color: 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> 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 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> Form elements</label>
|
||||
<div style="display:inline-block; width:78%; border:1px solid #ccc; border-radius:5px; box-sizing:border-box;">
|
||||
<div style="width:100%; display: inline-block; background-color:#f3f3f3; padding-top:10px; padding-buttom:10px; border-top:0px solid; border-radius:5px 5px 0 0; border-bottom:1px solid #ccc;">
|
||||
<div style="width:94%; display:inline-block; margin-left:27px">
|
||||
<div style="width:20%; text-align:center; float:left;">Label</div>
|
||||
<div style="width:20%; text-align:center; float:left; margin-left:9px">Name</div>
|
||||
<div style="margin-left:7px; width:16%; text-align:center; float:left; margin-left:9px">Type</div>
|
||||
<div style="width:16%; text-align:center; float:left;">Required</div>
|
||||
<div style="width:10%; text-align:center; float:left;">Rows</div>
|
||||
<div style="width:12%; text-align:center; float:left;">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>element</span></a>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-submit"><i class="fa fa-square"></i> Buttons</label>
|
||||
<i class="fa fa-thumbs-o-up"></i> <input type="text" id="node-input-submit" placeholder="submit button text" style="width:35%;">
|
||||
<span style="margin-left:16px"><i class="fa fa-thumbs-o-down"></i></span>
|
||||
<input type="text" id="node-input-cancel" placeholder="cancel button text" style="width:35%;">
|
||||
</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">
|
||||
</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>
|
||||
|
||||
<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 drown 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 form 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>
|
||||
</script>
|
||||
38
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_form.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
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; }
|
||||
|
||||
var done = ui.add({
|
||||
node: node,
|
||||
tab: tab,
|
||||
group: group,
|
||||
forwardInputMessages: 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.options.length ,
|
||||
options: config.options,
|
||||
formValue: config.formValue,
|
||||
submit: config.submit,
|
||||
cancel: config.cancel,
|
||||
sy: ui.getSizes().sy
|
||||
},
|
||||
beforeSend: function (msg) {
|
||||
msg.topic = config.topic || undefined;
|
||||
}
|
||||
});
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_form", FormNode);
|
||||
};
|
||||
183
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_gauge.html
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
<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: 'dashboard',
|
||||
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: ""}
|
||||
},
|
||||
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() === "wave") {
|
||||
$("#ui-gauge-format").hide();
|
||||
}
|
||||
else {
|
||||
$("#ui-gauge-format").show();
|
||||
}
|
||||
if (($(this).val() === "compass") || ($(this).val() === "wave")) {
|
||||
$("#ui-gauge-colours").hide();
|
||||
$("#ui-gauge-segments").hide();
|
||||
}
|
||||
else {
|
||||
$("#ui-gauge-colours").show();
|
||||
$("#ui-gauge-segments").show();
|
||||
}
|
||||
});
|
||||
$("#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:200px !important">
|
||||
<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">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<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://scotch.io/tutorials/all-about-the-built-in-angularjs-filters" 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>
|
||||
</script>
|
||||
85
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_gauge.js
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
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':'#0b8489', 'theme-light':'#1784be', 'theme-custom':theme["widget-backgroundColor"].value};
|
||||
|
||||
var waveoptions = {};
|
||||
waveoptions.circleColor = {'theme-dark':'#097479', 'theme-light':'#0094ce', 'theme-custom':theme["widget-backgroundColor"].value};
|
||||
waveoptions.waveColor = {'theme-dark':'#097479', 'theme-light':'#0094ce', 'theme-custom':theme["widget-backgroundColor"].value};
|
||||
waveoptions.textColor = {'theme-dark':'#0b8489', 'theme-light':'#1784be', 'theme-custom':theme["widget-textColor"].value};
|
||||
waveoptions.waveTextColor = {'theme-dark':'#0fbbc3', 'theme-light':'#a4dbf8', '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,
|
||||
gageoptions: gageoptions,
|
||||
waveoptions: waveoptions,
|
||||
options: null
|
||||
},
|
||||
convert: function(p,o,m) {
|
||||
var form = config.format.replace(/{{/g,"").replace(/}}/g,"").replace(/\s/g,"") || "_zzz_zzz_zzz_";
|
||||
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);
|
||||
};
|
||||
86
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_group.html
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<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}
|
||||
},
|
||||
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-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>
|
||||
|
||||
<script type="text/html" data-help-name="ui_group">
|
||||
<p>Group</p>
|
||||
</script>
|
||||
19
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_group.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
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
|
||||
};
|
||||
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);
|
||||
};
|
||||
68
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_link.html
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
<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}
|
||||
},
|
||||
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-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>
|
||||
|
||||
<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>
|
||||
</script>
|
||||
12
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_link.js
generated
vendored
Normal 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);
|
||||
node.on("close", done);
|
||||
}
|
||||
|
||||
RED.nodes.registerType("ui_link", LinkNode);
|
||||
};
|
||||
118
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_numeric.html
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_numeric',{
|
||||
category: 'dashboard',
|
||||
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: ''},
|
||||
format: {value: '{{value}}'},
|
||||
min: {value: 0, required: true, validate: RED.validators.number()},
|
||||
max: {value: 10, required: true, validate: RED.validators.number()},
|
||||
step: {value: 1}
|
||||
},
|
||||
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"
|
||||
});
|
||||
}
|
||||
});
|
||||
</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">
|
||||
</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>
|
||||
|
||||
<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 messages 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>&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>
|
||||
</script>
|
||||
60
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_numeric.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
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; }
|
||||
|
||||
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)
|
||||
},
|
||||
beforeSend: function (msg) {
|
||||
msg.payload = parseFloat(msg.payload);
|
||||
msg.topic = config.topic || msg.topic;
|
||||
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);
|
||||
};
|
||||
116
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_slider.html
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_slider',{
|
||||
category: 'dashboard',
|
||||
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: ''},
|
||||
min: {value: 0, required:true, validate:RED.validators.number()},
|
||||
max: {value: 10, required:true, validate:RED.validators.number()},
|
||||
step: {value: 1}
|
||||
},
|
||||
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"
|
||||
});
|
||||
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, set slider to new payload value: </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-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<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>Input messages 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 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>Note: An input msg to the slider node will not change the status information displayed unless the slider
|
||||
node output is connected to another node.</p>
|
||||
</script>
|
||||
56
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_slider.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
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; }
|
||||
|
||||
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
|
||||
},
|
||||
beforeSend: function (msg) {
|
||||
msg.topic = config.topic || msg.topic;
|
||||
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_slider", SliderNode);
|
||||
};
|
||||
51
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_spacer.html
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_spacer', {
|
||||
category: 'config',
|
||||
color: '#45ADB4',
|
||||
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}
|
||||
},
|
||||
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"
|
||||
});
|
||||
}
|
||||
});
|
||||
</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>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="ui_spacer">
|
||||
</script>
|
||||
27
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_spacer.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
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
|
||||
}
|
||||
});
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_spacer", SpacerNode);
|
||||
};
|
||||
185
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_switch.html
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_switch',{
|
||||
category: 'dashboard',
|
||||
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: ''},
|
||||
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: ''},
|
||||
},
|
||||
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-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">
|
||||
<option value="default">Default</option>
|
||||
<option value="custom">Custom</option>
|
||||
</select>
|
||||
</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 though <code>msg</code> if payload matches new 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">
|
||||
</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>
|
||||
|
||||
<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>
|
||||
</script>
|
||||
138
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_switch.js
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
width: config.width || group.config.width || 6,
|
||||
height: config.height || 1
|
||||
},
|
||||
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 { value = RED.util.evaluateNodeProperty(payload,payloadType,node); }
|
||||
return value;
|
||||
},
|
||||
beforeSend: function (msg) {
|
||||
msg.topic = config.topic || msg.topic;
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
};
|
||||
105
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_tab.html
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<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>
|
||||
|
||||
<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>
|
||||
15
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_tab.js
generated
vendored
Normal 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);
|
||||
};
|
||||
246
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_template.html
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
<script type="text/javascript">
|
||||
// convert to i18 text
|
||||
function c_(x) {
|
||||
return RED._("node-red-dashboard/ui_template:ui_template."+x);
|
||||
}
|
||||
|
||||
RED.nodes.registerType('ui_template',{
|
||||
category: c_("label.category"),
|
||||
color: 'rgb( 63, 173, 181)',
|
||||
defaults: {
|
||||
group: {type: 'ui_group', required:false},
|
||||
name: {value: ''},
|
||||
order: {value: 0},
|
||||
width: {value: 0, validate: function(v) {
|
||||
var valid = true
|
||||
if (this.templateScope !== 'global') {
|
||||
var width = v||0;
|
||||
var currentGroup = $('#node-input-group').val()||this.group;
|
||||
var groupNode = RED.nodes.node(currentGroup);
|
||||
valid = !groupNode || +width <= +groupNode.width;
|
||||
$("#node-input-size").toggleClass("input-error",!valid);
|
||||
}
|
||||
return valid;
|
||||
}},
|
||||
height: {value: 0},
|
||||
format: {value: '<div ng-bind-html="msg.payload"></div>'},
|
||||
storeOutMessages: {value: true},
|
||||
fwdInMessages: {value: true},
|
||||
templateScope: {value: 'local'}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "ui_template.png",
|
||||
paletteLabel: 'template',
|
||||
label: function() { return this.name || 'template'; },
|
||||
labelStyle: function() { return this.name?"node_label_italic":""; },
|
||||
oneditprepare: function() {
|
||||
if (RED.editor.hasOwnProperty("editText") && typeof RED.editor.editText === "function") {
|
||||
$("#node-template-expand-editor").show();
|
||||
}
|
||||
else {
|
||||
$("#node-template-expand-editor").hide();
|
||||
}
|
||||
var that = this;
|
||||
$("#node-input-size").elementSizer({
|
||||
width: "#node-input-width",
|
||||
height: "#node-input-height",
|
||||
group: "#node-input-group"
|
||||
});
|
||||
|
||||
if (typeof this.storeOutMessages === 'undefined') {
|
||||
this.storeOutMessages = true;
|
||||
$('#node-input-storeOutMessages').prop('checked', true);
|
||||
}
|
||||
|
||||
if (typeof this.fwdInMessages === 'undefined') {
|
||||
this.fwdInMessages = true;
|
||||
$('#node-input-fwdInMessages').prop('checked', true);
|
||||
}
|
||||
|
||||
if (typeof this.templateScope === 'undefined') {
|
||||
this.templateScope = 'local';
|
||||
$('#node-input-templateScope').val(this.templateScope);
|
||||
}
|
||||
|
||||
$('#node-input-templateScope').on('change', function() {
|
||||
if ($('#node-input-templateScope').val() === 'global') {
|
||||
$('#template-row-group, #template-row-size, #template-pass-store').hide();
|
||||
that._def.defaults.group.required = false;
|
||||
}
|
||||
else {
|
||||
$('#template-row-group, #template-row-size, #template-pass-store').show();
|
||||
that._def.defaults.group.required = true;
|
||||
}
|
||||
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0; i<rows.size(); i++) {
|
||||
height = height - $(rows[i]).outerHeight(true);
|
||||
}
|
||||
if ($('#node-input-templateScope').val() === "global") { height += 240; }
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop")) + parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
if (this.editor) { this.editor.resize(); }
|
||||
})
|
||||
|
||||
this.editor = RED.editor.createEditor({
|
||||
id: 'node-input-format-editor',
|
||||
mode: 'ace/mode/html',
|
||||
value: $("#node-input-format").val()
|
||||
});
|
||||
|
||||
RED.library.create({
|
||||
url:"uitemplates", // where to get the data from
|
||||
type:"ui_template", // the type of object the library is for
|
||||
editor:this.editor, // the field name the main text body goes to
|
||||
mode:"ace/mode/html",
|
||||
fields:['name']
|
||||
});
|
||||
|
||||
this.editor.focus();
|
||||
|
||||
RED.popover.tooltip($("#node-template-expand-editor"),c_("label.expand"));
|
||||
|
||||
$("#node-template-expand-editor").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
var value = that.editor.getValue();
|
||||
RED.editor.editText({
|
||||
mode: 'html',
|
||||
value: value,
|
||||
width: "Infinity",
|
||||
cursor: that.editor.getCursorPosition(),
|
||||
complete: function(v,cursor) {
|
||||
that.editor.setValue(v, -1);
|
||||
that.editor.gotoLine(cursor.row+1,cursor.column,false);
|
||||
setTimeout(function() { that.editor.focus(); },300);
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
oneditsave: function() {
|
||||
var annot = this.editor.getSession().getAnnotations();
|
||||
this.noerr = 0;
|
||||
$("#node-input-noerr").val(0);
|
||||
for (var k=0; k < annot.length; k++) {
|
||||
if (annot[k].type === "error") {
|
||||
$("#node-input-noerr").val(annot.length);
|
||||
this.noerr = annot.length;
|
||||
}
|
||||
}
|
||||
$("#node-input-format").val(this.editor.getValue());
|
||||
this.editor.destroy();
|
||||
delete this.editor;
|
||||
},
|
||||
oneditcancel: function() {
|
||||
this.editor.destroy();
|
||||
delete this.editor;
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0; i<rows.size(); i++) {
|
||||
height = height - $(rows[i]).outerHeight(true);
|
||||
}
|
||||
if ($('#node-input-templateScope').val() === "global") { height += 232; }
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop")) + parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
this.editor.resize();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="ui_template">
|
||||
<div class="form-row">
|
||||
<label for="node-input-format"><span data-i18n="ui_template.label.type"></span></label>
|
||||
<select style="width:76%" id="node-input-templateScope">
|
||||
<option value="local" data-i18n="ui_template.label.local"></option>
|
||||
<option value="global" data-i18n="ui_template.label.global"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row" id="template-row-group">
|
||||
<label for="node-input-group"><i class="fa fa-table"></i> <span data-i18n="ui_template.label.group"></span></label>
|
||||
<input type="text" id="node-input-group">
|
||||
</div>
|
||||
<div class="form-row" id="template-row-size">
|
||||
<label><i class="fa fa-object-group"></i> <span data-i18n="ui_template.label.size"></span></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-name"><i class="fa fa-tag"></i> <span data-i18n="ui_template.label.name"></span></label>
|
||||
<div style="display:inline-block; width:calc(100% - 105px)">
|
||||
<input type="text" id="node-input-name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom:0px;">
|
||||
<label for="node-input-format"><i class="fa fa-copy"></i> <span data-i18n="ui_template.label.template"></span></label>
|
||||
<input type="hidden" id="node-input-format">
|
||||
<button id="node-template-expand-editor" class="red-ui-button red-ui-button-small" style="float:right"><i class="fa fa-expand"></i></button>
|
||||
</div>
|
||||
<div class="form-row node-text-editor-row">
|
||||
<div style="height:250px; min-height:100px" class="node-text-editor" id="node-input-format-editor" ></div>
|
||||
</div>
|
||||
<div id="template-pass-store">
|
||||
<div class="form-row" style="margin-bottom:0px;">
|
||||
<input type="checkbox" id="node-input-fwdInMessages" style="display:inline-block; margin-left:8px; width:auto; vertical-align:top;">
|
||||
<label for="node-input-fwdInMessages" style="width:70%;"> <span data-i18n="ui_template.label.pass-through"></span></label>
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom:0px;">
|
||||
<input type="checkbox" id="node-input-storeOutMessages" style="display:inline-block; margin-left:8px; width:auto; vertical-align:top;">
|
||||
<label for="node-input-storeOutMessages" style="width:70%;"> <span data-i18n="ui_template.label.store-state"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<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 appearence
|
||||
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;"><div layout="row" layout-align="space-between">
|
||||
<p>The number is</p>
|
||||
<font color="{{((msg.payload || 0) % 2 === 0) ? 'green' : 'red'}}">
|
||||
{{(msg.payload || 0) % 2 === 0 ? 'even' : 'odd'}}
|
||||
</font>
|
||||
</div></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;">
|
||||
<div id="{{'my_'+$id}}" style="{{'color:'+theme.base_color}}">Some text</div>
|
||||
<script>
|
||||
(function(scope) {
|
||||
scope.$watch('msg', function(msg) {
|
||||
if (msg) {
|
||||
// Do something when msg arrives
|
||||
$("#my_"+scope.$id).html(msg.payload);
|
||||
}
|
||||
});
|
||||
})(scope);
|
||||
</script></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;">
|
||||
<script>
|
||||
var value = "hello world";
|
||||
// or overwrite value in your callback function ...
|
||||
this.scope.action = function() { return value; }
|
||||
</script>
|
||||
<md-button ng-click="send({payload:action()})">
|
||||
Click me to send a hello world
|
||||
</md-button></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>
|
||||
</script>
|
||||
88
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_template.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
module.exports = function(RED) {
|
||||
var ui = require('../ui')(RED);
|
||||
|
||||
function TemplateNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
var node = this;
|
||||
|
||||
var group = RED.nodes.getNode(config.group);
|
||||
if (!group && config.templateScope !== 'global') { return; }
|
||||
var tab = null;
|
||||
if (config.templateScope !== 'global') {
|
||||
tab = RED.nodes.getNode(group.config.tab);
|
||||
if (!tab) { return; }
|
||||
if (!config.width) {
|
||||
config.width = group.config.width;
|
||||
}
|
||||
}
|
||||
var hei = Number(config.height|| 0);
|
||||
var previousTemplate = null
|
||||
var theme = ui.getTheme();
|
||||
var colortheme = {};
|
||||
for (var i in theme) {
|
||||
if (theme.hasOwnProperty(i)) {
|
||||
colortheme[i.replace(/-/g, "_")] = theme[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
var done = ui.add({
|
||||
forwardInputMessages: config.fwdInMessages,
|
||||
storeFrontEndInputAsState: config.storeOutMessages,
|
||||
emitOnlyNewValues: false,
|
||||
node: node,
|
||||
tab: tab,
|
||||
group: group,
|
||||
control: {
|
||||
type: 'template',
|
||||
order: config.order,
|
||||
width: config.width || 6,
|
||||
height: hei,
|
||||
format: config.format,
|
||||
templateScope: config.templateScope,
|
||||
theme: colortheme
|
||||
},
|
||||
beforeEmit: function(msg) {
|
||||
var properties = Object.getOwnPropertyNames(msg).filter(function (p) { return p[0] != '_'; });
|
||||
var clonedMsg = {
|
||||
templateScope: config.templateScope
|
||||
};
|
||||
for (var i=0; i<properties.length; i++) {
|
||||
var property = properties[i];
|
||||
clonedMsg[property] = msg[property];
|
||||
}
|
||||
|
||||
// transform to string if msg.template is buffer
|
||||
if (clonedMsg.template !== undefined && Buffer.isBuffer(clonedMsg.template)) {
|
||||
clonedMsg.template = clonedMsg.template.toString();
|
||||
}
|
||||
|
||||
if (clonedMsg.template === undefined && previousTemplate !== null) {
|
||||
clonedMsg.template = previousTemplate;
|
||||
}
|
||||
|
||||
//This updates the whole page if the template input changes and
|
||||
//height set to auto - performance killer, but here just in case
|
||||
// if ((config.height == "0") && (value !== node.oldvalue)) {
|
||||
// node.oldvalue = value;
|
||||
// setImmediate(function() { ui.updateUi(); });
|
||||
// }
|
||||
|
||||
if (clonedMsg.template) {
|
||||
previousTemplate = clonedMsg.template
|
||||
}
|
||||
|
||||
return { msg:clonedMsg };
|
||||
},
|
||||
beforeSend: function (msg, original) {
|
||||
if (original) {
|
||||
var om = original.msg;
|
||||
om.socketid = original.socketid;
|
||||
return om;
|
||||
}
|
||||
}
|
||||
});
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_template", TemplateNode);
|
||||
RED.library.register("uitemplates");
|
||||
};
|
||||
182
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_text.html
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_text',{
|
||||
category: 'dashboard',
|
||||
color: 'rgb(119, 198, 204)',
|
||||
defaults: {
|
||||
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},
|
||||
name: {value: ''},
|
||||
label: {value: 'text'},
|
||||
format: {value: '{{msg.payload}}'},
|
||||
layout: {value:'row-spread'}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
align: "right",
|
||||
icon: "ui_text.png",
|
||||
paletteLabel: 'text',
|
||||
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || 'text'; },
|
||||
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"
|
||||
});
|
||||
|
||||
$(".nr-db-text-layout-"+(this.layout||'row-spread')).addClass('selected');
|
||||
|
||||
[ ".nr-db-text-layout-row-left",".nr-db-text-layout-row-center",".nr-db-text-layout-row-right",
|
||||
".nr-db-text-layout-row-spread",".nr-db-text-layout-col-center"].forEach(function(id) {
|
||||
$(id).click(function(e) {
|
||||
$(".nr-db-text-layout").removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
$('#node-input-layout').val(id.substring(".nr-db-text-layout-".length));
|
||||
e.preventDefault();
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="ui_text">
|
||||
<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-i-cursor"></i> Value format</label>
|
||||
<input type="text" id="node-input-format" placeholder="{{msg.payload}}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label style="vertical-align: top"><i class="fa fa-th-large"></i> Layout</label>
|
||||
<div style="display:inline-block">
|
||||
<input type="hidden" id="node-input-layout"><input type="hidden" id="node-input-layoutAlign">
|
||||
<div>
|
||||
<a href="#" class="nr-db-text-layout nr-db-text-layout-row nr-db-text-layout-row-left">
|
||||
<span class="nr-db-text-layout-label">label</span>
|
||||
<span class="nr-db-text-layout-value">value</span>
|
||||
<div class="nr-db-text-layout-checkbox"></div>
|
||||
</a>
|
||||
<a href="#" class="nr-db-text-layout nr-db-text-layout-row nr-db-text-layout-row-center">
|
||||
<span class="nr-db-text-layout-label">label</span>
|
||||
<span class="nr-db-text-layout-value">value</span>
|
||||
<div class="nr-db-text-layout-checkbox"></div>
|
||||
</a>
|
||||
<a href="#" class="nr-db-text-layout nr-db-text-layout-row nr-db-text-layout-row-right">
|
||||
<span class="nr-db-text-layout-label">label</span>
|
||||
<span class="nr-db-text-layout-value">value</span>
|
||||
<div class="nr-db-text-layout-checkbox"></div>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="nr-db-text-layout nr-db-text-layout-row nr-db-text-layout-row-spread">
|
||||
<span class="nr-db-text-layout-label">label</span>
|
||||
<span class="nr-db-text-layout-value">value</span>
|
||||
<div class="nr-db-text-layout-checkbox"></div>
|
||||
</a>
|
||||
<a href="#" class="nr-db-text-layout nr-db-text-layout-col nr-db-text-layout-col-center">
|
||||
<span class="nr-db-text-layout-label">label</span>
|
||||
<span class="nr-db-text-layout-value">value</span>
|
||||
<div class="nr-db-text-layout-checkbox"></div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<style>
|
||||
.nr-db-text-layout {
|
||||
position:relative;
|
||||
display: inline-block;
|
||||
width: 90px;
|
||||
height: 60px;
|
||||
border-radius:3px;
|
||||
border:1px solid #bbb;
|
||||
cursor:pointer;
|
||||
color: #666;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.nr-db-text-layout.selected, .nr-db-text-layout:hover {
|
||||
border-color: #333;
|
||||
color: #333;
|
||||
}
|
||||
.nr-db-text-layout span {
|
||||
position: absolute;
|
||||
}
|
||||
.nr-db-text-layout-value {
|
||||
font-weight: bold;
|
||||
}
|
||||
.nr-db-text-layout-row span { top: 20px; }
|
||||
.nr-db-text-layout-row-left .nr-db-text-layout-label { left: 2px; }
|
||||
.nr-db-text-layout-row-left .nr-db-text-layout-value { left: 34px; }
|
||||
.nr-db-text-layout-row-spread .nr-db-text-layout-label { left: 2px; }
|
||||
.nr-db-text-layout-row-spread .nr-db-text-layout-value { right: 2px; }
|
||||
.nr-db-text-layout-row-center .nr-db-text-layout-label { left: 11px; }
|
||||
.nr-db-text-layout-row-center .nr-db-text-layout-value { right: 11px; }
|
||||
.nr-db-text-layout-row-right .nr-db-text-layout-label { right: 40px; }
|
||||
.nr-db-text-layout-row-right .nr-db-text-layout-value { right: 2px; }
|
||||
|
||||
.nr-db-text-layout-col span { width: 90px; text-align: center; left: 0px;}
|
||||
.nr-db-text-layout-col-center .nr-db-text-layout-label { top: 12px; }
|
||||
.nr-db-text-layout-col-center .nr-db-text-layout-value { top: 26px; }
|
||||
.nr-db-text-layout-checkbox {
|
||||
display: none;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #bbb;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
top: -5px;
|
||||
background: #fff;
|
||||
}
|
||||
.nr-db-text-layout.selected .nr-db-text-layout-checkbox {
|
||||
display:inline-block;
|
||||
box-shadow: inset 0px 0px 0px 2px #fff;
|
||||
background: #333;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
</style>
|
||||
<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}} &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>
|
||||
</script>
|
||||
51
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_text.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
module.exports = function(RED) {
|
||||
var ui = require('../ui')(RED);
|
||||
|
||||
function TextNode(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 layout = config.layout||"row-spread";
|
||||
var angLayout = "row";
|
||||
var angLayoutAlign = "space-between center";
|
||||
if (layout === "row-spread") { angLayout = 'row'; angLayoutAlign = 'space-between center'}
|
||||
else if (layout === "row-left") { angLayout = 'row'; angLayoutAlign = 'start center'}
|
||||
else if (layout === "row-center") { angLayout = 'row'; angLayoutAlign = 'center center'}
|
||||
else if (layout === "row-right") { angLayout = 'row'; angLayoutAlign = 'end center'}
|
||||
else if (layout === "col-center") { angLayout = 'column'; angLayoutAlign = 'center center'}
|
||||
var done = ui.add({
|
||||
emitOnlyNewValues: false,
|
||||
node: node,
|
||||
tab: tab,
|
||||
group: group,
|
||||
control: {
|
||||
type: 'text',
|
||||
label: config.label,
|
||||
order: config.order,
|
||||
format: config.format,
|
||||
width: config.width || group.config.width || 6,
|
||||
height: config.height || 1,
|
||||
layout: angLayout,
|
||||
layoutAlign: angLayoutAlign
|
||||
},
|
||||
convert: function(value) {
|
||||
if (value !== undefined) {
|
||||
if (Buffer.isBuffer(value)) {
|
||||
value = value.toString('binary');
|
||||
}
|
||||
else {
|
||||
value = value.toString();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
});
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_text", TextNode);
|
||||
};
|
||||
114
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_text_input.html
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_text_input',{
|
||||
category: 'dashboard',
|
||||
color: 'rgb(176, 223, 227)',
|
||||
defaults: {
|
||||
name: {value: ''},
|
||||
label: {value: ''},
|
||||
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},
|
||||
mode: {value: 'text', required: true},
|
||||
delay: {value: 300, validate: RED.validators.number()},
|
||||
topic: {value: ''}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
outputLabels: function() { return this.mode; },
|
||||
icon: "ui_text.png",
|
||||
paletteLabel: 'text input',
|
||||
label: function() { return this.name || (~this.label.indexOf("{{") ? null : this.label) || this.mode+' input'; },
|
||||
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"
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="ui_text_input">
|
||||
<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-mode"><i class="fa fa-keyboard-o"></i> Mode</label>
|
||||
<select style="width:128px" id="node-input-mode">
|
||||
<option value="text">text input</option>
|
||||
<option value="email">email address</option>
|
||||
<option value="password">password</option>
|
||||
<option value="number">number</option>
|
||||
<option value="tel">telephone input</option>
|
||||
<option value="color">color picker</option>
|
||||
<option value="time">time picker</option>
|
||||
<option value="week">week picker</option>
|
||||
<option value="month">month picker</option>
|
||||
</select>
|
||||
<label for="node-input-delay" style="text-align:right; width:100px"><i class="fa fa-clock-o"></i> Delay (ms)</label>
|
||||
<input type="text" style="width:58px" id="node-input-delay">
|
||||
</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-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name">
|
||||
</div>
|
||||
<div class="form-tips">Setting <b>Delay</b> to 0 waits for Enter or Tab key, to send input.</span></div>
|
||||
</script>
|
||||
|
||||
<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>
|
||||
</script>
|
||||
43
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_text_input.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
module.exports = function(RED) {
|
||||
var ui = require('../ui')(RED);
|
||||
|
||||
function TextInputNode(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,
|
||||
forwardInputMessages: config.passthru,
|
||||
control: {
|
||||
type: (config.delay <= 0 ? 'text-input-CR' : 'text-input'),
|
||||
label: config.label,
|
||||
tooltip: config.tooltip,
|
||||
mode: config.mode,
|
||||
delay: config.delay,
|
||||
order: config.order,
|
||||
value: '',
|
||||
width: config.width || group.config.width || 6,
|
||||
height: config.height || 1
|
||||
},
|
||||
beforeSend: function (msg) {
|
||||
if (config.mode === "time") {
|
||||
if (typeof msg.payload === "string") {
|
||||
msg.payload = Date.parse(msg.payload);
|
||||
}
|
||||
}
|
||||
// if (config.mode === "week") { msg.payload = Date.parse(msg.payload); }
|
||||
// if (config.mode === "month") { msg.payload = Date.parse(msg.payload); }
|
||||
msg.topic = config.topic || msg.topic;
|
||||
}
|
||||
});
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_text_input", TextInputNode);
|
||||
};
|
||||
108
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_toast.html
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ui_toast', {
|
||||
category: 'dashboard',
|
||||
color: 'rgb(119, 198, 204)',
|
||||
defaults: {
|
||||
position: {value: 'top right'},
|
||||
displayTime: {value: '3'},
|
||||
highlight: {value: ''},
|
||||
sendall: {value: true},
|
||||
outputs: {value: 0},
|
||||
ok: {value: 'OK', required: true},
|
||||
cancel: {value: ''},
|
||||
raw: {value: false},
|
||||
topic: {value: ''},
|
||||
name: {value: ''}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
align: "right",
|
||||
icon: "ui_toast.png",
|
||||
paletteLabel: 'notification',
|
||||
label: function() { return this.name || (this.position==="dialog" ? "show dialog" : "show notification"); },
|
||||
labelStyle: function() { return this.name?"node_label_italic":""; },
|
||||
oneditprepare: function() {
|
||||
$("#node-input-position").on("change", function() {
|
||||
if ($("#node-input-position option:selected").val() === 'dialog') {
|
||||
$("#node-toast-displaytime").hide();
|
||||
$("#node-toast-highlightcolor").hide();
|
||||
$("#node-toast-sendall").hide();
|
||||
$("#node-dialog-displayok").show();
|
||||
$("#node-dialog-displaycancel").show();
|
||||
$("#node-dialog-topic").show();
|
||||
}
|
||||
else {
|
||||
$("#node-toast-displaytime").show();
|
||||
$("#node-toast-highlightcolor").show();
|
||||
$("#node-toast-sendall").show();
|
||||
$("#node-dialog-displayok").hide();
|
||||
$("#node-dialog-displaycancel").hide();
|
||||
$("#node-dialog-topic").show();
|
||||
}
|
||||
});
|
||||
},
|
||||
oneditsave: function() {
|
||||
if ($("#node-input-position option:selected").val() === 'dialog') { this.outputs = 1; }
|
||||
else { this.outputs = 0; }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="ui_toast">
|
||||
<div class="form-row">
|
||||
<label for="node-input-position"><i class="fa fa-th-large"></i> Layout</label>
|
||||
<select type="text" id="node-input-position" style="display:inline-block; width:70%; vertical-align:baseline;">
|
||||
<option value="top right">Top Right</option>
|
||||
<option value="bottom right">Bottom Right</option>
|
||||
<option value="top left">Top Left</option>
|
||||
<option value="bottom left">Bottom Left</option>
|
||||
<option value="dialog">OK / Cancel Dialog</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row" id="node-toast-displaytime">
|
||||
<label for="node-input-displayTime"><i class="fa fa-clock-o"></i> Timeout (S)</label>
|
||||
<input type="text" id="node-input-displayTime" placeholder="e.g. 3 seconds">
|
||||
</div>
|
||||
<div class="form-row" id="node-toast-highlightcolor">
|
||||
<label for="node-input-highlight"><i class="fa fa-square-o"></i> Border</label>
|
||||
<input type="text" id="node-input-highlight" placeholder="(optional) border highlight colour">
|
||||
</div>
|
||||
<div class="form-row" id="node-toast-sendtoall">
|
||||
<label style="width:auto" for="node-input-sendall"><i class="fa fa-arrow-right"></i> Send to all browser sessions. </label>
|
||||
<input type="checkbox" checked id="node-input-sendall" style="display:inline-block; width:auto; vertical-align:baseline;">
|
||||
</div>
|
||||
<div class="form-row" id="node-dialog-displayok">
|
||||
<label for="node-input-ok"><i class="fa fa-check"></i> Default action label</label>
|
||||
<input type="text" id="node-input-ok" placeholder="label for OK button">
|
||||
</div>
|
||||
<div class="form-row" id="node-dialog-displaycancel">
|
||||
<label for="node-input-cancel"><i class="fa fa-times"></i> Secondary action label</label>
|
||||
<input type="text" id="node-input-cancel" placeholder="(optional label for Cancel button)">
|
||||
</div>
|
||||
<div class="form-row" id="node-toast-raw">
|
||||
<label style="width:auto" for="node-input-raw"><i class="fa fa-exclamation-triangle"></i> Accept raw HTML/JavaScript input. </label>
|
||||
<input type="checkbox" id="node-input-raw" style="display:inline-block; width:auto; vertical-align:baseline;">
|
||||
</div>
|
||||
<div class="form-row" id="node-dialog-topic">
|
||||
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
|
||||
<input type="text" id="node-input-topic" placeholder="(optional msg.topic)">
|
||||
</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>
|
||||
<div class="form-tips"><b>Note</b>: checking <i>Accept raw HTML/JavaScript</i> can allow injection of code.
|
||||
Ensure the input comes from trusted sources.</span></div>
|
||||
</script>
|
||||
|
||||
<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 notification.</p>
|
||||
<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>Sending a blank payload will remove any active dialog without sending any data.</p>
|
||||
</script>
|
||||
65
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_toast.js
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
module.exports = function(RED) {
|
||||
var ui = require('../ui')(RED);
|
||||
|
||||
function ToastNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
if (config.hasOwnProperty("displayTime") && (config.displayTime.length > 0)) {
|
||||
try { this.displayTime = parseFloat(config.displayTime) * 1000; }
|
||||
catch(e) { this.displayTime = 3000; }
|
||||
}
|
||||
else { this.displayTime = 3000; }
|
||||
if (this.displayTime <= 0) { this.displayTime = 1; }
|
||||
this.position = config.position || "top right";
|
||||
this.highlight = config.highlight;
|
||||
this.ok = config.ok;
|
||||
this.cancel = config.cancel;
|
||||
this.topic = config.topic;
|
||||
if (config.sendall === undefined) { this.sendall = true; }
|
||||
else { this.sendall = config.sendall; }
|
||||
this.raw = config.raw || false;
|
||||
var node = this;
|
||||
|
||||
// var noscript = function (content) {
|
||||
// if (typeof content === "object") { return null; }
|
||||
// content = '' + content;
|
||||
// content = content.replace(/<.*cript.*/ig, '');
|
||||
// content = content.replace(/.on\w+=.*".*"/g, '');
|
||||
// content = content.replace(/.on\w+=.*\'.*\'/g, '');
|
||||
// return content;
|
||||
// }
|
||||
|
||||
var done = ui.add({
|
||||
node: node,
|
||||
control: {},
|
||||
storeFrontEndInputAsState: false,
|
||||
forwardInputMessages: false,
|
||||
beforeSend: function (msg) {
|
||||
var m = msg.payload.msg;
|
||||
m.topic = node.topic || m.topic;
|
||||
return m;
|
||||
}
|
||||
});
|
||||
|
||||
node.on('input', function(msg) {
|
||||
if (node.sendall === true) { delete msg.socketid; }
|
||||
//msg.payload = noscript(msg.payload);
|
||||
ui.emitSocket('show-toast', {
|
||||
title: node.topic || msg.topic,
|
||||
message: msg.payload,
|
||||
highlight: node.highlight || msg.highlight,
|
||||
displayTime: node.displayTime,
|
||||
position: node.position,
|
||||
id: node.id,
|
||||
dialog: (node.position === "dialog") || false,
|
||||
ok: node.ok,
|
||||
cancel: node.cancel,
|
||||
socketid: msg.socketid,
|
||||
raw: node.raw,
|
||||
msg: msg
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", done);
|
||||
}
|
||||
RED.nodes.registerType("ui_toast", ToastNode);
|
||||
};
|
||||
64
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_ui_control.html
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<script type="text/javascript">
|
||||
// convert to i18 text
|
||||
function c_(x) {
|
||||
return RED._("node-red-dashboard/ui_ui_control:ui_ui_control."+x);
|
||||
}
|
||||
|
||||
RED.nodes.registerType('ui_ui_control', {
|
||||
category: c_("label.category"),
|
||||
color: 'rgb( 63, 173, 181)',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
events: {value:"all"}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
align: "right",
|
||||
icon: "ui_link.png",
|
||||
paletteLabel: 'ui control',
|
||||
label: function() { return this.name || "ui control"; },
|
||||
labelStyle: function() { return this.name?"node_label_italic":""; },
|
||||
outputLabels: function() { return this.events; },
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="ui_ui_control">
|
||||
<div class="form-row">
|
||||
<label for="node-input-events"><i class="fa fa-sign-out"></i> Output</label>
|
||||
<select id="node-input-events" style="width:70%;">
|
||||
<option value="all">Connect, lost, change events</option>
|
||||
<option value="connect">Connect event only</option>
|
||||
<option value="change">Change tab event only</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="ui_ui_control.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]ui_ui_control.placeholder.name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<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. 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, or changes tab, this node will emit a <code>msg</code> containing:</p>
|
||||
<ul>
|
||||
<li><code>payload</code> - <i>connect</i>, <i>lost</i>, or <i>change</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).
|
||||
</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>
|
||||
64
nodered/rootfs/data/node_modules/node-red-dashboard/nodes/ui_ui_control.js
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
module.exports = function(RED) {
|
||||
var ui = require('../ui')(RED);
|
||||
|
||||
function UiControlNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
this.events = config.events || "all";
|
||||
var node = this;
|
||||
|
||||
this.on('input', function(msg) {
|
||||
if (typeof msg.payload !== "object") { msg.payload = {tab:msg.payload}; }
|
||||
// show/hide or enable/disable tabs
|
||||
if (msg.payload.hasOwnProperty("tabs")) {
|
||||
ui.emit('ui-control', {tabs:msg.payload.tabs, socketid:msg.socketid});
|
||||
}
|
||||
// switch to tab name (or number)
|
||||
if (msg.payload.hasOwnProperty("tab")) {
|
||||
ui.emit('ui-control', {tab:msg.payload.tab, socketid:msg.socketid});
|
||||
}
|
||||
// show or hide ui groups
|
||||
if (msg.payload.hasOwnProperty("group")) {
|
||||
ui.emit('ui-control', {group:msg.payload.group, socketid:msg.socketid});
|
||||
}
|
||||
});
|
||||
|
||||
var sendconnect = function(id, ip) {
|
||||
node.send({payload:"connect", socketid:id, socketip:ip});
|
||||
};
|
||||
|
||||
var sendlost = function(id, ip) {
|
||||
node.send({payload:"lost", socketid:id, socketip:ip});
|
||||
};
|
||||
|
||||
var sendchange = function(index, name, id, ip, p) {
|
||||
node.send({payload:"change", tab:index, name:name, socketid:id, socketip:ip, params:p});
|
||||
}
|
||||
|
||||
if (node.events === "connect") {
|
||||
ui.ev.on('newsocket', sendconnect);
|
||||
}
|
||||
else if (node.events === "change") {
|
||||
ui.ev.on('changetab', sendchange);
|
||||
}
|
||||
else {
|
||||
ui.ev.on('newsocket', sendconnect);
|
||||
ui.ev.on('changetab', sendchange);
|
||||
ui.ev.on('endsocket', sendlost);
|
||||
}
|
||||
|
||||
this.on('close', function() {
|
||||
if (node.events === "connect") {
|
||||
ui.ev.removeListener('newsocket', sendconnect);
|
||||
}
|
||||
else if (node.events === "change") {
|
||||
ui.ev.removeListener('changetab', sendchange);
|
||||
}
|
||||
else {
|
||||
ui.ev.removeListener('newsocket', sendconnect);
|
||||
ui.ev.removeListener('changetab', sendchange);
|
||||
ui.ev.removeListener('endsocket', sendlost);
|
||||
}
|
||||
})
|
||||
}
|
||||
RED.nodes.registerType("ui_ui_control", UiControlNode);
|
||||
};
|
||||