mokuji.js 3.0.1 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +15 -18
- package/dist/mokuji.js +1 -1
- package/dist/mokuji.js.map +1 -1
- package/dist/mokuji.modern.js +1 -1
- package/dist/mokuji.modern.js.map +1 -1
- package/dist/mokuji.module.js +1 -1
- package/dist/mokuji.module.js.map +1 -1
- package/dist/mokuji.umd.js +1 -1
- package/dist/mokuji.umd.js.map +1 -1
- package/package.json +12 -22
- package/src/index.ts +99 -87
- package/src/utils.ts +3 -6
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
declare type MokujiOption = {
|
|
2
|
-
anchorType
|
|
3
|
-
anchorLink
|
|
4
|
-
anchorLinkSymbol
|
|
5
|
-
anchorLinkBefore
|
|
6
|
-
anchorLinkClassName
|
|
7
|
-
anchorContainerTagName
|
|
1
|
+
export declare type MokujiOption = {
|
|
2
|
+
anchorType?: boolean;
|
|
3
|
+
anchorLink?: boolean;
|
|
4
|
+
anchorLinkSymbol?: string;
|
|
5
|
+
anchorLinkBefore?: boolean;
|
|
6
|
+
anchorLinkClassName?: string;
|
|
7
|
+
anchorContainerTagName?: 'ul' | 'ol';
|
|
8
8
|
};
|
|
9
|
-
export
|
|
10
|
-
|
|
9
|
+
export declare const renderAnchorLink: (headings: NodeListOf<HTMLHeadingElement>, anchors: NodeListOf<HTMLAnchorElement> | undefined, options: MokujiOption) => void;
|
|
10
|
+
export declare class Mokuji {
|
|
11
11
|
options: MokujiOption;
|
|
12
12
|
storeIds: string[];
|
|
13
|
-
constructor(element: HTMLElement, externalOptions: MokujiOption);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
renderAnchorLink(anchors: NodeListOf<HTMLAnchorElement> | undefined): void;
|
|
20
|
-
removeDuplicateIds(anchors: HTMLCollectionOf<HTMLAnchorElement>): void;
|
|
13
|
+
constructor(element: HTMLElement | null, externalOptions: MokujiOption);
|
|
14
|
+
generateMokuji(headings: NodeListOf<HTMLHeadingElement>): HTMLUListElement | HTMLOListElement;
|
|
15
|
+
generateHierarchyList(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement): void;
|
|
16
|
+
censorshipId(headings: NodeListOf<HTMLHeadingElement>, textContent: string | null): string;
|
|
17
|
+
generateAnchorText(text: string, type: boolean): string;
|
|
18
|
+
removeDuplicateIds(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement): void;
|
|
21
19
|
}
|
|
22
|
-
export {};
|
package/dist/mokuji.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=function(e,
|
|
1
|
+
var e=function(e,n){for(;e;){if(e===n)return!0;e=e.parentNode}return!1},n=function(e,n,r){if(n){var t=document.createElement("a");r.anchorLinkClassName&&t.classList.add(r.anchorLinkClassName),e.forEach(function(e){for(var o=e.id,a=0;a<n.length;a++){var i=n[a].hash;if(i.replace("#","")===o){var h=t.cloneNode(!1);h.setAttribute("href",i),r.anchorLinkSymbol&&(h.textContent=r.anchorLinkSymbol),r.anchorLinkBefore?e.insertBefore(h,e.firstChild):e.appendChild(h)}}})}};exports.Mokuji=/*#__PURE__*/function(){function r(e,r){if(this.options={anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},this.storeIds=[],e){this.options=Object.assign(this.options,r);var t=function(e){return e.querySelectorAll("h1, h2, h3, h4, h5, h6")}(e),o=this.generateMokuji(t);if(this.options.anchorLink){var a=o.querySelectorAll("a");n(t,a,this.options)}return o}}var t=r.prototype;return t.generateMokuji=function(e){var n=document.createElement(this.options.anchorContainerTagName||"ol");return this.generateHierarchyList(e,n),this.removeDuplicateIds(e,n),n},t.generateHierarchyList=function(n,r){for(var t=0,o=document.createElement("li"),a=document.createElement("a"),i=0;i<n.length;i++){var h=n[i],c=Number(h.tagName.substring(1));if(0!==t&&t<c){var s=document.createElement("ol");r.lastChild.appendChild(s),r=s}else if(0!==t&&t>c)for(var l=0;l<t-c;l++)e(r,r.parentNode)&&(r=r.parentNode.parentNode);var f=this.censorshipId(n,h.textContent),u=this.generateAnchorText(f,!!this.options.anchorType);h.id=u;var d=a.cloneNode(!1);d.href="#"+u,d.textContent=h.textContent;var p=o.cloneNode(!1);p.appendChild(d),r.appendChild(p),t=c}},t.censorshipId=function(e,n){for(var r=n||"",t=1;t<=e.length;){var o=1===t?r:r+"_"+t;if(-1===this.storeIds.indexOf(o)){this.storeIds.push(r=o);break}t++}return r},t.generateAnchorText=function(e,n){var r=function(e){return e.replace(/\s+/g,"_")}(e);return r=r.replace(/\&+/g,"").replace(/\&+/g,""),!0===n&&(r=function(e){return(e=encodeURIComponent(e)).replace(/\%+/g,".")}(r)),r},t.removeDuplicateIds=function(e,n){for(var r=n.getElementsByTagName("a"),t=function(n){var t=r[n].innerText,o=r[n].hash,a=Array.from(e).filter(function(e){return e.id===t});if(1===a.length)return"continue";var i=0;a.forEach(function(e){for(var n=e.id+"-"+i,t=0,a=Array.from(r);t<a.length;t++){var h=a[t];if(h.hash===o){h.href="#"+n;break}}e.id=n,i++})},o=0;o<r.length;o++)t(o)},r}(),exports.renderAnchorLink=n;
|
|
2
2
|
//# sourceMappingURL=mokuji.js.map
|
package/dist/mokuji.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mokuji.js","sources":["../src/dom.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["export const hasParentNode = (element: Node | null, parent: Node | null) => {\n while (element) {\n if (element === parent) {\n return true;\n }\n element = element.parentNode;\n }\n return false;\n};\n\nexport const reverseElement = (element: Node) => {\n while (element.parentNode) {\n element = element.parentNode;\n }\n\n return element;\n};\n\nexport const getHeadingsElement = (element: Element): NodeListOf<HTMLHeadingElement> => {\n return element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n};\n","export const replaceSpace2Underscore = (text: string) => {\n return text.replace(/\\s+/g, \"_\");\n};\n\nexport const convert2WikipediaStyleAnchor = (anchor: string) => {\n anchor = encodeURIComponent(anchor);\n anchor = anchor.replace(/\\%+/g, \".\");\n\n return anchor;\n};\n\nexport const getHeadingTagName2Number = (tagName: string) => {\n // @ts-ignore\n const currentNumber = tagName.match(/\\d/g).join(\"\");\n\n return Number(currentNumber);\n};\n","import { hasParentNode, getHeadingsElement } from \"./dom\";\nimport { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from \"./utils\";\n\ntype MokujiOption = {\n anchorType: Boolean;\n anchorLink: Boolean;\n anchorLinkSymbol: string;\n anchorLinkBefore: Boolean;\n anchorLinkClassName: string;\n anchorContainerTagName: string;\n};\n\nexport default class Mokuji {\n headings: NodeListOf<HTMLHeadingElement>;\n options: MokujiOption;\n storeIds: string[] = [];\n\n constructor(element: HTMLElement, externalOptions: MokujiOption) {\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: \"#\",\n anchorLinkBefore: true,\n anchorLinkClassName: \"\",\n anchorContainerTagName: \"ol\",\n },\n externalOptions,\n );\n\n this.headings = getHeadingsElement(element);\n\n // mokuji start\n const mokuji = this.render();\n\n // @ts-ignore\n return mokuji;\n }\n\n render() {\n // generate mokuji list\n const list = this.generateMokuji();\n\n // setup anchor link\n if (this.options.anchorLink) {\n const anchors = list?.querySelectorAll(\"a\");\n this.renderAnchorLink(anchors);\n }\n\n return list;\n }\n\n generateMokuji() {\n let elementContainer = document.createElement(this.options.anchorContainerTagName);\n\n this.generateHierarchyList(elementContainer);\n\n // remove duplicates by adding suffix\n const anchors = elementContainer.getElementsByTagName(\"a\");\n this.removeDuplicateIds(anchors);\n\n return elementContainer;\n }\n\n generateHierarchyList(elementContainer: HTMLElement) {\n let number = 0;\n const elementListClone = document.createElement(\"li\");\n const elementAnchorClone = document.createElement(\"a\");\n\n for (let i = 0; i < this.headings.length; i++) {\n const heading = this.headings[i];\n const currentNumber = getHeadingTagName2Number(heading.tagName);\n\n // check list hierarchy\n if (number !== 0 && number < currentNumber) {\n // number of the heading is large (small as heading)\n const nextElementOListClone = document.createElement(\"ol\");\n // @ts-ignore\n elementContainer.lastChild.appendChild(nextElementOListClone);\n elementContainer = nextElementOListClone;\n } else if (number !== 0 && number > currentNumber) {\n // number of heading is small (large as heading)\n for (let i = 0; i < number - currentNumber; i++) {\n if (hasParentNode(elementContainer, elementContainer.parentNode)) {\n // @ts-ignore\n elementContainer = elementContainer.parentNode.parentNode;\n }\n }\n }\n\n const textContent = this.censorshipId(heading.textContent);\n\n // headingへidを付与\n heading.id = this.setAnchor(textContent, this.options.anchorType);\n\n // add to wrapper\n const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;\n elementAnchor.href = \"#\" + heading.id;\n elementAnchor.textContent = heading.textContent;\n const elementList = elementListClone.cloneNode(false);\n elementList.appendChild(elementAnchor);\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n }\n\n censorshipId(textContent: string | null) {\n let id = textContent || \"\";\n let suffix_count = 1;\n\n // IDが重複していた場合はsuffixを付ける\n while (suffix_count <= this.headings.length) {\n const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;\n\n if (this.storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n this.storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n }\n\n setAnchor(text: string, type: Boolean) {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, \"\").replace(/\\&+/g, \"\");\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n }\n\n renderAnchorLink(anchors: NodeListOf<HTMLAnchorElement> | undefined) {\n if (!anchors) return;\n\n const a = document.createElement(\"a\");\n a.classList.add(this.options.anchorLinkClassName);\n\n for (let i = 0; i < anchors.length; i++) {\n const hash = anchors[i].hash;\n const headings = document.querySelector(`[id=\"${hash.replace(\"#\", \"\")}\"]`);\n\n if (!headings) {\n continue;\n }\n\n // create anchor\n const anchor = a.cloneNode(false) as HTMLAnchorElement;\n anchor.setAttribute(\"href\", hash);\n anchor.textContent = this.options.anchorLinkSymbol;\n\n // insert anchor into headings\n if (this.options.anchorLinkBefore) {\n // before\n headings.insertBefore(anchor, headings.firstChild);\n } else {\n // after\n headings.appendChild(anchor);\n }\n }\n }\n\n removeDuplicateIds(anchors: HTMLCollectionOf<HTMLAnchorElement>) {\n for (let i = 0; i < anchors.length; i++) {\n const id = anchors[i].innerText;\n const hash = anchors[i].hash;\n const headings = document.querySelectorAll(`[id=\"${id}\"]`);\n\n if (headings.length === 1) {\n continue;\n }\n\n // duplicated id\n let count = 0;\n\n for (const heading of Array.from(headings)) {\n const heading_id = `${heading.id}-${count}`;\n\n // search duplicate list\n for (const anchor of Array.from(anchors)) {\n if (anchor.hash === hash) {\n // update hash\n anchor.href = `#${heading_id}`;\n break;\n }\n }\n\n // update id\n heading.id = heading_id;\n count++;\n }\n }\n }\n}\n"],"names":["hasParentNode","element","parent","parentNode","getHeadingTagName2Number","tagName","currentNumber","match","join","Number","externalOptions","this","options","Object","assign","anchorType","anchorLink","anchorLinkSymbol","anchorLinkBefore","anchorLinkClassName","anchorContainerTagName","headings","querySelectorAll","getHeadingsElement","render","list","generateMokuji","anchors","renderAnchorLink","elementContainer","document","createElement","generateHierarchyList","getElementsByTagName","removeDuplicateIds","number","elementListClone","elementAnchorClone","i","length","heading","nextElementOListClone","lastChild","appendChild","textContent","censorshipId","id","setAnchor","elementAnchor","cloneNode","href","elementList","suffix_count","tmp_id","storeIds","indexOf","push","text","type","anchor","replace","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","a","classList","add","hash","querySelector","setAttribute","insertBefore","firstChild","innerText","count","Array","from","heading_id"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mokuji.js","sources":["../src/dom.ts","../src/index.ts","../src/utils.ts"],"sourcesContent":["export const hasParentNode = (element: Node | null, parent: Node | null) => {\n while (element) {\n if (element === parent) {\n return true;\n }\n element = element.parentNode;\n }\n return false;\n};\n\nexport const reverseElement = (element: Node) => {\n while (element.parentNode) {\n element = element.parentNode;\n }\n\n return element;\n};\n\nexport const getHeadingsElement = (element: Element): NodeListOf<HTMLHeadingElement> => {\n return element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n};\n","import { hasParentNode, getHeadingsElement } from './dom';\nimport { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from './utils';\n\nexport type MokujiOption = {\n anchorType?: boolean;\n anchorLink?: boolean;\n anchorLinkSymbol?: string;\n anchorLinkBefore?: boolean;\n anchorLinkClassName?: string;\n anchorContainerTagName?: 'ul' | 'ol';\n};\n\nexport const renderAnchorLink = (\n headings: NodeListOf<HTMLHeadingElement>,\n anchors: NodeListOf<HTMLAnchorElement> | undefined,\n options: MokujiOption,\n) => {\n if (!anchors) return;\n\n const a = document.createElement('a');\n\n if (options.anchorLinkClassName) {\n a.classList.add(options.anchorLinkClassName);\n }\n\n headings.forEach((heading) => {\n const { id } = heading;\n\n for (let i = 0; i < anchors.length; i++) {\n const { hash } = anchors[i];\n\n if (hash.replace('#', '') !== id) {\n continue;\n }\n\n // create anchor\n const anchor = a.cloneNode(false) as HTMLAnchorElement;\n anchor.setAttribute('href', hash);\n\n if (options.anchorLinkSymbol) {\n anchor.textContent = options.anchorLinkSymbol;\n }\n\n // insert anchor into headings\n if (options.anchorLinkBefore) {\n // before\n heading.insertBefore(anchor, heading.firstChild);\n } else {\n // after\n heading.appendChild(anchor);\n }\n }\n });\n};\n\nexport class Mokuji {\n options: MokujiOption = {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n };\n storeIds: string[] = [];\n\n constructor(element: HTMLElement | null, externalOptions: MokujiOption) {\n if (!element) {\n return;\n }\n\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n this.options,\n externalOptions,\n );\n\n const headings = getHeadingsElement(element);\n\n // mokuji start\n // generate mokuji list\n const mokuji = this.generateMokuji(headings);\n\n // setup anchor link\n if (this.options.anchorLink) {\n const anchors = mokuji.querySelectorAll('a');\n renderAnchorLink(headings, anchors, this.options);\n }\n\n // @ts-ignore\n return mokuji;\n }\n\n generateMokuji(headings: NodeListOf<HTMLHeadingElement>) {\n let elementContainer = document.createElement(this.options.anchorContainerTagName || 'ol');\n\n this.generateHierarchyList(headings, elementContainer);\n\n // remove duplicates by adding suffix\n this.removeDuplicateIds(headings, elementContainer);\n\n return elementContainer;\n }\n\n generateHierarchyList(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {\n let number = 0;\n const elementListClone = document.createElement('li');\n const elementAnchorClone = document.createElement('a');\n\n for (let i = 0; i < headings.length; i++) {\n const heading = headings[i];\n const currentNumber = getHeadingTagName2Number(heading.tagName);\n\n // check list hierarchy\n if (number !== 0 && number < currentNumber) {\n // number of the heading is large (small as heading)\n const nextElementOListClone = document.createElement('ol');\n // @ts-ignore\n elementContainer.lastChild.appendChild(nextElementOListClone);\n elementContainer = nextElementOListClone;\n } else if (number !== 0 && number > currentNumber) {\n // number of heading is small (large as heading)\n for (let i = 0; i < number - currentNumber; i++) {\n if (hasParentNode(elementContainer, elementContainer.parentNode)) {\n // @ts-ignore\n elementContainer = elementContainer.parentNode.parentNode;\n }\n }\n }\n\n const textContent = this.censorshipId(headings, heading.textContent);\n\n // headingへidを付与\n const anchorText = this.generateAnchorText(textContent, !!this.options.anchorType);\n heading.id = anchorText;\n\n // add to wrapper\n const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;\n elementAnchor.href = `#${anchorText}`;\n elementAnchor.textContent = heading.textContent;\n const elementList = elementListClone.cloneNode(false);\n elementList.appendChild(elementAnchor);\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n }\n\n censorshipId(headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) {\n let id = textContent || '';\n let suffix_count = 1;\n\n // IDが重複していた場合はsuffixを付ける\n while (suffix_count <= headings.length) {\n const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;\n\n if (this.storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n this.storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n }\n\n generateAnchorText(text: string, type: boolean) {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, '').replace(/\\&+/g, '');\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n }\n\n removeDuplicateIds(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {\n const anchors = elementContainer.getElementsByTagName('a');\n\n for (let i = 0; i < anchors.length; i++) {\n const id = anchors[i].innerText;\n const hash = anchors[i].hash;\n const matchedHeadings = Array.from(headings).filter((heading) => heading.id === id);\n\n if (matchedHeadings.length === 1) {\n continue;\n }\n\n // duplicated id\n let count = 0;\n\n matchedHeadings.forEach((heading) => {\n const heading_id = `${heading.id}-${count}`;\n\n // search duplicate list\n for (const anchor of Array.from(anchors)) {\n if (anchor.hash === hash) {\n // update hash\n anchor.href = `#${heading_id}`;\n break;\n }\n }\n\n // update id\n heading.id = heading_id;\n count++;\n });\n }\n }\n}\n","export const replaceSpace2Underscore = (text: string) => {\n return text.replace(/\\s+/g, '_');\n};\n\nexport const convert2WikipediaStyleAnchor = (anchor: string) => {\n anchor = encodeURIComponent(anchor);\n anchor = anchor.replace(/\\%+/g, '.');\n\n return anchor;\n};\n\nexport const getHeadingTagName2Number = (tagName: string) => {\n return Number(tagName.substring(1));\n};\n"],"names":["hasParentNode","element","parent","parentNode","renderAnchorLink","headings","anchors","options","a","document","createElement","anchorLinkClassName","classList","add","forEach","heading","id","i","length","hash","replace","anchor","cloneNode","setAttribute","anchorLinkSymbol","textContent","anchorLinkBefore","insertBefore","firstChild","appendChild","externalOptions","anchorType","anchorLink","anchorContainerTagName","storeIds","this","Object","assign","querySelectorAll","getHeadingsElement","mokuji","generateMokuji","elementContainer","generateHierarchyList","removeDuplicateIds","number","elementListClone","elementAnchorClone","currentNumber","Number","tagName","substring","nextElementOListClone","lastChild","censorshipId","anchorText","generateAnchorText","elementAnchor","href","elementList","suffix_count","tmp_id","indexOf","push","text","type","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","getElementsByTagName","innerText","matchedHeadings","Array","from","filter","count","heading_id"],"mappings":"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"}
|
package/dist/mokuji.modern.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=(e,t)=>{for(;e;){if(e===t)return!0;e=e.parentNode}return!1},t=e=>{const
|
|
1
|
+
const e=(e,t)=>{for(;e;){if(e===t)return!0;e=e.parentNode}return!1},t=(e,t,n)=>{if(!t)return;const o=document.createElement("a");n.anchorLinkClassName&&o.classList.add(n.anchorLinkClassName),e.forEach(e=>{const{id:r}=e;for(let s=0;s<t.length;s++){const{hash:a}=t[s];if(a.replace("#","")!==r)continue;const i=o.cloneNode(!1);i.setAttribute("href",a),n.anchorLinkSymbol&&(i.textContent=n.anchorLinkSymbol),n.anchorLinkBefore?e.insertBefore(i,e.firstChild):e.appendChild(i)}})};class n{constructor(e,n){if(this.options={anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},this.storeIds=[],!e)return;this.options=Object.assign(this.options,n);const o=(e=>e.querySelectorAll("h1, h2, h3, h4, h5, h6"))(e),r=this.generateMokuji(o);if(this.options.anchorLink){const e=r.querySelectorAll("a");t(o,e,this.options)}return r}generateMokuji(e){let t=document.createElement(this.options.anchorContainerTagName||"ol");return this.generateHierarchyList(e,t),this.removeDuplicateIds(e,t),t}generateHierarchyList(t,n){let o=0;const r=document.createElement("li"),s=document.createElement("a");for(let a=0;a<t.length;a++){const i=t[a],c=Number(i.tagName.substring(1));if(0!==o&&o<c){const e=document.createElement("ol");n.lastChild.appendChild(e),n=e}else if(0!==o&&o>c)for(let t=0;t<o-c;t++)e(n,n.parentNode)&&(n=n.parentNode.parentNode);const h=this.censorshipId(t,i.textContent),l=this.generateAnchorText(h,!!this.options.anchorType);i.id=l;const d=s.cloneNode(!1);d.href=`#${l}`,d.textContent=i.textContent;const p=r.cloneNode(!1);p.appendChild(d),n.appendChild(p),o=c}}censorshipId(e,t){let n=t||"",o=1;for(;o<=e.length;){const e=1===o?n:`${n}_${o}`;if(-1===this.storeIds.indexOf(e)){n=e,this.storeIds.push(n);break}o++}return n}generateAnchorText(e,t){let n=(e=>e.replace(/\s+/g,"_"))(e);return n=n.replace(/\&+/g,"").replace(/\&+/g,""),!0===t&&(n=(e=>(e=encodeURIComponent(e)).replace(/\%+/g,"."))(n)),n}removeDuplicateIds(e,t){const n=t.getElementsByTagName("a");for(let t=0;t<n.length;t++){const o=n[t].innerText,r=n[t].hash,s=Array.from(e).filter(e=>e.id===o);if(1===s.length)continue;let a=0;s.forEach(e=>{const t=`${e.id}-${a}`;for(const e of Array.from(n))if(e.hash===r){e.href=`#${t}`;break}e.id=t,a++})}}}export{n as Mokuji,t as renderAnchorLink};
|
|
2
2
|
//# sourceMappingURL=mokuji.modern.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mokuji.modern.js","sources":["../src/dom.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["export const hasParentNode = (element: Node | null, parent: Node | null) => {\n while (element) {\n if (element === parent) {\n return true;\n }\n element = element.parentNode;\n }\n return false;\n};\n\nexport const reverseElement = (element: Node) => {\n while (element.parentNode) {\n element = element.parentNode;\n }\n\n return element;\n};\n\nexport const getHeadingsElement = (element: Element): NodeListOf<HTMLHeadingElement> => {\n return element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n};\n","export const replaceSpace2Underscore = (text: string) => {\n return text.replace(/\\s+/g, \"_\");\n};\n\nexport const convert2WikipediaStyleAnchor = (anchor: string) => {\n anchor = encodeURIComponent(anchor);\n anchor = anchor.replace(/\\%+/g, \".\");\n\n return anchor;\n};\n\nexport const getHeadingTagName2Number = (tagName: string) => {\n // @ts-ignore\n const currentNumber = tagName.match(/\\d/g).join(\"\");\n\n return Number(currentNumber);\n};\n","import { hasParentNode, getHeadingsElement } from \"./dom\";\nimport { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from \"./utils\";\n\ntype MokujiOption = {\n anchorType: Boolean;\n anchorLink: Boolean;\n anchorLinkSymbol: string;\n anchorLinkBefore: Boolean;\n anchorLinkClassName: string;\n anchorContainerTagName: string;\n};\n\nexport default class Mokuji {\n headings: NodeListOf<HTMLHeadingElement>;\n options: MokujiOption;\n storeIds: string[] = [];\n\n constructor(element: HTMLElement, externalOptions: MokujiOption) {\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: \"#\",\n anchorLinkBefore: true,\n anchorLinkClassName: \"\",\n anchorContainerTagName: \"ol\",\n },\n externalOptions,\n );\n\n this.headings = getHeadingsElement(element);\n\n // mokuji start\n const mokuji = this.render();\n\n // @ts-ignore\n return mokuji;\n }\n\n render() {\n // generate mokuji list\n const list = this.generateMokuji();\n\n // setup anchor link\n if (this.options.anchorLink) {\n const anchors = list?.querySelectorAll(\"a\");\n this.renderAnchorLink(anchors);\n }\n\n return list;\n }\n\n generateMokuji() {\n let elementContainer = document.createElement(this.options.anchorContainerTagName);\n\n this.generateHierarchyList(elementContainer);\n\n // remove duplicates by adding suffix\n const anchors = elementContainer.getElementsByTagName(\"a\");\n this.removeDuplicateIds(anchors);\n\n return elementContainer;\n }\n\n generateHierarchyList(elementContainer: HTMLElement) {\n let number = 0;\n const elementListClone = document.createElement(\"li\");\n const elementAnchorClone = document.createElement(\"a\");\n\n for (let i = 0; i < this.headings.length; i++) {\n const heading = this.headings[i];\n const currentNumber = getHeadingTagName2Number(heading.tagName);\n\n // check list hierarchy\n if (number !== 0 && number < currentNumber) {\n // number of the heading is large (small as heading)\n const nextElementOListClone = document.createElement(\"ol\");\n // @ts-ignore\n elementContainer.lastChild.appendChild(nextElementOListClone);\n elementContainer = nextElementOListClone;\n } else if (number !== 0 && number > currentNumber) {\n // number of heading is small (large as heading)\n for (let i = 0; i < number - currentNumber; i++) {\n if (hasParentNode(elementContainer, elementContainer.parentNode)) {\n // @ts-ignore\n elementContainer = elementContainer.parentNode.parentNode;\n }\n }\n }\n\n const textContent = this.censorshipId(heading.textContent);\n\n // headingへidを付与\n heading.id = this.setAnchor(textContent, this.options.anchorType);\n\n // add to wrapper\n const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;\n elementAnchor.href = \"#\" + heading.id;\n elementAnchor.textContent = heading.textContent;\n const elementList = elementListClone.cloneNode(false);\n elementList.appendChild(elementAnchor);\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n }\n\n censorshipId(textContent: string | null) {\n let id = textContent || \"\";\n let suffix_count = 1;\n\n // IDが重複していた場合はsuffixを付ける\n while (suffix_count <= this.headings.length) {\n const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;\n\n if (this.storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n this.storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n }\n\n setAnchor(text: string, type: Boolean) {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, \"\").replace(/\\&+/g, \"\");\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n }\n\n renderAnchorLink(anchors: NodeListOf<HTMLAnchorElement> | undefined) {\n if (!anchors) return;\n\n const a = document.createElement(\"a\");\n a.classList.add(this.options.anchorLinkClassName);\n\n for (let i = 0; i < anchors.length; i++) {\n const hash = anchors[i].hash;\n const headings = document.querySelector(`[id=\"${hash.replace(\"#\", \"\")}\"]`);\n\n if (!headings) {\n continue;\n }\n\n // create anchor\n const anchor = a.cloneNode(false) as HTMLAnchorElement;\n anchor.setAttribute(\"href\", hash);\n anchor.textContent = this.options.anchorLinkSymbol;\n\n // insert anchor into headings\n if (this.options.anchorLinkBefore) {\n // before\n headings.insertBefore(anchor, headings.firstChild);\n } else {\n // after\n headings.appendChild(anchor);\n }\n }\n }\n\n removeDuplicateIds(anchors: HTMLCollectionOf<HTMLAnchorElement>) {\n for (let i = 0; i < anchors.length; i++) {\n const id = anchors[i].innerText;\n const hash = anchors[i].hash;\n const headings = document.querySelectorAll(`[id=\"${id}\"]`);\n\n if (headings.length === 1) {\n continue;\n }\n\n // duplicated id\n let count = 0;\n\n for (const heading of Array.from(headings)) {\n const heading_id = `${heading.id}-${count}`;\n\n // search duplicate list\n for (const anchor of Array.from(anchors)) {\n if (anchor.hash === hash) {\n // update hash\n anchor.href = `#${heading_id}`;\n break;\n }\n }\n\n // update id\n heading.id = heading_id;\n count++;\n }\n }\n }\n}\n"],"names":["hasParentNode","element","parent","parentNode","getHeadingTagName2Number","tagName","currentNumber","match","join","Number","constructor","externalOptions","this","options","Object","assign","anchorType","anchorLink","anchorLinkSymbol","anchorLinkBefore","anchorLinkClassName","anchorContainerTagName","headings","querySelectorAll","getHeadingsElement","render","list","generateMokuji","anchors","renderAnchorLink","elementContainer","document","createElement","generateHierarchyList","getElementsByTagName","removeDuplicateIds","number","elementListClone","elementAnchorClone","i","length","heading","nextElementOListClone","lastChild","appendChild","textContent","censorshipId","id","setAnchor","elementAnchor","cloneNode","href","elementList","suffix_count","tmp_id","storeIds","indexOf","push","text","type","anchor","replace","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","a","classList","add","hash","querySelector","setAttribute","insertBefore","firstChild","innerText","count","Array","from","heading_id"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mokuji.modern.js","sources":["../src/dom.ts","../src/index.ts","../src/utils.ts"],"sourcesContent":["export const hasParentNode = (element: Node | null, parent: Node | null) => {\n while (element) {\n if (element === parent) {\n return true;\n }\n element = element.parentNode;\n }\n return false;\n};\n\nexport const reverseElement = (element: Node) => {\n while (element.parentNode) {\n element = element.parentNode;\n }\n\n return element;\n};\n\nexport const getHeadingsElement = (element: Element): NodeListOf<HTMLHeadingElement> => {\n return element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n};\n","import { hasParentNode, getHeadingsElement } from './dom';\nimport { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from './utils';\n\nexport type MokujiOption = {\n anchorType?: boolean;\n anchorLink?: boolean;\n anchorLinkSymbol?: string;\n anchorLinkBefore?: boolean;\n anchorLinkClassName?: string;\n anchorContainerTagName?: 'ul' | 'ol';\n};\n\nexport const renderAnchorLink = (\n headings: NodeListOf<HTMLHeadingElement>,\n anchors: NodeListOf<HTMLAnchorElement> | undefined,\n options: MokujiOption,\n) => {\n if (!anchors) return;\n\n const a = document.createElement('a');\n\n if (options.anchorLinkClassName) {\n a.classList.add(options.anchorLinkClassName);\n }\n\n headings.forEach((heading) => {\n const { id } = heading;\n\n for (let i = 0; i < anchors.length; i++) {\n const { hash } = anchors[i];\n\n if (hash.replace('#', '') !== id) {\n continue;\n }\n\n // create anchor\n const anchor = a.cloneNode(false) as HTMLAnchorElement;\n anchor.setAttribute('href', hash);\n\n if (options.anchorLinkSymbol) {\n anchor.textContent = options.anchorLinkSymbol;\n }\n\n // insert anchor into headings\n if (options.anchorLinkBefore) {\n // before\n heading.insertBefore(anchor, heading.firstChild);\n } else {\n // after\n heading.appendChild(anchor);\n }\n }\n });\n};\n\nexport class Mokuji {\n options: MokujiOption = {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n };\n storeIds: string[] = [];\n\n constructor(element: HTMLElement | null, externalOptions: MokujiOption) {\n if (!element) {\n return;\n }\n\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n this.options,\n externalOptions,\n );\n\n const headings = getHeadingsElement(element);\n\n // mokuji start\n // generate mokuji list\n const mokuji = this.generateMokuji(headings);\n\n // setup anchor link\n if (this.options.anchorLink) {\n const anchors = mokuji.querySelectorAll('a');\n renderAnchorLink(headings, anchors, this.options);\n }\n\n // @ts-ignore\n return mokuji;\n }\n\n generateMokuji(headings: NodeListOf<HTMLHeadingElement>) {\n let elementContainer = document.createElement(this.options.anchorContainerTagName || 'ol');\n\n this.generateHierarchyList(headings, elementContainer);\n\n // remove duplicates by adding suffix\n this.removeDuplicateIds(headings, elementContainer);\n\n return elementContainer;\n }\n\n generateHierarchyList(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {\n let number = 0;\n const elementListClone = document.createElement('li');\n const elementAnchorClone = document.createElement('a');\n\n for (let i = 0; i < headings.length; i++) {\n const heading = headings[i];\n const currentNumber = getHeadingTagName2Number(heading.tagName);\n\n // check list hierarchy\n if (number !== 0 && number < currentNumber) {\n // number of the heading is large (small as heading)\n const nextElementOListClone = document.createElement('ol');\n // @ts-ignore\n elementContainer.lastChild.appendChild(nextElementOListClone);\n elementContainer = nextElementOListClone;\n } else if (number !== 0 && number > currentNumber) {\n // number of heading is small (large as heading)\n for (let i = 0; i < number - currentNumber; i++) {\n if (hasParentNode(elementContainer, elementContainer.parentNode)) {\n // @ts-ignore\n elementContainer = elementContainer.parentNode.parentNode;\n }\n }\n }\n\n const textContent = this.censorshipId(headings, heading.textContent);\n\n // headingへidを付与\n const anchorText = this.generateAnchorText(textContent, !!this.options.anchorType);\n heading.id = anchorText;\n\n // add to wrapper\n const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;\n elementAnchor.href = `#${anchorText}`;\n elementAnchor.textContent = heading.textContent;\n const elementList = elementListClone.cloneNode(false);\n elementList.appendChild(elementAnchor);\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n }\n\n censorshipId(headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) {\n let id = textContent || '';\n let suffix_count = 1;\n\n // IDが重複していた場合はsuffixを付ける\n while (suffix_count <= headings.length) {\n const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;\n\n if (this.storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n this.storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n }\n\n generateAnchorText(text: string, type: boolean) {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, '').replace(/\\&+/g, '');\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n }\n\n removeDuplicateIds(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {\n const anchors = elementContainer.getElementsByTagName('a');\n\n for (let i = 0; i < anchors.length; i++) {\n const id = anchors[i].innerText;\n const hash = anchors[i].hash;\n const matchedHeadings = Array.from(headings).filter((heading) => heading.id === id);\n\n if (matchedHeadings.length === 1) {\n continue;\n }\n\n // duplicated id\n let count = 0;\n\n matchedHeadings.forEach((heading) => {\n const heading_id = `${heading.id}-${count}`;\n\n // search duplicate list\n for (const anchor of Array.from(anchors)) {\n if (anchor.hash === hash) {\n // update hash\n anchor.href = `#${heading_id}`;\n break;\n }\n }\n\n // update id\n heading.id = heading_id;\n count++;\n });\n }\n }\n}\n","export const replaceSpace2Underscore = (text: string) => {\n return text.replace(/\\s+/g, '_');\n};\n\nexport const convert2WikipediaStyleAnchor = (anchor: string) => {\n anchor = encodeURIComponent(anchor);\n anchor = anchor.replace(/\\%+/g, '.');\n\n return anchor;\n};\n\nexport const getHeadingTagName2Number = (tagName: string) => {\n return Number(tagName.substring(1));\n};\n"],"names":["hasParentNode","element","parent","parentNode","renderAnchorLink","headings","anchors","options","a","document","createElement","anchorLinkClassName","classList","add","forEach","heading","id","i","length","hash","replace","anchor","cloneNode","setAttribute","anchorLinkSymbol","textContent","anchorLinkBefore","insertBefore","firstChild","appendChild","Mokuji","constructor","externalOptions","anchorType","anchorLink","anchorContainerTagName","storeIds","this","Object","assign","querySelectorAll","getHeadingsElement","mokuji","generateMokuji","elementContainer","generateHierarchyList","removeDuplicateIds","number","elementListClone","elementAnchorClone","currentNumber","Number","tagName","substring","nextElementOListClone","lastChild","censorshipId","anchorText","generateAnchorText","elementAnchor","href","elementList","suffix_count","tmp_id","indexOf","push","text","type","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","getElementsByTagName","innerText","matchedHeadings","Array","from","filter","count","heading_id"],"mappings":"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"}
|
package/dist/mokuji.module.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=function(e,
|
|
1
|
+
var e=function(e,n){for(;e;){if(e===n)return!0;e=e.parentNode}return!1},n=function(e,n,r){if(n){var t=document.createElement("a");r.anchorLinkClassName&&t.classList.add(r.anchorLinkClassName),e.forEach(function(e){for(var o=e.id,a=0;a<n.length;a++){var i=n[a].hash;if(i.replace("#","")===o){var h=t.cloneNode(!1);h.setAttribute("href",i),r.anchorLinkSymbol&&(h.textContent=r.anchorLinkSymbol),r.anchorLinkBefore?e.insertBefore(h,e.firstChild):e.appendChild(h)}}})}},r=/*#__PURE__*/function(){function r(e,r){if(this.options={anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},this.storeIds=[],e){this.options=Object.assign(this.options,r);var t=function(e){return e.querySelectorAll("h1, h2, h3, h4, h5, h6")}(e),o=this.generateMokuji(t);if(this.options.anchorLink){var a=o.querySelectorAll("a");n(t,a,this.options)}return o}}var t=r.prototype;return t.generateMokuji=function(e){var n=document.createElement(this.options.anchorContainerTagName||"ol");return this.generateHierarchyList(e,n),this.removeDuplicateIds(e,n),n},t.generateHierarchyList=function(n,r){for(var t=0,o=document.createElement("li"),a=document.createElement("a"),i=0;i<n.length;i++){var h=n[i],c=Number(h.tagName.substring(1));if(0!==t&&t<c){var s=document.createElement("ol");r.lastChild.appendChild(s),r=s}else if(0!==t&&t>c)for(var l=0;l<t-c;l++)e(r,r.parentNode)&&(r=r.parentNode.parentNode);var f=this.censorshipId(n,h.textContent),u=this.generateAnchorText(f,!!this.options.anchorType);h.id=u;var d=a.cloneNode(!1);d.href="#"+u,d.textContent=h.textContent;var p=o.cloneNode(!1);p.appendChild(d),r.appendChild(p),t=c}},t.censorshipId=function(e,n){for(var r=n||"",t=1;t<=e.length;){var o=1===t?r:r+"_"+t;if(-1===this.storeIds.indexOf(o)){this.storeIds.push(r=o);break}t++}return r},t.generateAnchorText=function(e,n){var r=function(e){return e.replace(/\s+/g,"_")}(e);return r=r.replace(/\&+/g,"").replace(/\&+/g,""),!0===n&&(r=function(e){return(e=encodeURIComponent(e)).replace(/\%+/g,".")}(r)),r},t.removeDuplicateIds=function(e,n){for(var r=n.getElementsByTagName("a"),t=function(n){var t=r[n].innerText,o=r[n].hash,a=Array.from(e).filter(function(e){return e.id===t});if(1===a.length)return"continue";var i=0;a.forEach(function(e){for(var n=e.id+"-"+i,t=0,a=Array.from(r);t<a.length;t++){var h=a[t];if(h.hash===o){h.href="#"+n;break}}e.id=n,i++})},o=0;o<r.length;o++)t(o)},r}();export{r as Mokuji,n as renderAnchorLink};
|
|
2
2
|
//# sourceMappingURL=mokuji.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mokuji.module.js","sources":["../src/dom.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["export const hasParentNode = (element: Node | null, parent: Node | null) => {\n while (element) {\n if (element === parent) {\n return true;\n }\n element = element.parentNode;\n }\n return false;\n};\n\nexport const reverseElement = (element: Node) => {\n while (element.parentNode) {\n element = element.parentNode;\n }\n\n return element;\n};\n\nexport const getHeadingsElement = (element: Element): NodeListOf<HTMLHeadingElement> => {\n return element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n};\n","export const replaceSpace2Underscore = (text: string) => {\n return text.replace(/\\s+/g, \"_\");\n};\n\nexport const convert2WikipediaStyleAnchor = (anchor: string) => {\n anchor = encodeURIComponent(anchor);\n anchor = anchor.replace(/\\%+/g, \".\");\n\n return anchor;\n};\n\nexport const getHeadingTagName2Number = (tagName: string) => {\n // @ts-ignore\n const currentNumber = tagName.match(/\\d/g).join(\"\");\n\n return Number(currentNumber);\n};\n","import { hasParentNode, getHeadingsElement } from \"./dom\";\nimport { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from \"./utils\";\n\ntype MokujiOption = {\n anchorType: Boolean;\n anchorLink: Boolean;\n anchorLinkSymbol: string;\n anchorLinkBefore: Boolean;\n anchorLinkClassName: string;\n anchorContainerTagName: string;\n};\n\nexport default class Mokuji {\n headings: NodeListOf<HTMLHeadingElement>;\n options: MokujiOption;\n storeIds: string[] = [];\n\n constructor(element: HTMLElement, externalOptions: MokujiOption) {\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: \"#\",\n anchorLinkBefore: true,\n anchorLinkClassName: \"\",\n anchorContainerTagName: \"ol\",\n },\n externalOptions,\n );\n\n this.headings = getHeadingsElement(element);\n\n // mokuji start\n const mokuji = this.render();\n\n // @ts-ignore\n return mokuji;\n }\n\n render() {\n // generate mokuji list\n const list = this.generateMokuji();\n\n // setup anchor link\n if (this.options.anchorLink) {\n const anchors = list?.querySelectorAll(\"a\");\n this.renderAnchorLink(anchors);\n }\n\n return list;\n }\n\n generateMokuji() {\n let elementContainer = document.createElement(this.options.anchorContainerTagName);\n\n this.generateHierarchyList(elementContainer);\n\n // remove duplicates by adding suffix\n const anchors = elementContainer.getElementsByTagName(\"a\");\n this.removeDuplicateIds(anchors);\n\n return elementContainer;\n }\n\n generateHierarchyList(elementContainer: HTMLElement) {\n let number = 0;\n const elementListClone = document.createElement(\"li\");\n const elementAnchorClone = document.createElement(\"a\");\n\n for (let i = 0; i < this.headings.length; i++) {\n const heading = this.headings[i];\n const currentNumber = getHeadingTagName2Number(heading.tagName);\n\n // check list hierarchy\n if (number !== 0 && number < currentNumber) {\n // number of the heading is large (small as heading)\n const nextElementOListClone = document.createElement(\"ol\");\n // @ts-ignore\n elementContainer.lastChild.appendChild(nextElementOListClone);\n elementContainer = nextElementOListClone;\n } else if (number !== 0 && number > currentNumber) {\n // number of heading is small (large as heading)\n for (let i = 0; i < number - currentNumber; i++) {\n if (hasParentNode(elementContainer, elementContainer.parentNode)) {\n // @ts-ignore\n elementContainer = elementContainer.parentNode.parentNode;\n }\n }\n }\n\n const textContent = this.censorshipId(heading.textContent);\n\n // headingへidを付与\n heading.id = this.setAnchor(textContent, this.options.anchorType);\n\n // add to wrapper\n const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;\n elementAnchor.href = \"#\" + heading.id;\n elementAnchor.textContent = heading.textContent;\n const elementList = elementListClone.cloneNode(false);\n elementList.appendChild(elementAnchor);\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n }\n\n censorshipId(textContent: string | null) {\n let id = textContent || \"\";\n let suffix_count = 1;\n\n // IDが重複していた場合はsuffixを付ける\n while (suffix_count <= this.headings.length) {\n const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;\n\n if (this.storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n this.storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n }\n\n setAnchor(text: string, type: Boolean) {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, \"\").replace(/\\&+/g, \"\");\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n }\n\n renderAnchorLink(anchors: NodeListOf<HTMLAnchorElement> | undefined) {\n if (!anchors) return;\n\n const a = document.createElement(\"a\");\n a.classList.add(this.options.anchorLinkClassName);\n\n for (let i = 0; i < anchors.length; i++) {\n const hash = anchors[i].hash;\n const headings = document.querySelector(`[id=\"${hash.replace(\"#\", \"\")}\"]`);\n\n if (!headings) {\n continue;\n }\n\n // create anchor\n const anchor = a.cloneNode(false) as HTMLAnchorElement;\n anchor.setAttribute(\"href\", hash);\n anchor.textContent = this.options.anchorLinkSymbol;\n\n // insert anchor into headings\n if (this.options.anchorLinkBefore) {\n // before\n headings.insertBefore(anchor, headings.firstChild);\n } else {\n // after\n headings.appendChild(anchor);\n }\n }\n }\n\n removeDuplicateIds(anchors: HTMLCollectionOf<HTMLAnchorElement>) {\n for (let i = 0; i < anchors.length; i++) {\n const id = anchors[i].innerText;\n const hash = anchors[i].hash;\n const headings = document.querySelectorAll(`[id=\"${id}\"]`);\n\n if (headings.length === 1) {\n continue;\n }\n\n // duplicated id\n let count = 0;\n\n for (const heading of Array.from(headings)) {\n const heading_id = `${heading.id}-${count}`;\n\n // search duplicate list\n for (const anchor of Array.from(anchors)) {\n if (anchor.hash === hash) {\n // update hash\n anchor.href = `#${heading_id}`;\n break;\n }\n }\n\n // update id\n heading.id = heading_id;\n count++;\n }\n }\n }\n}\n"],"names":["hasParentNode","element","parent","parentNode","getHeadingTagName2Number","tagName","currentNumber","match","join","Number","Mokuji","externalOptions","this","options","Object","assign","anchorType","anchorLink","anchorLinkSymbol","anchorLinkBefore","anchorLinkClassName","anchorContainerTagName","headings","querySelectorAll","getHeadingsElement","render","list","generateMokuji","anchors","renderAnchorLink","elementContainer","document","createElement","generateHierarchyList","getElementsByTagName","removeDuplicateIds","number","elementListClone","elementAnchorClone","i","length","heading","nextElementOListClone","lastChild","appendChild","textContent","censorshipId","id","setAnchor","elementAnchor","cloneNode","href","elementList","suffix_count","tmp_id","storeIds","indexOf","push","text","type","anchor","replace","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","a","classList","add","hash","querySelector","setAttribute","insertBefore","firstChild","innerText","count","Array","from","heading_id"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mokuji.module.js","sources":["../src/dom.ts","../src/index.ts","../src/utils.ts"],"sourcesContent":["export const hasParentNode = (element: Node | null, parent: Node | null) => {\n while (element) {\n if (element === parent) {\n return true;\n }\n element = element.parentNode;\n }\n return false;\n};\n\nexport const reverseElement = (element: Node) => {\n while (element.parentNode) {\n element = element.parentNode;\n }\n\n return element;\n};\n\nexport const getHeadingsElement = (element: Element): NodeListOf<HTMLHeadingElement> => {\n return element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n};\n","import { hasParentNode, getHeadingsElement } from './dom';\nimport { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from './utils';\n\nexport type MokujiOption = {\n anchorType?: boolean;\n anchorLink?: boolean;\n anchorLinkSymbol?: string;\n anchorLinkBefore?: boolean;\n anchorLinkClassName?: string;\n anchorContainerTagName?: 'ul' | 'ol';\n};\n\nexport const renderAnchorLink = (\n headings: NodeListOf<HTMLHeadingElement>,\n anchors: NodeListOf<HTMLAnchorElement> | undefined,\n options: MokujiOption,\n) => {\n if (!anchors) return;\n\n const a = document.createElement('a');\n\n if (options.anchorLinkClassName) {\n a.classList.add(options.anchorLinkClassName);\n }\n\n headings.forEach((heading) => {\n const { id } = heading;\n\n for (let i = 0; i < anchors.length; i++) {\n const { hash } = anchors[i];\n\n if (hash.replace('#', '') !== id) {\n continue;\n }\n\n // create anchor\n const anchor = a.cloneNode(false) as HTMLAnchorElement;\n anchor.setAttribute('href', hash);\n\n if (options.anchorLinkSymbol) {\n anchor.textContent = options.anchorLinkSymbol;\n }\n\n // insert anchor into headings\n if (options.anchorLinkBefore) {\n // before\n heading.insertBefore(anchor, heading.firstChild);\n } else {\n // after\n heading.appendChild(anchor);\n }\n }\n });\n};\n\nexport class Mokuji {\n options: MokujiOption = {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n };\n storeIds: string[] = [];\n\n constructor(element: HTMLElement | null, externalOptions: MokujiOption) {\n if (!element) {\n return;\n }\n\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n this.options,\n externalOptions,\n );\n\n const headings = getHeadingsElement(element);\n\n // mokuji start\n // generate mokuji list\n const mokuji = this.generateMokuji(headings);\n\n // setup anchor link\n if (this.options.anchorLink) {\n const anchors = mokuji.querySelectorAll('a');\n renderAnchorLink(headings, anchors, this.options);\n }\n\n // @ts-ignore\n return mokuji;\n }\n\n generateMokuji(headings: NodeListOf<HTMLHeadingElement>) {\n let elementContainer = document.createElement(this.options.anchorContainerTagName || 'ol');\n\n this.generateHierarchyList(headings, elementContainer);\n\n // remove duplicates by adding suffix\n this.removeDuplicateIds(headings, elementContainer);\n\n return elementContainer;\n }\n\n generateHierarchyList(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {\n let number = 0;\n const elementListClone = document.createElement('li');\n const elementAnchorClone = document.createElement('a');\n\n for (let i = 0; i < headings.length; i++) {\n const heading = headings[i];\n const currentNumber = getHeadingTagName2Number(heading.tagName);\n\n // check list hierarchy\n if (number !== 0 && number < currentNumber) {\n // number of the heading is large (small as heading)\n const nextElementOListClone = document.createElement('ol');\n // @ts-ignore\n elementContainer.lastChild.appendChild(nextElementOListClone);\n elementContainer = nextElementOListClone;\n } else if (number !== 0 && number > currentNumber) {\n // number of heading is small (large as heading)\n for (let i = 0; i < number - currentNumber; i++) {\n if (hasParentNode(elementContainer, elementContainer.parentNode)) {\n // @ts-ignore\n elementContainer = elementContainer.parentNode.parentNode;\n }\n }\n }\n\n const textContent = this.censorshipId(headings, heading.textContent);\n\n // headingへidを付与\n const anchorText = this.generateAnchorText(textContent, !!this.options.anchorType);\n heading.id = anchorText;\n\n // add to wrapper\n const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;\n elementAnchor.href = `#${anchorText}`;\n elementAnchor.textContent = heading.textContent;\n const elementList = elementListClone.cloneNode(false);\n elementList.appendChild(elementAnchor);\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n }\n\n censorshipId(headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) {\n let id = textContent || '';\n let suffix_count = 1;\n\n // IDが重複していた場合はsuffixを付ける\n while (suffix_count <= headings.length) {\n const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;\n\n if (this.storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n this.storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n }\n\n generateAnchorText(text: string, type: boolean) {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, '').replace(/\\&+/g, '');\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n }\n\n removeDuplicateIds(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {\n const anchors = elementContainer.getElementsByTagName('a');\n\n for (let i = 0; i < anchors.length; i++) {\n const id = anchors[i].innerText;\n const hash = anchors[i].hash;\n const matchedHeadings = Array.from(headings).filter((heading) => heading.id === id);\n\n if (matchedHeadings.length === 1) {\n continue;\n }\n\n // duplicated id\n let count = 0;\n\n matchedHeadings.forEach((heading) => {\n const heading_id = `${heading.id}-${count}`;\n\n // search duplicate list\n for (const anchor of Array.from(anchors)) {\n if (anchor.hash === hash) {\n // update hash\n anchor.href = `#${heading_id}`;\n break;\n }\n }\n\n // update id\n heading.id = heading_id;\n count++;\n });\n }\n }\n}\n","export const replaceSpace2Underscore = (text: string) => {\n return text.replace(/\\s+/g, '_');\n};\n\nexport const convert2WikipediaStyleAnchor = (anchor: string) => {\n anchor = encodeURIComponent(anchor);\n anchor = anchor.replace(/\\%+/g, '.');\n\n return anchor;\n};\n\nexport const getHeadingTagName2Number = (tagName: string) => {\n return Number(tagName.substring(1));\n};\n"],"names":["hasParentNode","element","parent","parentNode","renderAnchorLink","headings","anchors","options","a","document","createElement","anchorLinkClassName","classList","add","forEach","heading","id","i","length","hash","replace","anchor","cloneNode","setAttribute","anchorLinkSymbol","textContent","anchorLinkBefore","insertBefore","firstChild","appendChild","Mokuji","externalOptions","anchorType","anchorLink","anchorContainerTagName","storeIds","this","Object","assign","querySelectorAll","getHeadingsElement","mokuji","generateMokuji","elementContainer","generateHierarchyList","removeDuplicateIds","number","elementListClone","elementAnchorClone","currentNumber","Number","tagName","substring","nextElementOListClone","lastChild","censorshipId","anchorText","generateAnchorText","elementAnchor","href","elementList","suffix_count","tmp_id","indexOf","push","text","type","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","getElementsByTagName","innerText","matchedHeadings","Array","from","filter","count","heading_id"],"mappings":"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"}
|
package/dist/mokuji.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?
|
|
1
|
+
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e||self).mokujiJs={})}(this,function(e){var n=function(e,n){for(;e;){if(e===n)return!0;e=e.parentNode}return!1},t=function(e,n,t){if(n){var r=document.createElement("a");t.anchorLinkClassName&&r.classList.add(t.anchorLinkClassName),e.forEach(function(e){for(var o=e.id,i=0;i<n.length;i++){var a=n[i].hash;if(a.replace("#","")===o){var c=r.cloneNode(!1);c.setAttribute("href",a),t.anchorLinkSymbol&&(c.textContent=t.anchorLinkSymbol),t.anchorLinkBefore?e.insertBefore(c,e.firstChild):e.appendChild(c)}}})}};e.Mokuji=/*#__PURE__*/function(){function e(e,n){if(this.options={anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},this.storeIds=[],e){this.options=Object.assign(this.options,n);var r=function(e){return e.querySelectorAll("h1, h2, h3, h4, h5, h6")}(e),o=this.generateMokuji(r);if(this.options.anchorLink){var i=o.querySelectorAll("a");t(r,i,this.options)}return o}}var r=e.prototype;return r.generateMokuji=function(e){var n=document.createElement(this.options.anchorContainerTagName||"ol");return this.generateHierarchyList(e,n),this.removeDuplicateIds(e,n),n},r.generateHierarchyList=function(e,t){for(var r=0,o=document.createElement("li"),i=document.createElement("a"),a=0;a<e.length;a++){var c=e[a],h=Number(c.tagName.substring(1));if(0!==r&&r<h){var s=document.createElement("ol");t.lastChild.appendChild(s),t=s}else if(0!==r&&r>h)for(var f=0;f<r-h;f++)n(t,t.parentNode)&&(t=t.parentNode.parentNode);var l=this.censorshipId(e,c.textContent),u=this.generateAnchorText(l,!!this.options.anchorType);c.id=u;var d=i.cloneNode(!1);d.href="#"+u,d.textContent=c.textContent;var p=o.cloneNode(!1);p.appendChild(d),t.appendChild(p),r=h}},r.censorshipId=function(e,n){for(var t=n||"",r=1;r<=e.length;){var o=1===r?t:t+"_"+r;if(-1===this.storeIds.indexOf(o)){this.storeIds.push(t=o);break}r++}return t},r.generateAnchorText=function(e,n){var t=function(e){return e.replace(/\s+/g,"_")}(e);return t=t.replace(/\&+/g,"").replace(/\&+/g,""),!0===n&&(t=function(e){return(e=encodeURIComponent(e)).replace(/\%+/g,".")}(t)),t},r.removeDuplicateIds=function(e,n){for(var t=n.getElementsByTagName("a"),r=function(n){var r=t[n].innerText,o=t[n].hash,i=Array.from(e).filter(function(e){return e.id===r});if(1===i.length)return"continue";var a=0;i.forEach(function(e){for(var n=e.id+"-"+a,r=0,i=Array.from(t);r<i.length;r++){var c=i[r];if(c.hash===o){c.href="#"+n;break}}e.id=n,a++})},o=0;o<t.length;o++)r(o)},e}(),e.renderAnchorLink=t});
|
|
2
2
|
//# sourceMappingURL=mokuji.umd.js.map
|
package/dist/mokuji.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mokuji.umd.js","sources":["../src/dom.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["export const hasParentNode = (element: Node | null, parent: Node | null) => {\n while (element) {\n if (element === parent) {\n return true;\n }\n element = element.parentNode;\n }\n return false;\n};\n\nexport const reverseElement = (element: Node) => {\n while (element.parentNode) {\n element = element.parentNode;\n }\n\n return element;\n};\n\nexport const getHeadingsElement = (element: Element): NodeListOf<HTMLHeadingElement> => {\n return element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n};\n","export const replaceSpace2Underscore = (text: string) => {\n return text.replace(/\\s+/g, \"_\");\n};\n\nexport const convert2WikipediaStyleAnchor = (anchor: string) => {\n anchor = encodeURIComponent(anchor);\n anchor = anchor.replace(/\\%+/g, \".\");\n\n return anchor;\n};\n\nexport const getHeadingTagName2Number = (tagName: string) => {\n // @ts-ignore\n const currentNumber = tagName.match(/\\d/g).join(\"\");\n\n return Number(currentNumber);\n};\n","import { hasParentNode, getHeadingsElement } from \"./dom\";\nimport { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from \"./utils\";\n\ntype MokujiOption = {\n anchorType: Boolean;\n anchorLink: Boolean;\n anchorLinkSymbol: string;\n anchorLinkBefore: Boolean;\n anchorLinkClassName: string;\n anchorContainerTagName: string;\n};\n\nexport default class Mokuji {\n headings: NodeListOf<HTMLHeadingElement>;\n options: MokujiOption;\n storeIds: string[] = [];\n\n constructor(element: HTMLElement, externalOptions: MokujiOption) {\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: \"#\",\n anchorLinkBefore: true,\n anchorLinkClassName: \"\",\n anchorContainerTagName: \"ol\",\n },\n externalOptions,\n );\n\n this.headings = getHeadingsElement(element);\n\n // mokuji start\n const mokuji = this.render();\n\n // @ts-ignore\n return mokuji;\n }\n\n render() {\n // generate mokuji list\n const list = this.generateMokuji();\n\n // setup anchor link\n if (this.options.anchorLink) {\n const anchors = list?.querySelectorAll(\"a\");\n this.renderAnchorLink(anchors);\n }\n\n return list;\n }\n\n generateMokuji() {\n let elementContainer = document.createElement(this.options.anchorContainerTagName);\n\n this.generateHierarchyList(elementContainer);\n\n // remove duplicates by adding suffix\n const anchors = elementContainer.getElementsByTagName(\"a\");\n this.removeDuplicateIds(anchors);\n\n return elementContainer;\n }\n\n generateHierarchyList(elementContainer: HTMLElement) {\n let number = 0;\n const elementListClone = document.createElement(\"li\");\n const elementAnchorClone = document.createElement(\"a\");\n\n for (let i = 0; i < this.headings.length; i++) {\n const heading = this.headings[i];\n const currentNumber = getHeadingTagName2Number(heading.tagName);\n\n // check list hierarchy\n if (number !== 0 && number < currentNumber) {\n // number of the heading is large (small as heading)\n const nextElementOListClone = document.createElement(\"ol\");\n // @ts-ignore\n elementContainer.lastChild.appendChild(nextElementOListClone);\n elementContainer = nextElementOListClone;\n } else if (number !== 0 && number > currentNumber) {\n // number of heading is small (large as heading)\n for (let i = 0; i < number - currentNumber; i++) {\n if (hasParentNode(elementContainer, elementContainer.parentNode)) {\n // @ts-ignore\n elementContainer = elementContainer.parentNode.parentNode;\n }\n }\n }\n\n const textContent = this.censorshipId(heading.textContent);\n\n // headingへidを付与\n heading.id = this.setAnchor(textContent, this.options.anchorType);\n\n // add to wrapper\n const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;\n elementAnchor.href = \"#\" + heading.id;\n elementAnchor.textContent = heading.textContent;\n const elementList = elementListClone.cloneNode(false);\n elementList.appendChild(elementAnchor);\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n }\n\n censorshipId(textContent: string | null) {\n let id = textContent || \"\";\n let suffix_count = 1;\n\n // IDが重複していた場合はsuffixを付ける\n while (suffix_count <= this.headings.length) {\n const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;\n\n if (this.storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n this.storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n }\n\n setAnchor(text: string, type: Boolean) {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, \"\").replace(/\\&+/g, \"\");\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n }\n\n renderAnchorLink(anchors: NodeListOf<HTMLAnchorElement> | undefined) {\n if (!anchors) return;\n\n const a = document.createElement(\"a\");\n a.classList.add(this.options.anchorLinkClassName);\n\n for (let i = 0; i < anchors.length; i++) {\n const hash = anchors[i].hash;\n const headings = document.querySelector(`[id=\"${hash.replace(\"#\", \"\")}\"]`);\n\n if (!headings) {\n continue;\n }\n\n // create anchor\n const anchor = a.cloneNode(false) as HTMLAnchorElement;\n anchor.setAttribute(\"href\", hash);\n anchor.textContent = this.options.anchorLinkSymbol;\n\n // insert anchor into headings\n if (this.options.anchorLinkBefore) {\n // before\n headings.insertBefore(anchor, headings.firstChild);\n } else {\n // after\n headings.appendChild(anchor);\n }\n }\n }\n\n removeDuplicateIds(anchors: HTMLCollectionOf<HTMLAnchorElement>) {\n for (let i = 0; i < anchors.length; i++) {\n const id = anchors[i].innerText;\n const hash = anchors[i].hash;\n const headings = document.querySelectorAll(`[id=\"${id}\"]`);\n\n if (headings.length === 1) {\n continue;\n }\n\n // duplicated id\n let count = 0;\n\n for (const heading of Array.from(headings)) {\n const heading_id = `${heading.id}-${count}`;\n\n // search duplicate list\n for (const anchor of Array.from(anchors)) {\n if (anchor.hash === hash) {\n // update hash\n anchor.href = `#${heading_id}`;\n break;\n }\n }\n\n // update id\n heading.id = heading_id;\n count++;\n }\n }\n }\n}\n"],"names":["hasParentNode","element","parent","parentNode","getHeadingTagName2Number","tagName","currentNumber","match","join","Number","externalOptions","this","options","Object","assign","anchorType","anchorLink","anchorLinkSymbol","anchorLinkBefore","anchorLinkClassName","anchorContainerTagName","headings","querySelectorAll","getHeadingsElement","render","list","generateMokuji","anchors","renderAnchorLink","elementContainer","document","createElement","generateHierarchyList","getElementsByTagName","removeDuplicateIds","number","elementListClone","elementAnchorClone","i","length","heading","nextElementOListClone","lastChild","appendChild","textContent","censorshipId","id","setAnchor","elementAnchor","cloneNode","href","elementList","suffix_count","tmp_id","storeIds","indexOf","push","text","type","anchor","replace","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","a","classList","add","hash","querySelector","setAttribute","insertBefore","firstChild","innerText","count","Array","from","heading_id"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mokuji.umd.js","sources":["../src/dom.ts","../src/index.ts","../src/utils.ts"],"sourcesContent":["export const hasParentNode = (element: Node | null, parent: Node | null) => {\n while (element) {\n if (element === parent) {\n return true;\n }\n element = element.parentNode;\n }\n return false;\n};\n\nexport const reverseElement = (element: Node) => {\n while (element.parentNode) {\n element = element.parentNode;\n }\n\n return element;\n};\n\nexport const getHeadingsElement = (element: Element): NodeListOf<HTMLHeadingElement> => {\n return element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n};\n","import { hasParentNode, getHeadingsElement } from './dom';\nimport { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from './utils';\n\nexport type MokujiOption = {\n anchorType?: boolean;\n anchorLink?: boolean;\n anchorLinkSymbol?: string;\n anchorLinkBefore?: boolean;\n anchorLinkClassName?: string;\n anchorContainerTagName?: 'ul' | 'ol';\n};\n\nexport const renderAnchorLink = (\n headings: NodeListOf<HTMLHeadingElement>,\n anchors: NodeListOf<HTMLAnchorElement> | undefined,\n options: MokujiOption,\n) => {\n if (!anchors) return;\n\n const a = document.createElement('a');\n\n if (options.anchorLinkClassName) {\n a.classList.add(options.anchorLinkClassName);\n }\n\n headings.forEach((heading) => {\n const { id } = heading;\n\n for (let i = 0; i < anchors.length; i++) {\n const { hash } = anchors[i];\n\n if (hash.replace('#', '') !== id) {\n continue;\n }\n\n // create anchor\n const anchor = a.cloneNode(false) as HTMLAnchorElement;\n anchor.setAttribute('href', hash);\n\n if (options.anchorLinkSymbol) {\n anchor.textContent = options.anchorLinkSymbol;\n }\n\n // insert anchor into headings\n if (options.anchorLinkBefore) {\n // before\n heading.insertBefore(anchor, heading.firstChild);\n } else {\n // after\n heading.appendChild(anchor);\n }\n }\n });\n};\n\nexport class Mokuji {\n options: MokujiOption = {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n };\n storeIds: string[] = [];\n\n constructor(element: HTMLElement | null, externalOptions: MokujiOption) {\n if (!element) {\n return;\n }\n\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n this.options,\n externalOptions,\n );\n\n const headings = getHeadingsElement(element);\n\n // mokuji start\n // generate mokuji list\n const mokuji = this.generateMokuji(headings);\n\n // setup anchor link\n if (this.options.anchorLink) {\n const anchors = mokuji.querySelectorAll('a');\n renderAnchorLink(headings, anchors, this.options);\n }\n\n // @ts-ignore\n return mokuji;\n }\n\n generateMokuji(headings: NodeListOf<HTMLHeadingElement>) {\n let elementContainer = document.createElement(this.options.anchorContainerTagName || 'ol');\n\n this.generateHierarchyList(headings, elementContainer);\n\n // remove duplicates by adding suffix\n this.removeDuplicateIds(headings, elementContainer);\n\n return elementContainer;\n }\n\n generateHierarchyList(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {\n let number = 0;\n const elementListClone = document.createElement('li');\n const elementAnchorClone = document.createElement('a');\n\n for (let i = 0; i < headings.length; i++) {\n const heading = headings[i];\n const currentNumber = getHeadingTagName2Number(heading.tagName);\n\n // check list hierarchy\n if (number !== 0 && number < currentNumber) {\n // number of the heading is large (small as heading)\n const nextElementOListClone = document.createElement('ol');\n // @ts-ignore\n elementContainer.lastChild.appendChild(nextElementOListClone);\n elementContainer = nextElementOListClone;\n } else if (number !== 0 && number > currentNumber) {\n // number of heading is small (large as heading)\n for (let i = 0; i < number - currentNumber; i++) {\n if (hasParentNode(elementContainer, elementContainer.parentNode)) {\n // @ts-ignore\n elementContainer = elementContainer.parentNode.parentNode;\n }\n }\n }\n\n const textContent = this.censorshipId(headings, heading.textContent);\n\n // headingへidを付与\n const anchorText = this.generateAnchorText(textContent, !!this.options.anchorType);\n heading.id = anchorText;\n\n // add to wrapper\n const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;\n elementAnchor.href = `#${anchorText}`;\n elementAnchor.textContent = heading.textContent;\n const elementList = elementListClone.cloneNode(false);\n elementList.appendChild(elementAnchor);\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n }\n\n censorshipId(headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) {\n let id = textContent || '';\n let suffix_count = 1;\n\n // IDが重複していた場合はsuffixを付ける\n while (suffix_count <= headings.length) {\n const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;\n\n if (this.storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n this.storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n }\n\n generateAnchorText(text: string, type: boolean) {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, '').replace(/\\&+/g, '');\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n }\n\n removeDuplicateIds(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {\n const anchors = elementContainer.getElementsByTagName('a');\n\n for (let i = 0; i < anchors.length; i++) {\n const id = anchors[i].innerText;\n const hash = anchors[i].hash;\n const matchedHeadings = Array.from(headings).filter((heading) => heading.id === id);\n\n if (matchedHeadings.length === 1) {\n continue;\n }\n\n // duplicated id\n let count = 0;\n\n matchedHeadings.forEach((heading) => {\n const heading_id = `${heading.id}-${count}`;\n\n // search duplicate list\n for (const anchor of Array.from(anchors)) {\n if (anchor.hash === hash) {\n // update hash\n anchor.href = `#${heading_id}`;\n break;\n }\n }\n\n // update id\n heading.id = heading_id;\n count++;\n });\n }\n }\n}\n","export const replaceSpace2Underscore = (text: string) => {\n return text.replace(/\\s+/g, '_');\n};\n\nexport const convert2WikipediaStyleAnchor = (anchor: string) => {\n anchor = encodeURIComponent(anchor);\n anchor = anchor.replace(/\\%+/g, '.');\n\n return anchor;\n};\n\nexport const getHeadingTagName2Number = (tagName: string) => {\n return Number(tagName.substring(1));\n};\n"],"names":["hasParentNode","element","parent","parentNode","renderAnchorLink","headings","anchors","options","a","document","createElement","anchorLinkClassName","classList","add","forEach","heading","id","i","length","hash","replace","anchor","cloneNode","setAttribute","anchorLinkSymbol","textContent","anchorLinkBefore","insertBefore","firstChild","appendChild","externalOptions","anchorType","anchorLink","anchorContainerTagName","storeIds","this","Object","assign","querySelectorAll","getHeadingsElement","mokuji","generateMokuji","elementContainer","generateHierarchyList","removeDuplicateIds","number","elementListClone","elementAnchorClone","currentNumber","Number","tagName","substring","nextElementOListClone","lastChild","censorshipId","anchorText","generateAnchorText","elementAnchor","href","elementList","suffix_count","tmp_id","indexOf","push","text","type","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","getElementsByTagName","innerText","matchedHeadings","Array","from","filter","count","heading_id"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mokuji.js",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "A table of content JavaScript Library",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "microbundle",
|
|
7
7
|
"clean": "rimraf lib/",
|
|
8
8
|
"watch": "microbundle watch",
|
|
9
9
|
"test": "npm run lint",
|
|
10
|
-
"
|
|
10
|
+
"prepare": "npm run clean && npm run build && husky install"
|
|
11
11
|
},
|
|
12
12
|
"source": "src/index.ts",
|
|
13
13
|
"main": "dist/mokuji.js",
|
|
14
|
-
"types": "dist/
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
15
|
"module": "dist/mokuji.module.js",
|
|
16
16
|
"esmodule": "dist/mokuji.modern.js",
|
|
17
17
|
"unpkg": "dist/mokuji.umd.js",
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist/",
|
|
23
23
|
"lib/",
|
|
24
|
-
"src/"
|
|
24
|
+
"src/",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"READNME.md"
|
|
25
27
|
],
|
|
26
28
|
"lint-staged": {
|
|
27
29
|
"*.{js,ts}": [
|
|
@@ -44,27 +46,15 @@
|
|
|
44
46
|
"url": "https://github.com/hiro0218/mokuji.js/issues"
|
|
45
47
|
},
|
|
46
48
|
"homepage": "https://github.com/hiro0218/mokuji.js",
|
|
47
|
-
"prettier": {
|
|
48
|
-
"printWidth": 120,
|
|
49
|
-
"singleQuote": false,
|
|
50
|
-
"trailingComma": "all",
|
|
51
|
-
"bracketSpacing": true
|
|
52
|
-
},
|
|
53
49
|
"devDependencies": {
|
|
54
50
|
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
|
55
|
-
"@
|
|
56
|
-
"@types/node": "~14.14.41",
|
|
51
|
+
"@types/node": "~17.0.23",
|
|
57
52
|
"cross-env": "^5.2.0",
|
|
58
|
-
"husky": "^
|
|
59
|
-
"lint-staged": "^
|
|
60
|
-
"microbundle": "^0.
|
|
61
|
-
"prettier": "^2.0
|
|
53
|
+
"husky": "^7.0.4",
|
|
54
|
+
"lint-staged": "^12.3.7",
|
|
55
|
+
"microbundle": "^0.14.2",
|
|
56
|
+
"prettier": "^2.6.0",
|
|
62
57
|
"rimraf": "^3.0.2",
|
|
63
|
-
"typescript": "~4.2
|
|
64
|
-
},
|
|
65
|
-
"husky": {
|
|
66
|
-
"hooks": {
|
|
67
|
-
"pre-commit": "lint-staged"
|
|
68
|
-
}
|
|
58
|
+
"typescript": "~4.6.2"
|
|
69
59
|
}
|
|
70
60
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,82 +1,121 @@
|
|
|
1
|
-
import { hasParentNode, getHeadingsElement } from
|
|
2
|
-
import { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from
|
|
3
|
-
|
|
4
|
-
type MokujiOption = {
|
|
5
|
-
anchorType
|
|
6
|
-
anchorLink
|
|
7
|
-
anchorLinkSymbol
|
|
8
|
-
anchorLinkBefore
|
|
9
|
-
anchorLinkClassName
|
|
10
|
-
anchorContainerTagName
|
|
1
|
+
import { hasParentNode, getHeadingsElement } from './dom';
|
|
2
|
+
import { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from './utils';
|
|
3
|
+
|
|
4
|
+
export type MokujiOption = {
|
|
5
|
+
anchorType?: boolean;
|
|
6
|
+
anchorLink?: boolean;
|
|
7
|
+
anchorLinkSymbol?: string;
|
|
8
|
+
anchorLinkBefore?: boolean;
|
|
9
|
+
anchorLinkClassName?: string;
|
|
10
|
+
anchorContainerTagName?: 'ul' | 'ol';
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export
|
|
14
|
-
headings: NodeListOf<HTMLHeadingElement
|
|
15
|
-
|
|
13
|
+
export const renderAnchorLink = (
|
|
14
|
+
headings: NodeListOf<HTMLHeadingElement>,
|
|
15
|
+
anchors: NodeListOf<HTMLAnchorElement> | undefined,
|
|
16
|
+
options: MokujiOption,
|
|
17
|
+
) => {
|
|
18
|
+
if (!anchors) return;
|
|
19
|
+
|
|
20
|
+
const a = document.createElement('a');
|
|
21
|
+
|
|
22
|
+
if (options.anchorLinkClassName) {
|
|
23
|
+
a.classList.add(options.anchorLinkClassName);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
headings.forEach((heading) => {
|
|
27
|
+
const { id } = heading;
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < anchors.length; i++) {
|
|
30
|
+
const { hash } = anchors[i];
|
|
31
|
+
|
|
32
|
+
if (hash.replace('#', '') !== id) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// create anchor
|
|
37
|
+
const anchor = a.cloneNode(false) as HTMLAnchorElement;
|
|
38
|
+
anchor.setAttribute('href', hash);
|
|
39
|
+
|
|
40
|
+
if (options.anchorLinkSymbol) {
|
|
41
|
+
anchor.textContent = options.anchorLinkSymbol;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// insert anchor into headings
|
|
45
|
+
if (options.anchorLinkBefore) {
|
|
46
|
+
// before
|
|
47
|
+
heading.insertBefore(anchor, heading.firstChild);
|
|
48
|
+
} else {
|
|
49
|
+
// after
|
|
50
|
+
heading.appendChild(anchor);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export class Mokuji {
|
|
57
|
+
options: MokujiOption = {
|
|
58
|
+
anchorType: true,
|
|
59
|
+
anchorLink: false,
|
|
60
|
+
anchorLinkSymbol: '#',
|
|
61
|
+
anchorLinkBefore: true,
|
|
62
|
+
anchorLinkClassName: '',
|
|
63
|
+
anchorContainerTagName: 'ol',
|
|
64
|
+
};
|
|
16
65
|
storeIds: string[] = [];
|
|
17
66
|
|
|
18
|
-
constructor(element: HTMLElement, externalOptions: MokujiOption) {
|
|
67
|
+
constructor(element: HTMLElement | null, externalOptions: MokujiOption) {
|
|
68
|
+
if (!element) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
19
72
|
// Merge the default options with the external options.
|
|
20
73
|
this.options = Object.assign(
|
|
21
74
|
// default options
|
|
22
|
-
|
|
23
|
-
anchorType: true,
|
|
24
|
-
anchorLink: false,
|
|
25
|
-
anchorLinkSymbol: "#",
|
|
26
|
-
anchorLinkBefore: true,
|
|
27
|
-
anchorLinkClassName: "",
|
|
28
|
-
anchorContainerTagName: "ol",
|
|
29
|
-
},
|
|
75
|
+
this.options,
|
|
30
76
|
externalOptions,
|
|
31
77
|
);
|
|
32
78
|
|
|
33
|
-
|
|
79
|
+
const headings = getHeadingsElement(element);
|
|
34
80
|
|
|
35
81
|
// mokuji start
|
|
36
|
-
const mokuji = this.render();
|
|
37
|
-
|
|
38
|
-
// @ts-ignore
|
|
39
|
-
return mokuji;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
render() {
|
|
43
82
|
// generate mokuji list
|
|
44
|
-
const
|
|
83
|
+
const mokuji = this.generateMokuji(headings);
|
|
45
84
|
|
|
46
85
|
// setup anchor link
|
|
47
86
|
if (this.options.anchorLink) {
|
|
48
|
-
const anchors =
|
|
49
|
-
|
|
87
|
+
const anchors = mokuji.querySelectorAll('a');
|
|
88
|
+
renderAnchorLink(headings, anchors, this.options);
|
|
50
89
|
}
|
|
51
90
|
|
|
52
|
-
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
return mokuji;
|
|
53
93
|
}
|
|
54
94
|
|
|
55
|
-
generateMokuji() {
|
|
56
|
-
let elementContainer = document.createElement(this.options.anchorContainerTagName);
|
|
95
|
+
generateMokuji(headings: NodeListOf<HTMLHeadingElement>) {
|
|
96
|
+
let elementContainer = document.createElement(this.options.anchorContainerTagName || 'ol');
|
|
57
97
|
|
|
58
|
-
this.generateHierarchyList(elementContainer);
|
|
98
|
+
this.generateHierarchyList(headings, elementContainer);
|
|
59
99
|
|
|
60
100
|
// remove duplicates by adding suffix
|
|
61
|
-
|
|
62
|
-
this.removeDuplicateIds(anchors);
|
|
101
|
+
this.removeDuplicateIds(headings, elementContainer);
|
|
63
102
|
|
|
64
103
|
return elementContainer;
|
|
65
104
|
}
|
|
66
105
|
|
|
67
|
-
generateHierarchyList(elementContainer: HTMLElement) {
|
|
106
|
+
generateHierarchyList(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {
|
|
68
107
|
let number = 0;
|
|
69
|
-
const elementListClone = document.createElement(
|
|
70
|
-
const elementAnchorClone = document.createElement(
|
|
108
|
+
const elementListClone = document.createElement('li');
|
|
109
|
+
const elementAnchorClone = document.createElement('a');
|
|
71
110
|
|
|
72
|
-
for (let i = 0; i <
|
|
73
|
-
const heading =
|
|
111
|
+
for (let i = 0; i < headings.length; i++) {
|
|
112
|
+
const heading = headings[i];
|
|
74
113
|
const currentNumber = getHeadingTagName2Number(heading.tagName);
|
|
75
114
|
|
|
76
115
|
// check list hierarchy
|
|
77
116
|
if (number !== 0 && number < currentNumber) {
|
|
78
117
|
// number of the heading is large (small as heading)
|
|
79
|
-
const nextElementOListClone = document.createElement(
|
|
118
|
+
const nextElementOListClone = document.createElement('ol');
|
|
80
119
|
// @ts-ignore
|
|
81
120
|
elementContainer.lastChild.appendChild(nextElementOListClone);
|
|
82
121
|
elementContainer = nextElementOListClone;
|
|
@@ -90,14 +129,15 @@ export default class Mokuji {
|
|
|
90
129
|
}
|
|
91
130
|
}
|
|
92
131
|
|
|
93
|
-
const textContent = this.censorshipId(heading.textContent);
|
|
132
|
+
const textContent = this.censorshipId(headings, heading.textContent);
|
|
94
133
|
|
|
95
134
|
// headingへidを付与
|
|
96
|
-
|
|
135
|
+
const anchorText = this.generateAnchorText(textContent, !!this.options.anchorType);
|
|
136
|
+
heading.id = anchorText;
|
|
97
137
|
|
|
98
138
|
// add to wrapper
|
|
99
139
|
const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;
|
|
100
|
-
elementAnchor.href =
|
|
140
|
+
elementAnchor.href = `#${anchorText}`;
|
|
101
141
|
elementAnchor.textContent = heading.textContent;
|
|
102
142
|
const elementList = elementListClone.cloneNode(false);
|
|
103
143
|
elementList.appendChild(elementAnchor);
|
|
@@ -108,12 +148,12 @@ export default class Mokuji {
|
|
|
108
148
|
}
|
|
109
149
|
}
|
|
110
150
|
|
|
111
|
-
censorshipId(textContent: string | null) {
|
|
112
|
-
let id = textContent ||
|
|
151
|
+
censorshipId(headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) {
|
|
152
|
+
let id = textContent || '';
|
|
113
153
|
let suffix_count = 1;
|
|
114
154
|
|
|
115
155
|
// IDが重複していた場合はsuffixを付ける
|
|
116
|
-
while (suffix_count <=
|
|
156
|
+
while (suffix_count <= headings.length) {
|
|
117
157
|
const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;
|
|
118
158
|
|
|
119
159
|
if (this.storeIds.indexOf(tmp_id) === -1) {
|
|
@@ -128,12 +168,12 @@ export default class Mokuji {
|
|
|
128
168
|
return id;
|
|
129
169
|
}
|
|
130
170
|
|
|
131
|
-
|
|
171
|
+
generateAnchorText(text: string, type: boolean) {
|
|
132
172
|
// convert spaces to _
|
|
133
173
|
let anchor = replaceSpace2Underscore(text);
|
|
134
174
|
|
|
135
175
|
// remove &
|
|
136
|
-
anchor = anchor.replace(/\&+/g,
|
|
176
|
+
anchor = anchor.replace(/\&+/g, '').replace(/\&+/g, '');
|
|
137
177
|
|
|
138
178
|
if (type === true) {
|
|
139
179
|
anchor = convert2WikipediaStyleAnchor(anchor);
|
|
@@ -142,50 +182,22 @@ export default class Mokuji {
|
|
|
142
182
|
return anchor;
|
|
143
183
|
}
|
|
144
184
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const a = document.createElement("a");
|
|
149
|
-
a.classList.add(this.options.anchorLinkClassName);
|
|
185
|
+
removeDuplicateIds(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {
|
|
186
|
+
const anchors = elementContainer.getElementsByTagName('a');
|
|
150
187
|
|
|
151
|
-
for (let i = 0; i < anchors.length; i++) {
|
|
152
|
-
const hash = anchors[i].hash;
|
|
153
|
-
const headings = document.querySelector(`[id="${hash.replace("#", "")}"]`);
|
|
154
|
-
|
|
155
|
-
if (!headings) {
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// create anchor
|
|
160
|
-
const anchor = a.cloneNode(false) as HTMLAnchorElement;
|
|
161
|
-
anchor.setAttribute("href", hash);
|
|
162
|
-
anchor.textContent = this.options.anchorLinkSymbol;
|
|
163
|
-
|
|
164
|
-
// insert anchor into headings
|
|
165
|
-
if (this.options.anchorLinkBefore) {
|
|
166
|
-
// before
|
|
167
|
-
headings.insertBefore(anchor, headings.firstChild);
|
|
168
|
-
} else {
|
|
169
|
-
// after
|
|
170
|
-
headings.appendChild(anchor);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
removeDuplicateIds(anchors: HTMLCollectionOf<HTMLAnchorElement>) {
|
|
176
188
|
for (let i = 0; i < anchors.length; i++) {
|
|
177
189
|
const id = anchors[i].innerText;
|
|
178
190
|
const hash = anchors[i].hash;
|
|
179
|
-
const
|
|
191
|
+
const matchedHeadings = Array.from(headings).filter((heading) => heading.id === id);
|
|
180
192
|
|
|
181
|
-
if (
|
|
193
|
+
if (matchedHeadings.length === 1) {
|
|
182
194
|
continue;
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
// duplicated id
|
|
186
198
|
let count = 0;
|
|
187
199
|
|
|
188
|
-
|
|
200
|
+
matchedHeadings.forEach((heading) => {
|
|
189
201
|
const heading_id = `${heading.id}-${count}`;
|
|
190
202
|
|
|
191
203
|
// search duplicate list
|
|
@@ -200,7 +212,7 @@ export default class Mokuji {
|
|
|
200
212
|
// update id
|
|
201
213
|
heading.id = heading_id;
|
|
202
214
|
count++;
|
|
203
|
-
}
|
|
215
|
+
});
|
|
204
216
|
}
|
|
205
217
|
}
|
|
206
218
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
export const replaceSpace2Underscore = (text: string) => {
|
|
2
|
-
return text.replace(/\s+/g,
|
|
2
|
+
return text.replace(/\s+/g, '_');
|
|
3
3
|
};
|
|
4
4
|
|
|
5
5
|
export const convert2WikipediaStyleAnchor = (anchor: string) => {
|
|
6
6
|
anchor = encodeURIComponent(anchor);
|
|
7
|
-
anchor = anchor.replace(/\%+/g,
|
|
7
|
+
anchor = anchor.replace(/\%+/g, '.');
|
|
8
8
|
|
|
9
9
|
return anchor;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export const getHeadingTagName2Number = (tagName: string) => {
|
|
13
|
-
|
|
14
|
-
const currentNumber = tagName.match(/\d/g).join("");
|
|
15
|
-
|
|
16
|
-
return Number(currentNumber);
|
|
13
|
+
return Number(tagName.substring(1));
|
|
17
14
|
};
|