mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add editorTheme setting
This commit is contained in:
parent
1aaef598a5
commit
c9d2d301aa
182
editor/templates/index.mst
Normal file
182
editor/templates/index.mst
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<!--
|
||||||
|
Copyright 2013, 2015 IBM Corp.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<title>{{ page.title }}</title>
|
||||||
|
<link rel="icon" type="image/png" href="{{ page.favicon }}">
|
||||||
|
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
|
||||||
|
<link href="vendor/jquery/css/smoothness/jquery-ui-1.10.3.custom.min.css" rel="stylesheet" media="screen">
|
||||||
|
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css">
|
||||||
|
<link rel="stylesheet" href="vendor/vendor.css">
|
||||||
|
<link rel="stylesheet" href="red/style.min.css">
|
||||||
|
{{#page.css}}
|
||||||
|
<link rel="stylesheet" href="{{.}}">
|
||||||
|
{{/page.css}}
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body spellcheck="false">
|
||||||
|
<div id="header">
|
||||||
|
<span class="logo">{{#header.image}}<img src="{{.}}">{{/header.image}} <span>{{ header.title }}</span></span>
|
||||||
|
<ul class="header-toolbar hide">
|
||||||
|
<li><a id="btn-sidemenu" class="button" data-toggle="dropdown" href="#"><i class="fa fa-bars"></i></a></li>
|
||||||
|
<ul>
|
||||||
|
</div>
|
||||||
|
<div id="main-container" class="sidebar-closed hide">
|
||||||
|
<div id="palette">
|
||||||
|
<img src="red/images/spin.svg" class="palette-spinner hide"/>
|
||||||
|
<div id="palette-container" class="palette-scroll">
|
||||||
|
</div>
|
||||||
|
<div id="palette-search">
|
||||||
|
<i class="fa fa-search"></i><input id="palette-search-input" type="text" placeholder="filter"><a href="#" id="palette-search-clear"><i class="fa fa-times"></i></a></input>
|
||||||
|
</div>
|
||||||
|
</div><!-- /palette -->
|
||||||
|
|
||||||
|
<div id="workspace">
|
||||||
|
<ul id="workspace-tabs"></ul>
|
||||||
|
<div id="workspace-add-tab"><a id="btn-workspace-add-tab" href="#"><i class="fa fa-plus"></i></a></div>
|
||||||
|
<div id="chart"></div>
|
||||||
|
<div id="workspace-toolbar">
|
||||||
|
<a class="button" id="workspace-subflow-edit" href="#"><i class="fa fa-pencil"></i> edit name</a>
|
||||||
|
<a class="button disabled" id="workspace-subflow-add-input" href="#"><i class="fa fa-plus"></i> input</a>
|
||||||
|
<a class="button" id="workspace-subflow-add-output" href="#"><i class="fa fa-plus"></i> output</a>
|
||||||
|
<a class="button" id="workspace-subflow-delete" href="#"><i class="fa fa-trash"></i> delete subflow</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="chart-zoom-controls">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn btn-mini" id="btn-zoom-out" href="#"><i class="fa fa-search-minus"></i></a>
|
||||||
|
<a class="btn btn-mini" id="btn-zoom-zero" href="#"><i class="fa fa-dot-circle-o"></i></a>
|
||||||
|
<a class="btn btn-mini" id="btn-zoom-in" href="#"><i class="fa fa-search-plus"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="sidebar">
|
||||||
|
<ul id="sidebar-tabs"></ul>
|
||||||
|
<div id="sidebar-content"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="sidebar-separator"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="notifications"></div>
|
||||||
|
<div id="dropTarget"><div>Drop the flow here<br/><i class="fa fa-download"></i></div></div>
|
||||||
|
|
||||||
|
<div id="dialog" class="hide"><form id="dialog-form" class="form-horizontal"></form></div>
|
||||||
|
<div id="node-config-dialog" class="hide"><form id="dialog-config-form" class="form-horizontal"></form><div class="form-tips" id="node-config-dialog-user-count"></div></div>
|
||||||
|
<div id="subflow-dialog" class="hide">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Name</label><input type="text" id="subflow-input-name">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="form-tips" id="subflow-dialog-user-count"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="node-dialog-confirm-deploy" class="hide">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div id="node-dialog-confirm-deploy-config" style="text-align: center; padding-top: 30px;">
|
||||||
|
Some of the nodes are not properly configured. Are you sure you want to deploy?
|
||||||
|
</div>
|
||||||
|
<div id="node-dialog-confirm-deploy-unknown" style="text-align: center; padding-top: 10px;">
|
||||||
|
The workspace contains some unknown node types:
|
||||||
|
<ul style="width: 300px; margin: auto; text-align: left;" id="node-dialog-confirm-deploy-unknown-list"></ul>
|
||||||
|
Are you sure you want to deploy?
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="node-dialog-library-save-confirm" class="hide">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div style="text-align: center; padding-top: 30px;">
|
||||||
|
A <span id="node-dialog-library-save-type"></span> called <span id="node-dialog-library-save-name"></span> already exists. Overwrite?
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="node-dialog-library-save" class="hide">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-dialog-library-save-folder"><i class="fa fa-folder-open"></i> Folder</label>
|
||||||
|
<input type="text" id="node-dialog-library-save-folder" placeholder="Folder">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-dialog-library-save-filename"><i class="fa fa-file"></i> Filename</label>
|
||||||
|
<input type="text" id="node-dialog-library-save-filename" placeholder="Filename">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="node-dialog-library-lookup" class="hide">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-row">
|
||||||
|
<ul id="node-dialog-library-breadcrumbs" class="breadcrumb">
|
||||||
|
<li class="active"><a href="#">Library</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div style="vertical-align: top; display: inline-block; height: 100%; width: 30%; padding-right: 20px;">
|
||||||
|
<div id="node-select-library" style="border: 1px solid #999; width: 100%; height: 100%; overflow:scroll;"><ul></ul></div>
|
||||||
|
</div>
|
||||||
|
<div style="vertical-align: top; display: inline-block;width: 65%; height: 100%;">
|
||||||
|
<div style="height: 100%; width: 95%;" class="node-text-editor" id="node-select-library-text" ></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="node-dialog-rename-workspace" class="hide">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-workspace-name" ><i class="fa fa-tag"></i> Name:</label>
|
||||||
|
<input type="text" id="node-input-workspace-name">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="node-dialog-delete-workspace" class="hide">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div style="text-align: center; padding-top: 30px;">
|
||||||
|
Are you sure you want to delete '<span id="node-dialog-delete-workspace-name"></span>'?
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/x-red" data-template-name="export-library-dialog">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-filename" ><i class="fa fa-file"></i> Filename:</label>
|
||||||
|
<input type="text" id="node-input-filename" placeholder="Filename">
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/x-red" data-template-name="subflow">
|
||||||
|
<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 src="vendor/vendor.js"></script>
|
||||||
|
<script src="vendor/ace/ace.js"></script>
|
||||||
|
<script src="vendor/ace/ext-language_tools.js"></script>
|
||||||
|
<script src="red/red.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -24,6 +24,7 @@ var nodes = require("./nodes");
|
|||||||
var flows = require("./flows");
|
var flows = require("./flows");
|
||||||
var library = require("./library");
|
var library = require("./library");
|
||||||
var info = require("./info");
|
var info = require("./info");
|
||||||
|
var theme = require("./theme");
|
||||||
|
|
||||||
var auth = require("./auth");
|
var auth = require("./auth");
|
||||||
var needsPermission = auth.needsPermission;
|
var needsPermission = auth.needsPermission;
|
||||||
@ -41,9 +42,13 @@ function init(adminApp,storage) {
|
|||||||
|
|
||||||
// Editor
|
// Editor
|
||||||
if (!settings.disableEditor) {
|
if (!settings.disableEditor) {
|
||||||
|
ui.init(settings);
|
||||||
var editorApp = express();
|
var editorApp = express();
|
||||||
editorApp.get("/",ui.ensureSlash,ui.editor);
|
editorApp.get("/",ui.ensureSlash,ui.editor);
|
||||||
editorApp.get("/icons/:icon",ui.icon);
|
editorApp.get("/icons/:icon",ui.icon);
|
||||||
|
if (settings.editorTheme) {
|
||||||
|
editorApp.use("/theme",theme.init(settings));
|
||||||
|
}
|
||||||
editorApp.use("/",ui.editorResources);
|
editorApp.use("/",ui.editorResources);
|
||||||
adminApp.use(editorApp);
|
adminApp.use(editorApp);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
var settings = require('../settings');
|
var settings = require('../settings');
|
||||||
|
var theme = require("./theme");
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
@ -23,7 +24,12 @@ module.exports = {
|
|||||||
httpNodeRoot: settings.httpNodeRoot,
|
httpNodeRoot: settings.httpNodeRoot,
|
||||||
version: settings.version,
|
version: settings.version,
|
||||||
user: req.user
|
user: req.user
|
||||||
};
|
}
|
||||||
|
|
||||||
|
var themeSettings = theme.settings();
|
||||||
|
if (themeSettings) {
|
||||||
|
safeSettings.editorTheme = themeSettings;
|
||||||
|
}
|
||||||
|
|
||||||
if (util.isArray(settings.paletteCategories)) {
|
if (util.isArray(settings.paletteCategories)) {
|
||||||
safeSettings.paletteCategories = settings.paletteCategories;
|
safeSettings.paletteCategories = settings.paletteCategories;
|
||||||
|
113
red/api/theme.js
Normal file
113
red/api/theme.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2015 IBM Corp.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
var express = require("express");
|
||||||
|
var util = require("util");
|
||||||
|
var path = require("path");
|
||||||
|
var fs = require("fs");
|
||||||
|
|
||||||
|
var themeContext = {
|
||||||
|
page: {
|
||||||
|
title: "Node-RED",
|
||||||
|
favicon: "favicon.ico"
|
||||||
|
//css: [""]
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
title: "Node-RED",
|
||||||
|
image: "red/images/node-red.png"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var themeSettings = null;
|
||||||
|
|
||||||
|
function serveFile(app,baseUrl,file) {
|
||||||
|
try {
|
||||||
|
var stats = fs.statSync(file);
|
||||||
|
var url = baseUrl+path.basename(file);
|
||||||
|
//console.log(url,"->",file);
|
||||||
|
app.get(url,function(req, res) {
|
||||||
|
res.sendfile(file);
|
||||||
|
});
|
||||||
|
return url;
|
||||||
|
} catch(err) {
|
||||||
|
//TODO: log filenotfound
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
init: function(settings) {
|
||||||
|
var i;
|
||||||
|
var url;
|
||||||
|
if (settings.editorTheme) {
|
||||||
|
var theme = settings.editorTheme;
|
||||||
|
themeSettings = {};
|
||||||
|
|
||||||
|
var themeApp = express();
|
||||||
|
|
||||||
|
if (theme.page) {
|
||||||
|
if (theme.page.css) {
|
||||||
|
var styles = theme.page.css;
|
||||||
|
if (!util.isArray(styles)) {
|
||||||
|
styles = [styles];
|
||||||
|
}
|
||||||
|
themeContext.page.css = [];
|
||||||
|
|
||||||
|
for (i=0;i<styles.length;i++) {
|
||||||
|
url = serveFile(themeApp,"/css/",styles[i]);
|
||||||
|
if (url) {
|
||||||
|
themeContext.page.css.push("/theme"+url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme.page.favicon) {
|
||||||
|
url = serveFile(themeApp,"/favicon/",theme.page.favicon)
|
||||||
|
if (url) {
|
||||||
|
themeContext.page.favicon = "/theme"+url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
themeContext.page.title = theme.page.title || themeContext.page.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme.header) {
|
||||||
|
|
||||||
|
themeContext.header.title = theme.header.title || themeContext.header.title;
|
||||||
|
if (theme.header.hasOwnProperty("image")) {
|
||||||
|
if (theme.header.image) {
|
||||||
|
url = serveFile(themeApp,"/header/",theme.header.image);
|
||||||
|
if (url) {
|
||||||
|
themeContext.header.image = "/theme"+url;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
themeContext.header.image = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//themeSettings.deployButton = theme.deployButton || themeSettings.deployButton;
|
||||||
|
|
||||||
|
return themeApp;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
context: function() {
|
||||||
|
return themeContext;
|
||||||
|
},
|
||||||
|
settings: function() {
|
||||||
|
return themeSettings;
|
||||||
|
}
|
||||||
|
}
|
@ -17,8 +17,12 @@ var express = require('express');
|
|||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
|
|
||||||
|
var theme = require("./theme");
|
||||||
|
|
||||||
|
var Mustache = require("mustache");
|
||||||
|
|
||||||
var events = require("../events");
|
var events = require("../events");
|
||||||
var settings = require("../settings");
|
var settings;
|
||||||
|
|
||||||
var icon_paths = [path.resolve(__dirname + '/../../public/icons')];
|
var icon_paths = [path.resolve(__dirname + '/../../public/icons')];
|
||||||
var iconCache = {};
|
var iconCache = {};
|
||||||
@ -29,7 +33,16 @@ events.on("node-icon-dir",function(dir) {
|
|||||||
icon_paths.push(path.resolve(dir));
|
icon_paths.push(path.resolve(dir));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var templateDir = path.resolve(__dirname+"/../../editor/templates");
|
||||||
|
var editorTemplate;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
init: function(_settings) {
|
||||||
|
settings = _settings;
|
||||||
|
editorTemplate = fs.readFileSync(path.join(templateDir,"index.mst"),"utf8");
|
||||||
|
Mustache.parse(editorTemplate);
|
||||||
|
},
|
||||||
|
|
||||||
ensureSlash: function(req,res,next) {
|
ensureSlash: function(req,res,next) {
|
||||||
var parts = req.originalUrl.split("?");
|
var parts = req.originalUrl.split("?");
|
||||||
if (parts[0].slice(-1) != "/") {
|
if (parts[0].slice(-1) != "/") {
|
||||||
@ -56,7 +69,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
editor: function(req,res) {
|
editor: function(req,res) {
|
||||||
res.sendfile(path.resolve(__dirname + '/../../public/index.html'));
|
res.send(Mustache.render(editorTemplate,theme.context()));
|
||||||
},
|
},
|
||||||
editorResources: express.static(__dirname + '/../../public')
|
editorResources: express.static(__dirname + '/../../public')
|
||||||
};
|
};
|
||||||
|
0
test/red/api/theme_spec.js
Normal file
0
test/red/api/theme_spec.js
Normal file
Loading…
Reference in New Issue
Block a user