# Arc XP Bookmarklet

Source: https://dev.arcxp.com/pagebuilder-engine/how-to-guides/components-and-content/arc-xp-bookmarklet/

> **Note:**
> 
> We released [Arc XP Browser Extension User Guide](https://docs.arcxp.com/en/products/general/arc-xp-browser-extension-user-guide.html) which comes with the same features this bookmarklet provides and more.

The Arc XP bookmarklet is a useful front-end developer utility for inspecting Fusion site data, global content, content cache, CSS, and the DOM.

![Arc XP Bookmarklet](https://dev.arcxp.com/images/pagebuilder-engine/arcXPBookmarklet.jpg)

You can run the following script in your browser’s console to help you during your front-end development in Arc. Paste the script into your browser’s console command and press Enter. You also can create a bookmark, and then see the script anytime you click the bookmark in your browser’s bookmark bar.

Click to display

```javascript
javascript: (function () {
  const a = (a, c, d, e) => {
      const f = [];
      a.reduce(function (a, b) {
        return (
          a[b[c]] || ((a[b[c]] = { [c]: b[c], [d]: 0 }), f.push(a[b[c]])),
          (a[b[c]][d] += 1),
          a
        );
      }, {});
      const g =
        "count" === e
          ? f.sort((c, a) => (c[d] < a[d] ? 1 : -1))
          : f.sort((d, a) => (d[c] > a[c] ? 1 : -1));
      return g;
    },
    b = () => {
      const a = document.body,
        b = document.documentElement;
      return Math.max(
        a.scrollHeight,
        a.offsetHeight,
        b.clientHeight,
        b.scrollHeight,
        b.offsetHeight
      );
    },
    c = (a) =>
      a.reduce((a, b) => {
        if ("text" === b.type) {
          const c = b.content.split(" ").length;
          return a + c;
        }
        return a;
      }, 0),
    d = (a) => {
      const b = [],
        c = a["site-service"],
        d = Object.keys(c || {});
      d.map((a) => {
        const d = a
          .toString()
          .split(",")[0]
          .split(":")[1]
          .replace("}", "")
          .replace(/\"/g, "");
        b.push({
          [d]: `${(JSON.stringify(c[a]).toString().length / 1024).toFixed(
            2
          )}KB`,
        });
      });
      const e = Object.keys(a || {});
      return (
        e.map((c) => {
          "site-service" !== c &&
            b.push({
              [c]: `${(JSON.stringify(a[c]).toString().length / 1024).toFixed(
                2
              )}KB`,
            });
        }),
        JSON.stringify(b)
      );
    },
    e = (a) => (a.length / 1024).toFixed(2),
    f = () => {
      const b = (a) => {
          const b = [],
            c = (a) => {
              for (a = a.firstChild; a; ) {
                const d = a.classList;
                d && 0 < d.length && b.push(a.classList),
                  c(a),
                  (a = a.nextSibling);
              }
            };
          return a && 0 < a.length && c(a[0]), b;
        },
        c = (a) => {
          const b = [],
            c = [];
          a.map((a) => {
            b.push(a.count), c.push(a.className.length);
          });
          const d = c.reduce((a, b) => (b += a));
          return {
            unique: a.length,
            total: b.reduce((c, a) => c + a, 0),
            avgLength: parseInt(d / c.length, 10),
            maxLength: Math.max(...c),
          };
        },
        d = (c, d, e) => {
          const f = b(c),
            g = [];
          for (const a in f)
            [...f[a]].map((a) => {
              (!e || 0 <= a.indexOf(e)) && g.push({ className: a, count: 1 });
            });
          return a(g, "className", "count", d);
        };
      ((a, b) => {
        const e = d(document.querySelectorAll("body"), a || selectedSortBy, b);
        (l.innerText = "CSS Class Data"),
          (n.innerText = JSON.stringify(c(e))),
          (n.style.display = "block"),
          (o.innerHTML = ""),
          o.appendChild(h(e, 2));
      })("count");
    },
    g = (function () {
      function k(f, g, e, v, n) {
        var h = e ? "" : g,
          i = function (b, f, e, g, i) {
            var j,
              k = c(g),
              m = function () {
                j ||
                  t(
                    k.parentNode,
                    (j = a(
                      i(),
                      d(n.hide, "disclosure", function () {
                        (j.style.display = "none"),
                          (k.style.display = "inline");
                      })
                    ))
                  ),
                  (j.style.display = "inline"),
                  (k.style.display = "none");
              };
            t(
              k,
              d(n.show, "disclosure", m),
              s(g + " syntax", b),
              d(f, null, m),
              s(g + " syntax", e)
            );
            var o = t(c(), u(h.slice(0, -1)), k);
            return 0 < v && "string" != g && m(), o;
          };
        return null === f
          ? s(null, h, "keyword", "null")
          : void 0 === f
          ? s(null, h, "keyword", "undefined")
          : "string" == typeof f && f.length > n.max_string_length
          ? i(
              '"',
              f.substr(0, n.max_string_length) + " ...",
              '"',
              "string",
              function () {
                return t(c("string"), s(null, h, "string", JSON.stringify(f)));
              }
            )
          : "object" != typeof f ||
            0 <= [Number, String, Boolean, Date].indexOf(f.constructor)
          ? s(null, h, typeof f, JSON.stringify(f))
          : f.constructor == Array
          ? 0 == f.length
            ? s(null, h, "array syntax", "[]")
            : i("[", n.collapse_msg(f.length), "]", "array", function () {
                for (
                  var a = t(c("array"), s("array syntax", "[", null, "\n")),
                    b = 0;
                  b < f.length;
                  b++
                )
                  t(
                    a,
                    k(n.replacer.call(f, b, f[b]), g + " ", !1, v - 1, n),
                    b == f.length - 1 ? [] : s("syntax", ","),
                    u("\n")
                  );
                return t(a, s(null, g, "array syntax", "]")), a;
              })
          : b(f, n.property_list)
          ? s(null, h, "object syntax", "{}")
          : i(
              "{",
              n.collapse_msg(Object.keys(f).length),
              "}",
              "object",
              function () {
                var a = t(c("object"), s("object syntax", "{", null, "\n"));
                for (var b in f) var d = b;
                var h = n.property_list || Object.keys(f);
                for (var i in (n.sort_objects && (h = h.sort()), h)) {
                  var b = h[i];
                  b in f &&
                    t(
                      a,
                      s(
                        null,
                        g + " ",
                        "key",
                        '"' + b + '"',
                        "object syntax",
                        ": "
                      ),
                      k(n.replacer.call(f, b, f[b]), g + " ", !0, v - 1, n),
                      b == d ? [] : s("syntax", ","),
                      u("\n")
                    );
                }
                return t(a, s(null, g, "object syntax", "}")), a;
              }
            );
      }
      var s = function () {
          for (var b = []; arguments.length; )
            b.push(
              t(
                c(Array.prototype.shift.call(arguments)),
                u(Array.prototype.shift.call(arguments))
              )
            );
          return b;
        },
        t = function () {
          for (
            var a = Array.prototype.shift.call(arguments), b = 0;
            b < arguments.length;
            b++
          )
            arguments[b].constructor == Array
              ? t.apply(this, [a].concat(arguments[b]))
              : a.appendChild(arguments[b]);
          return a;
        },
        a = function (c, a) {
          return c.insertBefore(a, c.firstChild), c;
        },
        b = function (e, a) {
          var b = a || Object.keys(e);
          for (var c in b) if (Object.hasOwnProperty.call(e, b[c])) return !1;
          return !0;
        },
        u = function (b) {
          return document.createTextNode(b);
        },
        c = function (c) {
          var a = document.createElement("span");
          return c && (a.className = c), a;
        },
        d = function (a, b, c) {
          var d = document.createElement("a");
          return (
            (d.style.color = "#ecab12"),
            b && (d.className = b),
            d.appendChild(u(a)),
            (d.href = "#"),
            (d.onclick = function (b) {
              return c(), b && b.stopPropagation(), !1;
            }),
            d
          );
        },
        e = function a(b) {
          var c = Object(a.options);
          c.replacer =
            "function" == typeof c.replacer
              ? c.replacer
              : function (c, a) {
                  return a;
                };
          var d = t(
            document.createElement("pre"),
            k(b, "", !1, c.show_to_level, c)
          );
          return (
            (d.style.backgroundColor = "transparent"),
            (d.style.color = "white"),
            (d.style.border = "none"),
            (d.className = "renderjson"),
            d
          );
        };
      return (
        (e.set_icons = function (c, a) {
          return (e.options.show = c), (e.options.hide = a), e;
        }),
        (e.set_show_to_level = function (b) {
          return (
            (e.options.show_to_level =
              "string" == typeof b && "all" === b.toLowerCase()
                ? Number.MAX_VALUE
                : b),
            e
          );
        }),
        (e.set_max_string_length = function (b) {
          return (
            (e.options.max_string_length =
              "string" == typeof b && "none" === b.toLowerCase()
                ? Number.MAX_VALUE
                : b),
            e
          );
        }),
        (e.set_sort_objects = function (b) {
          return (e.options.sort_objects = b), e;
        }),
        (e.set_replacer = function (b) {
          return (e.options.replacer = b), e;
        }),
        (e.set_collapse_msg = function (b) {
          return (e.options.collapse_msg = b), e;
        }),
        (e.set_property_list = function (b) {
          return (e.options.property_list = b), e;
        }),
        (e.set_show_by_default = function (b) {
          return (e.options.show_to_level = b ? Number.MAX_VALUE : 0), e;
        }),
        (e.options = {}),
        e.set_icons("\u2295", "\u2296"),
        e.set_show_by_default(!1),
        e.set_sort_objects(!1),
        e.set_max_string_length("none"),
        e.set_replacer(void 0),
        e.set_property_list(void 0),
        e.set_collapse_msg(function (b) {
          return b + " item" + (1 == b ? "" : "s");
        }),
        e
      );
    })(),
    h = (a, b) => {
      const c = g.set_show_to_level(b || "3")(a),
        d = c.getElementsByTagName("a");
      for (let c = 0; c < d.length; c++)
        d[c].setAttribute("style", "color: #ecab12;");
      return c;
    },
    i = null !== document.getElementById("fusion-app");
  ((a) => {
    const b = document.getElementsByClassName(a);
    if (0 < b.length)
      for (let a = 0; a < b.length; a++) b[a].parentElement.removeChild(b[a]);
  })("fusion_data_panel");
  const j = document.createElement("div");
  j.setAttribute("class", "fusion_data_panel"),
    j.setAttribute(
      "style",
      "background-color: #0c253ade; color: #FFF; position: fixed;top: 0; bottom: 0; right: 0; width: 40%; border: 2px solid #036;font: 12px/1.2 monospace; padding: 0; z-index: 9000000000;"
    );
  const k = document.createElement("div");
  k.setAttribute("class", "fusion_data_panel_buttons"),
    k.setAttribute(
      "style",
      "display: inline-block; padding: 10px; background: #162a3c;width: 100%;"
    ),
    j.append(k);
  const l = document.createElement("h2");
  l.setAttribute("class", "fusion_data_panel_title"),
    l.setAttribute(
      "style",
      "color: white;font-family: arial;padding: 10px;font-size: 24px;"
    ),
    j.append(l);
  const m = document.createElement("div");
  m.setAttribute("class", "data_panel_metrics"),
    m.setAttribute(
      "style",
      "font-size: .8rem;padding: 10px;font-family: arial;line-height: 1.3rem;margin-bottom:4px;"
    ),
    j.append(m);
  const n = document.createElement("div");
  n.setAttribute("class", "data_panel_metrics"),
    n.setAttribute(
      "style",
      "font-size: .8rem;padding: 10px;font-family: arial;line-height: 1.3rem;"
    ),
    j.append(n);
  const o = document.createElement("pre");
  o.setAttribute("class", "fusion_data_panel_content"),
    o.setAttribute(
      "style",
      "background:transparent;color: white;border:none;font-size: 100%;font-family: arial;padding: 10px; overflow: scroll;height: 82vh; margin: 0"
    ),
    j.append(o),
    (createButton = (a, b) => {
      const c = document.createElement("button");
      return (c.innerText = a), b.append(c), b;
    });
  const p = document.createElement("button");
  (p.innerText = "Site"), k.append(p);
  const q = document.createElement("button"),
    r = document.createElement("button"),
    s = document.createElement("button");
  i &&
    ((q.innerText = "Global Content"),
    k.append(q),
    (r.innerText = "Content Cache"),
    k.append(r),
    (s.innerText = "Tree"),
    k.append(s));
  const t = document.createElement("button");
  (t.innerText = "DOM"), k.append(t);
  const u = document.createElement("button");
  (u.innerText = "CSS"), k.append(u);
  const v = document.createElement("button");
  (v.innerText = "Close"),
    k.append(v),
    (v.style.cssFloat = "right"),
    v.addEventListener("click", function () {
      j.parentElement.removeChild(j);
    });
  const w = k.getElementsByTagName("button");
  for (let a = 0; a < w.length; a++)
    w[a].setAttribute(
      "style",
      "float: left; border: solid 1px #999; background: #24425d; padding: 3px 13px; margin-right: 10px; color: #ffffff; font-family: sans-serif"
    );
  document.body.append(j),
    p.addEventListener("click", function () {
      const a = {},
        {
          mxId = "",
          arcSite: c,
          contextPath: d,
          deployment: e,
          environment: k = {},
          getProperties: f,
          lastModified: g,
          origin: i,
          staticElements: p = {},
          template: j,
        } = window.Fusion || {},
        q = document.getElementsByTagName("iframe").length,
        r = document.getElementsByTagName("title")[0].text,
        s = new Date(g),
        t = Object.entries(p)
          .map((a) => a[1].length)
          .reduce((c, a) => c + a, 0),
        u = document.getElementsByTagName("script").length,
        v = document.querySelectorAll("[async]").length,
        w = document.querySelectorAll("[defer]").length,
        x = document.querySelectorAll("[rel='preload']").length,
        y = {
          "Unresized Images": [...document.querySelectorAll("img")]
            .map((a) => (-1 === a.src.indexOf("resize") ? a.src : null))
            .filter((a) => a),
        },
        z = {
          scripts: [...document.querySelectorAll("script")]
            .map((a) => a.src || a.text)
            .filter((a) => "" !== a),
        };
      (m.style.display = "none"),
        (n.style.display = "none"),
        (a.mxId = mxId),
        (a.arcSite = c),
        (a.lastModified = s.toLocaleString()),
        (a.origin = i),
        (a.contextPath = d),
        (a.deployment = e),
        (a.template = j),
        (a.iFrames = q),
        (a.scriptsCount = u),
        (a.pageHeight = b()),
        (a.asyncScripts = `${v} (${Math.round(100 * (v / u))}%)`),
        (a.deferredScripts = `${w} (${Math.round(100 * (w / u))}%)`),
        (a.preloadAssets = x),
        (a.staticElements = t),
        (o.innerHTML = ""),
        o.appendChild(h(a)),
        o.appendChild(h(k)),
        o.appendChild(h(y)),
        o.appendChild(h(z)),
        o.appendChild(h(f())),
        (l.innerText = `Title: ${r}`);
    }),
    q.addEventListener("click", function () {
      const { globalContent: b, globalContentConfig: d } = window.Fusion,
        f = b.content_elements || [],
        g = JSON.stringify(b, null, 2);
      l.innerText = `Global Content: ${e(g)} KB`;
      const i = f.filter((a) => "text" === a.type).length,
        j = c(f),
        k =
          `Total Word Count: ${j}\r\n` +
          `Avg Words per Paragraph: ${parseInt(j / i, 10)}\r\n`,
        p = JSON.stringify(a(f, "type", "count", "count"))
          .replace("[", "")
          .replace(/"/g, "")
          .replace("]", "");
      (m.style.display = "none"),
        (n.style.display = "block"),
        (n.innerText = k + p),
        (o.innerHTML = ""),
        o.appendChild(h(d)),
        o.appendChild(h(b));
    }),
    r.addEventListener("click", function () {
      const a = JSON.stringify(window.Fusion.contentCache);
      l.innerText = `Content Cache: ${e(a)} KB`;
      const b = d(window.Fusion.contentCache);
      let c = 0;
      for (let b = a.indexOf('"data":'); -1 !== b; )
        c++, (b = a.indexOf('"data":', b + 1));
      (m.innerText = `${c} Data request(s)`),
        (n.innerText = b),
        (n.style.display = "block"),
        (o.innerHTML = ""),
        o.appendChild(h(window.Fusion.contentCache)),
        (m.style.display = "block");
    }),
    t.addEventListener("click", function () {
      const { components: d = {}, layout: b, metas: c } = window.Fusion || {},
        f = {},
        g = e(document.querySelectorAll("head")[0].innerHTML),
        i = e(document.querySelectorAll("body")[0].innerHTML),
        j = parseFloat(g) + parseFloat(i),
        k = [],
        p = [],
        q = (a) => {
          for (a = a.firstChild; a; ) {
            const b = a.nodeName.toLowerCase(),
              c = (a.classList || "").toString(),
              d = 0 <= c.indexOf("|") ? c.split("|")[0] : "",
              e = a.innerText ? a.innerText.split(" ").length : null;
            k.push({
              type: b,
              length: a.innerHTML ? a.innerHTML.length : 0,
              count: 1,
              primaryClass: d,
              wordCount: e,
            }),
              "" === a.innerHTML &&
                p.push({
                  type: b,
                  length: 0,
                  count: 1,
                  primaryClass: d,
                  wordCount: e,
                }),
              q(a),
              (a = a.nextSibling);
          }
        };
      q(document.querySelectorAll("html")[0]);
      const r = k.length,
        s = p.length,
        t = { chains: Object.keys(d.chains || {}) },
        u = { features: Object.keys(d.features || {}) },
        v = { elementsByType: a(k, "type", "count", "count") || [] },
        w = { emptyElementsByType: a(p, "type", "count", "count") || [] },
        x = {
          elementsByPrimaryClass: a(k, "primaryClass", "count", "count") || [],
        };
      (f.layout = b),
        (f.emptyElements = `${s} (${Math.round(100 * (s / r))}%)`),
        (f.headLength = `${g} KB (${Math.round(100 * (g / j))}%)`),
        (f.bodyLength = `${i} KB (${Math.round(100 * (i / j))}%)`),
        (f.wordCount = k.map((a) => a.wordCount).reduce((c, a) => c + a, 0)),
        (l.innerText = `Total Elements: ${r}`),
        (n.style.display = "none"),
        (m.style.display = "none"),
        (o.innerHTML = ""),
        o.appendChild(h(f)),
        o.appendChild(h(t)),
        o.appendChild(h(u)),
        o.appendChild(h({ metas: c || {} })),
        o.appendChild(h(v)),
        o.appendChild(h(w)),
        o.appendChild(h(x));
    }),
    s.addEventListener("click", function () {
      (m.style.display = "none"),
        (n.style.display = "none"),
        (o.innerHTML = ""),
        o.appendChild(h(window.Fusion.tree)),
        (l.innerText = `Components Tree`);
    }),
    u.addEventListener("click", function () {
      (l.innerText = `Class Viewer`), (m.style.display = "none"), f();
    });
})();
```

## Creating a Bookmarklet in Chrome

1. Right-click the bookmark bar, and choose **Bookmark Manager**. (Press Ctrl+Shift+B to display the bookmark bar if it isn’t already appearing.)
2. Click the Organize menu icon > **Add new bookmark**
3. Name the bookmark whatever you like
4. In the **URL** field, paste in the JavaScript code provided in this document

## Creating a Bookmarklet in Safari

1. Click the arrow button next to the URL bar and select **Bookmark**.
2. Click the bookmark button, and then click **Edit**.
3. Select the bookmark you just created and in the **URL** field, paste in the JavaScript code provided in this document.

## Creating a Bookmarklet in Edge

1. Ensure the Favorites Bar or Bookmarks Toolbar is enabled and showing
2. Click **Favorites > Add Bookmark**.
3. Edit the bookmark you just created.
4. In the **URL** field, paste the JavaScript code provided in this document.
