mokuji.js 3.2.1 → 4.0.0

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 CHANGED
@@ -14,11 +14,13 @@ npm install --save mokuji.js
14
14
  ```javascript
15
15
  import { Mokuji } from 'mokuji.js';
16
16
 
17
- let textElement = document.querySelector('.text');
18
- let mokuji = new Mokuji(textElement);
17
+ const textElement = document.querySelector('.text');
18
+ const mokujiList = Mokuji(textElement);
19
19
 
20
- let listElement = document.querySelector('.list');
21
- listElement.appendChild(mokuji);
20
+ if (!mokujiList) return;
21
+
22
+ const listElement = document.querySelector('.list');
23
+ listElement?.appendChild(mokujiList);
22
24
  ```
23
25
 
24
26
  ## Options
package/dist/index.d.ts CHANGED
@@ -1,19 +1,11 @@
1
+ declare type AnchorContainerTagNameProps = 'ul' | 'ol';
1
2
  export declare type MokujiOption = {
2
3
  anchorType?: boolean;
3
4
  anchorLink?: boolean;
4
5
  anchorLinkSymbol?: string;
5
6
  anchorLinkBefore?: boolean;
6
7
  anchorLinkClassName?: string;
7
- anchorContainerTagName?: 'ul' | 'ol';
8
+ anchorContainerTagName?: AnchorContainerTagNameProps;
8
9
  };
9
- export declare const renderAnchorLink: (headings: NodeListOf<HTMLHeadingElement>, anchors: NodeListOf<HTMLAnchorElement> | undefined, options: MokujiOption) => void;
10
- export declare class Mokuji {
11
- options: MokujiOption;
12
- storeIds: string[];
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;
19
- }
10
+ export declare const Mokuji: (element: HTMLElement | null, externalOptions?: MokujiOption | undefined) => HTMLOListElement | undefined;
11
+ export {};
package/dist/mokuji.js CHANGED
@@ -1,2 +1,2 @@
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(/\&amp;+/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;
1
+ var e=function(e,n){for(;e;){if(e===n)return!0;e=e.parentNode}return!1},n={anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},r=[],a=function(e,n){for(var a=n||"",t=1;t<=e.length;){var o=1===t?a:a+"_"+t;if(-1===r.indexOf(o)){r.push(a=o);break}t++}return a};exports.Mokuji=function(r,t){if(r){var o=Object.assign(n,t),i=function(e){return e.querySelectorAll("h1, h2, h3, h4, h5, h6")}(r),c=document.createElement(o.anchorContainerTagName||n.anchorContainerTagName);return function(n,r,t){for(var o=0,i=document.createElement("li"),c=document.createElement("a"),h=0;h<n.length;h++){var l=n[h],f=Number(l.tagName.substring(1));if(0!==o&&o<f){var u=document.createElement("ol");r.lastChild.appendChild(u),r=u}else if(0!==o&&o>f)for(var d=0;d<o-f;d++)e(r,r.parentNode)&&(r=r.parentNode.parentNode);var m=(g=t,C=void 0,C=(v=a(n,l.textContent),C=v.replace(/\s+/g,"_")).replace(/\&+/g,"").replace(/\&amp;+/g,""),!0===g&&(C=function(e){return(e=encodeURIComponent(e)).replace(/\%+/g,".")}(C)),C);l.id=m;var s=c.cloneNode(!1);s.href="#"+m,s.textContent=l.textContent;var p=i.cloneNode(!1);p.appendChild(s),r.appendChild(p),o=f}var v,g,C}(i,c,!!o.anchorType),function(e,n){for(var r=n.getElementsByTagName("a"),a=function(n){var a=r[n].innerText,t=r[n].hash,o=Array.from(e).filter(function(e){return e.id===a});if(1===o.length)return"continue";var i=0;o.forEach(function(e){for(var n=e.id+"-"+i,a=0,o=Array.from(r);a<o.length;a++){var c=o[a];if(c.hash===t){c.href="#"+n;break}}e.id=n,i++})},t=0;t<r.length;t++)a(t)}(i,c),o.anchorLink&&function(e,n,r){if(n){var a=document.createElement("a");r.anchorLinkClassName&&a.classList.add(r.anchorLinkClassName),e.forEach(function(e){for(var t=e.id,o=0;o<n.length;o++){var i=n[o].hash;if(i.replace("#","")===t){var c=a.cloneNode(!1);c.setAttribute("href",i),r.anchorLinkSymbol&&(c.textContent=r.anchorLinkSymbol),r.anchorLinkBefore?e.insertBefore(c,e.firstChild):e.appendChild(c)}}})}}(i,c.querySelectorAll("a"),o),c}};
2
2
  //# sourceMappingURL=mokuji.js.map
@@ -1 +1 @@
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(/\\&amp;+/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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"}
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\ntype AnchorContainerTagNameProps = 'ul' | 'ol';\n\nexport type MokujiOption = {\n anchorType?: boolean;\n anchorLink?: boolean;\n anchorLinkSymbol?: string;\n anchorLinkBefore?: boolean;\n anchorLinkClassName?: string;\n anchorContainerTagName?: AnchorContainerTagNameProps;\n};\n\nconst defaultOptions = {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n} as const;\n\nconst storeIds: string[] = [];\n\nconst 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\nconst 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\nconst 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 (storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n};\n\nconst generateAnchorText = (text: string, type: boolean) => {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, '').replace(/\\&amp;+/g, '');\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n};\n\nconst generateHierarchyList = (\n headings: NodeListOf<HTMLHeadingElement>,\n elementContainer: HTMLUListElement | HTMLOListElement,\n anchorType: boolean,\n) => {\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 = censorshipId(headings, heading.textContent);\n\n // headingへidを付与\n const anchorText = generateAnchorText(textContent, 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) as HTMLLIElement;\n elementList.appendChild(elementAnchor);\n\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n};\n\nexport const Mokuji = (element: HTMLElement | null, externalOptions?: MokujiOption): HTMLOListElement | undefined => {\n if (!element) {\n return;\n }\n\n // Merge the default options with the external options.\n const options = Object.assign(\n // default options\n defaultOptions,\n externalOptions,\n );\n\n const headings = getHeadingsElement(element);\n\n // mokuji start\n const elementContainer = document.createElement(\n options.anchorContainerTagName || defaultOptions.anchorContainerTagName,\n );\n\n // generate mokuji list\n generateHierarchyList(headings, elementContainer, !!options.anchorType);\n\n // remove duplicates by adding suffix\n removeDuplicateIds(headings, elementContainer);\n\n // setup anchor link\n if (options.anchorLink) {\n const anchors = elementContainer.querySelectorAll('a');\n renderAnchorLink(headings, anchors, options);\n }\n\n return elementContainer;\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","defaultOptions","anchorType","anchorLink","anchorLinkSymbol","anchorLinkBefore","anchorLinkClassName","anchorContainerTagName","storeIds","censorshipId","headings","textContent","id","suffix_count","length","tmp_id","indexOf","push","externalOptions","options","Object","assign","querySelectorAll","getHeadingsElement","elementContainer","document","createElement","number","elementListClone","elementAnchorClone","i","heading","currentNumber","Number","tagName","substring","nextElementOListClone","lastChild","appendChild","anchorText","type","anchor","text","replace","encodeURIComponent","convert2WikipediaStyleAnchor","elementAnchor","cloneNode","href","elementList","generateHierarchyList","anchors","getElementsByTagName","innerText","hash","matchedHeadings","Array","from","filter","count","forEach","heading_id","removeDuplicateIds","a","classList","add","setAttribute","insertBefore","firstChild","renderAnchorLink"],"mappings":"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"}
@@ -1,2 +1,2 @@
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(/\&amp;+/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};
1
+ const e=(e,n)=>{for(;e;){if(e===n)return!0;e=e.parentNode}return!1},n={anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},t=[],o=(e,n)=>{let o=n||"",r=1;for(;r<=e.length;){const e=1===r?o:`${o}_${r}`;if(-1===t.indexOf(e)){o=e,t.push(o);break}r++}return o},r=(e,n)=>{let t=(e=>e.replace(/\s+/g,"_"))(e);return t=t.replace(/\&+/g,"").replace(/\&amp;+/g,""),!0===n&&(t=(e=>(e=encodeURIComponent(e)).replace(/\%+/g,"."))(t)),t},a=(t,a)=>{if(!t)return;const c=Object.assign(n,a),l=(e=>e.querySelectorAll("h1, h2, h3, h4, h5, h6"))(t),i=document.createElement(c.anchorContainerTagName||n.anchorContainerTagName);return((n,t,a)=>{let c=0;const l=document.createElement("li"),i=document.createElement("a");for(let h=0;h<n.length;h++){const s=n[h],d=Number(s.tagName.substring(1));if(0!==c&&c<d){const e=document.createElement("ol");t.lastChild.appendChild(e),t=e}else if(0!==c&&c>d)for(let n=0;n<c-d;n++)e(t,t.parentNode)&&(t=t.parentNode.parentNode);const f=o(n,s.textContent),m=r(f,a);s.id=m;const p=i.cloneNode(!1);p.href=`#${m}`,p.textContent=s.textContent;const u=l.cloneNode(!1);u.appendChild(p),t.appendChild(u),c=d}})(l,i,!!c.anchorType),((e,n)=>{const t=n.getElementsByTagName("a");for(let n=0;n<t.length;n++){const o=t[n].innerText,r=t[n].hash,a=Array.from(e).filter(e=>e.id===o);if(1===a.length)continue;let c=0;a.forEach(e=>{const n=`${e.id}-${c}`;for(const e of Array.from(t))if(e.hash===r){e.href=`#${n}`;break}e.id=n,c++})}})(l,i),c.anchorLink&&((e,n,t)=>{if(!n)return;const o=document.createElement("a");t.anchorLinkClassName&&o.classList.add(t.anchorLinkClassName),e.forEach(e=>{const{id:r}=e;for(let a=0;a<n.length;a++){const{hash:c}=n[a];if(c.replace("#","")!==r)continue;const l=o.cloneNode(!1);l.setAttribute("href",c),t.anchorLinkSymbol&&(l.textContent=t.anchorLinkSymbol),t.anchorLinkBefore?e.insertBefore(l,e.firstChild):e.appendChild(l)}})})(l,i.querySelectorAll("a"),c),i};export{a as Mokuji};
2
2
  //# sourceMappingURL=mokuji.modern.js.map
@@ -1 +1 @@
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(/\\&amp;+/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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"}
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\ntype AnchorContainerTagNameProps = 'ul' | 'ol';\n\nexport type MokujiOption = {\n anchorType?: boolean;\n anchorLink?: boolean;\n anchorLinkSymbol?: string;\n anchorLinkBefore?: boolean;\n anchorLinkClassName?: string;\n anchorContainerTagName?: AnchorContainerTagNameProps;\n};\n\nconst defaultOptions = {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n} as const;\n\nconst storeIds: string[] = [];\n\nconst 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\nconst 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\nconst 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 (storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n};\n\nconst generateAnchorText = (text: string, type: boolean) => {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, '').replace(/\\&amp;+/g, '');\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n};\n\nconst generateHierarchyList = (\n headings: NodeListOf<HTMLHeadingElement>,\n elementContainer: HTMLUListElement | HTMLOListElement,\n anchorType: boolean,\n) => {\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 = censorshipId(headings, heading.textContent);\n\n // headingへidを付与\n const anchorText = generateAnchorText(textContent, 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) as HTMLLIElement;\n elementList.appendChild(elementAnchor);\n\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n};\n\nexport const Mokuji = (element: HTMLElement | null, externalOptions?: MokujiOption): HTMLOListElement | undefined => {\n if (!element) {\n return;\n }\n\n // Merge the default options with the external options.\n const options = Object.assign(\n // default options\n defaultOptions,\n externalOptions,\n );\n\n const headings = getHeadingsElement(element);\n\n // mokuji start\n const elementContainer = document.createElement(\n options.anchorContainerTagName || defaultOptions.anchorContainerTagName,\n );\n\n // generate mokuji list\n generateHierarchyList(headings, elementContainer, !!options.anchorType);\n\n // remove duplicates by adding suffix\n removeDuplicateIds(headings, elementContainer);\n\n // setup anchor link\n if (options.anchorLink) {\n const anchors = elementContainer.querySelectorAll('a');\n renderAnchorLink(headings, anchors, options);\n }\n\n return elementContainer;\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","defaultOptions","anchorType","anchorLink","anchorLinkSymbol","anchorLinkBefore","anchorLinkClassName","anchorContainerTagName","storeIds","censorshipId","headings","textContent","id","suffix_count","length","tmp_id","indexOf","push","generateAnchorText","text","type","anchor","replace","replaceSpace2Underscore","encodeURIComponent","convert2WikipediaStyleAnchor","Mokuji","externalOptions","options","Object","assign","querySelectorAll","getHeadingsElement","elementContainer","document","createElement","number","elementListClone","elementAnchorClone","i","heading","currentNumber","Number","tagName","substring","nextElementOListClone","lastChild","appendChild","anchorText","elementAnchor","cloneNode","href","elementList","generateHierarchyList","anchors","getElementsByTagName","innerText","hash","matchedHeadings","Array","from","filter","count","forEach","heading_id","removeDuplicateIds","a","classList","add","setAttribute","insertBefore","firstChild","renderAnchorLink"],"mappings":"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"}
@@ -1,2 +1,2 @@
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(/\&amp;+/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};
1
+ var e=function(e,n){for(;e;){if(e===n)return!0;e=e.parentNode}return!1},n={anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},r=[],a=function(e,n){for(var a=n||"",t=1;t<=e.length;){var o=1===t?a:a+"_"+t;if(-1===r.indexOf(o)){r.push(a=o);break}t++}return a},t=function(r,t){if(r){var o=Object.assign(n,t),i=function(e){return e.querySelectorAll("h1, h2, h3, h4, h5, h6")}(r),c=document.createElement(o.anchorContainerTagName||n.anchorContainerTagName);return function(n,r,t){for(var o=0,i=document.createElement("li"),c=document.createElement("a"),h=0;h<n.length;h++){var l=n[h],f=Number(l.tagName.substring(1));if(0!==o&&o<f){var d=document.createElement("ol");r.lastChild.appendChild(d),r=d}else if(0!==o&&o>f)for(var u=0;u<o-f;u++)e(r,r.parentNode)&&(r=r.parentNode.parentNode);var m=(g=t,C=void 0,C=(v=a(n,l.textContent),C=v.replace(/\s+/g,"_")).replace(/\&+/g,"").replace(/\&amp;+/g,""),!0===g&&(C=function(e){return(e=encodeURIComponent(e)).replace(/\%+/g,".")}(C)),C);l.id=m;var s=c.cloneNode(!1);s.href="#"+m,s.textContent=l.textContent;var p=i.cloneNode(!1);p.appendChild(s),r.appendChild(p),o=f}var v,g,C}(i,c,!!o.anchorType),function(e,n){for(var r=n.getElementsByTagName("a"),a=function(n){var a=r[n].innerText,t=r[n].hash,o=Array.from(e).filter(function(e){return e.id===a});if(1===o.length)return"continue";var i=0;o.forEach(function(e){for(var n=e.id+"-"+i,a=0,o=Array.from(r);a<o.length;a++){var c=o[a];if(c.hash===t){c.href="#"+n;break}}e.id=n,i++})},t=0;t<r.length;t++)a(t)}(i,c),o.anchorLink&&function(e,n,r){if(n){var a=document.createElement("a");r.anchorLinkClassName&&a.classList.add(r.anchorLinkClassName),e.forEach(function(e){for(var t=e.id,o=0;o<n.length;o++){var i=n[o].hash;if(i.replace("#","")===t){var c=a.cloneNode(!1);c.setAttribute("href",i),r.anchorLinkSymbol&&(c.textContent=r.anchorLinkSymbol),r.anchorLinkBefore?e.insertBefore(c,e.firstChild):e.appendChild(c)}}})}}(i,c.querySelectorAll("a"),o),c}};export{t as Mokuji};
2
2
  //# sourceMappingURL=mokuji.module.js.map
@@ -1 +1 @@
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(/\\&amp;+/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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"}
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\ntype AnchorContainerTagNameProps = 'ul' | 'ol';\n\nexport type MokujiOption = {\n anchorType?: boolean;\n anchorLink?: boolean;\n anchorLinkSymbol?: string;\n anchorLinkBefore?: boolean;\n anchorLinkClassName?: string;\n anchorContainerTagName?: AnchorContainerTagNameProps;\n};\n\nconst defaultOptions = {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n} as const;\n\nconst storeIds: string[] = [];\n\nconst 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\nconst 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\nconst 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 (storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n};\n\nconst generateAnchorText = (text: string, type: boolean) => {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, '').replace(/\\&amp;+/g, '');\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n};\n\nconst generateHierarchyList = (\n headings: NodeListOf<HTMLHeadingElement>,\n elementContainer: HTMLUListElement | HTMLOListElement,\n anchorType: boolean,\n) => {\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 = censorshipId(headings, heading.textContent);\n\n // headingへidを付与\n const anchorText = generateAnchorText(textContent, 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) as HTMLLIElement;\n elementList.appendChild(elementAnchor);\n\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n};\n\nexport const Mokuji = (element: HTMLElement | null, externalOptions?: MokujiOption): HTMLOListElement | undefined => {\n if (!element) {\n return;\n }\n\n // Merge the default options with the external options.\n const options = Object.assign(\n // default options\n defaultOptions,\n externalOptions,\n );\n\n const headings = getHeadingsElement(element);\n\n // mokuji start\n const elementContainer = document.createElement(\n options.anchorContainerTagName || defaultOptions.anchorContainerTagName,\n );\n\n // generate mokuji list\n generateHierarchyList(headings, elementContainer, !!options.anchorType);\n\n // remove duplicates by adding suffix\n removeDuplicateIds(headings, elementContainer);\n\n // setup anchor link\n if (options.anchorLink) {\n const anchors = elementContainer.querySelectorAll('a');\n renderAnchorLink(headings, anchors, options);\n }\n\n return elementContainer;\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","defaultOptions","anchorType","anchorLink","anchorLinkSymbol","anchorLinkBefore","anchorLinkClassName","anchorContainerTagName","storeIds","censorshipId","headings","textContent","id","suffix_count","length","tmp_id","indexOf","push","Mokuji","externalOptions","options","Object","assign","querySelectorAll","getHeadingsElement","elementContainer","document","createElement","number","elementListClone","elementAnchorClone","i","heading","currentNumber","Number","tagName","substring","nextElementOListClone","lastChild","appendChild","anchorText","type","anchor","text","replace","encodeURIComponent","convert2WikipediaStyleAnchor","elementAnchor","cloneNode","href","elementList","generateHierarchyList","anchors","getElementsByTagName","innerText","hash","matchedHeadings","Array","from","filter","count","forEach","heading_id","removeDuplicateIds","a","classList","add","setAttribute","insertBefore","firstChild","renderAnchorLink"],"mappings":"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"}
@@ -1,2 +1,2 @@
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(/\&amp;+/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});
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},r={anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},t=[],a=function(e,n){for(var r=n||"",a=1;a<=e.length;){var o=1===a?r:r+"_"+a;if(-1===t.indexOf(o)){t.push(r=o);break}a++}return r};e.Mokuji=function(e,t){if(e){var o=Object.assign(r,t),i=function(e){return e.querySelectorAll("h1, h2, h3, h4, h5, h6")}(e),c=document.createElement(o.anchorContainerTagName||r.anchorContainerTagName);return function(e,r,t){for(var o=0,i=document.createElement("li"),c=document.createElement("a"),f=0;f<e.length;f++){var l=e[f],h=Number(l.tagName.substring(1));if(0!==o&&o<h){var d=document.createElement("ol");r.lastChild.appendChild(d),r=d}else if(0!==o&&o>h)for(var u=0;u<o-h;u++)n(r,r.parentNode)&&(r=r.parentNode.parentNode);var s=(g=t,C=void 0,C=(v=a(e,l.textContent),C=v.replace(/\s+/g,"_")).replace(/\&+/g,"").replace(/\&amp;+/g,""),!0===g&&(C=function(e){return(e=encodeURIComponent(e)).replace(/\%+/g,".")}(C)),C);l.id=s;var m=c.cloneNode(!1);m.href="#"+s,m.textContent=l.textContent;var p=i.cloneNode(!1);p.appendChild(m),r.appendChild(p),o=h}var v,g,C}(i,c,!!o.anchorType),function(e,n){for(var r=n.getElementsByTagName("a"),t=function(n){var t=r[n].innerText,a=r[n].hash,o=Array.from(e).filter(function(e){return e.id===t});if(1===o.length)return"continue";var i=0;o.forEach(function(e){for(var n=e.id+"-"+i,t=0,o=Array.from(r);t<o.length;t++){var c=o[t];if(c.hash===a){c.href="#"+n;break}}e.id=n,i++})},a=0;a<r.length;a++)t(a)}(i,c),o.anchorLink&&function(e,n,r){if(n){var t=document.createElement("a");r.anchorLinkClassName&&t.classList.add(r.anchorLinkClassName),e.forEach(function(e){for(var a=e.id,o=0;o<n.length;o++){var i=n[o].hash;if(i.replace("#","")===a){var c=t.cloneNode(!1);c.setAttribute("href",i),r.anchorLinkSymbol&&(c.textContent=r.anchorLinkSymbol),r.anchorLinkBefore?e.insertBefore(c,e.firstChild):e.appendChild(c)}}})}}(i,c.querySelectorAll("a"),o),c}}});
2
2
  //# sourceMappingURL=mokuji.umd.js.map
@@ -1 +1 @@
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(/\\&amp;+/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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"}
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\ntype AnchorContainerTagNameProps = 'ul' | 'ol';\n\nexport type MokujiOption = {\n anchorType?: boolean;\n anchorLink?: boolean;\n anchorLinkSymbol?: string;\n anchorLinkBefore?: boolean;\n anchorLinkClassName?: string;\n anchorContainerTagName?: AnchorContainerTagNameProps;\n};\n\nconst defaultOptions = {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n} as const;\n\nconst storeIds: string[] = [];\n\nconst 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\nconst 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\nconst 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 (storeIds.indexOf(tmp_id) === -1) {\n id = tmp_id;\n storeIds.push(id);\n break;\n }\n\n suffix_count++;\n }\n\n return id;\n};\n\nconst generateAnchorText = (text: string, type: boolean) => {\n // convert spaces to _\n let anchor = replaceSpace2Underscore(text);\n\n // remove &\n anchor = anchor.replace(/\\&+/g, '').replace(/\\&amp;+/g, '');\n\n if (type === true) {\n anchor = convert2WikipediaStyleAnchor(anchor);\n }\n\n return anchor;\n};\n\nconst generateHierarchyList = (\n headings: NodeListOf<HTMLHeadingElement>,\n elementContainer: HTMLUListElement | HTMLOListElement,\n anchorType: boolean,\n) => {\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 = censorshipId(headings, heading.textContent);\n\n // headingへidを付与\n const anchorText = generateAnchorText(textContent, 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) as HTMLLIElement;\n elementList.appendChild(elementAnchor);\n\n elementContainer.appendChild(elementList);\n\n // upadte current number\n number = currentNumber;\n }\n};\n\nexport const Mokuji = (element: HTMLElement | null, externalOptions?: MokujiOption): HTMLOListElement | undefined => {\n if (!element) {\n return;\n }\n\n // Merge the default options with the external options.\n const options = Object.assign(\n // default options\n defaultOptions,\n externalOptions,\n );\n\n const headings = getHeadingsElement(element);\n\n // mokuji start\n const elementContainer = document.createElement(\n options.anchorContainerTagName || defaultOptions.anchorContainerTagName,\n );\n\n // generate mokuji list\n generateHierarchyList(headings, elementContainer, !!options.anchorType);\n\n // remove duplicates by adding suffix\n removeDuplicateIds(headings, elementContainer);\n\n // setup anchor link\n if (options.anchorLink) {\n const anchors = elementContainer.querySelectorAll('a');\n renderAnchorLink(headings, anchors, options);\n }\n\n return elementContainer;\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","defaultOptions","anchorType","anchorLink","anchorLinkSymbol","anchorLinkBefore","anchorLinkClassName","anchorContainerTagName","storeIds","censorshipId","headings","textContent","id","suffix_count","length","tmp_id","indexOf","push","externalOptions","options","Object","assign","querySelectorAll","getHeadingsElement","elementContainer","document","createElement","number","elementListClone","elementAnchorClone","i","heading","currentNumber","Number","tagName","substring","nextElementOListClone","lastChild","appendChild","anchorText","type","anchor","text","replace","encodeURIComponent","convert2WikipediaStyleAnchor","elementAnchor","cloneNode","href","elementList","generateHierarchyList","anchors","getElementsByTagName","innerText","hash","matchedHeadings","Array","from","filter","count","forEach","heading_id","removeDuplicateIds","a","classList","add","setAttribute","insertBefore","firstChild","renderAnchorLink"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mokuji.js",
3
- "version": "3.2.1",
3
+ "version": "4.0.0",
4
4
  "description": "A table of content JavaScript Library",
5
5
  "scripts": {
6
6
  "build": "microbundle",
@@ -47,14 +47,12 @@
47
47
  },
48
48
  "homepage": "https://github.com/hiro0218/mokuji.js",
49
49
  "devDependencies": {
50
- "@babel/plugin-proposal-optional-chaining": "^7.13.12",
51
50
  "@types/node": "~17.0.23",
52
- "cross-env": "^5.2.0",
53
51
  "husky": "^7.0.4",
54
52
  "lint-staged": "^12.3.7",
55
53
  "microbundle": "^0.14.2",
56
- "prettier": "^2.6.0",
54
+ "prettier": "^2.6.1",
57
55
  "rimraf": "^3.0.2",
58
- "typescript": "~4.6.2"
56
+ "typescript": "~4.6.3"
59
57
  }
60
58
  }
package/src/index.ts CHANGED
@@ -1,16 +1,29 @@
1
1
  import { hasParentNode, getHeadingsElement } from './dom';
2
2
  import { replaceSpace2Underscore, convert2WikipediaStyleAnchor, getHeadingTagName2Number } from './utils';
3
3
 
4
+ type AnchorContainerTagNameProps = 'ul' | 'ol';
5
+
4
6
  export type MokujiOption = {
5
7
  anchorType?: boolean;
6
8
  anchorLink?: boolean;
7
9
  anchorLinkSymbol?: string;
8
10
  anchorLinkBefore?: boolean;
9
11
  anchorLinkClassName?: string;
10
- anchorContainerTagName?: 'ul' | 'ol';
12
+ anchorContainerTagName?: AnchorContainerTagNameProps;
11
13
  };
12
14
 
13
- export const renderAnchorLink = (
15
+ const defaultOptions = {
16
+ anchorType: true,
17
+ anchorLink: false,
18
+ anchorLinkSymbol: '#',
19
+ anchorLinkBefore: true,
20
+ anchorLinkClassName: '',
21
+ anchorContainerTagName: 'ol',
22
+ } as const;
23
+
24
+ const storeIds: string[] = [];
25
+
26
+ const renderAnchorLink = (
14
27
  headings: NodeListOf<HTMLHeadingElement>,
15
28
  anchors: NodeListOf<HTMLAnchorElement> | undefined,
16
29
  options: MokujiOption,
@@ -53,166 +66,154 @@ export const renderAnchorLink = (
53
66
  });
54
67
  };
55
68
 
56
- export class Mokuji {
57
- options: MokujiOption = {
58
- anchorType: true,
59
- anchorLink: false,
60
- anchorLinkSymbol: '#',
61
- anchorLinkBefore: true,
62
- anchorLinkClassName: '',
63
- anchorContainerTagName: 'ol',
64
- };
65
- storeIds: string[] = [];
66
-
67
- constructor(element: HTMLElement | null, externalOptions: MokujiOption) {
68
- if (!element) {
69
- return;
70
- }
69
+ const removeDuplicateIds = (headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) => {
70
+ const anchors = elementContainer.getElementsByTagName('a');
71
71
 
72
- // Merge the default options with the external options.
73
- this.options = Object.assign(
74
- // default options
75
- this.options,
76
- externalOptions,
77
- );
72
+ for (let i = 0; i < anchors.length; i++) {
73
+ const id = anchors[i].innerText;
74
+ const hash = anchors[i].hash;
75
+ const matchedHeadings = Array.from(headings).filter((heading) => heading.id === id);
78
76
 
79
- const headings = getHeadingsElement(element);
77
+ if (matchedHeadings.length === 1) {
78
+ continue;
79
+ }
80
80
 
81
- // mokuji start
82
- // generate mokuji list
83
- const mokuji = this.generateMokuji(headings);
81
+ // duplicated id
82
+ let count = 0;
84
83
 
85
- // setup anchor link
86
- if (this.options.anchorLink) {
87
- const anchors = mokuji.querySelectorAll('a');
88
- renderAnchorLink(headings, anchors, this.options);
89
- }
84
+ matchedHeadings.forEach((heading) => {
85
+ const heading_id = `${heading.id}-${count}`;
86
+
87
+ // search duplicate list
88
+ for (const anchor of Array.from(anchors)) {
89
+ if (anchor.hash === hash) {
90
+ // update hash
91
+ anchor.href = `#${heading_id}`;
92
+ break;
93
+ }
94
+ }
90
95
 
91
- // @ts-ignore
92
- return mokuji;
96
+ // update id
97
+ heading.id = heading_id;
98
+ count++;
99
+ });
93
100
  }
101
+ };
94
102
 
95
- generateMokuji(headings: NodeListOf<HTMLHeadingElement>) {
96
- let elementContainer = document.createElement(this.options.anchorContainerTagName || 'ol');
103
+ const censorshipId = (headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) => {
104
+ let id = textContent || '';
105
+ let suffix_count = 1;
97
106
 
98
- this.generateHierarchyList(headings, elementContainer);
107
+ // IDが重複していた場合はsuffixを付ける
108
+ while (suffix_count <= headings.length) {
109
+ const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;
99
110
 
100
- // remove duplicates by adding suffix
101
- this.removeDuplicateIds(headings, elementContainer);
111
+ if (storeIds.indexOf(tmp_id) === -1) {
112
+ id = tmp_id;
113
+ storeIds.push(id);
114
+ break;
115
+ }
102
116
 
103
- return elementContainer;
117
+ suffix_count++;
104
118
  }
105
119
 
106
- generateHierarchyList(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {
107
- let number = 0;
108
- const elementListClone = document.createElement('li');
109
- const elementAnchorClone = document.createElement('a');
110
-
111
- for (let i = 0; i < headings.length; i++) {
112
- const heading = headings[i];
113
- const currentNumber = getHeadingTagName2Number(heading.tagName);
114
-
115
- // check list hierarchy
116
- if (number !== 0 && number < currentNumber) {
117
- // number of the heading is large (small as heading)
118
- const nextElementOListClone = document.createElement('ol');
119
- // @ts-ignore
120
- elementContainer.lastChild.appendChild(nextElementOListClone);
121
- elementContainer = nextElementOListClone;
122
- } else if (number !== 0 && number > currentNumber) {
123
- // number of heading is small (large as heading)
124
- for (let i = 0; i < number - currentNumber; i++) {
125
- if (hasParentNode(elementContainer, elementContainer.parentNode)) {
126
- // @ts-ignore
127
- elementContainer = elementContainer.parentNode.parentNode;
128
- }
129
- }
130
- }
131
-
132
- const textContent = this.censorshipId(headings, heading.textContent);
120
+ return id;
121
+ };
133
122
 
134
- // headingへidを付与
135
- const anchorText = this.generateAnchorText(textContent, !!this.options.anchorType);
136
- heading.id = anchorText;
123
+ const generateAnchorText = (text: string, type: boolean) => {
124
+ // convert spaces to _
125
+ let anchor = replaceSpace2Underscore(text);
137
126
 
138
- // add to wrapper
139
- const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;
140
- elementAnchor.href = `#${anchorText}`;
141
- elementAnchor.textContent = heading.textContent;
142
- const elementList = elementListClone.cloneNode(false);
143
- elementList.appendChild(elementAnchor);
144
- elementContainer.appendChild(elementList);
127
+ // remove &
128
+ anchor = anchor.replace(/\&+/g, '').replace(/\&amp;+/g, '');
145
129
 
146
- // upadte current number
147
- number = currentNumber;
148
- }
130
+ if (type === true) {
131
+ anchor = convert2WikipediaStyleAnchor(anchor);
149
132
  }
150
133
 
151
- censorshipId(headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) {
152
- let id = textContent || '';
153
- let suffix_count = 1;
154
-
155
- // IDが重複していた場合はsuffixを付ける
156
- while (suffix_count <= headings.length) {
157
- const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;
134
+ return anchor;
135
+ };
158
136
 
159
- if (this.storeIds.indexOf(tmp_id) === -1) {
160
- id = tmp_id;
161
- this.storeIds.push(id);
162
- break;
137
+ const generateHierarchyList = (
138
+ headings: NodeListOf<HTMLHeadingElement>,
139
+ elementContainer: HTMLUListElement | HTMLOListElement,
140
+ anchorType: boolean,
141
+ ) => {
142
+ let number = 0;
143
+ const elementListClone = document.createElement('li');
144
+ const elementAnchorClone = document.createElement('a');
145
+
146
+ for (let i = 0; i < headings.length; i++) {
147
+ const heading = headings[i];
148
+ const currentNumber = getHeadingTagName2Number(heading.tagName);
149
+
150
+ // check list hierarchy
151
+ if (number !== 0 && number < currentNumber) {
152
+ // number of the heading is large (small as heading)
153
+ const nextElementOListClone = document.createElement('ol');
154
+ // @ts-ignore
155
+ elementContainer.lastChild.appendChild(nextElementOListClone);
156
+ elementContainer = nextElementOListClone;
157
+ } else if (number !== 0 && number > currentNumber) {
158
+ // number of heading is small (large as heading)
159
+ for (let i = 0; i < number - currentNumber; i++) {
160
+ if (hasParentNode(elementContainer, elementContainer.parentNode)) {
161
+ // @ts-ignore
162
+ elementContainer = elementContainer.parentNode.parentNode;
163
+ }
163
164
  }
164
-
165
- suffix_count++;
166
165
  }
167
166
 
168
- return id;
169
- }
167
+ const textContent = censorshipId(headings, heading.textContent);
170
168
 
171
- generateAnchorText(text: string, type: boolean) {
172
- // convert spaces to _
173
- let anchor = replaceSpace2Underscore(text);
169
+ // headingへidを付与
170
+ const anchorText = generateAnchorText(textContent, anchorType);
171
+ heading.id = anchorText;
174
172
 
175
- // remove &
176
- anchor = anchor.replace(/\&+/g, '').replace(/\&amp;+/g, '');
173
+ // add to wrapper
174
+ const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;
175
+ elementAnchor.href = `#${anchorText}`;
176
+ elementAnchor.textContent = heading.textContent;
177
+ const elementList = elementListClone.cloneNode(false) as HTMLLIElement;
178
+ elementList.appendChild(elementAnchor);
177
179
 
178
- if (type === true) {
179
- anchor = convert2WikipediaStyleAnchor(anchor);
180
- }
180
+ elementContainer.appendChild(elementList);
181
181
 
182
- return anchor;
182
+ // upadte current number
183
+ number = currentNumber;
183
184
  }
185
+ };
184
186
 
185
- removeDuplicateIds(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {
186
- const anchors = elementContainer.getElementsByTagName('a');
187
+ export const Mokuji = (element: HTMLElement | null, externalOptions?: MokujiOption): HTMLOListElement | undefined => {
188
+ if (!element) {
189
+ return;
190
+ }
187
191
 
188
- for (let i = 0; i < anchors.length; i++) {
189
- const id = anchors[i].innerText;
190
- const hash = anchors[i].hash;
191
- const matchedHeadings = Array.from(headings).filter((heading) => heading.id === id);
192
+ // Merge the default options with the external options.
193
+ const options = Object.assign(
194
+ // default options
195
+ defaultOptions,
196
+ externalOptions,
197
+ );
192
198
 
193
- if (matchedHeadings.length === 1) {
194
- continue;
195
- }
199
+ const headings = getHeadingsElement(element);
196
200
 
197
- // duplicated id
198
- let count = 0;
201
+ // mokuji start
202
+ const elementContainer = document.createElement(
203
+ options.anchorContainerTagName || defaultOptions.anchorContainerTagName,
204
+ );
199
205
 
200
- matchedHeadings.forEach((heading) => {
201
- const heading_id = `${heading.id}-${count}`;
206
+ // generate mokuji list
207
+ generateHierarchyList(headings, elementContainer, !!options.anchorType);
202
208
 
203
- // search duplicate list
204
- for (const anchor of Array.from(anchors)) {
205
- if (anchor.hash === hash) {
206
- // update hash
207
- anchor.href = `#${heading_id}`;
208
- break;
209
- }
210
- }
209
+ // remove duplicates by adding suffix
210
+ removeDuplicateIds(headings, elementContainer);
211
211
 
212
- // update id
213
- heading.id = heading_id;
214
- count++;
215
- });
216
- }
212
+ // setup anchor link
213
+ if (options.anchorLink) {
214
+ const anchors = elementContainer.querySelectorAll('a');
215
+ renderAnchorLink(headings, anchors, options);
217
216
  }
218
- }
217
+
218
+ return elementContainer;
219
+ };