mokuji.js 3.0.1 → 3.2.1

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