node-red/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js

1457 lines
65 KiB
JavaScript

/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* 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.
**/
RED.utils = (function() {
window._marked = window.marked;
window.marked = function(txt) {
console.warn("Use of 'marked()' is deprecated. Use RED.utils.renderMarkdown() instead");
return renderMarkdown(txt);
}
const descriptionList = {
name: 'descriptionList',
level: 'block', // Is this a block-level or inline-level tokenizer?
start(src) {
if (!src) { return null; }
let m = src.match(/:[^:\n]/g);
return m && m.index; // Hint to Marked.js to stop and check for a match
},
tokenizer(src, tokens) {
if (!src) { return null; }
const rule = /^(?::[^:\n]+:[^:\n]*(?:\n|$))+/; // Regex for the complete token
const match = rule.exec(src);
if (match) {
return { // Token to generate
type: 'descriptionList', // Should match "name" above
raw: match[0], // Text to consume from the source
text: match[0].trim(), // Additional custom properties
tokens: this.lexer.inlineTokens(match[0].trim()) // inlineTokens to process **bold**, *italics*, etc.
};
}
},
renderer(token) {
return `<dl class="message-properties">${this.parser.parseInline(token.tokens)}\n</dl>`; // parseInline to turn child tokens into HTML
}
};
const description = {
name: 'description',
level: 'inline', // Is this a block-level or inline-level tokenizer?
start(src) {
if (!src) { return null; }
let m = src.match(/:/g);
return m && m.index; // Hint to Marked.js to stop and check for a match
},
tokenizer(src, tokens) {
if (!src) { return null; }
const rule = /^:([^:\n]+)\(([^:\n]+)\).*?:([^:\n]*)(?:\n|$)/; // Regex for the complete token
const match = rule.exec(src);
if (match) {
return { // Token to generate
type: 'description', // Should match "name" above
raw: match[0], // Text to consume from the source
dt: this.lexer.inlineTokens(match[1].trim()), // Additional custom properties
types: this.lexer.inlineTokens(match[2].trim()),
dd: this.lexer.inlineTokens(match[3].trim()),
};
}
},
renderer(token) {
return `\n<dt>${this.parser.parseInline(token.dt)}<span class="property-type">${this.parser.parseInline(token.types)}</span></dt><dd>${this.parser.parseInline(token.dd)}</dd>`;
},
childTokens: ['dt', 'dd'], // Any child tokens to be visited by walkTokens
walkTokens(token) { // Post-processing on the completed token tree
if (token.type === 'strong') {
token.text += ' walked';
}
}
};
const renderer = new window._marked.Renderer();
//override list creation - add node-ports to order lists
renderer.list = function (body, ordered, start) {
let addClass = /dl.*?class.*?message-properties.*/.test(body);
if (addClass && ordered) {
return '<ol class="node-ports">' + body + '</ol>';
} else if (ordered) {
return '<ol>' + body + '</ol>';
} else {
return '<ul>' + body + '</ul>'
}
}
var mermaidIsInitialized = false;
var mermaidIsEnabled /* = undefined */;
renderer.code = function (code, lang) {
if(lang === "mermaid") {
// mermaid diagram rendering
if (mermaidIsEnabled === undefined) {
if (RED.settings.markdownEditor &&
RED.settings.markdownEditor.mermaid) {
mermaidIsEnabled = RED.settings.markdownEditor.mermaid.enabled;
}
else {
mermaidIsEnabled = true;
}
}
if (mermaidIsEnabled) {
if (!mermaidIsInitialized) {
mermaidIsInitialized = true;
mermaid.initialize({startOnLoad:false});
}
return `<pre class='mermaid'>${code}</pre>`;
}
else {
return `<details><summary>${RED._("markdownEditor.mermaid.summary")}</summary><pre><code>${code}</code></pre></details>`;
}
}
else {
return "<pre><code>" +code +"</code></pre>";
}
};
window._marked.setOptions({
renderer: renderer,
gfm: true,
tables: true,
breaks: false,
pedantic: false,
smartLists: true,
smartypants: false
});
window._marked.use({extensions: [descriptionList, description] } );
function renderMarkdown(txt) {
var rendered = _marked.parse(txt);
var cleaned = DOMPurify.sanitize(rendered, {SAFE_FOR_JQUERY: true})
return cleaned;
}
function formatString(str) {
return str.replace(/\r?\n/g,"&crarr;").replace(/\t/g,"&rarr;");
}
function sanitize(m) {
return m.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
}
function buildMessageSummaryValue(value) {
var result;
if (Array.isArray(value)) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('array['+value.length+']');
} else if (value === null) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-null">null</span>');
} else if (typeof value === 'object') {
if (value.hasOwnProperty('type') && value.type === 'undefined') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-null">undefined</span>');
} else if (value.hasOwnProperty('type') && value.type === 'Buffer' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('buffer['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'array' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('array['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'set' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('set['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'map' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('map');
} else if (value.hasOwnProperty('type') && value.type === 'function') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('function');
} else if (value.hasOwnProperty('type') && (value.type === 'number' || value.type === 'bigint')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-number"></span>').text(value.data);
} else if (value.hasOwnProperty('type') && value.type === 'regexp') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-string"></span>').text(value.data);
} else {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta">object</span>');
}
} else if (typeof value === 'string') {
var subvalue;
if (value.length > 30) {
subvalue = sanitize(value.substring(0,30))+"&hellip;";
} else {
subvalue = sanitize(value);
}
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-string"></span>').html('"'+formatString(subvalue)+'"');
} else if (typeof value === 'number') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-number"></span>').text(""+value);
} else {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-other"></span>').text(""+value);
}
return result;
}
function makeExpandable(el,onbuild,ontoggle,expand) {
el.addClass("red-ui-debug-msg-expandable");
el.prop('toggle',function() {
return function(state) {
var parent = el.parent();
if (parent.hasClass('collapsed')) {
if (state) {
if (onbuild && !parent.hasClass('built')) {
onbuild();
parent.addClass('built');
}
parent.removeClass('collapsed');
return true;
}
} else {
if (!state) {
parent.addClass('collapsed');
return true;
}
}
return false;
}
});
el.on("click", function(e) {
var parent = $(this).parent();
var currentState = !parent.hasClass('collapsed');
if ($(this).prop('toggle')(!currentState)) {
if (ontoggle) {
ontoggle(!currentState);
}
}
// if (parent.hasClass('collapsed')) {
// if (onbuild && !parent.hasClass('built')) {
// onbuild();
// parent.addClass('built');
// }
// if (ontoggle) {
// ontoggle(true);
// }
// parent.removeClass('collapsed');
// } else {
// parent.addClass('collapsed');
// if (ontoggle) {
// ontoggle(false);
// }
// }
e.preventDefault();
});
if (expand) {
el.trigger("click");
}
}
var pinnedPaths = {};
var formattedPaths = {};
function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools) {
if (!pinnedPaths.hasOwnProperty(sourceId)) {
pinnedPaths[sourceId] = {}
}
var tools = $('<span class="red-ui-debug-msg-tools"></span>').appendTo(obj);
var copyTools = $('<span class="red-ui-debug-msg-tools-copy button-group"></span>').appendTo(tools);
if (!!key) {
var copyPath = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-terminal"></i></button>').appendTo(copyTools).on("click", function(e) {
e.preventDefault();
e.stopPropagation();
RED.clipboard.copyText(key,copyPath,"clipboard.copyMessagePath");
})
RED.popover.tooltip(copyPath,RED._("node-red:debug.sidebar.copyPath"));
}
var copyPayload = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-clipboard"></i></button>').appendTo(copyTools).on("click", function(e) {
e.preventDefault();
e.stopPropagation();
RED.clipboard.copyText(msg,copyPayload,"clipboard.copyMessageValue");
})
RED.popover.tooltip(copyPayload,RED._("node-red:debug.sidebar.copyPayload"));
if (strippedKey !== undefined && strippedKey !== '') {
var isPinned = pinnedPaths[sourceId].hasOwnProperty(strippedKey);
var pinPath = $('<button class="red-ui-button red-ui-button-small red-ui-debug-msg-tools-pin"><i class="fa fa-map-pin"></i></button>').appendTo(tools).on("click", function(e) {
e.preventDefault();
e.stopPropagation();
if (pinnedPaths[sourceId].hasOwnProperty(strippedKey)) {
delete pinnedPaths[sourceId][strippedKey];
$(this).removeClass("selected");
obj.removeClass("red-ui-debug-msg-row-pinned");
} else {
var rootedPath = "$"+(strippedKey[0] === '['?"":".")+strippedKey;
pinnedPaths[sourceId][strippedKey] = normalisePropertyExpression(rootedPath);
$(this).addClass("selected");
obj.addClass("red-ui-debug-msg-row-pinned");
}
}).toggleClass("selected",isPinned);
obj.toggleClass("red-ui-debug-msg-row-pinned",isPinned);
RED.popover.tooltip(pinPath,RED._("node-red:debug.sidebar.pinPath"));
}
if (extraTools) {
var t = extraTools;
if (typeof t === 'function') {
t = t(key,msg);
}
if (t) {
t.addClass("red-ui-debug-msg-tools-other");
t.appendTo(tools);
}
}
}
function checkExpanded(strippedKey,expandPaths,minRange,maxRange) {
if (expandPaths && expandPaths.length > 0) {
if (strippedKey === '' && minRange === undefined) {
return true;
}
for (var i=0;i<expandPaths.length;i++) {
var p = expandPaths[i];
if (p.indexOf(strippedKey) === 0 && (p[strippedKey.length] === "." || p[strippedKey.length] === "[") ) {
if (minRange !== undefined && p[strippedKey.length] === "[") {
var subkey = p.substring(strippedKey.length);
var m = (/\[(\d+)\]/.exec(subkey));
if (m) {
var index = parseInt(m[1]);
return minRange<=index && index<=maxRange;
}
} else {
return true;
}
}
}
}
return false;
}
function formatNumber(element,obj,sourceId,path,cycle,initialFormat) {
var format = (formattedPaths[sourceId] && formattedPaths[sourceId][path] && formattedPaths[sourceId][path]['number']) || initialFormat || "dec";
if (cycle) {
if (format === 'dec') {
if ((obj.toString().length===13) && (obj<=2147483647000)) {
format = 'dateMS';
} else if ((obj.toString().length===10) && (obj<=2147483647)) {
format = 'dateS';
} else {
format = 'hex'
}
} else if (format === 'dateMS' || format == 'dateS') {
if ((obj.toString().length===13) && (obj<=2147483647000)) {
format = 'dateML';
} else if ((obj.toString().length===10) && (obj<=2147483647)) {
format = 'dateL';
} else {
format = 'hex'
}
} else if (format === 'dateML' || format == 'dateL') {
format = 'hex';
} else {
format = 'dec';
}
formattedPaths[sourceId] = formattedPaths[sourceId]||{};
formattedPaths[sourceId][path] = formattedPaths[sourceId][path]||{};
formattedPaths[sourceId][path]['number'] = format;
} else if (initialFormat !== undefined){
formattedPaths[sourceId] = formattedPaths[sourceId]||{};
formattedPaths[sourceId][path] = formattedPaths[sourceId][path]||{};
formattedPaths[sourceId][path]['number'] = format;
}
if (format === 'dec') {
element.text(""+obj);
} else if (format === 'dateMS') {
element.text((new Date(obj)).toISOString());
} else if (format === 'dateS') {
element.text((new Date(obj*1000)).toISOString());
} else if (format === 'dateML') {
var dd = new Date(obj);
element.text(dd.toLocaleString() + " [UTC" + ( dd.getTimezoneOffset()/-60 <=0?"":"+" ) + dd.getTimezoneOffset()/-60 +"]");
} else if (format === 'dateL') {
var ddl = new Date(obj*1000);
element.text(ddl.toLocaleString() + " [UTC" + ( ddl.getTimezoneOffset()/-60 <=0?"":"+" ) + ddl.getTimezoneOffset()/-60 +"]");
} else if (format === 'hex') {
element.text("0x"+(obj).toString(16));
}
}
function formatBuffer(element,button,sourceId,path,cycle) {
var format = (formattedPaths[sourceId] && formattedPaths[sourceId][path] && formattedPaths[sourceId][path]['buffer']) || "raw";
if (cycle) {
if (format === 'raw') {
format = 'string';
} else {
format = 'raw';
}
formattedPaths[sourceId] = formattedPaths[sourceId]||{};
formattedPaths[sourceId][path] = formattedPaths[sourceId][path]||{};
formattedPaths[sourceId][path]['buffer'] = format;
}
if (format === 'raw') {
button.text('raw');
element.removeClass('red-ui-debug-msg-buffer-string').addClass('red-ui-debug-msg-buffer-raw');
} else if (format === 'string') {
button.text('string');
element.addClass('red-ui-debug-msg-buffer-string').removeClass('red-ui-debug-msg-buffer-raw');
}
}
/**
* Create a DOM element representation of obj - as used by Debug sidebar etc
*
* @params obj - the data to display
* @params options - a bag of options
*
* - If you want the Copy Value button, then set `sourceId`
* - If you want the Copy Path button, also set `path` to the value to be copied
*/
function createObjectElement(obj,options) {
options = options || {};
var key = options.key;
var typeHint = options.typeHint;
var hideKey = options.hideKey;
var path = options.path;
var sourceId = options.sourceId;
var rootPath = options.rootPath;
var expandPaths = options.expandPaths;
var ontoggle = options.ontoggle;
var exposeApi = options.exposeApi;
var tools = options.tools;
var subElements = {};
var i;
var e;
var entryObj;
var expandableHeader;
var header;
var headerHead;
var value;
var strippedKey;
if (path !== undefined && rootPath !== undefined) {
strippedKey = path.substring(rootPath.length+(path[rootPath.length]==="."?1:0));
}
var element = $('<span class="red-ui-debug-msg-element"></span>');
element.collapse = function() {
element.find(".red-ui-debug-msg-expandable").parent().addClass("collapsed");
}
header = $('<span class="red-ui-debug-msg-row"></span>').appendTo(element);
if (sourceId) {
addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools);
}
if (!key) {
element.addClass("red-ui-debug-msg-top-level");
if (sourceId) {
var pinned = pinnedPaths[sourceId];
expandPaths = [];
if (pinned) {
for (var pinnedPath in pinned) {
if (pinned.hasOwnProperty(pinnedPath)) {
try {
var res = getMessageProperty({$:obj},pinned[pinnedPath]);
if (res !== undefined) {
expandPaths.push(pinnedPath);
}
} catch(err) {
}
}
}
expandPaths.sort();
}
element.clearPinned = function() {
element.find(".red-ui-debug-msg-row-pinned").removeClass("red-ui-debug-msg-row-pinned");
pinnedPaths[sourceId] = {};
}
}
} else {
if (!hideKey) {
$('<span class="red-ui-debug-msg-object-key"></span>').text(key).appendTo(header);
$('<span>: </span>').appendTo(header);
}
}
entryObj = $('<span class="red-ui-debug-msg-object-value"></span>').appendTo(header);
var isArray = Array.isArray(obj);
var isArrayObject = false;
if (obj && typeof obj === 'object' && obj.hasOwnProperty('type') && obj.hasOwnProperty('data') && ((obj.__enc__ && obj.type === 'set') || (obj.__enc__ && obj.type === 'array') || obj.type === 'Buffer')) {
isArray = true;
isArrayObject = true;
}
if (obj === null || obj === undefined) {
$('<span class="red-ui-debug-msg-type-null">'+obj+'</span>').appendTo(entryObj);
} else if (obj.__enc__ && obj.type === 'undefined') {
$('<span class="red-ui-debug-msg-type-null">undefined</span>').appendTo(entryObj);
} else if (obj.__enc__ && (obj.type === 'number' || obj.type === 'bigint')) {
e = $('<span class="red-ui-debug-msg-type-number red-ui-debug-msg-object-header"></span>').text(obj.data).appendTo(entryObj);
} else if (typeHint === "regexp" || (obj.__enc__ && obj.type === 'regexp')) {
e = $('<span class="red-ui-debug-msg-type-string red-ui-debug-msg-object-header"></span>').text((typeof obj === "string")?obj:obj.data).appendTo(entryObj);
} else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) {
e = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-header"></span>').text("function").appendTo(entryObj);
} else if (typeHint === "internal" || (obj.__enc__ && obj.type === 'internal')) {
e = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-header"></span>').text("[internal]").appendTo(entryObj);
} else if (typeof obj === 'string') {
if (/[\t\n\r]/.test(obj)) {
element.addClass('collapsed');
$('<i class="fa fa-caret-right red-ui-debug-msg-object-handle"></i> ').prependTo(header);
makeExpandable(header, function() {
$('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-type-header"></span>').text(typeHint||'string').appendTo(header);
var row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(element);
$('<pre class="red-ui-debug-msg-type-string"></pre>').text(obj).appendTo(row);
},function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey,expandPaths));
}
e = $('<span class="red-ui-debug-msg-type-string red-ui-debug-msg-object-header"></span>').html('"'+formatString(sanitize(obj))+'"').appendTo(entryObj);
if (/^#[0-9a-f]{6}$/i.test(obj)) {
$('<span class="red-ui-debug-msg-type-string-swatch"></span>').css('backgroundColor',obj).appendTo(e);
}
} else if (typeof obj === 'number') {
e = $('<span class="red-ui-debug-msg-type-number"></span>').appendTo(entryObj);
if (Number.isInteger(obj) && (obj >= 0)) { // if it's a +ve integer
e.addClass("red-ui-debug-msg-type-number-toggle");
e.on("click", function(evt) {
evt.preventDefault();
formatNumber($(this), obj, sourceId, path, true);
});
}
formatNumber(e,obj,sourceId,path,false,typeHint==='hex'?'hex':undefined);
} else if (isArray) {
element.addClass('collapsed');
var originalLength = obj.length;
if (typeHint) {
var m = /\[(\d+)\]/.exec(typeHint);
if (m) {
originalLength = parseInt(m[1]);
}
}
var data = obj;
var type = 'array';
if (isArrayObject) {
data = obj.data;
if (originalLength === undefined) {
originalLength = data.length;
}
if (data.__enc__) {
data = data.data;
}
type = obj.type.toLowerCase();
} else if (/buffer/.test(typeHint)) {
type = 'buffer';
}
var fullLength = data.length;
if (originalLength > 0) {
$('<i class="fa fa-caret-right red-ui-debug-msg-object-handle"></i> ').prependTo(header);
var arrayRows = $('<div class="red-ui-debug-msg-array-rows"></div>').appendTo(element);
element.addClass('red-ui-debug-msg-buffer-raw');
}
if (key) {
headerHead = $('<span class="red-ui-debug-msg-type-meta"></span>').text(typeHint||(type+'['+originalLength+']')).appendTo(entryObj);
} else {
headerHead = $('<span class="red-ui-debug-msg-object-header"></span>').appendTo(entryObj);
$('<span>[ </span>').appendTo(headerHead);
var arrayLength = Math.min(originalLength,10);
for (i=0;i<arrayLength;i++) {
buildMessageSummaryValue(data[i]).appendTo(headerHead);
if (i < arrayLength-1) {
$('<span>, </span>').appendTo(headerHead);
}
}
if (originalLength > arrayLength) {
$('<span> &hellip;</span>').appendTo(headerHead);
}
if (arrayLength === 0) {
$('<span class="red-ui-debug-msg-type-meta">empty</span>').appendTo(headerHead);
}
$('<span> ]</span>').appendTo(headerHead);
}
if (originalLength > 0) {
makeExpandable(header,function() {
if (!key) {
headerHead = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-type-header"></span>').text(typeHint||(type+'['+originalLength+']')).appendTo(header);
}
if (type === 'buffer') {
var stringRow = $('<div class="red-ui-debug-msg-string-rows"></div>').appendTo(element);
var sr = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(stringRow);
var stringEncoding = "";
try {
stringEncoding = String.fromCharCode.apply(null, new Uint16Array(data))
} catch(err) {
console.log(err);
}
$('<pre class="red-ui-debug-msg-type-string"></pre>').text(stringEncoding).appendTo(sr);
var bufferOpts = $('<span class="red-ui-debug-msg-buffer-opts"></span>').appendTo(headerHead);
var switchFormat = $('<a class="red-ui-button red-ui-button-small" href="#"></a>').text('raw').appendTo(bufferOpts).on("click", function(e) {
e.preventDefault();
e.stopPropagation();
formatBuffer(element,$(this),sourceId,path,true);
});
formatBuffer(element,switchFormat,sourceId,path,false);
}
var row;
if (fullLength <= 10) {
for (i=0;i<fullLength;i++) {
row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(arrayRows);
subElements[path+"["+i+"]"] = createObjectElement(
data[i],
{
key: ""+i,
typeHint: type==='buffer'?'hex':false,
hideKey: false,
path: path+"["+i+"]",
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
} else {
for (i=0;i<fullLength;i+=10) {
var minRange = i;
row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(arrayRows);
header = $('<span></span>').appendTo(row);
$('<i class="fa fa-caret-right red-ui-debug-msg-object-handle"></i> ').appendTo(header);
makeExpandable(header, (function() {
var min = minRange;
var max = Math.min(fullLength-1,(minRange+9));
var parent = row;
return function() {
for (var i=min;i<=max;i++) {
var row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(parent);
subElements[path+"["+i+"]"] = createObjectElement(
data[i],
{
key: ""+i,
typeHint: type==='buffer'?'hex':false,
hideKey: false,
path: path+"["+i+"]",
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
}
})(),
(function() { var path = path+"["+i+"]"; return function(state) {if (ontoggle) { ontoggle(path,state);}}})(),
checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9))));
$('<span class="red-ui-debug-msg-object-key"></span>').html("["+minRange+" &hellip; "+Math.min(fullLength-1,(minRange+9))+"]").appendTo(header);
}
if (fullLength < originalLength) {
$('<div class="red-ui-debug-msg-object-entry collapsed"><span class="red-ui-debug-msg-object-key">['+fullLength+' &hellip; '+originalLength+']</span></div>').appendTo(arrayRows);
}
}
},
function(state) {if (ontoggle) { ontoggle(path,state);}},
checkExpanded(strippedKey,expandPaths));
}
} else if (typeof obj === 'object') {
element.addClass('collapsed');
var data = obj;
var type = "object";
if (data.__enc__) {
data = data.data;
type = obj.type.toLowerCase();
}
var keys = Object.keys(data);
if (key || keys.length > 0) {
$('<i class="fa fa-caret-right red-ui-debug-msg-object-handle"></i> ').prependTo(header);
makeExpandable(header, function() {
if (!key) {
$('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-type-header"></span>').text(type).appendTo(header);
}
for (i=0;i<keys.length;i++) {
var row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(element);
var newPath = path;
if (newPath !== undefined) {
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(keys[i])) {
newPath += (newPath.length > 0?".":"")+keys[i];
} else {
newPath += "[\""+keys[i].replace(/"/,"\\\"")+"\"]"
}
}
subElements[newPath] = createObjectElement(
data[keys[i]],
{
key: keys[i],
typeHint: false,
hideKey: false,
path: newPath,
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
if (keys.length === 0) {
$('<div class="red-ui-debug-msg-object-entry red-ui-debug-msg-type-meta collapsed"></div>').text("empty").appendTo(element);
}
},
function(state) {if (ontoggle) { ontoggle(path,state);}},
checkExpanded(strippedKey,expandPaths));
}
if (key) {
$('<span class="red-ui-debug-msg-type-meta"></span>').text(type).appendTo(entryObj);
} else {
headerHead = $('<span class="red-ui-debug-msg-object-header"></span>').appendTo(entryObj);
$('<span>{ </span>').appendTo(headerHead);
var keysLength = Math.min(keys.length,5);
for (i=0;i<keysLength;i++) {
$('<span class="red-ui-debug-msg-object-key"></span>').text(keys[i]).appendTo(headerHead);
$('<span>: </span>').appendTo(headerHead);
buildMessageSummaryValue(data[keys[i]]).appendTo(headerHead);
if (i < keysLength-1) {
$('<span>, </span>').appendTo(headerHead);
}
}
if (keys.length > keysLength) {
$('<span> &hellip;</span>').appendTo(headerHead);
}
if (keysLength === 0) {
$('<span class="red-ui-debug-msg-type-meta">empty</span>').appendTo(headerHead);
}
$('<span> }</span>').appendTo(headerHead);
}
} else {
$('<span class="red-ui-debug-msg-type-other"></span>').text(""+obj).appendTo(entryObj);
}
if (exposeApi) {
element.prop('expand', function() { return function(targetPath, state) {
if (path === targetPath) {
if (header.prop('toggle')) {
header.prop('toggle')(state);
}
} else if (subElements[targetPath] && subElements[targetPath].prop('expand') ) {
subElements[targetPath].prop('expand')(targetPath,state);
} else {
for (var p in subElements) {
if (subElements.hasOwnProperty(p)) {
if (targetPath.indexOf(p) === 0) {
if (subElements[p].prop('expand') ) {
subElements[p].prop('expand')(targetPath,state);
}
break;
}
}
}
}
}});
}
return element;
}
function createError(code, message) {
var e = new Error(message);
e.code = code;
return e;
}
function normalisePropertyExpression(str,msg) {
// This must be kept in sync with validatePropertyExpression
// in editor/js/ui/utils.js
var length = str.length;
if (length === 0) {
throw createError("INVALID_EXPR","Invalid property expression: zero-length");
}
var parts = [];
var start = 0;
var inString = false;
var inBox = false;
var boxExpression = false;
var quoteChar;
var v;
for (var i=0;i<length;i++) {
var c = str[i];
if (!inString) {
if (c === "'" || c === '"') {
if (i != start) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
}
inString = true;
quoteChar = c;
start = i+1;
} else if (c === '.') {
if (i===0) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected . at position 0");
}
if (start != i) {
v = str.substring(start,i);
if (/^\d+$/.test(v)) {
parts.push(parseInt(v));
} else {
parts.push(v);
}
}
if (i===length-1) {
throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
}
// Next char is first char of an identifier: a-z 0-9 $ _
if (!/[a-z0-9\$\_]/i.test(str[i+1])) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
}
start = i+1;
} else if (c === '[') {
if (i === 0) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
}
if (start != i) {
parts.push(str.substring(start,i));
}
if (i===length-1) {
throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
}
// Start of a new expression. If it starts with msg it is a nested expression
// Need to scan ahead to find the closing bracket
if (/^msg[.\[]/.test(str.substring(i+1))) {
var depth = 1;
var inLocalString = false;
var localStringQuote;
for (var j=i+1;j<length;j++) {
if (/["']/.test(str[j])) {
if (inLocalString) {
if (str[j] === localStringQuote) {
inLocalString = false
}
} else {
inLocalString = true;
localStringQuote = str[j]
}
}
if (str[j] === '[') {
depth++;
} else if (str[j] === ']') {
depth--;
}
if (depth === 0) {
try {
if (msg) {
parts.push(getMessageProperty(msg, str.substring(i+1,j)))
} else {
parts.push(normalisePropertyExpression(str.substring(i+1,j), msg));
}
inBox = false;
i = j;
start = j+1;
break;
} catch(err) {
throw createError("INVALID_EXPR","Invalid expression started at position "+(i+1))
}
}
}
if (depth > 0) {
throw createError("INVALID_EXPR","Invalid property expression: unmatched '[' at position "+i);
}
continue;
} else if (!/["'\d]/.test(str[i+1])) {
// Next char is either a quote or a number
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
}
start = i+1;
inBox = true;
} else if (c === ']') {
if (!inBox) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
}
if (start != i) {
v = str.substring(start,i);
if (/^\d+$/.test(v)) {
parts.push(parseInt(v));
} else {
throw createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
}
}
start = i+1;
inBox = false;
} else if (c === ' ') {
throw createError("INVALID_EXPR","Invalid property expression: unexpected ' ' at position "+i);
}
} else {
if (c === quoteChar) {
if (i-start === 0) {
throw createError("INVALID_EXPR","Invalid property expression: zero-length string at position "+start);
}
parts.push(str.substring(start,i));
// If inBox, next char must be a ]. Otherwise it may be [ or .
if (inBox && !/\]/.test(str[i+1])) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
} else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1));
}
start = i+1;
inString = false;
}
}
}
if (inBox || inString) {
throw new createError("INVALID_EXPR","Invalid property expression: unterminated expression");
}
if (start < length) {
parts.push(str.substring(start));
}
return parts;
}
function validatePropertyExpression(str) {
try {
var parts = normalisePropertyExpression(str);
return true;
} catch(err) {
return false;
}
}
function getMessageProperty(msg,expr) {
var result = null;
var msgPropParts;
if (typeof expr === 'string') {
if (expr.indexOf('msg.')===0) {
expr = expr.substring(4);
}
msgPropParts = normalisePropertyExpression(expr);
} else {
msgPropParts = expr;
}
var m;
msgPropParts.reduce(function(obj, key) {
result = (typeof obj[key] !== "undefined" ? obj[key] : undefined);
if (result === undefined && obj.hasOwnProperty('type') && obj.hasOwnProperty('data')&& obj.hasOwnProperty('length')) {
result = (typeof obj.data[key] !== "undefined" ? obj.data[key] : undefined);
}
return result;
}, msg);
return result;
}
function setMessageProperty(msg,prop,value,createMissing) {
if (typeof createMissing === 'undefined') {
createMissing = (typeof value !== 'undefined');
}
if (prop.indexOf('msg.')===0) {
prop = prop.substring(4);
}
var msgPropParts = normalisePropertyExpression(prop);
var depth = 0;
var length = msgPropParts.length;
var obj = msg;
var key;
for (var i=0;i<length-1;i++) {
key = msgPropParts[i];
if (typeof key === 'string' || (typeof key === 'number' && !Array.isArray(obj))) {
if (obj.hasOwnProperty(key)) {
obj = obj[key];
} else if (createMissing) {
if (typeof msgPropParts[i+1] === 'string') {
obj[key] = {};
} else {
obj[key] = [];
}
obj = obj[key];
} else {
return null;
}
} else if (typeof key === 'number') {
// obj is an array
if (obj[key] === undefined) {
if (createMissing) {
if (typeof msgPropParts[i+1] === 'string') {
obj[key] = {};
} else {
obj[key] = [];
}
obj = obj[key];
} else {
return null;
}
} else {
obj = obj[key];
}
}
}
key = msgPropParts[length-1];
if (typeof value === "undefined") {
if (typeof key === 'number' && Array.isArray(obj)) {
obj.splice(key,1);
} else {
delete obj[key]
}
} else {
obj[key] = value;
}
}
function separateIconPath(icon) {
var result = {module: "", file: ""};
if (icon) {
var index = icon.indexOf(RED.settings.apiRootUrl+'icons/');
if (index === 0) {
icon = icon.substring((RED.settings.apiRootUrl+'icons/').length);
}
var match = /^((?:@[^/]+\/)?[^/]+)\/(.*)$/.exec(icon);
if (match) {
result.module = match[1];
result.file = match[2];
} else {
result.file = icon;
}
}
return result;
}
function getDefaultNodeIcon(def,node) {
def = def || {};
var icon_url;
if (node && node.type === "subflow") {
icon_url = "node-red/subflow.svg";
} else if (typeof def.icon === "function") {
try {
icon_url = def.icon.call(node);
} catch(err) {
console.log("Definition error: "+def.type+".icon",err);
icon_url = "arrow-in.svg";
}
} else {
icon_url = def.icon;
}
var iconPath = separateIconPath(icon_url);
if (!iconPath.module) {
if (def.set) {
iconPath.module = def.set.module;
} else {
// Handle subflow instance nodes that don't have def.set
iconPath.module = "node-red";
}
}
return iconPath;
}
function isIconExists(iconPath) {
var iconSets = RED.nodes.getIconSets();
var iconFileList = iconSets[iconPath.module];
if (iconFileList && iconFileList.indexOf(iconPath.file) !== -1) {
return true;
} else {
return false;
}
}
function getNodeIcon(def,node) {
def = def || {};
if (node && node.type === '_selection_') {
return "font-awesome/fa-object-ungroup";
} else if (node && node.type === 'group') {
return "font-awesome/fa-object-group"
} else if ((node && node.type === 'junction') || (def.type === "junction") ) {
return "font-awesome/fa-circle-o"
} else if (def.category === 'config') {
return RED.settings.apiRootUrl+"icons/node-red/cog.svg"
} else if ((node && /^_action_:/.test(node.type)) || /^_action_:/.test(def.type)) {
return "font-awesome/fa-cogs"
} else if (node && node.type === 'tab') {
return "red-ui-icons/red-ui-icons-flow"
// return RED.settings.apiRootUrl+"images/subflow_tab.svg"
} else if (node && node.type === 'unknown') {
return RED.settings.apiRootUrl+"icons/node-red/alert.svg"
} else if (node && node.icon) {
var iconPath = separateIconPath(node.icon);
if (isIconExists(iconPath)) {
if (iconPath.module === "font-awesome") {
return node.icon;
} else {
return RED.settings.apiRootUrl+"icons/" + node.icon;
}
} else if (iconPath.module !== "font-awesome" && /.png$/i.test(iconPath.file)) {
iconPath.file = iconPath.file.replace(/.png$/,".svg");
if (isIconExists(iconPath)) {
return RED.settings.apiRootUrl+"icons/" + node.icon.replace(/.png$/,".svg");
}
}
}
var iconPath = getDefaultNodeIcon(def, node);
if (isIconExists(iconPath)) {
if (iconPath.module === "font-awesome") {
return iconPath.module+"/"+iconPath.file;
} else {
return RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file;
}
}
if (/.png$/i.test(iconPath.file)) {
var originalFile = iconPath.file;
iconPath.file = iconPath.file.replace(/.png$/,".svg");
if (isIconExists(iconPath)) {
return RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file;
}
iconPath.file = originalFile;
}
// This could be a non-core node trying to use a core icon.
iconPath.module = 'node-red';
if (isIconExists(iconPath)) {
return RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file;
}
if (/.png$/i.test(iconPath.file)) {
iconPath.file = iconPath.file.replace(/.png$/,".svg");
if (isIconExists(iconPath)) {
return RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file;
}
}
if (def.category === 'subflows') {
return RED.settings.apiRootUrl+"icons/node-red/subflow.svg";
}
return RED.settings.apiRootUrl+"icons/node-red/arrow-in.svg";
}
function getNodeLabel(node,defaultLabel) {
defaultLabel = defaultLabel||"";
var l;
if (node.type === 'tab') {
l = node.label || defaultLabel
} else if (node.type === 'group') {
l = node.name || defaultLabel
} else if (node.type === 'junction') {
l = 'junction'
} else {
l = node._def.label;
try {
l = (typeof l === "function" ? l.call(node) : l)||defaultLabel;
} catch(err) {
console.log("Definition error: "+node.type+".label",err);
l = defaultLabel;
}
}
return RED.text.bidi.enforceTextDirectionWithUCC(l);
}
function getPaletteLabel(nodeType, def) {
var label = nodeType;
if (typeof def.paletteLabel !== "undefined") {
try {
label = (typeof def.paletteLabel === "function" ? def.paletteLabel.call(def) : def.paletteLabel)||"";
} catch(err) {
console.log("Definition error: "+nodeType+".paletteLabel",err);
}
}
return label
}
var nodeColorCache = {};
function clearNodeColorCache() {
nodeColorCache = {};
}
function getNodeColor(type, def) {
def = def || {};
var result = def.color;
var paletteTheme = RED.settings.theme('palette.theme') || [];
if (paletteTheme.length > 0) {
if (!nodeColorCache.hasOwnProperty(type)) {
nodeColorCache[type] = def.color;
var l = paletteTheme.length;
for (var i = 0; i < l; i++ ){
var themeRule = paletteTheme[i];
if (themeRule.hasOwnProperty('category')) {
if (!themeRule.hasOwnProperty('_category')) {
themeRule._category = new RegExp(themeRule.category);
}
if (!themeRule._category.test(def.category)) {
continue;
}
}
if (themeRule.hasOwnProperty('type')) {
if (!themeRule.hasOwnProperty('_type')) {
themeRule._type = new RegExp(themeRule.type);
}
if (!themeRule._type.test(type)) {
continue;
}
}
nodeColorCache[type] = themeRule.color || def.color;
break;
}
}
result = nodeColorCache[type];
}
if (result) {
return result;
} else {
return "#ddd";
}
}
function addSpinnerOverlay(container,contain) {
var spinner = $('<div class="red-ui-component-spinner "><img src="red/images/spin.svg"/></div>').appendTo(container);
if (contain) {
spinner.addClass('red-ui-component-spinner-contain');
}
return spinner;
}
function decodeObject(payload,format) {
if ((format === 'number') && (payload === "NaN")) {
payload = Number.NaN;
} else if ((format === 'number') && (payload === "Infinity")) {
payload = Infinity;
} else if ((format === 'number') && (payload === "-Infinity")) {
payload = -Infinity;
} else if (format === 'Object' || /^(array|set|map)/.test(format) || format === 'boolean' || format === 'number' ) {
payload = JSON.parse(payload);
} else if (/error/i.test(format)) {
payload = JSON.parse(payload);
payload = (payload.name?payload.name+": ":"")+payload.message;
} else if (format === 'null') {
payload = null;
} else if (format === 'undefined') {
payload = undefined;
} else if (/^buffer/.test(format)) {
var buffer = payload;
payload = [];
for (var c = 0; c < buffer.length; c += 2) {
payload.push(parseInt(buffer.substr(c, 2), 16));
}
}
return payload;
}
function parseContextKey(key, defaultStore) {
var parts = {};
var m = /^#:\((\S+?)\)::(.*)$/.exec(key);
if (m) {
parts.store = m[1];
parts.key = m[2];
} else {
parts.key = key;
if (defaultStore) {
parts.store = defaultStore;
} else if (RED.settings.context) {
parts.store = RED.settings.context.default;
}
}
return parts;
}
/**
* Create or update an icon element and append it to iconContainer.
* @param iconUrl - Url of icon.
* @param iconContainer - Icon container element with red-ui-palette-icon-container class.
* @param isLarge - Whether the icon size is large.
*/
function createIconElement(iconUrl, iconContainer, isLarge) {
// Removes the previous icon when icon was changed.
var iconElement = iconContainer.find(".red-ui-palette-icon");
if (iconElement.length !== 0) {
iconElement.remove();
}
var faIconElement = iconContainer.find("i");
if (faIconElement.length !== 0) {
faIconElement.remove();
}
// Show either icon image or font-awesome icon
var iconPath = separateIconPath(iconUrl);
if (iconPath.module === "font-awesome") {
var fontAwesomeUnicode = RED.nodes.fontAwesome.getIconUnicode(iconPath.file);
if (fontAwesomeUnicode) {
var faIconElement = $('<i/>').appendTo(iconContainer);
var faLarge = isLarge ? "fa-lg " : "";
faIconElement.addClass("red-ui-palette-icon-fa fa fa-fw " + faLarge + iconPath.file);
return;
}
// If the specified name is not defined in font-awesome, show arrow-in icon.
iconUrl = RED.settings.apiRootUrl+"icons/node-red/arrow-in.svg"
} else if (iconPath.module === "red-ui-icons") {
var redIconElement = $('<i/>').appendTo(iconContainer);
redIconElement.addClass("red-ui-palette-icon red-ui-icons " + iconPath.file);
return;
}
var imageIconElement = $('<div/>',{class:"red-ui-palette-icon"}).appendTo(iconContainer);
imageIconElement.css("backgroundImage", "url("+iconUrl+")");
}
function createNodeIcon(node, includeLabel) {
var container = $('<span class="red-ui-node-icon-container">');
var def = node._def;
var nodeDiv = $('<div>',{class:"red-ui-node-icon"})
if (node.type === "_selection_") {
nodeDiv.addClass("red-ui-palette-icon-selection");
} else if (node.type === "group") {
nodeDiv.addClass("red-ui-palette-icon-group");
} else if (node.type === "junction") {
nodeDiv.addClass("red-ui-palette-icon-junction");
} else if (node.type === 'tab') {
nodeDiv.addClass("red-ui-palette-icon-flow");
} else {
var colour = RED.utils.getNodeColor(node.type,def);
// if (node.type === 'tab') {
// colour = "#C0DEED";
// }
nodeDiv.css('backgroundColor',colour);
var borderColor = getDarkerColor(colour);
if (borderColor !== colour) {
nodeDiv.css('border-color',borderColor)
}
}
var icon_url = RED.utils.getNodeIcon(def,node);
RED.utils.createIconElement(icon_url, nodeDiv, true);
nodeDiv.appendTo(container);
if (includeLabel) {
var labelText = RED.utils.getNodeLabel(node,node.name || (node.type+": "+node.id));
var label = $('<div>',{class:"red-ui-node-label"}).appendTo(container);
if (labelText) {
label.text(labelText)
} else {
label.html("&nbsp;")
}
}
return container;
}
function getDarkerColor(c) {
var r,g,b;
if (/^#[a-f0-9]{6}$/i.test(c)) {
r = parseInt(c.substring(1, 3), 16);
g = parseInt(c.substring(3, 5), 16);
b = parseInt(c.substring(5, 7), 16);
} else if (/^#[a-f0-9]{3}$/i.test(c)) {
r = parseInt(c.substring(1, 2)+c.substring(1, 2), 16);
g = parseInt(c.substring(2, 3)+c.substring(2, 3), 16);
b = parseInt(c.substring(3, 4)+c.substring(3, 4), 16);
} else {
return c;
}
var l = 0.3 * r/255 + 0.59 * g/255 + 0.11 * b/255 ;
r = Math.max(0,r-50);
g = Math.max(0,g-50);
b = Math.max(0,b-50);
var s = ((r<<16) + (g<<8) + b).toString(16);
return '#'+'000000'.slice(0, 6-s.length)+s;
}
function parseModuleList(list) {
list = list || ["*"];
return list.map(function(rule) {
var m = /^(.+?)(?:@(.*))?$/.exec(rule);
var wildcardPos = m[1].indexOf("*");
wildcardPos = wildcardPos===-1?Infinity:wildcardPos;
return {
module: new RegExp("^"+m[1].replace(/\*/g,".*")+"$"),
version: m[2],
wildcardPos: wildcardPos
}
})
}
function checkAgainstList(module,version,list) {
for (var i=0;i<list.length;i++) {
var rule = list[i];
if (rule.module.test(module)) {
// Without a full semver library in the editor,
// we skip the version check.
// Not ideal - but will get caught in the runtime
// if the user tries to install.
return rule;
}
}
}
function checkModuleAllowed(module,version,allowList,denyList) {
if (!allowList && !denyList) {
// Default to allow
return true;
}
if (allowList.length === 0 && denyList.length === 0) {
return true;
}
var allowedRule = checkAgainstList(module,version,allowList);
var deniedRule = checkAgainstList(module,version,denyList);
// console.log("A",allowedRule)
// console.log("D",deniedRule)
if (allowedRule && !deniedRule) {
return true;
}
if (!allowedRule && deniedRule) {
return false;
}
if (!allowedRule && !deniedRule) {
return true;
}
if (allowedRule.wildcardPos !== deniedRule.wildcardPos) {
return allowedRule.wildcardPos > deniedRule.wildcardPos
} else {
// First wildcard in same position.
// Go with the longer matching rule. This isn't going to be 100%
// right, but we are deep into edge cases at this point.
return allowedRule.module.toString().length > deniedRule.module.toString().length
}
return false;
}
function getBrowserInfo() {
var r = {}
try {
var ua = navigator.userAgent;
r.ua = ua;
r.browser = /Edge\/\d+/.test(ua) ? 'ed' : /MSIE 9/.test(ua) ? 'ie9' : /MSIE 10/.test(ua) ? 'ie10' : /MSIE 11/.test(ua) ? 'ie11' : /MSIE\s\d/.test(ua) ? 'ie?' : /rv\:11/.test(ua) ? 'ie11' : /Firefox\W\d/.test(ua) ? 'ff' : /Chrom(e|ium)\W\d|CriOS\W\d/.test(ua) ? 'gc' : /\bSafari\W\d/.test(ua) ? 'sa' : /\bOpera\W\d/.test(ua) ? 'op' : /\bOPR\W\d/i.test(ua) ? 'op' : typeof MSPointerEvent !== 'undefined' ? 'ie?' : '';
r.os = /Windows NT 10/.test(ua) ? "win10" : /Windows NT 6\.0/.test(ua) ? "winvista" : /Windows NT 6\.1/.test(ua) ? "win7" : /Windows NT 6\.\d/.test(ua) ? "win8" : /Windows NT 5\.1/.test(ua) ? "winxp" : /Windows NT [1-5]\./.test(ua) ? "winnt" : /Mac/.test(ua) ? "mac" : /Linux/.test(ua) ? "linux" : /X11/.test(ua) ? "nix" : "";
r.touch = 'ontouchstart' in document.documentElement;
r.mobile = /IEMobile|Windows Phone|Lumia/i.test(ua) ? 'w' : /iPhone|iP[oa]d/.test(ua) ? 'i' : /Android/.test(ua) ? 'a' : /BlackBerry|PlayBook|BB10/.test(ua) ? 'b' : /Mobile Safari/.test(ua) ? 's' : /webOS|Mobile|Tablet|Opera Mini|\bCrMo\/|Opera Mobi/i.test(ua) ? 1 : 0;
r.tablet = /Tablet|iPad/i.test(ua);
r.ie = /MSIE \d|Trident.*rv:/.test(navigator.userAgent);
r.android = /android/i.test(navigator.userAgent);
} catch (error) { }
return r;
}
return {
createObjectElement: createObjectElement,
getMessageProperty: getMessageProperty,
setMessageProperty: setMessageProperty,
normalisePropertyExpression: normalisePropertyExpression,
validatePropertyExpression: validatePropertyExpression,
separateIconPath: separateIconPath,
getDefaultNodeIcon: getDefaultNodeIcon,
getNodeIcon: getNodeIcon,
getNodeLabel: getNodeLabel,
getNodeColor: getNodeColor,
getPaletteLabel: getPaletteLabel,
clearNodeColorCache: clearNodeColorCache,
addSpinnerOverlay: addSpinnerOverlay,
decodeObject: decodeObject,
parseContextKey: parseContextKey,
createIconElement: createIconElement,
sanitize: sanitize,
renderMarkdown: renderMarkdown,
createNodeIcon: createNodeIcon,
getDarkerColor: getDarkerColor,
parseModuleList: parseModuleList,
checkModuleAllowed: checkModuleAllowed,
getBrowserInfo: getBrowserInfo
}
})();