Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[PDF.js] Set light and dark theme manually

The new PDF.js viewer design Photon supports a light and a dark mode. By default, the theme is set automatically. To overwrite this, you can set the viewerCssTheme property.

I’ve embedded my PDF viewer in an iFrame like this:

<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>

By setting the viewerCssTheme property, you are able to change the PDF viewer theme. It can be modified using the PDFViewerApplicationOptions.set() function and there are three possible values. The property has to be set before the Viewer is initialized. To archive this, you can listen to the event webviewerloaded (read more about it here). In my case, I also had to call _forceCssTheme() after that.

                document.addEventListener("webviewerloaded", async () => {
                    let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
                    // https://github.com/mozilla/pdf.js/blob/master/web/app_options.js
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("viewerCssTheme", 1)       // 0=automatic theme, 1=light theme, 2=dark theme 
                    pdfViewerIFrame.contentWindow.PDFViewerApplication._forceCssTheme()
                })

[PDF.js] Allow PDF documents to disable copying in the viewer

With pull request 11789, the PDF Viewer respects the PDF property Content Copying, if the viewer option enablePermissions is set to true.

I’ve embedded my viewer in an iFrame like this:

<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>

The enablePermissions property can be modified using the PDFViewerApplicationOptions.set() function. The property has to be set before the Viewer is initialized. To archive this, you can listen to the event webviewerloaded (read more about it here).

                document.addEventListener("webviewerloaded", async () => {
                    let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
                    //https://github.com/mozilla/pdf.js/blob/master/web/app_options.js
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("enablePermissions", true) //allow PDF documents to disable copying in the viewer
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("defaultUrl", "") //prevent loading default pdf
                    }
                })

[JavaScript] Find specific object from an array of nested objects

I was again working on a task for my hierarchical tree structure (see previous post here). This time I needed to find a specific object with a specific property value.

So here again my nested array:

const aTreeData = [{
    "NodeId": 1,
    "HierarchyLevel": 1,
    "type": "folder",
    "nodes": [{
        "NodeId": 2,
        "HierarchyLevel": 2,
        "type": "folder",
        "nodes": [{
          "NodeId": 3,
          "HierarchyLevel": 3,
          "type": "category"
        }]
      },
      {
        "NodeId": 4,
        "HierarchyLevel": 2,
        "type": "category",
        "nodes": [{
          "NodeId": 5,
          "HierarchyLevel": 3,
          "type": "file"
        }]
      }
    ]
  },
  {
    "NodeId": 6,
    "HierarchyLevel": 1,
    "type": "folder",
    "nodes": [{
      "NodeId": 7,
      "HierarchyLevel": 2,
      "type": "category"
    }]
  }
]

I needed a function that was able to find any object by providing only the NodeId value. Fortunately I stumbled accross some excellent code snippet by Scott Sauyet which is exactly doing what I needed:

https://stackoverflow.com/questions/68559392/find-object-from-an-array-of-nested-objects-by-key-in-javascript

I just had to change the properties to match my objects and it was working!

const findNodeInTreeData = (aTreeData, nodeId) => {
  const deepFind = pred => ([x, ...xs] = []) =>
    x && (pred(x) ? x : deepFind(pred)(x.nodes) || deepFind(pred)(xs))

  const findByNodeId = id => obj =>
    deepFind(o => o.NodeId == id)([obj])

  return findByNodeId(nodeId)(aTreeData[0])
}

console.log(findNodeInTreeData(aTreeData, 5))

[SuccessFactors] Create JavaScript Date-Object from DateTimeOffset

The SuccessFactors oData v2 API is returning timestamps in Unix Epoch format (unix-style milliseconds since 1/1/1970).

Many timestamp fields are of type Edm.Int64. When receiving the milliseconds as Integer, you can directly create a date-object of it using Date(1658237847).

But some timestamps are of type Edm.DateTimeOffset, i.e.: "createdDate": "/Date(1652252620000+0000)/".
When binding a timestamp property with an ODataModel, the internal lib datajs will convert the /Date(...)/ value to a standard JS date-object.

But in my case I manually had to convert the timestamp and this is the shortest way I found to convert the epoch string into a JS date-object.

// SF epoch date string
const SFdateString = '/Date(1652252620000+0000)/' 

// remove the '/' on both sides and create the date object
const oDate = eval('new ' + SFdateString .slice(1, -1))

console.log(typeof oDate )
console.log(oDate )

const oDateTimeFormat = sap.ui.core.format.DateFormat.getDateTimeInstance({
          pattern: "YYYY-MM-dd HH:mm"
})
return oDateTimeFormat.format(oDate)


[JavaScript] Itereate through array of nested objects and delete specific objects

[
    {
        "NodeId": 1,
        "HierarchyLevel": 1,
        "type": "folder",
        "nodes": [
            {
                "NodeId": 2,
                "HierarchyLevel": 2,
                "type": "folder",
                "nodes": [
                    {
                        "NodeId": 3,
                        "HierarchyLevel": 3,
                        "type": "category"
                    }
                ]
            },
            {
                "NodeId": 4,
                "HierarchyLevel": 2,
                "type": "category",
                "nodes": [
                    {
                        "NodeId": 5,
                        "HierarchyLevel": 3,
                        "type": "file"
                    }
                ]
            }
        ]
    },
    {
        "NodeId": 6,
        "HierarchyLevel": 1,
        "type": "folder",
        "nodes": [
            {
                "NodeId": 7,
                "HierarchyLevel": 2,
                "type": "category"
            }
        ]
    }
]

My task was to get rid of every Node which has no subnodes of type file at the last level of the hierachy. So for this example the result I needed was an array containing only the nodes 1,2,4,5.
Of course in reality the nested structure was way more complex. My approach was a recursive function which checks every element’s type and nodes length property and calls itself if there are any subnodes. Also it is recommended to loop backwards through the array while deleting from it.

            
            const removeEmptyNodes = nodes => {
                for (let i = nodes.length - 1; i > -1; i--) {
                    const n = nodes[i]
                    //call function recursive to go deeper through the nested structure
                    if (n.nodes) removeEmptyNodes(n.nodes)                        
                    //remove element if it's not a file and has no subnodes
                    if (n.type !== 'file' && (!n.nodes || n.nodes.length === 0)) nodes.splice(i, 1)
                }
            }

            // nodes contains the array data from above
            removeEmptyNodes(nodes)

[JavaScript] Copy an array of objects to a new array without any object references

The user tim-montague posted a great overview on the different deep copy techniques on stackoverflow:

In my case I had an array of objects which I wanted to copy without reference, therefore I used the option in the middle.

const copy = JSON.parse(JSON.stringify(myArray))

# Another way using ES6 Syntax
const copy = myArray.map(object => ({ ...object }))