gen-bookmark-page - Generates a bookmark web page from a folder in the Chrome bookmarks bar

#!/usr/bin/env bash
help_text="
NAME
  gen-bookmark-page - generates a bookmark web page from a folder in the Chrome bookmarks bar.

USAGE
  gen-bookmark-page [options]

OPTIONS
  -h|--help
    Show help text.

DESCRIPTION
  Generates a bookmark web page from a folder in the Chrome bookmarks bar.

AUTHOR
  mjnurse.github.io - 2022
"
help_line="Generates a bookmark web page from a folder in the Chrome bookmarks bar"
web_desc_line="Generates a bookmark web page from a folder in the Chrome bookmarks bar"

try="Try ${0##*/} -h for more information"
tmp="${help_text##*USAGE}"
usage="$(echo Usage: ${tmp%%OPTIONS*})"

while [[ "$1" != "" ]]; do
  case $1 in 
    -h|--help)
      echo "$help_text"
      exit
      ;;
    *)
      echo "${usage}"
      echo "${try}"
      exit 1
      ;;
  esac 
  shift
done 

winroot="$(echo $MJNWINROOT | sed 's/\//\\/g; s/\\c/C:/')"

bm_page="$MJNWINROOT/MJN/bookmarks.html"
bm_file="/c/Users/MartinNurse/AppData/Local/Google/Chrome/User*Data/Default/Bookmarks"
tmp_file="/tmp/bm.tmp"

echo '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<head>
<TITLE>Bookmarks</TITLE>

<link rel="icon" type="image/x-icon" href="'"$winroot"'\Pictures\Star.png">

<style>
  h3, p {
    font-family: Arial, Helvetica, sans-serif;
  }
  p {
    font-size: 12pt;
  }
  a {
    color: #155799;
    text-decoration: none;
  }
  a:hover {
    text-decoration: underline;
  }
  h3 {
    font-size: 20pt;
  }
  table {
    margin-left:auto;
    margin-right:auto;
  }
  table td  {
    vertical-align: top;
    width: 250px;
    border: 0px solid black;
  }
  .input {
    width: 300px;
    margin: 0 auto;
  }
  input {
    font-size: 12pt;
    margin: 10px;
  }
</style>
</head>

<body scroll="no" style="overflow: hidden">

<div>
<div class="input">
  <input type="text" id="search" onkeyup="search()" style="visibility: hidden">
</div>
<div id="links">
</div>

<p style="text-align: center; color: #BBBBBB; font-size: 12px">Last Updated: '$(date)'</p>

</div>
<script>
  const l=[' > "$bm_page"

cat $bm_file | jq '.roots.bookmark_bar.children[] |
  select(.name == "Page").children[] | .name' > $tmp_file

let c=0
for f in $(cat $tmp_file | sed 's/"//g'); do
  if [[ $c != 0 ]]; then
    echo "{c:$c}," >> "$bm_page"
  fi
  let c=c+1
  echo '{s:"'$f'"},' >> "$bm_page"

  cat $bm_file | jq '.roots.bookmark_bar.children[] | 
    select(.name == "Page").children[] | 
    select (.name == "'$f'").children[] | 
    "{t:#" + .name + "#, h:#" + .url + "#},"' | sed 's/"//g; s/#/"/g' >> "$bm_page"
done

rm -f $tmp_file

 echo '  ]
  let lDiv=document.getElementById("links");
  let h="<table><tr><td>";
  let s="";
  let pos=1
  for (i in l) {
    const lk=l[i];
    if (lk.c) {
      h+= "</td><td>";
    }
    if (lk.s && s!=lk.s) {
      s=lk.s;
      h+="<h3>"+s+"</h3>"
    }
    if (lk.h) {
      h += "<p><a id=\"item"+pos+"\" href=\""+lk.h+"\" target=\"_blank\">"+lk.t+" ("+pos+")</a></p>";
      pos++;
    }
  }
  lDiv.innerHTML = h + "</td></tr></table>";

  numTyped = "";
  maxNum=pos-1;
  lastKeyPress = Date.now();

  function highlightListItems() {
    try {
      document.getElementById("item"+numTyped).style.background = "lightgrey";
    } catch { }
    try {
      for (i=1; i<100; i++) {
        if (i != numTyped ) {
          document.getElementById("item"+i).style.background = "transparent";
          document.getElementById("item"+i).style.visibility = "visible";
        }
      }
    } catch { }
  }

  function addKeyListener() {
    document.addEventListener("keyup", function() {
      if (event.code == "Escape") {
        document.getElementById("search").blur();
        document.getElementById("search").value = "";
        document.getElementById("search").style.visibility = "hidden";
        for (let i = 1; i <= maxNum; i++) {
          const item = document.getElementById("item"+i);
          item.style.background = "transparent";
          item.style.color = "#155799";
        }
      }
      if (document.activeElement.tagName == "INPUT" ||
          document.activeElement.tagName == "TEXTAREA") {
        return;
      }

      if (event.ctrlKey) {
        return;
      }
      switch (event.code) {
        case "Slash":
          document.getElementById("search").style.visibility = "visible";
          document.getElementById("search").focus();
          document.getElementById("search").value = "";
          break;
        case "Backspace":
        case "Delete":
          numTyped = numTyped.slice(0, -1);
          highlightListItems();
          break;
        case "Escape":
          document.getElementById("search").blur();
          document.getElementById("search").style.visibility = "hidden";
          numTyped = "";
          highlightListItems();
          break;
        case "Enter":
          if (numTyped != "") {
            window.open( document.getElementById("item"+numTyped).href, "_blank").focus();
          }
          break;
        case "ArrowDown":
          if (numTyped == "") {
            numTyped = "1";
          } else {
            if (parseInt(numTyped) < maxNum) {
              const tmpNum = parseInt(numTyped) + 1;
              numTyped = tmpNum.toString();
            }
          }
          highlightListItems();
          break;
        case "ArrowUp":
          if (numTyped != "" && numTyped != "1") {
            const tmpNum = parseInt(numTyped) - 1;
            numTyped = tmpNum.toString();
          }
          if (numTyped == "0") {
            numTyped = "";
          }
          highlightListItems();
          break;
      }

      if (event.key >= "0" && event.key <= "9") {
        if ( (Date.now() - lastKeyPress) > 500 ) {
          numTyped = event.key;
        } else {
          numTyped += event.key;
        }
        if (numTyped == "0") {
          numTyped = "";
        }
        lastKeyPress = Date.now();
        highlightListItems();
      }
    });
  }

  function search() {
    let go = false;
    let href;
    const searchVal = document.getElementById("search").value.toUpperCase();

    if (event.code == "Enter") {
      go = true;
    }
    for (let i = 1; i <= maxNum; i++) {
      const item = document.getElementById("item"+i);
      const itemVal = item.innerHTML;
      if (itemVal.toUpperCase().indexOf(searchVal) > -1) {
        if (go) {
          if (!href) href = item.href;
        } else {
          item.style.color = "#155799";
        }
      } else {
        item.style.color = "#DDDDDD";
      }
    }
    if (go) {
      for (let i = 1; i <= maxNum; i++) {
        const item = document.getElementById("item"+i);
        item.style.background = "transparent";
        item.style.color = "#155799";
      }
      document.getElementById("search").blur();
      document.getElementById("search").value = "";
      document.getElementById("search").style.visibility = "hidden";
      window.open(href, "_blank").focus();
    }
  }
  addKeyListener();

</script>
</body>' >> "$bm_page"