mokuji.js 3.1.0 → 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
@@ -12,13 +12,15 @@ npm install --save mokuji.js
12
12
  ## Usage
13
13
 
14
14
  ```javascript
15
- import Mokuji from 'mokuji.js';
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
- anchorType: boolean;
3
- anchorLink: boolean;
4
- anchorLinkSymbol: string;
5
- anchorLinkBefore: boolean;
6
- anchorLinkClassName: string;
7
- anchorContainerTagName: 'ul' | 'ol';
3
+ anchorType?: boolean;
4
+ anchorLink?: boolean;
5
+ anchorLinkSymbol?: string;
6
+ anchorLinkBefore?: boolean;
7
+ anchorLinkClassName?: string;
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, 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,r){for(;e;){if(e===r)return!0;e=e.parentNode}return!1},r=function(e,r,n){if(r){var t=document.createElement("a");t.classList.add(n.anchorLinkClassName),e.forEach(function(e){for(var o=e.id,a=0;a<r.length;a++){var i=r[a].hash;if(i.replace("#","")===o){var c=t.cloneNode(!1);c.setAttribute("href",i),c.textContent=n.anchorLinkSymbol,n.anchorLinkBefore?e.insertBefore(c,e.firstChild):e.appendChild(c)}}})}};exports.Mokuji=function(){function n(e,n){this.storeIds=[],this.options=Object.assign({anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},n);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");r(t,a,this.options)}return o}var t=n.prototype;return t.generateMokuji=function(e){var r=document.createElement(this.options.anchorContainerTagName);return this.generateHierarchyList(e,r),this.removeDuplicateIds(e,r),r},t.generateHierarchyList=function(r,n){for(var t=0,o=document.createElement("li"),a=document.createElement("a"),i=0;i<r.length;i++){var c=r[i],h=Number(c.tagName.substring(1));if(0!==t&&t<h){var s=document.createElement("ol");n.lastChild.appendChild(s),n=s}else if(0!==t&&t>h)for(var f=0;f<t-h;f++)e(n,n.parentNode)&&(n=n.parentNode.parentNode);var l=this.censorshipId(r,c.textContent),u=this.generateAnchorText(l,this.options.anchorType);c.id=u;var d=a.cloneNode(!1);d.href="#"+u,d.textContent=c.textContent;var p=o.cloneNode(!1);p.appendChild(d),n.appendChild(p),t=h}},t.censorshipId=function(e,r){for(var n=r||"",t=1;t<=e.length;){var o=1===t?n:n+"_"+t;if(-1===this.storeIds.indexOf(o)){this.storeIds.push(n=o);break}t++}return n},t.generateAnchorText=function(e,r){var n=function(e){return e.replace(/\s+/g,"_")}(e);return n=n.replace(/\&+/g,"").replace(/\&amp;+/g,""),!0===r&&(n=function(e){return(e=encodeURIComponent(e)).replace(/\%+/g,".")}(n)),n},t.removeDuplicateIds=function(e,r){for(var n=r.getElementsByTagName("a"),t=function(r){var t=n[r].innerText,o=n[r].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 r=e.id+"-"+i,t=0,a=Array.from(n);t<a.length;t++){var c=a[t];if(c.hash===o){c.href="#"+r;break}}e.id=r,i++})},o=0;o<n.length;o++)t(o)},n}(),exports.renderAnchorLink=r;
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 a.classList.add(options.anchorLinkClassName);\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 anchor.textContent = options.anchorLinkSymbol;\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 storeIds: string[] = [];\n\n constructor(element: HTMLElement, externalOptions: MokujiOption) {\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n } as MokujiOption,\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);\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","classList","add","anchorLinkClassName","forEach","heading","id","i","length","hash","replace","anchor","cloneNode","setAttribute","textContent","anchorLinkSymbol","anchorLinkBefore","insertBefore","firstChild","appendChild","externalOptions","this","Object","assign","anchorType","anchorLink","anchorContainerTagName","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","storeIds","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");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),i.textContent=n.anchorLinkSymbol,n.anchorLinkBefore?e.insertBefore(i,e.firstChild):e.appendChild(i)}})};class n{constructor(e,n){this.storeIds=[],this.options=Object.assign({anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},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);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 a.classList.add(options.anchorLinkClassName);\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 anchor.textContent = options.anchorLinkSymbol;\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 storeIds: string[] = [];\n\n constructor(element: HTMLElement, externalOptions: MokujiOption) {\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n } as MokujiOption,\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);\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","classList","add","anchorLinkClassName","forEach","heading","id","i","length","hash","replace","anchor","cloneNode","setAttribute","textContent","anchorLinkSymbol","anchorLinkBefore","insertBefore","firstChild","appendChild","Mokuji","constructor","externalOptions","this","Object","assign","anchorType","anchorLink","anchorContainerTagName","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","storeIds","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,r){for(;e;){if(e===r)return!0;e=e.parentNode}return!1},r=function(e,r,n){if(r){var t=document.createElement("a");t.classList.add(n.anchorLinkClassName),e.forEach(function(e){for(var o=e.id,a=0;a<r.length;a++){var i=r[a].hash;if(i.replace("#","")===o){var c=t.cloneNode(!1);c.setAttribute("href",i),c.textContent=n.anchorLinkSymbol,n.anchorLinkBefore?e.insertBefore(c,e.firstChild):e.appendChild(c)}}})}},n=function(){function n(e,n){this.storeIds=[],this.options=Object.assign({anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},n);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");r(t,a,this.options)}return o}var t=n.prototype;return t.generateMokuji=function(e){var r=document.createElement(this.options.anchorContainerTagName);return this.generateHierarchyList(e,r),this.removeDuplicateIds(e,r),r},t.generateHierarchyList=function(r,n){for(var t=0,o=document.createElement("li"),a=document.createElement("a"),i=0;i<r.length;i++){var c=r[i],h=Number(c.tagName.substring(1));if(0!==t&&t<h){var s=document.createElement("ol");n.lastChild.appendChild(s),n=s}else if(0!==t&&t>h)for(var f=0;f<t-h;f++)e(n,n.parentNode)&&(n=n.parentNode.parentNode);var l=this.censorshipId(r,c.textContent),u=this.generateAnchorText(l,this.options.anchorType);c.id=u;var d=a.cloneNode(!1);d.href="#"+u,d.textContent=c.textContent;var p=o.cloneNode(!1);p.appendChild(d),n.appendChild(p),t=h}},t.censorshipId=function(e,r){for(var n=r||"",t=1;t<=e.length;){var o=1===t?n:n+"_"+t;if(-1===this.storeIds.indexOf(o)){this.storeIds.push(n=o);break}t++}return n},t.generateAnchorText=function(e,r){var n=function(e){return e.replace(/\s+/g,"_")}(e);return n=n.replace(/\&+/g,"").replace(/\&amp;+/g,""),!0===r&&(n=function(e){return(e=encodeURIComponent(e)).replace(/\%+/g,".")}(n)),n},t.removeDuplicateIds=function(e,r){for(var n=r.getElementsByTagName("a"),t=function(r){var t=n[r].innerText,o=n[r].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 r=e.id+"-"+i,t=0,a=Array.from(n);t<a.length;t++){var c=a[t];if(c.hash===o){c.href="#"+r;break}}e.id=r,i++})},o=0;o<n.length;o++)t(o)},n}();export{n as Mokuji,r 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 a.classList.add(options.anchorLinkClassName);\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 anchor.textContent = options.anchorLinkSymbol;\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 storeIds: string[] = [];\n\n constructor(element: HTMLElement, externalOptions: MokujiOption) {\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n } as MokujiOption,\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);\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","classList","add","anchorLinkClassName","forEach","heading","id","i","length","hash","replace","anchor","cloneNode","setAttribute","textContent","anchorLinkSymbol","anchorLinkBefore","insertBefore","firstChild","appendChild","Mokuji","externalOptions","this","Object","assign","anchorType","anchorLink","anchorContainerTagName","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","storeIds","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":"IAAaA,EAAgB,SAACC,EAAsBC,GAClD,KAAOD,GAAS,CACd,GAAIA,IAAYC,EACd,SAEFD,EAAUA,EAAQE,WAEpB,UCOIC,EAAiB,CACrBC,YAAY,EACZC,YAAY,EACZC,iBAAkB,IAClBC,kBAAkB,EAClBC,oBAAqB,GACrBC,uBAAwB,MAGpBC,EAAqB,GA+ErBC,EAAe,SAACC,EAA0CC,GAK9D,IAJA,IAAIC,EAAKD,GAAe,GACpBE,EAAe,EAGZA,GAAgBH,EAASI,QAAQ,CACtC,IAAMC,EAA0B,IAAjBF,EAAqBD,EAAQA,MAAMC,EAElD,IAAkC,IAA9BL,EAASQ,QAAQD,GAAgB,CAEnCP,EAASS,KADTL,EAAKG,GAEL,MAGFF,IAGF,OAAOD,GAmEIM,EAAS,SAACpB,EAA6BqB,GAClD,GAAKrB,EAAL,CAKA,IAAMsB,EAAUC,OAAOC,OAErBrB,EACAkB,GAGIT,EDpL0B,SAACZ,GACjC,OAAOA,EAAQyB,iBAAiB,0BCmLfC,CAAmB1B,GAG9B2B,EAAmBC,SAASC,cAChCP,EAAQb,wBAA0BN,EAAeM,wBAenD,OAjF4B,SAC5BG,EACAe,EACAvB,GAMA,IAJA,IAAI0B,EAAS,EACPC,EAAmBH,SAASC,cAAc,MAC1CG,EAAqBJ,SAASC,cAAc,KAEzCI,EAAI,EAAGA,EAAIrB,EAASI,OAAQiB,IAAK,CACxC,IAAMC,EAAUtB,EAASqB,GACnBE,ECvIDC,ODuI0CF,EAAQG,QCvInCC,UAAU,ID0I9B,GAAe,IAAXR,GAAgBA,EAASK,EAAe,CAE1C,IAAMI,EAAwBX,SAASC,cAAc,MAErDF,EAAiBa,UAAUC,YAAYF,GACvCZ,EAAmBY,UACC,IAAXT,GAAgBA,EAASK,EAElC,IAAK,IAAIF,EAAI,EAAGA,EAAIH,EAASK,EAAeF,IACtClC,EAAc4B,EAAkBA,EAAiBzB,cAEnDyB,EAAmBA,EAAiBzB,WAAWA,YAKrD,IAGMwC,GA/CgCC,EA+CavC,EA7CjDwC,OAAAA,EAGJA,GC/HsCC,EDsKhBlC,EAAaC,EAAUsB,EAAQrB,aA1CjD+B,EC3HGC,EAAKC,QAAQ,OAAQ,MD8HZA,QAAQ,OAAQ,IAAIA,QAAQ,WAAY,KAE3C,IAATH,IACFC,EC9HwC,SAACA,GAI3C,OAHAA,EAASG,mBAAmBH,IACZE,QAAQ,OAAQ,KD4HrBE,CAA6BJ,IAGjCA,GAqCLV,EAAQpB,GAAK4B,EAGb,IAAMO,EAAgBjB,EAAmBkB,WAAU,GACnDD,EAAcE,SAAWT,EACzBO,EAAcpC,YAAcqB,EAAQrB,YACpC,IAAMuC,EAAcrB,EAAiBmB,WAAU,GAC/CE,EAAYX,YAAYQ,GAExBtB,EAAiBc,YAAYW,GAG7BtB,EAASK,ECtL0B,IAACU,ED0HEF,EAEpCC,EAkFJS,CAAsBzC,EAAUe,IAAoBL,EAAQlB,YA1InC,SAACQ,EAA0Ce,GAGpE,IAFA,IAAM2B,EAAU3B,EAAiB4B,qBAAqB,gBAE7CtB,GACP,IAAMnB,EAAKwC,EAAQrB,GAAGuB,UAChBC,EAAOH,EAAQrB,GAAGwB,KAClBC,EAAkBC,MAAMC,KAAKhD,GAAUiD,OAAO,SAAC3B,UAAYA,EAAQpB,KAAOA,IAEhF,GAA+B,IAA3B4C,EAAgB1C,OAClB,iBAIF,IAAI8C,EAAQ,EAEZJ,EAAgBK,QAAQ,SAAC7B,GAIvB,IAHA,IAAM8B,EAAgB9B,EAAQpB,OAAMgD,QAGfH,MAAMC,KAAKN,kBAAU,CAArC,IAAMV,OACT,GAAIA,EAAOa,OAASA,EAAM,CAExBb,EAAOO,SAAWa,EAClB,OAKJ9B,EAAQpB,GAAKkD,EACbF,OA1BK7B,EAAI,EAAGA,EAAIqB,EAAQtC,OAAQiB,MAA3BA,GA0ITgC,CAAmBrD,EAAUe,GAGzBL,EAAQjB,YA3LW,SACvBO,EACA0C,EACAhC,GAEA,GAAKgC,EAAL,CAEA,IAAMY,EAAItC,SAASC,cAAc,KAE7BP,EAAQd,qBACV0D,EAAEC,UAAUC,IAAI9C,EAAQd,qBAG1BI,EAASmD,QAAQ,SAAC7B,GAGhB,IAFA,IAAQpB,EAAOoB,EAAPpB,GAECmB,EAAI,EAAGA,EAAIqB,EAAQtC,OAAQiB,IAAK,CACvC,IAAQwB,EAASH,EAAQrB,GAAjBwB,KAER,GAAIA,EAAKX,QAAQ,IAAK,MAAQhC,EAA9B,CAKA,IAAM8B,EAASsB,EAAEhB,WAAU,GAC3BN,EAAOyB,aAAa,OAAQZ,GAExBnC,EAAQhB,mBACVsC,EAAO/B,YAAcS,EAAQhB,kBAI3BgB,EAAQf,iBAEV2B,EAAQoC,aAAa1B,EAAQV,EAAQqC,YAGrCrC,EAAQO,YAAYG,QAwJxB4B,CAAiB5D,EADDe,EAAiBF,iBAAiB,KACdH,GAG/BK"}
@@ -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");r.classList.add(t.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 c=r.cloneNode(!1);c.setAttribute("href",i),c.textContent=t.anchorLinkSymbol,t.anchorLinkBefore?e.insertBefore(c,e.firstChild):e.appendChild(c)}}})}};e.Mokuji=function(){function e(e,n){this.storeIds=[],this.options=Object.assign({anchorType:!0,anchorLink:!1,anchorLinkSymbol:"#",anchorLinkBefore:!0,anchorLinkClassName:"",anchorContainerTagName:"ol"},n);var r=function(e){return e.querySelectorAll("h1, h2, h3, h4, h5, h6")}(e),o=this.generateMokuji(r);if(this.options.anchorLink){var a=o.querySelectorAll("a");t(r,a,this.options)}return o}var r=e.prototype;return r.generateMokuji=function(e){var n=document.createElement(this.options.anchorContainerTagName);return this.generateHierarchyList(e,n),this.removeDuplicateIds(e,n),n},r.generateHierarchyList=function(e,t){for(var r=0,o=document.createElement("li"),a=document.createElement("a"),i=0;i<e.length;i++){var c=e[i],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 u=this.censorshipId(e,c.textContent),l=this.generateAnchorText(u,this.options.anchorType);c.id=l;var d=a.cloneNode(!1);d.href="#"+l,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,a=Array.from(e).filter(function(e){return e.id===r});if(1===a.length)return"continue";var i=0;a.forEach(function(e){for(var n=e.id+"-"+i,r=0,a=Array.from(t);r<a.length;r++){var c=a[r];if(c.hash===o){c.href="#"+n;break}}e.id=n,i++})},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 a.classList.add(options.anchorLinkClassName);\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 anchor.textContent = options.anchorLinkSymbol;\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 storeIds: string[] = [];\n\n constructor(element: HTMLElement, externalOptions: MokujiOption) {\n // Merge the default options with the external options.\n this.options = Object.assign(\n // default options\n {\n anchorType: true,\n anchorLink: false,\n anchorLinkSymbol: '#',\n anchorLinkBefore: true,\n anchorLinkClassName: '',\n anchorContainerTagName: 'ol',\n } as MokujiOption,\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);\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","classList","add","anchorLinkClassName","forEach","heading","id","i","length","hash","replace","anchor","cloneNode","setAttribute","textContent","anchorLinkSymbol","anchorLinkBefore","insertBefore","firstChild","appendChild","externalOptions","this","Object","assign","anchorType","anchorLink","anchorContainerTagName","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","storeIds","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,17 +1,17 @@
1
1
  {
2
2
  "name": "mokuji.js",
3
- "version": "3.1.0",
3
+ "version": "4.0.0",
4
4
  "description": "A table of content JavaScript Library",
5
5
  "scripts": {
6
6
  "build": "microbundle",
7
7
  "clean": "rimraf lib/",
8
8
  "watch": "microbundle watch",
9
9
  "test": "npm run lint",
10
- "prepare": "npm run clean && npm run build"
10
+ "prepare": "npm run clean && npm run build && husky install"
11
11
  },
12
12
  "source": "src/index.ts",
13
13
  "main": "dist/mokuji.js",
14
- "types": "dist/mokuji.d.ts",
14
+ "types": "dist/index.d.ts",
15
15
  "module": "dist/mokuji.module.js",
16
16
  "esmodule": "dist/mokuji.modern.js",
17
17
  "unpkg": "dist/mokuji.umd.js",
@@ -21,7 +21,9 @@
21
21
  "files": [
22
22
  "dist/",
23
23
  "lib/",
24
- "src/"
24
+ "src/",
25
+ "LICENSE",
26
+ "READNME.md"
25
27
  ],
26
28
  "lint-staged": {
27
29
  "*.{js,ts}": [
@@ -45,19 +47,12 @@
45
47
  },
46
48
  "homepage": "https://github.com/hiro0218/mokuji.js",
47
49
  "devDependencies": {
48
- "@babel/plugin-proposal-optional-chaining": "^7.13.12",
49
- "@types/node": "~14.14.41",
50
- "cross-env": "^5.2.0",
51
- "husky": "^4.2.5",
52
- "lint-staged": "^10.2.11",
53
- "microbundle": "^0.13.3",
54
- "prettier": "^2.3.2",
50
+ "@types/node": "~17.0.23",
51
+ "husky": "^7.0.4",
52
+ "lint-staged": "^12.3.7",
53
+ "microbundle": "^0.14.2",
54
+ "prettier": "^2.6.1",
55
55
  "rimraf": "^3.0.2",
56
- "typescript": "~4.2.4"
57
- },
58
- "husky": {
59
- "hooks": {
60
- "pre-commit": "lint-staged"
61
- }
56
+ "typescript": "~4.6.3"
62
57
  }
63
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
- anchorType: boolean;
6
- anchorLink: boolean;
7
- anchorLinkSymbol: string;
8
- anchorLinkBefore: boolean;
9
- anchorLinkClassName: string;
10
- anchorContainerTagName: 'ul' | 'ol';
7
+ anchorType?: boolean;
8
+ anchorLink?: boolean;
9
+ anchorLinkSymbol?: string;
10
+ anchorLinkBefore?: boolean;
11
+ anchorLinkClassName?: string;
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,
@@ -18,7 +31,10 @@ export const renderAnchorLink = (
18
31
  if (!anchors) return;
19
32
 
20
33
  const a = document.createElement('a');
21
- a.classList.add(options.anchorLinkClassName);
34
+
35
+ if (options.anchorLinkClassName) {
36
+ a.classList.add(options.anchorLinkClassName);
37
+ }
22
38
 
23
39
  headings.forEach((heading) => {
24
40
  const { id } = heading;
@@ -33,7 +49,10 @@ export const renderAnchorLink = (
33
49
  // create anchor
34
50
  const anchor = a.cloneNode(false) as HTMLAnchorElement;
35
51
  anchor.setAttribute('href', hash);
36
- anchor.textContent = options.anchorLinkSymbol;
52
+
53
+ if (options.anchorLinkSymbol) {
54
+ anchor.textContent = options.anchorLinkSymbol;
55
+ }
37
56
 
38
57
  // insert anchor into headings
39
58
  if (options.anchorLinkBefore) {
@@ -47,162 +66,154 @@ export const renderAnchorLink = (
47
66
  });
48
67
  };
49
68
 
50
- export class Mokuji {
51
- options: MokujiOption;
52
- storeIds: string[] = [];
53
-
54
- constructor(element: HTMLElement, externalOptions: MokujiOption) {
55
- // Merge the default options with the external options.
56
- this.options = Object.assign(
57
- // default options
58
- {
59
- anchorType: true,
60
- anchorLink: false,
61
- anchorLinkSymbol: '#',
62
- anchorLinkBefore: true,
63
- anchorLinkClassName: '',
64
- anchorContainerTagName: 'ol',
65
- } as MokujiOption,
66
- externalOptions,
67
- );
68
-
69
- const headings = getHeadingsElement(element);
70
-
71
- // mokuji start
72
- // generate mokuji list
73
- const mokuji = this.generateMokuji(headings);
74
-
75
- // setup anchor link
76
- if (this.options.anchorLink) {
77
- const anchors = mokuji.querySelectorAll('a');
78
- renderAnchorLink(headings, anchors, this.options);
79
- }
80
-
81
- // @ts-ignore
82
- return mokuji;
83
- }
69
+ const removeDuplicateIds = (headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) => {
70
+ const anchors = elementContainer.getElementsByTagName('a');
84
71
 
85
- generateMokuji(headings: NodeListOf<HTMLHeadingElement>) {
86
- let elementContainer = document.createElement(this.options.anchorContainerTagName);
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);
87
76
 
88
- this.generateHierarchyList(headings, elementContainer);
77
+ if (matchedHeadings.length === 1) {
78
+ continue;
79
+ }
89
80
 
90
- // remove duplicates by adding suffix
91
- this.removeDuplicateIds(headings, elementContainer);
81
+ // duplicated id
82
+ let count = 0;
92
83
 
93
- return elementContainer;
94
- }
84
+ matchedHeadings.forEach((heading) => {
85
+ const heading_id = `${heading.id}-${count}`;
95
86
 
96
- generateHierarchyList(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {
97
- let number = 0;
98
- const elementListClone = document.createElement('li');
99
- const elementAnchorClone = document.createElement('a');
100
-
101
- for (let i = 0; i < headings.length; i++) {
102
- const heading = headings[i];
103
- const currentNumber = getHeadingTagName2Number(heading.tagName);
104
-
105
- // check list hierarchy
106
- if (number !== 0 && number < currentNumber) {
107
- // number of the heading is large (small as heading)
108
- const nextElementOListClone = document.createElement('ol');
109
- // @ts-ignore
110
- elementContainer.lastChild.appendChild(nextElementOListClone);
111
- elementContainer = nextElementOListClone;
112
- } else if (number !== 0 && number > currentNumber) {
113
- // number of heading is small (large as heading)
114
- for (let i = 0; i < number - currentNumber; i++) {
115
- if (hasParentNode(elementContainer, elementContainer.parentNode)) {
116
- // @ts-ignore
117
- elementContainer = elementContainer.parentNode.parentNode;
118
- }
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;
119
93
  }
120
94
  }
121
95
 
122
- const textContent = this.censorshipId(headings, heading.textContent);
96
+ // update id
97
+ heading.id = heading_id;
98
+ count++;
99
+ });
100
+ }
101
+ };
123
102
 
124
- // headingへidを付与
125
- const anchorText = this.generateAnchorText(textContent, this.options.anchorType);
126
- heading.id = anchorText;
103
+ const censorshipId = (headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) => {
104
+ let id = textContent || '';
105
+ let suffix_count = 1;
127
106
 
128
- // add to wrapper
129
- const elementAnchor = elementAnchorClone.cloneNode(false) as HTMLAnchorElement;
130
- elementAnchor.href = `#${anchorText}`;
131
- elementAnchor.textContent = heading.textContent;
132
- const elementList = elementListClone.cloneNode(false);
133
- elementList.appendChild(elementAnchor);
134
- elementContainer.appendChild(elementList);
107
+ // IDが重複していた場合はsuffixを付ける
108
+ while (suffix_count <= headings.length) {
109
+ const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;
135
110
 
136
- // upadte current number
137
- number = currentNumber;
111
+ if (storeIds.indexOf(tmp_id) === -1) {
112
+ id = tmp_id;
113
+ storeIds.push(id);
114
+ break;
138
115
  }
116
+
117
+ suffix_count++;
139
118
  }
140
119
 
141
- censorshipId(headings: NodeListOf<HTMLHeadingElement>, textContent: string | null) {
142
- let id = textContent || '';
143
- let suffix_count = 1;
120
+ return id;
121
+ };
122
+
123
+ const generateAnchorText = (text: string, type: boolean) => {
124
+ // convert spaces to _
125
+ let anchor = replaceSpace2Underscore(text);
144
126
 
145
- // IDが重複していた場合はsuffixを付ける
146
- while (suffix_count <= headings.length) {
147
- const tmp_id = suffix_count === 1 ? id : `${id}_${suffix_count}`;
127
+ // remove &
128
+ anchor = anchor.replace(/\&+/g, '').replace(/\&amp;+/g, '');
148
129
 
149
- if (this.storeIds.indexOf(tmp_id) === -1) {
150
- id = tmp_id;
151
- this.storeIds.push(id);
152
- break;
153
- }
130
+ if (type === true) {
131
+ anchor = convert2WikipediaStyleAnchor(anchor);
132
+ }
133
+
134
+ return anchor;
135
+ };
154
136
 
155
- suffix_count++;
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
+ }
164
+ }
156
165
  }
157
166
 
158
- return id;
159
- }
167
+ const textContent = censorshipId(headings, heading.textContent);
160
168
 
161
- generateAnchorText(text: string, type: boolean) {
162
- // convert spaces to _
163
- let anchor = replaceSpace2Underscore(text);
169
+ // headingへidを付与
170
+ const anchorText = generateAnchorText(textContent, anchorType);
171
+ heading.id = anchorText;
164
172
 
165
- // remove &
166
- 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);
167
179
 
168
- if (type === true) {
169
- anchor = convert2WikipediaStyleAnchor(anchor);
170
- }
180
+ elementContainer.appendChild(elementList);
171
181
 
172
- return anchor;
182
+ // upadte current number
183
+ number = currentNumber;
173
184
  }
185
+ };
174
186
 
175
- removeDuplicateIds(headings: NodeListOf<HTMLHeadingElement>, elementContainer: HTMLElement) {
176
- const anchors = elementContainer.getElementsByTagName('a');
187
+ export const Mokuji = (element: HTMLElement | null, externalOptions?: MokujiOption): HTMLOListElement | undefined => {
188
+ if (!element) {
189
+ return;
190
+ }
177
191
 
178
- for (let i = 0; i < anchors.length; i++) {
179
- const id = anchors[i].innerText;
180
- const hash = anchors[i].hash;
181
- 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
+ );
182
198
 
183
- if (matchedHeadings.length === 1) {
184
- continue;
185
- }
199
+ const headings = getHeadingsElement(element);
186
200
 
187
- // duplicated id
188
- let count = 0;
201
+ // mokuji start
202
+ const elementContainer = document.createElement(
203
+ options.anchorContainerTagName || defaultOptions.anchorContainerTagName,
204
+ );
189
205
 
190
- matchedHeadings.forEach((heading) => {
191
- const heading_id = `${heading.id}-${count}`;
206
+ // generate mokuji list
207
+ generateHierarchyList(headings, elementContainer, !!options.anchorType);
192
208
 
193
- // search duplicate list
194
- for (const anchor of Array.from(anchors)) {
195
- if (anchor.hash === hash) {
196
- // update hash
197
- anchor.href = `#${heading_id}`;
198
- break;
199
- }
200
- }
209
+ // remove duplicates by adding suffix
210
+ removeDuplicateIds(headings, elementContainer);
201
211
 
202
- // update id
203
- heading.id = heading_id;
204
- count++;
205
- });
206
- }
212
+ // setup anchor link
213
+ if (options.anchorLink) {
214
+ const anchors = elementContainer.querySelectorAll('a');
215
+ renderAnchorLink(headings, anchors, options);
207
216
  }
208
- }
217
+
218
+ return elementContainer;
219
+ };