use-page-headings-tree
Version:
A React hook to get tree data from a list of heading nodes
280 lines (224 loc) • 7.94 kB
JavaScript
import crypto from 'crypto';
import { useEffect } from 'react';
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function rng() {
return crypto.randomBytes(16);
}
/**
* Convert array of 16 byte values to UUID string format of the form:
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*/
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
byteToHex[i] = (i + 0x100).toString(16).substr(1);
}
function bytesToUuid(buf, offset) {
var i = offset || 0;
var bth = byteToHex; // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
return [bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], '-', bth[buf[i++]], bth[buf[i++]], '-', bth[buf[i++]], bth[buf[i++]], '-', bth[buf[i++]], bth[buf[i++]], '-', bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]], bth[buf[i++]]].join('');
}
function uuidToBytes(uuid) {
// Note: We assume we're being passed a valid uuid string
var bytes = [];
uuid.replace(/[a-fA-F0-9]{2}/g, function (hex) {
bytes.push(parseInt(hex, 16));
});
return bytes;
}
function stringToBytes(str) {
str = unescape(encodeURIComponent(str)); // UTF8 escape
var bytes = new Array(str.length);
for (var i = 0; i < str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
}
const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
function v35 (name, version, hashfunc) {
var generateUUID = function (value, namespace, buf, offset) {
var off = buf && offset || 0;
if (typeof value == 'string') value = stringToBytes(value);
if (typeof namespace == 'string') namespace = uuidToBytes(namespace);
if (!Array.isArray(value)) throw TypeError('value must be an array of bytes');
if (!Array.isArray(namespace) || namespace.length !== 16) throw TypeError('namespace must be uuid string or an Array of 16 byte values'); // Per 4.3
var bytes = hashfunc(namespace.concat(value));
bytes[6] = bytes[6] & 0x0f | version;
bytes[8] = bytes[8] & 0x3f | 0x80;
if (buf) {
for (var idx = 0; idx < 16; ++idx) {
buf[off + idx] = bytes[idx];
}
}
return buf || bytesToUuid(bytes);
}; // Function#name is not settable on some platforms (#270)
try {
generateUUID.name = name;
} catch (err) {} // For CommonJS default export support
generateUUID.DNS = DNS;
generateUUID.URL = URL;
return generateUUID;
}
function md5(bytes) {
if (Array.isArray(bytes)) {
bytes = Buffer.from(bytes);
} else if (typeof bytes === 'string') {
bytes = Buffer.from(bytes, 'utf8');
}
return crypto.createHash('md5').update(bytes).digest();
}
const v3 = v35('v3', 0x30, md5);
function v4(options, buf, offset) {
var i = buf && offset || 0;
if (typeof options == 'string') {
buf = options === 'binary' ? new Array(16) : null;
options = null;
}
options = options || {};
var rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = rnds[6] & 0x0f | 0x40;
rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided
if (buf) {
for (var ii = 0; ii < 16; ++ii) {
buf[i + ii] = rnds[ii];
}
}
return buf || bytesToUuid(rnds);
}
function sha1(bytes) {
if (Array.isArray(bytes)) {
bytes = Buffer.from(bytes);
} else if (typeof bytes === 'string') {
bytes = Buffer.from(bytes, 'utf8');
}
return crypto.createHash('sha1').update(bytes).digest();
}
const v5 = v35('v5', 0x50, sha1);
var getParentMap = function getParentMap(headingLevel, latestRoots) {
return Object.keys(latestRoots).map(function (k) {
return latestRoots[k];
}).slice(0, headingLevel[1] - 2);
};
var transformRootNode = function transformRootNode(node, expanded) {
return _objectSpread2({}, node, {
id: node.element.id,
text: node.element.innerText || node.element.innerHTML,
expanded: expanded
});
};
var checkParent = function checkParent(parent) {
if (!parent) {
throw Error("(usePageHeadingsTree.js): One of the tree nodes is missing a parent.\n" + "You may have skipped a heading level in your document or your query may " + 'not contain a sequential list of heading nodes (Correct: "h2,h3,h4" Incorrect: "h2,h4")');
}
};
var checkTag = function checkTag(tagName) {
if (!/^H[2-6]$/.test(tagName)) {
throw Error("(usePageHeadingsTree.js): <".concat(tagName.toLowerCase(), "> elements are not supported. ") + " Only heading elements (<h2> through <h6>) are supported (for now).");
}
};
var getFlatNodeListFromHeadings = function getFlatNodeListFromHeadings(headings) {
var latestRoots = {};
var roots = {
H2: {},
H3: {},
H4: {},
H5: {},
H6: {}
};
var getDefaultRoot = function getDefaultRoot(heading, index) {
return {
childrenCount: 0,
rootId: latestRoots[heading.tagName],
element: heading,
childNodes: [],
index: index
};
};
headings.forEach(function (h, i) {
checkTag(h.tagName);
latestRoots[h.tagName] = v4();
if (h.tagName === "H2") {
roots.H2[latestRoots.H2] = getDefaultRoot(h, i);
return;
}
roots[h.tagName][latestRoots[h.tagName]] = _objectSpread2({}, getDefaultRoot(h, i), {
parentMap: getParentMap(h.tagName, latestRoots)
});
});
return roots;
};
var getNodeTreeFromFlatNodeList = function getNodeTreeFromFlatNodeList(roots, shouldDefaultToExpand) {
var rootKeys = Object.keys(roots);
var _loop = function _loop(i) {
var parents = roots[rootKeys[i - 1]];
var currentRoot = roots[rootKeys[i]];
if (currentRoot) {
Object.keys(currentRoot).forEach(function (childKey) {
var child = transformRootNode(currentRoot[childKey], shouldDefaultToExpand);
if (child.parentMap) {
var parentId = child.parentMap[child.parentMap.length - 1];
var parent = parents[parentId];
checkParent(parent);
parent.childrenCount += child.childNodes.length + 1;
parent.childNodes.push(child);
}
});
}
};
for (var i = rootKeys.length - 1; i >= 0; i--) {
_loop(i);
}
var finalRoots = Object.keys(roots.H2).map(function (k) {
return transformRootNode(roots.H2[k], shouldDefaultToExpand);
});
return finalRoots;
};
var usePageHeadingsTree = function usePageHeadingsTree(headings, callback, shouldDefaultToExpand) {
useEffect(function () {
var roots = getFlatNodeListFromHeadings(headings);
var finalRoots = getNodeTreeFromFlatNodeList(roots, shouldDefaultToExpand);
callback(finalRoots);
}, [headings, shouldDefaultToExpand]);
};
export { usePageHeadingsTree };
//# sourceMappingURL=index.es.js.map