├── lib ├── type.js ├── type.js.map ├── adaptor │ ├── jquery.d.ts │ ├── jquery.js │ └── jquery.js.map ├── util.d.ts ├── make-list.d.ts ├── section.d.ts ├── type.d.ts ├── index.d.ts ├── outline.d.ts ├── section.js.map ├── outline.js.map ├── util.js.map ├── util.js ├── section.js ├── outline.js ├── make-list.js.map ├── make-list.js ├── index.js.map └── index.js ├── jest.config.js ├── .gitignore ├── webpack.dev.js ├── tsconfig.json ├── src ├── adaptor │ └── jquery.ts ├── type.ts ├── section.ts ├── util.ts ├── outline.ts ├── make-list.ts └── index.ts ├── .circleci ├── setup_puppeteer.sh └── config.yml ├── __tests__ ├── spec-3.html ├── spec-4.html ├── spec-5.html ├── spec-6.html ├── _test.js ├── default-option.html ├── spec-1.html ├── jquery-plugin.html ├── spec-2.html └── full-option.html ├── webpack.prod.js ├── LICENSE ├── package.json ├── example └── index.html ├── README.md ├── bundle ├── document-outliner.js └── jquery-document-outliner.js └── docs └── index.html /lib/type.js: -------------------------------------------------------------------------------- 1 | //# sourceMappingURL=type.js.map -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-puppeteer' 3 | }; 4 | -------------------------------------------------------------------------------- /lib/type.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"type.js","sourceRoot":"","sources":["../src/type.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dotenv environment variables file 2 | .env 3 | .idea 4 | .DS_Store 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | 11 | # Dependency directories 12 | node_modules/ 13 | -------------------------------------------------------------------------------- /lib/adaptor/jquery.d.ts: -------------------------------------------------------------------------------- 1 | import { OptionsType } from "../type"; 2 | declare global { 3 | interface JQuery { 4 | documentOutliner(target: string | NodeListOf, options: OptionsType): JQuery; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /lib/adaptor/jquery.js: -------------------------------------------------------------------------------- 1 | import DocumentOutliner from '../index'; 2 | (function ($) { 3 | $.fn.documentOutliner = function (target, options) { 4 | var outliner = new DocumentOutliner(this.get(0)); 5 | outliner.makeList(target, options); 6 | return this; 7 | }; 8 | })(jQuery); 9 | //# sourceMappingURL=jquery.js.map -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const prod = require('./webpack.prod.js'); 3 | 4 | module.exports = merge(prod, { 5 | mode: "development", 6 | devtool: 'inline-source-map', 7 | devServer: { 8 | port: 3000, 9 | inline: true, 10 | open: true, 11 | openPage: 'example' 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "outDir": "./lib", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "lib": [ 12 | "es2018", 13 | "dom" 14 | ] 15 | }, 16 | "include": ["src"], 17 | "exclude": ["node_modules", "**/__tests__/*"] 18 | } 19 | -------------------------------------------------------------------------------- /lib/adaptor/jquery.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"jquery.js","sourceRoot":"","sources":["../../src/adaptor/jquery.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,UAAU,CAAC;AASxC,CAAC,UAAC,CAAC;IACD,CAAC,CAAC,EAAE,CAAC,gBAAgB,GAAG,UAAS,MAAM,EAAE,OAAO;QAC9C,IAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC"} -------------------------------------------------------------------------------- /lib/util.d.ts: -------------------------------------------------------------------------------- 1 | import { NodeType } from "./type"; 2 | declare const isSectioningRoot: (el: NodeType) => boolean; 3 | declare const isSectioningContent: (el: NodeType) => boolean; 4 | declare const isHeadingContent: (el: NodeType) => boolean; 5 | declare const isHidden: (el: NodeType) => boolean; 6 | declare const getHeadingLevel: (el: NodeType) => number; 7 | export { isSectioningRoot, isSectioningContent, isHeadingContent, isHidden, getHeadingLevel }; 8 | -------------------------------------------------------------------------------- /src/adaptor/jquery.ts: -------------------------------------------------------------------------------- 1 | import DocumentOutliner from '../index'; 2 | import {OptionsType} from "../type"; 3 | 4 | declare global { 5 | interface JQuery { 6 | documentOutliner(target: string | NodeListOf, options: OptionsType): JQuery; 7 | } 8 | } 9 | 10 | (($) => { 11 | $.fn.documentOutliner = function(target, options) { 12 | const outliner = new DocumentOutliner(this.get(0)); 13 | outliner.makeList(target, options); 14 | return this; 15 | }; 16 | })(jQuery); 17 | -------------------------------------------------------------------------------- /.circleci/setup_puppeteer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo apt-get update 4 | sudo apt-get install -yq gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \ 5 | libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \ 6 | libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \ 7 | libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \ 8 | ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget 9 | -------------------------------------------------------------------------------- /src/type.ts: -------------------------------------------------------------------------------- 1 | import Outline from "./outline"; 2 | import Section from "./section"; 3 | 4 | type OutlineType = Outline; 5 | type SectionType = Section; 6 | type NodeType = HTMLElement | Node | ChildNode | null; 7 | type ListOptionType = 'ul' | 'ol'; 8 | type ConfigType = { 9 | link: boolean, 10 | listType: ListOptionType, 11 | listClassName: string, 12 | itemClassName: string, 13 | linkClassName: string, 14 | exceptClass: string, 15 | anchorName: string, 16 | levelLimit: number 17 | }; 18 | type Partial = { 19 | [P in keyof T]?: T[P]; 20 | }; 21 | type OptionsType = Partial 22 | 23 | export {OutlineType, SectionType, NodeType, OptionsType, ConfigType}; 24 | -------------------------------------------------------------------------------- /lib/make-list.d.ts: -------------------------------------------------------------------------------- 1 | import { SectionType, OutlineType, OptionsType } from "./type"; 2 | export default class MakeList { 3 | private readonly outline; 4 | private readonly options; 5 | private anchor; 6 | private html; 7 | constructor(outline: OutlineType, options: OptionsType); 8 | getHtml(): string; 9 | protected getListType(level: number): string; 10 | protected build(outline: OutlineType | SectionType, level: number): void; 11 | protected buildSections(sections: Array, hasHeading: boolean, level: number): void; 12 | protected buildLink(heading: HTMLElement): string; 13 | protected hasHeading(sections: Array): boolean; 14 | } 15 | -------------------------------------------------------------------------------- /__tests__/spec-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Apples

10 |

Pomaceous.

11 |

Bananas

12 |

Edible.

13 |

Carambola

14 |

Star.

15 |
16 | 17 |
18 |
    19 |
  1. Apples
  2. 20 |
  3. Bananas
  4. 21 |
  5. Carambola
  6. 22 |
23 |
24 | 25 |
26 | 27 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /lib/section.d.ts: -------------------------------------------------------------------------------- 1 | import { NodeType, SectionType } from "./type"; 2 | export default class Section { 3 | protected node: NodeType; 4 | protected implied: boolean; 5 | protected heading: NodeType; 6 | protected parent: SectionType | null; 7 | protected sections: Array; 8 | constructor(node: NodeType); 9 | getNode(): NodeType; 10 | setNode(node: NodeType): void; 11 | getImplied(): boolean; 12 | setImplied(implied: boolean): void; 13 | getHeading(): NodeType; 14 | setHeading(heading: NodeType): void; 15 | getParent(): SectionType | null; 16 | setParent(section: SectionType): void; 17 | getSections(): Array; 18 | addSection(section: SectionType): void; 19 | } 20 | -------------------------------------------------------------------------------- /lib/type.d.ts: -------------------------------------------------------------------------------- 1 | import Outline from "./outline"; 2 | import Section from "./section"; 3 | declare type OutlineType = Outline; 4 | declare type SectionType = Section; 5 | declare type NodeType = HTMLElement | Node | ChildNode | null; 6 | declare type ListOptionType = 'ul' | 'ol'; 7 | declare type ConfigType = { 8 | link: boolean; 9 | listType: ListOptionType; 10 | listClassName: string; 11 | itemClassName: string; 12 | linkClassName: string; 13 | exceptClass: string; 14 | anchorName: string; 15 | levelLimit: number; 16 | }; 17 | declare type Partial = { 18 | [P in keyof T]?: T[P]; 19 | }; 20 | declare type OptionsType = Partial; 21 | export { OutlineType, SectionType, NodeType, OptionsType, ConfigType }; 22 | -------------------------------------------------------------------------------- /__tests__/spec-4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |

Apples

11 |

Pomaceous.

12 |

Bananas

13 |

Edible.

14 |

Carambola

15 |

Star.

16 |
17 |
18 | 19 |
20 |
    21 |
  1. Apples
  2. 22 |
  3. Bananas
  4. 23 |
  5. Carambola
  6. 24 |
25 |
26 | 27 |
28 | 29 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import { OutlineType, NodeType, OptionsType } from "./type"; 2 | /** 3 | * @see https://html.spec.whatwg.org/multipage/sections.html#outline [4.3.11.1 Creating an outline] 4 | */ 5 | export default class DocumentOutliner { 6 | private currentOutlineTarget; 7 | private currentSection; 8 | private readonly stack; 9 | private readonly rootNode; 10 | constructor(root: string | NodeType); 11 | getOutlineObject(): OutlineType | boolean; 12 | makeList(target: string | NodeListOf, options: OptionsType): void; 13 | protected static walk(root: NodeType, enter: (node: NodeType) => void, exit: (node: NodeType) => void): void; 14 | protected enter(node: NodeType): void; 15 | protected exit(node: NodeType): void; 16 | protected getStackTopNode(): OutlineType; 17 | } 18 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { CheckerPlugin } = require('awesome-typescript-loader'); 3 | 4 | module.exports = { 5 | mode: "production", 6 | entry: { 7 | 'document-outliner': './src/index', 8 | 'jquery-document-outliner': './src/adaptor/jquery' 9 | }, 10 | output: { 11 | path: path.resolve(__dirname, 'bundle'), 12 | publicPath: "bundle", 13 | filename: '[name].js', 14 | library: ['DocumentOutliner'], 15 | libraryExport: "default", 16 | libraryTarget: 'umd' 17 | }, 18 | resolve: { 19 | extensions: ['.ts', '.tsx', '.js'] 20 | }, 21 | module: { 22 | rules: [{ 23 | test: /\.tsx?$/, 24 | exclude: /node_modules/, 25 | loader: 'awesome-typescript-loader' 26 | }] 27 | }, 28 | plugins: [ 29 | new CheckerPlugin 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /lib/outline.d.ts: -------------------------------------------------------------------------------- 1 | import { SectionType, OutlineType, NodeType } from "./type"; 2 | declare type SectionsType = Array; 3 | export default class Outline { 4 | protected sections: SectionsType; 5 | protected parentSection: SectionType | null; 6 | protected node: NodeType; 7 | protected outline: OutlineType; 8 | constructor(node: NodeType, section?: SectionType | null); 9 | getOutline(): OutlineType; 10 | setOutline(outline: OutlineType): void; 11 | getSections(): SectionsType; 12 | setSections(sections: SectionsType): void; 13 | addSection(section: SectionType): void; 14 | setParentSection(section: SectionType): void; 15 | getParentSection(): SectionType | null; 16 | getLastSection(): SectionType; 17 | setNode(node: NodeType): void; 18 | getNode(): NodeType; 19 | } 20 | export {}; 21 | -------------------------------------------------------------------------------- /__tests__/spec-5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |

A plea from our caretakers

11 |

Please, we beg of you, send help! We're stuck in the server room!

12 |
13 |

Feathers

14 |

Epidermal growths.

15 |
16 | 17 |
18 |
    19 |
  1. 20 |
      21 |
    1. A plea from our caretakers
    2. 22 |
    23 |
  2. 24 |
  3. Feathers
  4. 25 |
26 |
27 | 28 |
29 | 30 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/section.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"section.js","sourceRoot":"","sources":["../src/section.ts"],"names":[],"mappings":"AAEA;IAQE,iBAAY,IAAc;QANhB,SAAI,GAAa,IAAI,CAAC;QACtB,YAAO,GAAY,KAAK,CAAC;QACzB,YAAO,GAAa,IAAI,CAAC;QACzB,WAAM,GAAuB,IAAI,CAAC;QAClC,aAAQ,GAAuB,EAAE,CAAC;QAG1C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAEM,yBAAO,GAAd;QACE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAEM,yBAAO,GAAd,UAAe,IAAc;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAEM,4BAAU,GAAjB;QACE,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEM,4BAAU,GAAjB,UAAkB,OAAgB;QAChC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAEM,4BAAU,GAAjB;QACE,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEM,4BAAU,GAAjB,UAAkB,OAAiB;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAEM,2BAAS,GAAhB;QACE,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAEM,2BAAS,GAAhB,UAAiB,OAAoB;QACnC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;IACxB,CAAC;IAEM,6BAAW,GAAlB;QACE,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEM,4BAAU,GAAjB,UAAkB,OAAoB;QACpC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IACH,cAAC;AAAD,CAAC,AApDD,IAoDC"} -------------------------------------------------------------------------------- /__tests__/spec-6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Ray's blog

10 |
11 |
12 | 17 |

We're adopting a child!

18 |
19 |

As of today, Janine and I have signed the papers to become 20 | the proud parents of baby Diane! We've been looking forward to 21 | this day for weeks.

22 |
23 |
24 | 25 |
26 |
    27 |
  1. Ray's blog 28 |
      29 |
    1. We're adopting a child!
    2. 30 |
    31 |
  2. 32 |
33 |
34 | 35 |
36 | 37 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /lib/outline.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"outline.js","sourceRoot":"","sources":["../src/outline.ts"],"names":[],"mappings":"AAIA;IAOE,iBAAY,IAAc,EAAE,OAAkC;QAAlC,wBAAA,EAAA,cAAkC;QALpD,aAAQ,GAAiB,EAAE,CAAC;QAC5B,kBAAa,GAAuB,IAAI,CAAC;QACzC,SAAI,GAAa,IAAI,CAAC;QACtB,YAAO,GAAgB,IAAI,CAAC;QAGpC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,OAAO,EAAE;YACX,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;SAC1B;IACH,CAAC;IAEM,4BAAU,GAAjB;QACE,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEM,4BAAU,GAAjB,UAAkB,OAAoB;QACpC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAEM,6BAAW,GAAlB;QACE,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEM,6BAAW,GAAlB,UAAmB,QAAsB;QACvC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAEM,4BAAU,GAAjB,UAAkB,OAAoB;QACpC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAEM,kCAAgB,GAAvB,UAAwB,OAAoB;QAC1C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;IAC/B,CAAC;IAEM,kCAAgB,GAAvB;QACE,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAEM,gCAAc,GAArB;QACE,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjD,CAAC;IAEM,yBAAO,GAAd,UAAe,IAAc;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAEM,yBAAO,GAAd;QACE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IACH,cAAC;AAAD,CAAC,AArDD,IAqDC"} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 appleple 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /__tests__/_test.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const minify = require('html-minifier').minify; 3 | 4 | let page; 5 | 6 | beforeAll(async () => { 7 | page = await browser.newPage(); 8 | }); 9 | 10 | const minifyHtml = (html) => { 11 | return minify(html, { 12 | collapseWhitespace: true, 13 | }); 14 | }; 15 | 16 | const domTest = (testFile, message) => { 17 | test(message, async () => { 18 | await page.goto(`file://${path.resolve(__dirname, `./${testFile}.html`)}`); 19 | const res = await page.evaluate(() => { 20 | return { 21 | test: document.querySelector('.test').innerHTML, 22 | result: document.querySelector('.result').innerHTML, 23 | } 24 | }); 25 | await expect(minifyHtml(res.result)).toEqual(minifyHtml(res.test)); 26 | }); 27 | }; 28 | 29 | domTest('spec-1', 'spec-1 test.'); 30 | domTest('spec-2', 'spec-2 test.'); 31 | domTest('spec-3', 'spec-3 test.'); 32 | domTest('spec-4', 'spec-4 test.'); 33 | domTest('spec-5', 'spec-5 test.'); 34 | domTest('spec-6', 'spec-6 test.'); 35 | domTest('default-option', 'default option test.'); 36 | domTest('full-option', 'full option test.'); 37 | domTest('jquery-plugin', 'jquery plugin test.'); 38 | -------------------------------------------------------------------------------- /lib/util.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAEA,IAAM,SAAS,GAAG,UAAC,EAAY;IAC7B,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC,CAAC;AAEF,IAAM,iBAAiB,GAAG,UAAC,EAAY,EAAE,KAAa;IACpD,IAAI,EAAE,KAAK,IAAI,EAAE;QACf,OAAO,KAAK,CAAC;KACd;IACD,IAAI,CAAC,CAAC,EAAE,YAAY,WAAW,CAAC,EAAE;QAChC,OAAO,KAAK,CAAC;KACd;IACD,OAAO,SAAS,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/D,CAAC,CAAC;AAEF,IAAM,gBAAgB,GAAG,UAAC,EAAY;IACpC,OAAO,iBAAiB,CAAC,EAAE,EAAE,gDAAgD,CAAC,CAAC;AACjF,CAAC,CAAC;AAEF,IAAM,mBAAmB,GAAG,UAAC,EAAY;IACvC,OAAO,iBAAiB,CAAC,EAAE,EAAE,+BAA+B,CAAC,CAAC;AAChE,CAAC,CAAC;AAEF,IAAM,gBAAgB,GAAG,UAAC,EAAY;IACpC,OAAO,iBAAiB,CAAC,EAAE,EAAE,uBAAuB,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF,IAAM,QAAQ,GAAG,UAAC,EAAY;IAC5B,IAAI,EAAE,KAAK,IAAI,EAAE;QACf,OAAO,KAAK,CAAC;KACd;IACD,IAAI,CAAC,CAAC,EAAE,YAAY,WAAW,CAAC,EAAE;QAChC,OAAO,KAAK,CAAC;KACd;IACD,OAAO,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;AACpD,CAAC,CAAC;AAEF,IAAM,eAAe,GAAG,UAAC,EAAY;IACnC,IAAI,CAAC,CAAC,EAAE,YAAY,WAAW,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE;QACzD,OAAO,CAAC,CAAC;KACV;IACD,OAAO,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC"} -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | var isElement = function (el) { 2 | return !!(el && el.nodeName); 3 | }; 4 | var checkTagWithRegex = function (el, regex) { 5 | if (el === null) { 6 | return false; 7 | } 8 | if (!(el instanceof HTMLElement)) { 9 | return false; 10 | } 11 | return isElement(el) && regex.test(el.tagName.toLowerCase()); 12 | }; 13 | var isSectioningRoot = function (el) { 14 | return checkTagWithRegex(el, /^(blockquote|body|details|fieldset|figure|td)$/); 15 | }; 16 | var isSectioningContent = function (el) { 17 | return checkTagWithRegex(el, /^(article|aside|nav|section)$/); 18 | }; 19 | var isHeadingContent = function (el) { 20 | return checkTagWithRegex(el, /^(h1|h2|h3|h4|h5|h6)$/); 21 | }; 22 | var isHidden = function (el) { 23 | if (el === null) { 24 | return false; 25 | } 26 | if (!(el instanceof HTMLElement)) { 27 | return false; 28 | } 29 | return isElement(el) && el.hasAttribute('hidden'); 30 | }; 31 | var getHeadingLevel = function (el) { 32 | if (!(el instanceof HTMLElement) || !isHeadingContent(el)) { 33 | return 0; 34 | } 35 | return parseInt(el.tagName.toLowerCase().substr(1)); 36 | }; 37 | export { isSectioningRoot, isSectioningContent, isHeadingContent, isHidden, getHeadingLevel }; 38 | //# sourceMappingURL=util.js.map -------------------------------------------------------------------------------- /src/section.ts: -------------------------------------------------------------------------------- 1 | import {NodeType, SectionType} from "./type"; 2 | 3 | export default class Section { 4 | 5 | protected node: NodeType = null; 6 | protected implied: boolean = false; 7 | protected heading: NodeType = null; 8 | protected parent: SectionType | null = null; 9 | protected sections: Array = []; 10 | 11 | constructor(node: NodeType) { 12 | this.setNode(node); 13 | } 14 | 15 | public getNode(): NodeType { 16 | return this.node; 17 | } 18 | 19 | public setNode(node: NodeType) { 20 | this.node = node; 21 | } 22 | 23 | public getImplied(): boolean { 24 | return this.implied; 25 | } 26 | 27 | public setImplied(implied: boolean) { 28 | this.implied = implied; 29 | } 30 | 31 | public getHeading(): NodeType { 32 | return this.heading; 33 | } 34 | 35 | public setHeading(heading: NodeType) { 36 | this.heading = heading; 37 | } 38 | 39 | public getParent(): SectionType | null { 40 | return this.parent; 41 | } 42 | 43 | public setParent(section: SectionType) { 44 | this.parent = section; 45 | } 46 | 47 | public getSections(): Array { 48 | return this.sections; 49 | } 50 | 51 | public addSection(section: SectionType) { 52 | section.parent = this; 53 | this.sections.push(section); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "document-outliner", 3 | "version": "1.1.3", 4 | "description": "HTML5 Outliner.", 5 | "main": "lib/index.js", 6 | "type": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "jest", 9 | "dev": "webpack-dev-server --config webpack.dev.js", 10 | "build": "webpack --config webpack.prod.js", 11 | "build:lib": "tsc", 12 | "np": "np --no-cleanup", 13 | "deploy": "npm-run-all -s build build:lib test np" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/appleple/document-outliner" 18 | }, 19 | "keywords": [ 20 | "html5", 21 | "outline" 22 | ], 23 | "homepage": "https://appleple.github.io/document-outliner/", 24 | "author": { 25 | "name": "Atsushi Ito (atsu666)", 26 | "email": "ito@appleple.com" 27 | }, 28 | "license": "MIT", 29 | "devDependencies": { 30 | "awesome-typescript-loader": "^5.2.1", 31 | "html-minifier": "^3.5.21", 32 | "jest": "^23.6.0", 33 | "jest-puppeteer": "^3.7.0", 34 | "np": "^3.1.0", 35 | "npm-run-all": "^4.1.5", 36 | "puppeteer": "^1.11.0", 37 | "typescript": "^3.2.2", 38 | "webpack": "^4.27.1", 39 | "webpack-cli": "^3.1.2", 40 | "webpack-dev-server": "^3.1.10", 41 | "webpack-merge": "^4.1.5" 42 | }, 43 | "dependencies": { 44 | "@types/jquery": "^3.3.28" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/section.js: -------------------------------------------------------------------------------- 1 | var Section = /** @class */ (function () { 2 | function Section(node) { 3 | this.node = null; 4 | this.implied = false; 5 | this.heading = null; 6 | this.parent = null; 7 | this.sections = []; 8 | this.setNode(node); 9 | } 10 | Section.prototype.getNode = function () { 11 | return this.node; 12 | }; 13 | Section.prototype.setNode = function (node) { 14 | this.node = node; 15 | }; 16 | Section.prototype.getImplied = function () { 17 | return this.implied; 18 | }; 19 | Section.prototype.setImplied = function (implied) { 20 | this.implied = implied; 21 | }; 22 | Section.prototype.getHeading = function () { 23 | return this.heading; 24 | }; 25 | Section.prototype.setHeading = function (heading) { 26 | this.heading = heading; 27 | }; 28 | Section.prototype.getParent = function () { 29 | return this.parent; 30 | }; 31 | Section.prototype.setParent = function (section) { 32 | this.parent = section; 33 | }; 34 | Section.prototype.getSections = function () { 35 | return this.sections; 36 | }; 37 | Section.prototype.addSection = function (section) { 38 | section.parent = this; 39 | this.sections.push(section); 40 | }; 41 | return Section; 42 | }()); 43 | export default Section; 44 | //# sourceMappingURL=section.js.map -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import {NodeType} from "./type"; 2 | 3 | const isElement = (el: NodeType): boolean => { 4 | return !!(el && el.nodeName); 5 | }; 6 | 7 | const checkTagWithRegex = (el: NodeType, regex: RegExp): boolean => { 8 | if (el === null) { 9 | return false; 10 | } 11 | if (!(el instanceof HTMLElement)) { 12 | return false; 13 | } 14 | return isElement(el) && regex.test(el.tagName.toLowerCase()); 15 | }; 16 | 17 | const isSectioningRoot = (el: NodeType): boolean => { 18 | return checkTagWithRegex(el, /^(blockquote|body|details|fieldset|figure|td)$/); 19 | }; 20 | 21 | const isSectioningContent = (el: NodeType): boolean => { 22 | return checkTagWithRegex(el, /^(article|aside|nav|section)$/); 23 | }; 24 | 25 | const isHeadingContent = (el: NodeType): boolean => { 26 | return checkTagWithRegex(el, /^(h1|h2|h3|h4|h5|h6)$/); 27 | }; 28 | 29 | const isHidden = (el: NodeType): boolean => { 30 | if (el === null) { 31 | return false; 32 | } 33 | if (!(el instanceof HTMLElement)) { 34 | return false; 35 | } 36 | return isElement(el) && el.hasAttribute('hidden'); 37 | }; 38 | 39 | const getHeadingLevel = (el: NodeType): number => { 40 | if (!(el instanceof HTMLElement) || !isHeadingContent(el)) { 41 | return 0; 42 | } 43 | return parseInt(el.tagName.toLowerCase().substr(1)); 44 | }; 45 | 46 | export { isSectioningRoot, isSectioningContent, isHeadingContent, isHidden, getHeadingLevel }; 47 | -------------------------------------------------------------------------------- /src/outline.ts: -------------------------------------------------------------------------------- 1 | import {SectionType, OutlineType, NodeType} from "./type"; 2 | 3 | type SectionsType = Array; 4 | 5 | export default class Outline { 6 | 7 | protected sections: SectionsType = []; 8 | protected parentSection: SectionType | null = null; 9 | protected node: NodeType = null; 10 | protected outline: OutlineType = this; 11 | 12 | constructor(node: NodeType, section: SectionType | null = null) { 13 | this.setNode(node); 14 | if (section) { 15 | this.addSection(section); 16 | } 17 | } 18 | 19 | public getOutline(): OutlineType { 20 | return this.outline; 21 | } 22 | 23 | public setOutline(outline: OutlineType) { 24 | this.outline = outline; 25 | } 26 | 27 | public getSections(): SectionsType { 28 | return this.sections; 29 | } 30 | 31 | public setSections(sections: SectionsType) { 32 | this.sections = sections; 33 | } 34 | 35 | public addSection(section: SectionType): void { 36 | this.sections.push(section); 37 | } 38 | 39 | public setParentSection(section: SectionType): void { 40 | this.parentSection = section; 41 | } 42 | 43 | public getParentSection(): SectionType | null { 44 | return this.parentSection; 45 | } 46 | 47 | public getLastSection(): SectionType { 48 | return this.sections[this.sections.length - 1]; 49 | } 50 | 51 | public setNode(node: NodeType): void { 52 | this.node = node; 53 | } 54 | 55 | public getNode(): NodeType { 56 | return this.node; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/outline.js: -------------------------------------------------------------------------------- 1 | var Outline = /** @class */ (function () { 2 | function Outline(node, section) { 3 | if (section === void 0) { section = null; } 4 | this.sections = []; 5 | this.parentSection = null; 6 | this.node = null; 7 | this.outline = this; 8 | this.setNode(node); 9 | if (section) { 10 | this.addSection(section); 11 | } 12 | } 13 | Outline.prototype.getOutline = function () { 14 | return this.outline; 15 | }; 16 | Outline.prototype.setOutline = function (outline) { 17 | this.outline = outline; 18 | }; 19 | Outline.prototype.getSections = function () { 20 | return this.sections; 21 | }; 22 | Outline.prototype.setSections = function (sections) { 23 | this.sections = sections; 24 | }; 25 | Outline.prototype.addSection = function (section) { 26 | this.sections.push(section); 27 | }; 28 | Outline.prototype.setParentSection = function (section) { 29 | this.parentSection = section; 30 | }; 31 | Outline.prototype.getParentSection = function () { 32 | return this.parentSection; 33 | }; 34 | Outline.prototype.getLastSection = function () { 35 | return this.sections[this.sections.length - 1]; 36 | }; 37 | Outline.prototype.setNode = function (node) { 38 | this.node = node; 39 | }; 40 | Outline.prototype.getNode = function () { 41 | return this.node; 42 | }; 43 | return Outline; 44 | }()); 45 | export default Outline; 46 | //# sourceMappingURL=outline.js.map -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | references: 3 | container_config: &container_config 4 | docker: 5 | - image: circleci/node:8.14.0-jessie 6 | working_directory: ~/document-outliner 7 | parallelism: 1 8 | # Jobs 9 | jobs: 10 | # Build 11 | build: 12 | <<: *container_config 13 | steps: 14 | - checkout 15 | - restore_cache: 16 | keys: 17 | - v1-dependencies-{{ checksum "package.json" }} 18 | - v1-dependencies- # fallback to using the latest cache if no exact match is found 19 | - run: 20 | name: Install npm pacakges 21 | command: npm install 22 | - run: 23 | name: Transpile typescript to es5 & Module bundling 24 | command: npm run build 25 | - run: 26 | name: Transpile typescript to es5 27 | command: 'npm run build:lib' 28 | - save_cache: 29 | key: v1-dependencies-{{ checksum "package.json" }} 30 | paths: 31 | - node_modules 32 | - persist_to_workspace: 33 | root: . 34 | paths: 35 | - . 36 | # Test 37 | test: 38 | <<: *container_config 39 | steps: 40 | - attach_workspace: 41 | at: . 42 | - run: 43 | name: Workaround for GoogleChrome/puppeteer#290 44 | command: 'sh .circleci/setup_puppeteer.sh' 45 | - run: 46 | name: Test 47 | command: npm run test 48 | # Workflow 49 | workflows: 50 | version: 2 51 | build_and_test: 52 | jobs: 53 | - build 54 | - test: 55 | requires: 56 | - build 57 | filters: 58 | branches: 59 | only: 60 | - master 61 | 62 | -------------------------------------------------------------------------------- /__tests__/default-option.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

The Tax Book

10 |

Earning money

11 |

Earning money is good.

12 |

Getting a job

13 |

To earn money you typically need a job.

14 |

Spending money

15 |

Spending is what money is mainly used for.

16 |

Cheap things

17 |

Buying cheap things often not cost-effective.

18 |

Expensive things

19 |

The most expensive thing is often not the most cost-effective either.

20 |

Investing money

21 |

You can lend your money to other people.

22 |

Losing money

23 |

If you spend money or invest money, sooner or later you will lose money. 24 |

Poor judgement

25 |
26 | 27 |
28 |
    29 |
  1. The Tax Book 30 |
      31 |
    1. Earning money 32 |
        33 |
      1. Getting a job
      2. 34 |
      35 |
    2. 36 |
    3. Spending money 37 |
        38 |
      1. Cheap things
      2. 39 |
      3. Expensive things
      4. 40 |
      41 |
    4. 42 |
    5. Investing money
    6. 43 |
    7. Losing money 44 |
        45 |
      1. Poor judgement
      2. 46 |
      47 |
    8. 48 |
    49 |
  2. 50 |
51 |
52 | 53 |
54 | 55 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | html5-outliner 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 |

The Tax Book

13 |
14 |

Earning money

15 |

Earning money is good.

16 |
17 |

Getting a job

18 |

To earn money you typically need a job.

19 |
20 |
21 |
22 |

Spending money

23 |

Spending is what money is mainly used for.

24 |
25 |

Cheap things

26 |

Buying cheap things often not cost-effective.

27 |
28 |
29 |

Expensive things

30 |

The most expensive thing is often not the most cost-effective either.

31 |
32 |
33 |
34 |

Investing money

35 |

You can lend your money to other people.

36 |
37 |
38 |

Losing money

39 |

If you spend money or invest money, sooner or later you will lose money.

40 |
41 |

Poor judgement

42 |

Usually if you lose money it's because you made a mistake.

43 |
44 |
45 |
46 | 47 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /__tests__/spec-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

The Tax Book

10 |

Earning money

11 |

Earning money is good.

12 |

Getting a job

13 |

To earn money you typically need a job.

14 |

Spending money

15 |

Spending is what money is mainly used for.

16 |

Cheap things

17 |

Buying cheap things often not cost-effective.

18 |

Expensive things

19 |

The most expensive thing is often not the most cost-effective either.

20 |

Investing money

21 |

You can lend your money to other people.

22 |

Losing money

23 |

If you spend money or invest money, sooner or later you will lose money. 24 |

Poor judgement

25 |

Usually if you lose money it's because you made a mistake.

26 |
27 | 28 |
29 |
    30 |
  1. The Tax Book 31 |
      32 |
    1. Earning money 33 |
        34 |
      1. Getting a job
      2. 35 |
      36 |
    2. 37 |
    3. Spending money 38 |
        39 |
      1. Cheap things
      2. 40 |
      3. Expensive things
      4. 41 |
      42 |
    4. 43 |
    5. Investing money
    6. 44 |
    7. Losing money 45 |
        46 |
      1. Poor judgement
      2. 47 |
      48 |
    8. 49 |
    50 |
  2. 51 |
52 |
53 | 54 |
55 | 56 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /__tests__/jquery-plugin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 |
13 |

The Tax Book

14 |

Earning money

15 |

Earning money is good.

16 |

Getting a job

17 |

To earn money you typically need a job.

18 |

Spending money

19 |

Spending is what money is mainly used for.

20 |

Cheap things

21 |

Buying cheap things often not cost-effective.

22 |

Expensive things

23 |

The most expensive thing is often not the most cost-effective either.

24 |

Investing money

25 |

You can lend your money to other people.

26 |

Losing money

27 |

If you spend money or invest money, sooner or later you will lose money. 28 |

Poor judgement

29 | 30 |
31 | 32 |
33 |
    34 |
  1. The Tax Book 35 |
      36 |
    1. Earning money 37 |
        38 |
      1. Getting a job
      2. 39 |
      40 |
    2. 41 |
    3. Spending money 42 |
        43 |
      1. Cheap things
      2. 44 |
      3. Expensive things
      4. 45 |
      46 |
    4. 47 |
    5. Investing money
    6. 48 |
    7. Losing money 49 |
        50 |
      1. Poor judgement
      2. 51 |
      52 |
    8. 53 |
    54 |
  2. 55 |
56 |
57 | 58 |
59 | 60 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /__tests__/spec-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

The Tax Book

10 |
11 |

Earning money

12 |

Earning money is good.

13 |
14 |

Getting a job

15 |

To earn money you typically need a job.

16 |
17 |
18 |
19 |

Spending money

20 |

Spending is what money is mainly used for.

21 |
22 |

Cheap things

23 |

Buying cheap things often not cost-effective.

24 |
25 |
26 |

Expensive things

27 |

The most expensive thing is often not the most cost-effective either.

28 |
29 |
30 |
31 |

Investing money

32 |

You can lend your money to other people.

33 |
34 |
35 |

Losing money

36 |

If you spend money or invest money, sooner or later you will lose money.

37 |
38 |

Poor judgement

39 |

Usually if you lose money it's because you made a mistake.

40 |
41 |
42 |
43 | 44 |
45 |
    46 |
  1. The Tax Book 47 |
      48 |
    1. Earning money 49 |
        50 |
      1. Getting a job
      2. 51 |
      52 |
    2. 53 |
    3. Spending money 54 |
        55 |
      1. Cheap things
      2. 56 |
      3. Expensive things
      4. 57 |
      58 |
    4. 59 |
    5. Investing money
    6. 60 |
    7. Losing money 61 |
        62 |
      1. Poor judgement
      2. 63 |
      64 |
    8. 65 |
    66 |
  2. 67 |
68 |
69 | 70 |
71 | 72 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /__tests__/full-option.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

The Tax Book

10 |
11 |

Earning money

12 |

Earning money is good.

13 |
14 |

Getting a job

15 |

To earn money you typically need a job.

16 |
17 |
18 |
19 |

Spending money

20 |

Spending is what money is mainly used for.

21 |
22 |

Cheap things

23 |

Buying cheap things often not cost-effective.

24 |
25 |
26 |

Expensive things

27 |

The most expensive thing is often not the most cost-effective either.

28 |
29 |
30 |
31 |

Investing money

32 |

You can lend your money to other people.

33 |
34 |
35 |

Losing money

36 |

If you spend money or invest money, sooner or later you will lose money.

37 |
38 |

Poor judgement

39 |

Usually if you lose money it's because you made a mistake.

40 |
41 |
42 |
43 | 44 |
45 | 68 |
69 | 70 |
71 | 72 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /lib/make-list.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"make-list.js","sourceRoot":"","sources":["../src/make-list.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/make-list.ts: -------------------------------------------------------------------------------- 1 | import {SectionType, OutlineType, OptionsType, ConfigType} from "./type"; 2 | import {isHeadingContent} from "./util"; 3 | 4 | export default class MakeList { 5 | private readonly outline: OutlineType; 6 | private readonly options: ConfigType; 7 | private anchor: number = 1; 8 | private html: string = ''; 9 | 10 | constructor(outline: OutlineType, options: OptionsType) { 11 | this.outline = outline; 12 | this.options = Object.assign({}, { 13 | link: true, 14 | listType: 'ol', 15 | levelLimit: 99, 16 | listClassName: '', 17 | itemClassName: '', 18 | exceptClass: '', 19 | anchorName: 'header-$1' 20 | }, options) as ConfigType; 21 | } 22 | 23 | public getHtml(): string { 24 | if (!this.outline) { 25 | throw new Error("No sectioning contents."); 26 | } 27 | this.build(this.outline.getOutline(), 1); 28 | return this.html; 29 | } 30 | 31 | protected getListType(level: number): string { 32 | let defaultType = 'ol'; 33 | if (typeof this.options.listType === 'string') { 34 | if (/ol|ul/.test(this.options.listType)) { 35 | return this.options.listType; 36 | } 37 | } else if (Array.isArray(this.options.listType)) { 38 | if (/ol|ul/.test(this.options.listType[0])) { 39 | defaultType = this.options.listType[0]; 40 | } 41 | const type = this.options.listType[level]; 42 | if (/ol|ul/.test(type)) { 43 | return type; 44 | } 45 | } 46 | return defaultType; 47 | } 48 | 49 | protected build(outline: OutlineType | SectionType, level: number): void { 50 | if (level > this.options.levelLimit) { 51 | return; 52 | } 53 | const hasHeading = this.hasHeading(outline.getSections()); 54 | if (hasHeading) { 55 | const listClassName = this.options.listClassName ? ` ${this.options.listClassName}` : ''; 56 | this.html += `<${this.getListType(level - 1)} class="level-${level}${listClassName}">`; 57 | } 58 | this.buildSections(outline.getSections(), hasHeading, level); 59 | if (hasHeading) { 60 | this.html += ``; 61 | } 62 | } 63 | 64 | protected buildSections(sections: Array, hasHeading: boolean, level: number): void { 65 | sections.forEach((section) => { 66 | const heading = section.getHeading() as HTMLElement; 67 | const nextLevel = hasHeading ? level + 1 : level; 68 | if (isHeadingContent(heading) && !heading.classList.contains(this.options.exceptClass)) { 69 | const itemClassName = this.options.itemClassName ? ` class="${this.options.itemClassName}"` : ''; 70 | if (this.options.link) { 71 | this.html += `${this.buildLink(heading)}`; 72 | } else { 73 | this.html += `${heading.innerHTML}`; 74 | } 75 | if (section.getSections().length > 0) { 76 | this.build(section, nextLevel); 77 | } 78 | this.html += ''; 79 | } else if (section.getSections().length > 0) { 80 | if (hasHeading) { 81 | this.html += '
  • '; 82 | } 83 | const tmp = this.html; 84 | this.build(section, nextLevel); 85 | if (tmp === this.html) { 86 | this.html = this.html.slice(0, '
  • '.length * -1); 87 | } 88 | if (hasHeading) { 89 | this.html += '
  • '; 90 | } 91 | } 92 | }); 93 | } 94 | 95 | protected buildLink(heading: HTMLElement): string { 96 | const anchorClassName = this.options.linkClassName ? ` class="${this.options.linkClassName}"` : ''; 97 | let anchorName = this.options.anchorName.replace(/\$1/, this.anchor.toString()); 98 | if (heading.id) { 99 | anchorName = heading.id; 100 | } else { 101 | heading.id = anchorName; 102 | } 103 | this.anchor++; 104 | return `${heading.innerHTML}`; 105 | } 106 | 107 | protected hasHeading(sections: Array): boolean { 108 | let hasHeading = false; 109 | sections.forEach((section) => { 110 | const heading = section.getHeading(); 111 | if (heading 112 | && isHeadingContent(heading) 113 | && heading instanceof HTMLElement 114 | && !heading.classList.contains(this.options.exceptClass) 115 | ) { 116 | hasHeading = true; 117 | } 118 | }); 119 | return hasHeading; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/make-list.js: -------------------------------------------------------------------------------- 1 | import { isHeadingContent } from "./util"; 2 | var MakeList = /** @class */ (function () { 3 | function MakeList(outline, options) { 4 | this.anchor = 1; 5 | this.html = ''; 6 | this.outline = outline; 7 | this.options = Object.assign({}, { 8 | link: true, 9 | listType: 'ol', 10 | levelLimit: 99, 11 | listClassName: '', 12 | itemClassName: '', 13 | exceptClass: '', 14 | anchorName: 'header-$1' 15 | }, options); 16 | } 17 | MakeList.prototype.getHtml = function () { 18 | if (!this.outline) { 19 | throw new Error("No sectioning contents."); 20 | } 21 | this.build(this.outline.getOutline(), 1); 22 | return this.html; 23 | }; 24 | MakeList.prototype.getListType = function (level) { 25 | var defaultType = 'ol'; 26 | if (typeof this.options.listType === 'string') { 27 | if (/ol|ul/.test(this.options.listType)) { 28 | return this.options.listType; 29 | } 30 | } 31 | else if (Array.isArray(this.options.listType)) { 32 | if (/ol|ul/.test(this.options.listType[0])) { 33 | defaultType = this.options.listType[0]; 34 | } 35 | var type = this.options.listType[level]; 36 | if (/ol|ul/.test(type)) { 37 | return type; 38 | } 39 | } 40 | return defaultType; 41 | }; 42 | MakeList.prototype.build = function (outline, level) { 43 | if (level > this.options.levelLimit) { 44 | return; 45 | } 46 | var hasHeading = this.hasHeading(outline.getSections()); 47 | if (hasHeading) { 48 | var listClassName = this.options.listClassName ? " " + this.options.listClassName : ''; 49 | this.html += "<" + this.getListType(level - 1) + " class=\"level-" + level + listClassName + "\">"; 50 | } 51 | this.buildSections(outline.getSections(), hasHeading, level); 52 | if (hasHeading) { 53 | this.html += ""; 54 | } 55 | }; 56 | MakeList.prototype.buildSections = function (sections, hasHeading, level) { 57 | var _this = this; 58 | sections.forEach(function (section) { 59 | var heading = section.getHeading(); 60 | var nextLevel = hasHeading ? level + 1 : level; 61 | if (isHeadingContent(heading) && !heading.classList.contains(_this.options.exceptClass)) { 62 | var itemClassName = _this.options.itemClassName ? " class=\"" + _this.options.itemClassName + "\"" : ''; 63 | if (_this.options.link) { 64 | _this.html += "" + _this.buildLink(heading); 65 | } 66 | else { 67 | _this.html += "" + heading.innerHTML; 68 | } 69 | if (section.getSections().length > 0) { 70 | _this.build(section, nextLevel); 71 | } 72 | _this.html += ''; 73 | } 74 | else if (section.getSections().length > 0) { 75 | if (hasHeading) { 76 | _this.html += '
  • '; 77 | } 78 | var tmp = _this.html; 79 | _this.build(section, nextLevel); 80 | if (tmp === _this.html) { 81 | _this.html = _this.html.slice(0, '
  • '.length * -1); 82 | } 83 | if (hasHeading) { 84 | _this.html += '
  • '; 85 | } 86 | } 87 | }); 88 | }; 89 | MakeList.prototype.buildLink = function (heading) { 90 | var anchorClassName = this.options.linkClassName ? " class=\"" + this.options.linkClassName + "\"" : ''; 91 | var anchorName = this.options.anchorName.replace(/\$1/, this.anchor.toString()); 92 | if (heading.id) { 93 | anchorName = heading.id; 94 | } 95 | else { 96 | heading.id = anchorName; 97 | } 98 | this.anchor++; 99 | return "" + heading.innerHTML + ""; 100 | }; 101 | MakeList.prototype.hasHeading = function (sections) { 102 | var _this = this; 103 | var hasHeading = false; 104 | sections.forEach(function (section) { 105 | var heading = section.getHeading(); 106 | if (heading 107 | && isHeadingContent(heading) 108 | && heading instanceof HTMLElement 109 | && !heading.classList.contains(_this.options.exceptClass)) { 110 | hasHeading = true; 111 | } 112 | }); 113 | return hasHeading; 114 | }; 115 | return MakeList; 116 | }()); 117 | export default MakeList; 118 | //# sourceMappingURL=make-list.js.map -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # document-outliner 2 | [![npm version](https://badge.fury.io/js/document-outliner.svg)](https://badge.fury.io/js/document-outliner) 3 | [![CircleCI](https://circleci.com/gh/appleple/document-outliner/tree/master.svg?style=shield)](https://circleci.com/gh/appleple/document-outliner/tree/master) 4 | [![npm download](http://img.shields.io/npm/dm/document-outliner.svg)](https://www.npmjs.com/package/document-outliner) 5 | [![GitHub license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://raw.githubusercontent.com/appleple/document-outliner/master/LICENSE) 6 | 7 | A utility to make ol/ul list by using html5 outline algorithm. 8 | 9 | **Specifications** 10 | 11 | > [Living Standard — Last Updated 11 December 2018](https://html.spec.whatwg.org/multipage/sections.html#outlines) 12 | 13 | ## Installation 14 | 15 | ### Vanilla (Plain JavaScript) 16 | 17 | #### via npm 18 | 19 | ```sh 20 | npm install document-outliner 21 | ``` 22 | 23 | #### via yarn 24 | 25 | ```sh 26 | yarn add document-outliner 27 | ``` 28 | 29 | #### vid cdn 30 | 31 | ```html 32 | 33 | ``` 34 | 35 | ### jQuery plugin 36 | 37 | #### via cdn 38 | 39 | ```html 40 | 41 | ``` 42 | 43 | ## Usage 44 | 45 | ### Vanilla (Plain JavaScript) 46 | 47 | ```javascript 48 | import DocumentOutliner from 'document-outliner'; 49 | 50 | const outliner = new DocumentOutliner('.js-outline-src'); 51 | outliner.makeList('.js-outline-output', {listType: 'ul'}); 52 | ``` 53 | 54 | ### jQuery 55 | 56 | ```javascript 57 | $(function () { 58 | $('.container').documentOutliner('.output', {link: false}); 59 | }); 60 | ``` 61 | 62 | ## Options 63 | 64 | | name | description | default | 65 | |:---|:---|:---| 66 | | link | create HTML anchor link | true | 67 | | listType | HTML list type ('ol'|'ul') | 'ol' | 68 | | listClassName | ol/ul class name | '' | 69 | | itemClassName | li class name | '' | 70 | | linkClassName | link class name | '' | 71 | | anchorName | anchor link name ($1 is to be link number) | 'heading-$1' | 72 | | levelLimit | limit on number of level | 99 | 73 | 74 | ## Example 75 | 76 | **HTML** 77 | 78 | ```html 79 |
    80 | 81 |
    82 |

    The Tax Book

    83 |
    84 |

    Earning money

    85 |

    Earning money is good.

    86 |
    87 |

    Getting a job

    88 |

    To earn money you typically need a job.

    89 |
    90 |
    91 |
    92 |

    Spending money

    93 |

    Spending is what money is mainly used for.

    94 |
    95 |

    Cheap things

    96 |

    Buying cheap things often not cost-effective.

    97 |
    98 |
    99 |

    Expensive things

    100 |

    The most expensive thing is often not the most cost-effective either.

    101 |
    102 |
    103 |
    104 |

    Investing money

    105 |

    You can lend your money to other people.

    106 |
    107 |
    108 |

    Losing money

    109 |

    If you spend money or invest money, sooner or later you will lose money.

    110 |
    111 |

    Poor judgement

    112 |

    Usually if you lose money it's because you made a mistake.

    113 |
    114 |
    115 |
    116 | ``` 117 | 118 | **JavaScript** 119 | 120 | ```javascript 121 | const outliner = new DocumentOutliner('.container'); 122 | outliner.makeList('.outline', { 123 | link: true, 124 | listType: 'ul', 125 | listClassName: 'list-group', 126 | itemClassName: 'list-group-item', 127 | anchorName: 'heading-$1' 128 | }); 129 | ``` 130 | 131 | **Yield** 132 | 133 | ```html 134 |
    135 | 158 |
    159 | 160 |
    161 |

    The Tax Book

    162 |
    163 |

    Earning money

    164 |

    Earning money is good.

    165 |
    166 |

    Getting a job

    167 |

    To earn money you typically need a job.

    168 |
    169 |
    170 |
    171 |

    Spending money

    172 |

    Spending is what money is mainly used for.

    173 |
    174 |

    Cheap things

    175 |

    Buying cheap things often not cost-effective.

    176 |
    177 |
    178 |

    Expensive things

    179 |

    The most expensive thing is often not the most cost-effective either.

    180 |
    181 |
    182 |
    183 |

    Investing money

    184 |

    You can lend your money to other people.

    185 |
    186 |
    187 |

    Losing money

    188 |

    If you spend money or invest money, sooner or later you will lose money.

    189 |
    190 |

    Poor judgement

    191 |

    Usually if you lose money it's because you made a mistake.

    192 |
    193 |
    194 |
    195 | ``` 196 | 197 | ## Licence 198 | 199 | [MIT](https://github.com/appleple/document-outliner/blob/master/LICENSE) 200 | -------------------------------------------------------------------------------- /lib/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /bundle/document-outliner.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.DocumentOutliner=e():t.DocumentOutliner=e()}(window,(function(){return function(t){var e={};function n(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(i,o,function(e){return t[e]}.bind(null,o));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="bundle",n(n.s=0)}([function(t,e,n){"use strict";n.r(e);var i=function(){function t(t){this.node=null,this.implied=!1,this.heading=null,this.parent=null,this.sections=[],this.setNode(t)}return t.prototype.getNode=function(){return this.node},t.prototype.setNode=function(t){this.node=t},t.prototype.getImplied=function(){return this.implied},t.prototype.setImplied=function(t){this.implied=t},t.prototype.getHeading=function(){return this.heading},t.prototype.setHeading=function(t){this.heading=t},t.prototype.getParent=function(){return this.parent},t.prototype.setParent=function(t){this.parent=t},t.prototype.getSections=function(){return this.sections},t.prototype.addSection=function(t){t.parent=this,this.sections.push(t)},t}(),o=function(){function t(t,e){void 0===e&&(e=null),this.sections=[],this.parentSection=null,this.node=null,this.outline=this,this.setNode(t),e&&this.addSection(e)}return t.prototype.getOutline=function(){return this.outline},t.prototype.setOutline=function(t){this.outline=t},t.prototype.getSections=function(){return this.sections},t.prototype.setSections=function(t){this.sections=t},t.prototype.addSection=function(t){this.sections.push(t)},t.prototype.setParentSection=function(t){this.parentSection=t},t.prototype.getParentSection=function(){return this.parentSection},t.prototype.getLastSection=function(){return this.sections[this.sections.length-1]},t.prototype.setNode=function(t){this.node=t},t.prototype.getNode=function(){return this.node},t}(),r=function(t){return!(!t||!t.nodeName)},s=function(t,e){return null!==t&&(t instanceof HTMLElement&&(r(t)&&e.test(t.tagName.toLowerCase())))},u=function(t){return s(t,/^(blockquote|body|details|fieldset|figure|td)$/)},c=function(t){return s(t,/^(article|aside|nav|section)$/)},l=function(t){return s(t,/^(h1|h2|h3|h4|h5|h6)$/)},a=function(t){return null!==t&&(t instanceof HTMLElement&&(r(t)&&t.hasAttribute("hidden")))},h=function(t){return t instanceof HTMLElement&&l(t)?parseInt(t.tagName.toLowerCase().substr(1)):0},p=function(){function t(t,e){this.anchor=1,this.html="",this.outline=t,this.options=Object.assign({},{link:!0,listType:"ol",levelLimit:99,listClassName:"",itemClassName:"",exceptClass:"",anchorName:"header-$1"},e)}return t.prototype.getHtml=function(){if(!this.outline)throw new Error("No sectioning contents.");return this.build(this.outline.getOutline(),1),this.html},t.prototype.getListType=function(t){var e="ol";if("string"==typeof this.options.listType){if(/ol|ul/.test(this.options.listType))return this.options.listType}else if(Array.isArray(this.options.listType)){/ol|ul/.test(this.options.listType[0])&&(e=this.options.listType[0]);var n=this.options.listType[t];if(/ol|ul/.test(n))return n}return e},t.prototype.build=function(t,e){if(!(e>this.options.levelLimit)){var n=this.hasHeading(t.getSections());if(n){var i=this.options.listClassName?" "+this.options.listClassName:"";this.html+="<"+this.getListType(e-1)+' class="level-'+e+i+'">'}this.buildSections(t.getSections(),n,e),n&&(this.html+="")}},t.prototype.buildSections=function(t,e,n){var i=this;t.forEach((function(t){var o=t.getHeading(),r=e?n+1:n;if(l(o)&&!o.classList.contains(i.options.exceptClass)){var s=i.options.itemClassName?' class="'+i.options.itemClassName+'"':"";i.options.link?i.html+=""+i.buildLink(o):i.html+=""+o.innerHTML,t.getSections().length>0&&i.build(t,r),i.html+=""}else if(t.getSections().length>0){e&&(i.html+="
  • ");var u=i.html;i.build(t,r),u===i.html&&(i.html=i.html.slice(0,-1*"
  • ".length)),e&&(i.html+="
  • ")}}))},t.prototype.buildLink=function(t){var e=this.options.linkClassName?' class="'+this.options.linkClassName+'"':"",n=this.options.anchorName.replace(/\$1/,this.anchor.toString());return t.id?n=t.id:t.id=n,this.anchor++,'"+t.innerHTML+""},t.prototype.hasHeading=function(t){var e=this,n=!1;return t.forEach((function(t){var i=t.getHeading();i&&l(i)&&i instanceof HTMLElement&&!i.classList.contains(e.options.exceptClass)&&(n=!0)})),n},t}(),d=function(){function t(e){this.currentOutlineTarget=null,this.currentSection=null,this.stack=[],this.rootNode="string"==typeof e?document.querySelector(e):e,t.walk(this.rootNode,this.enter.bind(this),this.exit.bind(this))}return t.prototype.getOutlineObject=function(){return!!this.currentOutlineTarget&&this.currentOutlineTarget.getOutline()},t.prototype.makeList=function(t,e){var n=this.getOutlineObject();if(!n)throw new Error("No sectioning contents.");if(!t)throw new TypeError("Invalid options: target empty.");var i=new p(n,e).getHtml();if("string"==typeof t)[].forEach.call(document.querySelectorAll(t),(function(t){t.innerHTML=i}));else if(t instanceof NodeList)[].forEach.call(t,(function(t){t.innerHTML=i}));else{t.innerHTML=i}},t.walk=function(t,e,n){var i=t;t:for(;i;)if(e(i),i.firstChild)i=i.firstChild;else for(;i;)if(n(i),i===t)i=null;else{if(i.nextSibling){i=i.nextSibling;continue t}i=i.parentNode}},t.prototype.enter=function(t){var e=this.getStackTopNode();if(!e||!l(e.getNode())&&!a(e.getNode()))if(a(t))this.stack.push(new o(t));else{if(c(t))return null!==this.currentOutlineTarget&&(this.currentSection&&!this.currentSection.getHeading()&&(this.currentSection.setHeading(document.createTextNode("No title.")),this.currentSection.setImplied(!0)),this.stack.push(this.currentOutlineTarget)),this.currentOutlineTarget=new o(t),this.currentSection=new i(t),void this.currentOutlineTarget.setOutline(new o(this.currentOutlineTarget.getNode(),this.currentSection));if(u(t)||t===this.rootNode)return null!==this.currentOutlineTarget&&this.stack.push(this.currentOutlineTarget),this.currentOutlineTarget=new o(t),this.currentOutlineTarget.setParentSection(this.currentSection),this.currentSection=new i(t),void this.currentOutlineTarget.setOutline(new o(this.currentOutlineTarget.getNode(),this.currentSection));if(l(t)){if(this.currentSection&&!this.currentSection.getHeading())this.currentSection.setHeading(t);else if(this.currentOutlineTarget&&(this.currentOutlineTarget.getOutline().getLastSection().getImplied()||h(t)<=h(this.currentOutlineTarget.getOutline().getLastSection().getHeading()))){var n=new i(t);this.currentOutlineTarget.getOutline().addSection(n),this.currentSection=n,this.currentSection.setHeading(t)}else{var r=!1,s=this.currentSection,p=0;do{if(h(t)>h(s.getHeading())){n=new i(t);s.addSection(n),this.currentSection=n,this.currentSection.setHeading(t),r=!0}s=s.getParent(),p++}while(!r&&p<99)}this.stack.push(new o(t))}else;}},t.prototype.exit=function(t){var e=this,n=this.getStackTopNode();if(n&&n.getNode()===t&&this.stack.pop(),!n||!l(n.getNode())&&!a(n.getNode())){if(c(t)&&this.stack.length>0){this.currentSection&&!this.currentSection.getHeading()&&(this.currentSection.setHeading(document.createTextNode("No title.")),this.currentSection.setImplied(!0));var i=this.currentOutlineTarget;return this.currentOutlineTarget=this.stack.pop(),this.currentSection=this.currentOutlineTarget.getOutline().getLastSection(),void i.getOutline().getSections().forEach((function(t){e.currentSection&&e.currentSection.addSection(t)}))}if((u(t)||t===this.rootNode)&&this.stack.length>0)return this.currentSection&&!this.currentSection.getHeading()&&(this.currentSection.setHeading(document.createTextNode("No title.")),this.currentSection.setImplied(!0)),this.currentOutlineTarget&&(this.currentSection=this.currentOutlineTarget.getParentSection()),void(this.currentOutlineTarget=this.stack.pop());(u(t)||t===this.rootNode||c(t))&&this.currentSection&&!this.currentSection.getHeading()&&(this.currentSection.setHeading(document.createTextNode("No title.")),this.currentSection.setImplied(!0))}},t.prototype.getStackTopNode=function(){return this.stack[this.stack.length-1]},t}();e.default=d}]).default})); -------------------------------------------------------------------------------- /bundle/jquery-document-outliner.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.DocumentOutliner=e():t.DocumentOutliner=e()}(window,(function(){return function(t){var e={};function n(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(i,o,function(e){return t[e]}.bind(null,o));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="bundle",n(n.s=1)}([function(t,e,n){"use strict";n.r(e);var i=function(){function t(t){this.node=null,this.implied=!1,this.heading=null,this.parent=null,this.sections=[],this.setNode(t)}return t.prototype.getNode=function(){return this.node},t.prototype.setNode=function(t){this.node=t},t.prototype.getImplied=function(){return this.implied},t.prototype.setImplied=function(t){this.implied=t},t.prototype.getHeading=function(){return this.heading},t.prototype.setHeading=function(t){this.heading=t},t.prototype.getParent=function(){return this.parent},t.prototype.setParent=function(t){this.parent=t},t.prototype.getSections=function(){return this.sections},t.prototype.addSection=function(t){t.parent=this,this.sections.push(t)},t}(),o=function(){function t(t,e){void 0===e&&(e=null),this.sections=[],this.parentSection=null,this.node=null,this.outline=this,this.setNode(t),e&&this.addSection(e)}return t.prototype.getOutline=function(){return this.outline},t.prototype.setOutline=function(t){this.outline=t},t.prototype.getSections=function(){return this.sections},t.prototype.setSections=function(t){this.sections=t},t.prototype.addSection=function(t){this.sections.push(t)},t.prototype.setParentSection=function(t){this.parentSection=t},t.prototype.getParentSection=function(){return this.parentSection},t.prototype.getLastSection=function(){return this.sections[this.sections.length-1]},t.prototype.setNode=function(t){this.node=t},t.prototype.getNode=function(){return this.node},t}(),r=function(t){return!(!t||!t.nodeName)},s=function(t,e){return null!==t&&(t instanceof HTMLElement&&(r(t)&&e.test(t.tagName.toLowerCase())))},u=function(t){return s(t,/^(blockquote|body|details|fieldset|figure|td)$/)},c=function(t){return s(t,/^(article|aside|nav|section)$/)},l=function(t){return s(t,/^(h1|h2|h3|h4|h5|h6)$/)},a=function(t){return null!==t&&(t instanceof HTMLElement&&(r(t)&&t.hasAttribute("hidden")))},h=function(t){return t instanceof HTMLElement&&l(t)?parseInt(t.tagName.toLowerCase().substr(1)):0},p=function(){function t(t,e){this.anchor=1,this.html="",this.outline=t,this.options=Object.assign({},{link:!0,listType:"ol",levelLimit:99,listClassName:"",itemClassName:"",exceptClass:"",anchorName:"header-$1"},e)}return t.prototype.getHtml=function(){if(!this.outline)throw new Error("No sectioning contents.");return this.build(this.outline.getOutline(),1),this.html},t.prototype.getListType=function(t){var e="ol";if("string"==typeof this.options.listType){if(/ol|ul/.test(this.options.listType))return this.options.listType}else if(Array.isArray(this.options.listType)){/ol|ul/.test(this.options.listType[0])&&(e=this.options.listType[0]);var n=this.options.listType[t];if(/ol|ul/.test(n))return n}return e},t.prototype.build=function(t,e){if(!(e>this.options.levelLimit)){var n=this.hasHeading(t.getSections());if(n){var i=this.options.listClassName?" "+this.options.listClassName:"";this.html+="<"+this.getListType(e-1)+' class="level-'+e+i+'">'}this.buildSections(t.getSections(),n,e),n&&(this.html+="")}},t.prototype.buildSections=function(t,e,n){var i=this;t.forEach((function(t){var o=t.getHeading(),r=e?n+1:n;if(l(o)&&!o.classList.contains(i.options.exceptClass)){var s=i.options.itemClassName?' class="'+i.options.itemClassName+'"':"";i.options.link?i.html+=""+i.buildLink(o):i.html+=""+o.innerHTML,t.getSections().length>0&&i.build(t,r),i.html+=""}else if(t.getSections().length>0){e&&(i.html+="
  • ");var u=i.html;i.build(t,r),u===i.html&&(i.html=i.html.slice(0,-1*"
  • ".length)),e&&(i.html+="
  • ")}}))},t.prototype.buildLink=function(t){var e=this.options.linkClassName?' class="'+this.options.linkClassName+'"':"",n=this.options.anchorName.replace(/\$1/,this.anchor.toString());return t.id?n=t.id:t.id=n,this.anchor++,'"+t.innerHTML+""},t.prototype.hasHeading=function(t){var e=this,n=!1;return t.forEach((function(t){var i=t.getHeading();i&&l(i)&&i instanceof HTMLElement&&!i.classList.contains(e.options.exceptClass)&&(n=!0)})),n},t}(),d=function(){function t(e){this.currentOutlineTarget=null,this.currentSection=null,this.stack=[],this.rootNode="string"==typeof e?document.querySelector(e):e,t.walk(this.rootNode,this.enter.bind(this),this.exit.bind(this))}return t.prototype.getOutlineObject=function(){return!!this.currentOutlineTarget&&this.currentOutlineTarget.getOutline()},t.prototype.makeList=function(t,e){var n=this.getOutlineObject();if(!n)throw new Error("No sectioning contents.");if(!t)throw new TypeError("Invalid options: target empty.");var i=new p(n,e).getHtml();if("string"==typeof t)[].forEach.call(document.querySelectorAll(t),(function(t){t.innerHTML=i}));else if(t instanceof NodeList)[].forEach.call(t,(function(t){t.innerHTML=i}));else{t.innerHTML=i}},t.walk=function(t,e,n){var i=t;t:for(;i;)if(e(i),i.firstChild)i=i.firstChild;else for(;i;)if(n(i),i===t)i=null;else{if(i.nextSibling){i=i.nextSibling;continue t}i=i.parentNode}},t.prototype.enter=function(t){var e=this.getStackTopNode();if(!e||!l(e.getNode())&&!a(e.getNode()))if(a(t))this.stack.push(new o(t));else{if(c(t))return null!==this.currentOutlineTarget&&(this.currentSection&&!this.currentSection.getHeading()&&(this.currentSection.setHeading(document.createTextNode("No title.")),this.currentSection.setImplied(!0)),this.stack.push(this.currentOutlineTarget)),this.currentOutlineTarget=new o(t),this.currentSection=new i(t),void this.currentOutlineTarget.setOutline(new o(this.currentOutlineTarget.getNode(),this.currentSection));if(u(t)||t===this.rootNode)return null!==this.currentOutlineTarget&&this.stack.push(this.currentOutlineTarget),this.currentOutlineTarget=new o(t),this.currentOutlineTarget.setParentSection(this.currentSection),this.currentSection=new i(t),void this.currentOutlineTarget.setOutline(new o(this.currentOutlineTarget.getNode(),this.currentSection));if(l(t)){if(this.currentSection&&!this.currentSection.getHeading())this.currentSection.setHeading(t);else if(this.currentOutlineTarget&&(this.currentOutlineTarget.getOutline().getLastSection().getImplied()||h(t)<=h(this.currentOutlineTarget.getOutline().getLastSection().getHeading()))){var n=new i(t);this.currentOutlineTarget.getOutline().addSection(n),this.currentSection=n,this.currentSection.setHeading(t)}else{var r=!1,s=this.currentSection,p=0;do{if(h(t)>h(s.getHeading())){n=new i(t);s.addSection(n),this.currentSection=n,this.currentSection.setHeading(t),r=!0}s=s.getParent(),p++}while(!r&&p<99)}this.stack.push(new o(t))}else;}},t.prototype.exit=function(t){var e=this,n=this.getStackTopNode();if(n&&n.getNode()===t&&this.stack.pop(),!n||!l(n.getNode())&&!a(n.getNode())){if(c(t)&&this.stack.length>0){this.currentSection&&!this.currentSection.getHeading()&&(this.currentSection.setHeading(document.createTextNode("No title.")),this.currentSection.setImplied(!0));var i=this.currentOutlineTarget;return this.currentOutlineTarget=this.stack.pop(),this.currentSection=this.currentOutlineTarget.getOutline().getLastSection(),void i.getOutline().getSections().forEach((function(t){e.currentSection&&e.currentSection.addSection(t)}))}if((u(t)||t===this.rootNode)&&this.stack.length>0)return this.currentSection&&!this.currentSection.getHeading()&&(this.currentSection.setHeading(document.createTextNode("No title.")),this.currentSection.setImplied(!0)),this.currentOutlineTarget&&(this.currentSection=this.currentOutlineTarget.getParentSection()),void(this.currentOutlineTarget=this.stack.pop());(u(t)||t===this.rootNode||c(t))&&this.currentSection&&!this.currentSection.getHeading()&&(this.currentSection.setHeading(document.createTextNode("No title.")),this.currentSection.setImplied(!0))}},t.prototype.getStackTopNode=function(){return this.stack[this.stack.length-1]},t}();e.default=d},function(t,e,n){"use strict";n.r(e);var i=n(0);jQuery.fn.documentOutliner=function(t,e){return new i.default(this.get(0)).makeList(t,e),this}}]).default})); -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import {SectionType, OutlineType, NodeType, OptionsType} from "./type"; 2 | import Section from "./section"; 3 | import Outline from "./outline"; 4 | import MakeList from "./make-list"; 5 | import { 6 | isSectioningRoot, 7 | isSectioningContent, 8 | isHeadingContent, 9 | isHidden, 10 | getHeadingLevel 11 | } from "./util"; 12 | 13 | /** 14 | * @see https://html.spec.whatwg.org/multipage/sections.html#outline [4.3.11.1 Creating an outline] 15 | */ 16 | export default class DocumentOutliner { 17 | 18 | private currentOutlineTarget: OutlineType | null = null; 19 | private currentSection: SectionType | null = null; 20 | private readonly stack: Array = []; 21 | private readonly rootNode: NodeType; 22 | 23 | constructor(root: string | NodeType) { 24 | if (typeof root === 'string') { 25 | this.rootNode = document.querySelector(root); 26 | } else { 27 | this.rootNode = root; 28 | } 29 | DocumentOutliner.walk(this.rootNode, this.enter.bind(this), this.exit.bind(this)); 30 | } 31 | 32 | public getOutlineObject(): OutlineType | boolean { 33 | if (!this.currentOutlineTarget) { 34 | return false; 35 | } 36 | return this.currentOutlineTarget.getOutline(); 37 | } 38 | 39 | public makeList(target: string | NodeListOf, options: OptionsType): void { 40 | const outline = this.getOutlineObject(); 41 | if (!outline) { 42 | throw new Error('No sectioning contents.') 43 | } 44 | if (!target) { 45 | throw new TypeError('Invalid options: target empty.'); 46 | } 47 | const makeList = new MakeList(outline as OutlineType, options); 48 | const html = makeList.getHtml(); 49 | 50 | if (typeof target === 'string') { 51 | [].forEach.call(document.querySelectorAll(target), (dom: HTMLElement) => { 52 | dom.innerHTML = html; 53 | }); 54 | } else if (target instanceof NodeList) { 55 | [].forEach.call(target, (dom: HTMLElement) => { 56 | dom.innerHTML = html; 57 | }); 58 | } else { 59 | const tmp = target as HTMLElement; 60 | tmp.innerHTML = html; 61 | } 62 | } 63 | 64 | protected static walk(root: NodeType, enter: (node: NodeType) => void, exit: (node: NodeType) => void) { 65 | let node = root; 66 | start: while (node) { 67 | enter(node); 68 | if (node.firstChild) { 69 | node = node.firstChild; 70 | continue; 71 | } 72 | while (node) { 73 | exit(node); 74 | if (node === root) { 75 | node = null; 76 | } else if (node.nextSibling) { 77 | node = node.nextSibling; 78 | continue start; 79 | } else { 80 | node = node.parentNode; 81 | } 82 | } 83 | } 84 | } 85 | 86 | protected enter(node: NodeType): void { 87 | const stackTop = this.getStackTopNode(); 88 | 89 | // If the top of the stack is a heading content element or an element with a hidden attribute 90 | // Do nothing. 91 | if (stackTop && (isHeadingContent(stackTop.getNode()) || isHidden(stackTop.getNode()))) { 92 | return; 93 | } 94 | // When entering an element with a hidden attribute 95 | // Push the element being entered onto the stack. 96 | // (This causes the algorithm to skip that element and any descendants of the element.) 97 | if (isHidden(node)) { 98 | this.stack.push(new Outline(node)); 99 | return; 100 | } 101 | // When entering a sectioning content element 102 | if (isSectioningContent(node)) { 103 | // If current outline target is not null, then: 104 | if (this.currentOutlineTarget !== null) { 105 | // 1. If the current section has no heading, 106 | if (this.currentSection && !this.currentSection.getHeading()) { 107 | // create an implied heading and let that be the heading for the current section. 108 | this.currentSection.setHeading(document.createTextNode('No title.')); 109 | this.currentSection.setImplied(true); 110 | } 111 | // Push current outline target onto the stack. 112 | this.stack.push(this.currentOutlineTarget); 113 | } 114 | // 2. Let current outline target be the element that is being entered. 115 | this.currentOutlineTarget = new Outline(node); 116 | // 3. Let current section be a newly created section for the current outline target element. 117 | // 4. Associate current outline target with current section. 118 | this.currentSection = new Section(node); 119 | // 5. Let there be a new outline for the new current outline target, 120 | // initialized with just the new current section as the only section in the outline. 121 | this.currentOutlineTarget.setOutline(new Outline(this.currentOutlineTarget.getNode(), this.currentSection)); 122 | return; 123 | } 124 | // When entering a sectioning root element 125 | if (isSectioningRoot(node) || node === this.rootNode) { 126 | // 1. If current outline target is not null, push current outline target onto the stack. 127 | if (this.currentOutlineTarget !== null) { 128 | this.stack.push(this.currentOutlineTarget); 129 | } 130 | // 2. Let current outline target be the element that is being entered. 131 | this.currentOutlineTarget = new Outline(node); 132 | // 3. Let current outline target's parent section be current section. 133 | this.currentOutlineTarget.setParentSection(this.currentSection as SectionType); 134 | // 4. Let current section be a newly created section for the current outline target element. 135 | this.currentSection = new Section(node); 136 | // 5. Let there be a new outline for the new current outline target, initialized with just the new current section as the only section in the outline. 137 | this.currentOutlineTarget.setOutline(new Outline(this.currentOutlineTarget.getNode(), this.currentSection)); 138 | return; 139 | } 140 | // When entering a heading content element 141 | if (isHeadingContent(node)) { 142 | if (this.currentSection && !this.currentSection.getHeading()) { 143 | // If the current section has no heading, let the element being entered be the heading for the current section. 144 | this.currentSection.setHeading(node); 145 | } else if (this.currentOutlineTarget && (0 146 | || this.currentOutlineTarget.getOutline().getLastSection().getImplied() 147 | || getHeadingLevel(node) <= getHeadingLevel(this.currentOutlineTarget.getOutline().getLastSection().getHeading()) 148 | )) { 149 | // Otherwise, if the element being entered has a rank equal to or higher than the heading of the last section of the outline of the current outline target, 150 | // or if the heading of the last section of the outline of the current outline target is an implied heading, 151 | // then create a new section and 152 | const newSection = new Section(node); 153 | // append it to the outline of the current outline target element, so that this new section is the new last section of that outline. 154 | this.currentOutlineTarget.getOutline().addSection(newSection); 155 | // Let current section be that new section. 156 | this.currentSection = newSection; 157 | // Let the element being entered be the new heading for the current section. 158 | this.currentSection.setHeading(node); 159 | } else { 160 | // Otherwise, run these substeps: 161 | let abort = false; 162 | 163 | // 1. Let candidate section be current section. 164 | let candidateSection = this.currentSection as SectionType; 165 | 166 | // 2. Heading loop: 167 | let loop = 0; 168 | do { 169 | // If the element being entered has a rank lower than the rank of the heading of the candidate section, 170 | if (getHeadingLevel(node) > getHeadingLevel(candidateSection.getHeading())) { 171 | // then create a new section, 172 | const newSection = new Section(node); 173 | // and append it to candidate section. (This does not change which section is the last section in the outline.) 174 | candidateSection.addSection(newSection); 175 | // Let current section be this new section. 176 | this.currentSection = newSection; 177 | // Let the element being entered be the new heading for the current section. 178 | this.currentSection.setHeading(node); 179 | // Abort these substeps. 180 | abort = true; 181 | } 182 | // 3. Let new candidate section be the section that contains candidate section in the outline of current outline target. 183 | // 4. Let candidate section be new candidate section. 184 | candidateSection = candidateSection.getParent() as SectionType; 185 | // 5. Return to the step labeled heading loop. 186 | loop++; 187 | } while (!abort && loop < 99) 188 | } 189 | // Push the element being entered onto the stack. (This causes the algorithm to skip any descendants of the element.) 190 | this.stack.push(new Outline(node)); 191 | return; 192 | } 193 | } 194 | 195 | protected exit(node: NodeType) { 196 | // When exiting an element, if that element is the element at the top of the stack 197 | // Note: The element being exited is a heading content element or an element with a hidden attribute. 198 | // Pop that element from the stack. 199 | const stackTop = this.getStackTopNode(); 200 | if (stackTop && stackTop.getNode() === node) { 201 | this.stack.pop(); 202 | } 203 | // If the top of the stack is a heading content element or an element with a hidden attribute 204 | // Do nothing. 205 | if (stackTop && (isHeadingContent(stackTop.getNode()) || isHidden(stackTop.getNode()))) { 206 | return; 207 | } 208 | // When exiting a sectioning content element, if the stack is not empty 209 | if (isSectioningContent(node) && this.stack.length > 0) { 210 | // 1. If the current section has no heading, create an implied heading and let that be the heading for the current section. 211 | if (this.currentSection && !this.currentSection.getHeading()) { 212 | this.currentSection.setHeading(document.createTextNode('No title.')); 213 | this.currentSection.setImplied(true); 214 | } 215 | const tmpOutline = this.currentOutlineTarget as OutlineType; 216 | // 2. Pop the top element from the stack, and let the current outline target be that element. 217 | this.currentOutlineTarget = this.stack.pop() as OutlineType; 218 | // 3. Let current section be the last section in the outline of the current outline target element. 219 | this.currentSection = this.currentOutlineTarget.getOutline().getLastSection(); 220 | // 4. Append the outline of the sectioning content element being exited to the current section. (This does not change which section is the last section in the outline.) 221 | tmpOutline.getOutline().getSections().forEach((section) => { 222 | if (this.currentSection) { 223 | this.currentSection.addSection(section); 224 | } 225 | }); 226 | return; 227 | } 228 | // When exiting a sectioning root element, if the stack is not empty 229 | if ((isSectioningRoot(node) || node === this.rootNode) && this.stack.length > 0) { 230 | // 1. If the current section has no heading, create an implied heading and let that be the heading for the current section. 231 | if (this.currentSection && !this.currentSection.getHeading()) { 232 | this.currentSection.setHeading(document.createTextNode('No title.')); 233 | this.currentSection.setImplied(true); 234 | } 235 | // 2. Let current section be current outline target's parent section. 236 | if (this.currentOutlineTarget) { 237 | this.currentSection = this.currentOutlineTarget.getParentSection(); 238 | } 239 | // 3. Pop the top element from the stack, and let the current outline target be that element. 240 | this.currentOutlineTarget = this.stack.pop() as OutlineType; 241 | return; 242 | } 243 | // When exiting a sectioning content element or a sectioning root element (when the stack is empty) 244 | // The current outline target is the element being exited, 245 | // and it is the sectioning content element or a sectioning root element 246 | // at the root of the subtree for which an outline is being generated. 247 | if (isSectioningRoot(node) || node === this.rootNode || isSectioningContent(node)) { 248 | // If the current section has no heading, create an implied heading and let that be the heading for the current section. 249 | if (this.currentSection && !this.currentSection.getHeading()) { 250 | this.currentSection.setHeading(document.createTextNode('No title.')); 251 | this.currentSection.setImplied(true); 252 | } 253 | // Skip to the next step in the overall set of steps. (The walk is over.) 254 | return; 255 | } 256 | } 257 | 258 | protected getStackTopNode(): OutlineType { 259 | return this.stack[this.stack.length - 1]; 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | import Section from "./section"; 2 | import Outline from "./outline"; 3 | import MakeList from "./make-list"; 4 | import { isSectioningRoot, isSectioningContent, isHeadingContent, isHidden, getHeadingLevel } from "./util"; 5 | /** 6 | * @see https://html.spec.whatwg.org/multipage/sections.html#outline [4.3.11.1 Creating an outline] 7 | */ 8 | var DocumentOutliner = /** @class */ (function () { 9 | function DocumentOutliner(root) { 10 | this.currentOutlineTarget = null; 11 | this.currentSection = null; 12 | this.stack = []; 13 | if (typeof root === 'string') { 14 | this.rootNode = document.querySelector(root); 15 | } 16 | else { 17 | this.rootNode = root; 18 | } 19 | DocumentOutliner.walk(this.rootNode, this.enter.bind(this), this.exit.bind(this)); 20 | } 21 | DocumentOutliner.prototype.getOutlineObject = function () { 22 | if (!this.currentOutlineTarget) { 23 | return false; 24 | } 25 | return this.currentOutlineTarget.getOutline(); 26 | }; 27 | DocumentOutliner.prototype.makeList = function (target, options) { 28 | var outline = this.getOutlineObject(); 29 | if (!outline) { 30 | throw new Error('No sectioning contents.'); 31 | } 32 | if (!target) { 33 | throw new TypeError('Invalid options: target empty.'); 34 | } 35 | var makeList = new MakeList(outline, options); 36 | var html = makeList.getHtml(); 37 | if (typeof target === 'string') { 38 | [].forEach.call(document.querySelectorAll(target), function (dom) { 39 | dom.innerHTML = html; 40 | }); 41 | } 42 | else if (target instanceof NodeList) { 43 | [].forEach.call(target, function (dom) { 44 | dom.innerHTML = html; 45 | }); 46 | } 47 | else { 48 | var tmp = target; 49 | tmp.innerHTML = html; 50 | } 51 | }; 52 | DocumentOutliner.walk = function (root, enter, exit) { 53 | var node = root; 54 | start: while (node) { 55 | enter(node); 56 | if (node.firstChild) { 57 | node = node.firstChild; 58 | continue; 59 | } 60 | while (node) { 61 | exit(node); 62 | if (node === root) { 63 | node = null; 64 | } 65 | else if (node.nextSibling) { 66 | node = node.nextSibling; 67 | continue start; 68 | } 69 | else { 70 | node = node.parentNode; 71 | } 72 | } 73 | } 74 | }; 75 | DocumentOutliner.prototype.enter = function (node) { 76 | var stackTop = this.getStackTopNode(); 77 | // If the top of the stack is a heading content element or an element with a hidden attribute 78 | // Do nothing. 79 | if (stackTop && (isHeadingContent(stackTop.getNode()) || isHidden(stackTop.getNode()))) { 80 | return; 81 | } 82 | // When entering an element with a hidden attribute 83 | // Push the element being entered onto the stack. 84 | // (This causes the algorithm to skip that element and any descendants of the element.) 85 | if (isHidden(node)) { 86 | this.stack.push(new Outline(node)); 87 | return; 88 | } 89 | // When entering a sectioning content element 90 | if (isSectioningContent(node)) { 91 | // If current outline target is not null, then: 92 | if (this.currentOutlineTarget !== null) { 93 | // 1. If the current section has no heading, 94 | if (this.currentSection && !this.currentSection.getHeading()) { 95 | // create an implied heading and let that be the heading for the current section. 96 | this.currentSection.setHeading(document.createTextNode('No title.')); 97 | this.currentSection.setImplied(true); 98 | } 99 | // Push current outline target onto the stack. 100 | this.stack.push(this.currentOutlineTarget); 101 | } 102 | // 2. Let current outline target be the element that is being entered. 103 | this.currentOutlineTarget = new Outline(node); 104 | // 3. Let current section be a newly created section for the current outline target element. 105 | // 4. Associate current outline target with current section. 106 | this.currentSection = new Section(node); 107 | // 5. Let there be a new outline for the new current outline target, 108 | // initialized with just the new current section as the only section in the outline. 109 | this.currentOutlineTarget.setOutline(new Outline(this.currentOutlineTarget.getNode(), this.currentSection)); 110 | return; 111 | } 112 | // When entering a sectioning root element 113 | if (isSectioningRoot(node) || node === this.rootNode) { 114 | // 1. If current outline target is not null, push current outline target onto the stack. 115 | if (this.currentOutlineTarget !== null) { 116 | this.stack.push(this.currentOutlineTarget); 117 | } 118 | // 2. Let current outline target be the element that is being entered. 119 | this.currentOutlineTarget = new Outline(node); 120 | // 3. Let current outline target's parent section be current section. 121 | this.currentOutlineTarget.setParentSection(this.currentSection); 122 | // 4. Let current section be a newly created section for the current outline target element. 123 | this.currentSection = new Section(node); 124 | // 5. Let there be a new outline for the new current outline target, initialized with just the new current section as the only section in the outline. 125 | this.currentOutlineTarget.setOutline(new Outline(this.currentOutlineTarget.getNode(), this.currentSection)); 126 | return; 127 | } 128 | // When entering a heading content element 129 | if (isHeadingContent(node)) { 130 | if (this.currentSection && !this.currentSection.getHeading()) { 131 | // If the current section has no heading, let the element being entered be the heading for the current section. 132 | this.currentSection.setHeading(node); 133 | } 134 | else if (this.currentOutlineTarget && (0 135 | || this.currentOutlineTarget.getOutline().getLastSection().getImplied() 136 | || getHeadingLevel(node) <= getHeadingLevel(this.currentOutlineTarget.getOutline().getLastSection().getHeading()))) { 137 | // Otherwise, if the element being entered has a rank equal to or higher than the heading of the last section of the outline of the current outline target, 138 | // or if the heading of the last section of the outline of the current outline target is an implied heading, 139 | // then create a new section and 140 | var newSection = new Section(node); 141 | // append it to the outline of the current outline target element, so that this new section is the new last section of that outline. 142 | this.currentOutlineTarget.getOutline().addSection(newSection); 143 | // Let current section be that new section. 144 | this.currentSection = newSection; 145 | // Let the element being entered be the new heading for the current section. 146 | this.currentSection.setHeading(node); 147 | } 148 | else { 149 | // Otherwise, run these substeps: 150 | var abort = false; 151 | // 1. Let candidate section be current section. 152 | var candidateSection = this.currentSection; 153 | // 2. Heading loop: 154 | var loop = 0; 155 | do { 156 | // If the element being entered has a rank lower than the rank of the heading of the candidate section, 157 | if (getHeadingLevel(node) > getHeadingLevel(candidateSection.getHeading())) { 158 | // then create a new section, 159 | var newSection = new Section(node); 160 | // and append it to candidate section. (This does not change which section is the last section in the outline.) 161 | candidateSection.addSection(newSection); 162 | // Let current section be this new section. 163 | this.currentSection = newSection; 164 | // Let the element being entered be the new heading for the current section. 165 | this.currentSection.setHeading(node); 166 | // Abort these substeps. 167 | abort = true; 168 | } 169 | // 3. Let new candidate section be the section that contains candidate section in the outline of current outline target. 170 | // 4. Let candidate section be new candidate section. 171 | candidateSection = candidateSection.getParent(); 172 | // 5. Return to the step labeled heading loop. 173 | loop++; 174 | } while (!abort && loop < 99); 175 | } 176 | // Push the element being entered onto the stack. (This causes the algorithm to skip any descendants of the element.) 177 | this.stack.push(new Outline(node)); 178 | return; 179 | } 180 | }; 181 | DocumentOutliner.prototype.exit = function (node) { 182 | var _this = this; 183 | // When exiting an element, if that element is the element at the top of the stack 184 | // Note: The element being exited is a heading content element or an element with a hidden attribute. 185 | // Pop that element from the stack. 186 | var stackTop = this.getStackTopNode(); 187 | if (stackTop && stackTop.getNode() === node) { 188 | this.stack.pop(); 189 | } 190 | // If the top of the stack is a heading content element or an element with a hidden attribute 191 | // Do nothing. 192 | if (stackTop && (isHeadingContent(stackTop.getNode()) || isHidden(stackTop.getNode()))) { 193 | return; 194 | } 195 | // When exiting a sectioning content element, if the stack is not empty 196 | if (isSectioningContent(node) && this.stack.length > 0) { 197 | // 1. If the current section has no heading, create an implied heading and let that be the heading for the current section. 198 | if (this.currentSection && !this.currentSection.getHeading()) { 199 | this.currentSection.setHeading(document.createTextNode('No title.')); 200 | this.currentSection.setImplied(true); 201 | } 202 | var tmpOutline = this.currentOutlineTarget; 203 | // 2. Pop the top element from the stack, and let the current outline target be that element. 204 | this.currentOutlineTarget = this.stack.pop(); 205 | // 3. Let current section be the last section in the outline of the current outline target element. 206 | this.currentSection = this.currentOutlineTarget.getOutline().getLastSection(); 207 | // 4. Append the outline of the sectioning content element being exited to the current section. (This does not change which section is the last section in the outline.) 208 | tmpOutline.getOutline().getSections().forEach(function (section) { 209 | if (_this.currentSection) { 210 | _this.currentSection.addSection(section); 211 | } 212 | }); 213 | return; 214 | } 215 | // When exiting a sectioning root element, if the stack is not empty 216 | if ((isSectioningRoot(node) || node === this.rootNode) && this.stack.length > 0) { 217 | // 1. If the current section has no heading, create an implied heading and let that be the heading for the current section. 218 | if (this.currentSection && !this.currentSection.getHeading()) { 219 | this.currentSection.setHeading(document.createTextNode('No title.')); 220 | this.currentSection.setImplied(true); 221 | } 222 | // 2. Let current section be current outline target's parent section. 223 | if (this.currentOutlineTarget) { 224 | this.currentSection = this.currentOutlineTarget.getParentSection(); 225 | } 226 | // 3. Pop the top element from the stack, and let the current outline target be that element. 227 | this.currentOutlineTarget = this.stack.pop(); 228 | return; 229 | } 230 | // When exiting a sectioning content element or a sectioning root element (when the stack is empty) 231 | // The current outline target is the element being exited, 232 | // and it is the sectioning content element or a sectioning root element 233 | // at the root of the subtree for which an outline is being generated. 234 | if (isSectioningRoot(node) || node === this.rootNode || isSectioningContent(node)) { 235 | // If the current section has no heading, create an implied heading and let that be the heading for the current section. 236 | if (this.currentSection && !this.currentSection.getHeading()) { 237 | this.currentSection.setHeading(document.createTextNode('No title.')); 238 | this.currentSection.setImplied(true); 239 | } 240 | // Skip to the next step in the overall set of steps. (The walk is over.) 241 | return; 242 | } 243 | }; 244 | DocumentOutliner.prototype.getStackTopNode = function () { 245 | return this.stack[this.stack.length - 1]; 246 | }; 247 | return DocumentOutliner; 248 | }()); 249 | export default DocumentOutliner; 250 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | DocumentOutliner 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 185 | 186 | 187 | 188 |
    189 |
    190 |
    191 |

    DocumentOutliner

    192 | 198 |
    199 |
    200 |
    201 |
    202 |
    203 |
    204 |

    DocumentOutliner

    205 |

    A utility to make ol/ul list by using html5 outline algorithm.

    206 |

    207 | Download 208 | GitHub 209 |

    210 |
    211 |
    212 | 213 |
    214 |
    215 |
    216 |
    217 |
    218 |
    219 |
    220 | 221 |
    222 |
    223 |

    Demo

    224 |
    225 |
    226 | 227 |
    228 |
    229 |
    230 | 263 |
    264 |
    265 |
    266 |
    267 |
    268 |
    269 |

    Quick start

    270 |

    Using CDN

    271 |

    You can easily start by using CDN.

    272 |

    Add the following description in the 273 | <head> tag of the web page you want to use DocumentOutliner.

    274 |
    <script src="https://unpkg.com/document-outliner@latest/bundle/document-outliner.js"></script>
    275 |

    You can also bundle the library with Browserify/Webpack

    276 |
    npm install document-outliner --save
    277 |
    import DocumentOutliner from 'document-outliner'
    278 |

    Wrap the container you want to make outline with

    279 |
    <div class="outline"></div>
    280 | <div class="container">
    281 |   <h1>The Tax Book</h1>
    282 |   <section>
    283 |     <h1>Earning money</h1>
    284 |     <p>Earning money is good.</p>
    285 |     <section>
    286 |       <h1>Getting a job</h1>
    287 |       <p>To earn money you typically need a job.</p>
    288 |     </section>
    289 |   </section>
    290 | </div>
    291 |

    Write the following script

    292 |
    new DocumentOutliner('.container');
    293 | outliner.makeList('.outline', {
    294 |   link: true,
    295 |   listType: 'ul',
    296 |   listClassName: 'list-group',
    297 |   itemClassName: 'list-group-item',
    298 |   anchorName: 'heading-$1'
    299 | });
    300 |

    If you want to use it with jQuery

    301 |

    Load the following script

    302 |
    <script src="https://unpkg.com/document-outliner@latest/bundle/document-outliner.js"></script>
    303 |

    Execute!

    304 |
    $(function () {
    305 |   $('.container').documentOutliner('.output', {
    306 |     link: true,
    307 |     listType: 'ul',
    308 |     listClassName: 'list-group',
    309 |     itemClassName: 'list-group-item',
    310 |     anchorName: 'heading-$1'
    311 |   });
    312 | });
    313 |
    314 |
    315 | 316 |
    317 |
    318 |

    Options

    319 |

    Some options are available when making new instances like below.

    320 |
    const outliner = new DocumentOutliner('.container');
    321 | outliner.makeList('.outline', {
    322 |   link: true,
    323 |   listType: 'ul',
    324 |   listClassName: 'list-group',
    325 |   itemClassName: 'list-group-item',
    326 |   anchorName: 'heading-$1'
    327 | });
    328 |

    For your reference, below is the list of the options available.

    329 |
    330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 |
    NameDefaultDescription
    linktruecreate HTML anchor link
    linkType'ol'HTML list type
    listClassName''ol/ul class name
    itemClassName''li class name
    linkClassName''link class name
    anchorName'heading-$1'anchor link name ($1 is to be link number)
    levelLimit99limit on number of level
    372 |
    373 |
    374 |
    375 | 376 |
    377 |
    378 |
    379 |
    380 |

    Join development on

    381 |
    382 |
    383 | GitHub 384 |
    385 |
    386 | License 387 |
    388 |
    389 |
    390 |
    391 |
    392 |
    393 | 394 |
    395 |
    396 | 422 | 426 | © 2018 DocumentOutliner 427 |
    428 |
    429 | 430 | 431 | 432 | 433 | 436 | 456 | 457 | 458 | 459 | --------------------------------------------------------------------------------