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;
        const enablePinning = options.enablePinning
        const expandLeafNodes = options.expandLeafNodes;
        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, enablePinning);
        }
        if (!key) {
            element.addClass("red-ui-debug-msg-top-level");
            if (sourceId && !expandPaths) {
                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, { expandLeafNodes }));
            }
            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);
            }

            let n = RED.nodes.node(obj) ?? RED.nodes.workspace(obj);
            if (n) {
                if (options.nodeSelector && "function" == typeof options.nodeSelector) {
                    e.css('cursor', 'pointer').on("click", function(evt) {
                        evt.preventDefault();
                        options.nodeSelector(n.id);
                    })
                }
            }

        } 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,
                                    rootPath,
                                    expandPaths,
                                    expandLeafNodes,
                                    ontoggle,
                                    exposeApi,
                                    // tools: tools // Do not pass tools down as we
                                                    // keep them attached to the top-level header
                                    nodeSelector: options.nodeSelector,
                                    enablePinning
                                }
                            ).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,
                                                rootPath,
                                                expandPaths,
                                                expandLeafNodes,
                                                ontoggle,
                                                exposeApi,
                                                // tools: tools // Do not pass tools down as we
                                                                // keep them attached to the top-level header
                                                nodeSelector: options.nodeSelector,
                                                enablePinning
                                            }
                                        ).appendTo(row);
                                    }
                                }
                            })(),
                            (function() { var path = path+"["+i+"]"; return function(state) {if (ontoggle) { ontoggle(path,state);}}})(),
                            checkExpanded(strippedKey,expandPaths,{ minRange, maxRange: Math.min(fullLength-1,(minRange+9)), expandLeafNodes}));
                            $('<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, { expandLeafNodes }));
            }
        } 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,
                                rootPath,
                                expandPaths,
                                expandLeafNodes,
                                ontoggle,
                                exposeApi,
                                // tools: tools // Do not pass tools down as we
                                                // keep them attached to the top-level header
                                nodeSelector: options.nodeSelector,
                                enablePinning
                            }
                        ).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, { expandLeafNodes }));
            }
            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;
    }
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