├── .github
├── dependabot.yml
└── workflows
│ └── main.yml
├── .gitignore
├── .haxerc
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── CHANGELOG.md
├── LICENSE.md
├── Make.hx
├── README.md
├── haxe_libraries
├── dox.hxml
├── hscript.hxml
├── hxargs.hxml
├── hxcpp.hxml
├── hxcs.hxml
├── hxjava.hxml
├── hxnodejs.hxml
├── hxparse.hxml
├── hxtemplo.hxml
├── markdown.hxml
└── utest.hxml
├── haxelib.json
├── package-lock.json
├── package.json
├── resources
├── meta.json
└── screenshot.png
├── run.hxml
├── run.n
├── runBase.hxml
├── src
└── dox
│ ├── Api.hx
│ ├── Config.hx
│ ├── Dox.hx
│ ├── Generator.hx
│ ├── Infos.hx
│ ├── JavadocHandler.hx
│ ├── MarkdownHandler.hx
│ ├── Processor.hx
│ ├── Theme.hx
│ ├── Writer.hx
│ └── import.hx
├── test
└── dox
│ ├── sample
│ ├── DoxTest.hx
│ └── emptyPackage
│ │ └── HiddenClass.hx
│ └── test
│ ├── TestRunner.hx
│ └── processor
│ ├── ProcessorTest.hx
│ └── TestAbstract.hx
├── tests.hxml
├── themes
└── default
│ ├── config.json
│ ├── resources
│ ├── bootstrap
│ │ ├── css
│ │ │ ├── bootstrap-responsive.css
│ │ │ ├── bootstrap-responsive.min.css
│ │ │ ├── bootstrap-select.min.css
│ │ │ ├── bootstrap.css
│ │ │ └── bootstrap.min.css
│ │ ├── img
│ │ │ ├── glyphicons-halflings-white.png
│ │ │ └── glyphicons-halflings.png
│ │ └── js
│ │ │ ├── bootstrap-select.min.js
│ │ │ ├── bootstrap.js
│ │ │ └── bootstrap.min.js
│ ├── dark-mode.css
│ ├── extra-styles.css
│ ├── favicon.ico
│ ├── haxe-nav.css
│ ├── highlighter.css
│ ├── highlighter.js
│ ├── index.js
│ ├── jquery-3.6.0.min.js
│ ├── styles.css
│ ├── triangle-closed.png
│ └── triangle-opened.png
│ └── templates
│ ├── 404.mtt
│ ├── abstract.mtt
│ ├── class.mtt
│ ├── class_field.mtt
│ ├── doc.mtt
│ ├── enum.mtt
│ ├── enum_field.mtt
│ ├── extra-headers.mtt
│ ├── footer.mtt
│ ├── header.mtt
│ ├── macros.mtt
│ ├── main.mtt
│ ├── nav.mtt
│ ├── package.mtt
│ ├── package_description.mtt
│ ├── related_types.mtt
│ ├── topbar.mtt
│ └── typedef.mtt
└── xml.hxml
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2 | version: 2
3 | updates:
4 | - package-ecosystem: github-actions
5 | directory: /
6 | schedule:
7 | interval: weekly
8 | day: monday
9 | time: "14:00"
10 | commit-message:
11 | prefix: fix
12 | prefix-development: chore
13 | include: scope
14 | labels:
15 | - dependencies
16 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions
2 | name: CI
3 |
4 | on:
5 | push:
6 | branches: # build all branches
7 | - '**'
8 | tags-ignore: # but don't build tags
9 | - '**'
10 | paths-ignore:
11 | - '**/*.md'
12 | - '.github/*.yml'
13 | pull_request:
14 | workflow_dispatch:
15 | # https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/
16 |
17 | defaults:
18 | run:
19 | shell: bash
20 |
21 | jobs:
22 | build:
23 | strategy:
24 | matrix:
25 | haxe-version:
26 | - nightly
27 | - haxerc # use the version specified in .haxerc
28 | os:
29 | - ubuntu-latest
30 | - windows-latest
31 | fail-fast: false
32 |
33 | runs-on: ${{ matrix.os }}
34 |
35 | steps:
36 | - uses: actions/checkout@v3
37 |
38 | - name: Setup [node]
39 | uses: actions/setup-node@v3 # https://github.com/marketplace/actions/setup-node-js-environment
40 | with:
41 | node-version: 18
42 |
43 | - run: npm install
44 |
45 | - name: Run unit tests
46 | run: npx haxe tests.hxml
47 |
48 | - name: Test generation of XML files
49 | run: npx haxe xml.hxml
50 |
51 | - name: Test cli [eval]
52 | run: npx haxe -D eval-stack runBase.hxml --run dox.Dox --help
53 |
54 | - name: Test cli [neko]
55 | run: |
56 | set -eux
57 | npx haxe runBase.hxml -neko run.n
58 | npx neko run.n --help
59 |
60 | - name: Setup [python]
61 | uses: actions/setup-python@v4 # https://github.com/actions/setup-python
62 | with:
63 | python-version: 3.11
64 |
65 | - name: Test cli [python]
66 | run: |
67 | set -eux
68 | npx haxe runBase.hxml -python bin/dox.py
69 | python bin/dox.py --help
70 |
71 | - name: Setup [php]
72 | uses: shivammathur/setup-php@v2 # https://github.com/marketplace/actions/setup-php-action
73 | env:
74 | runner: ${{ env.ACT && 'self-hosted' || 'github' }}
75 | with:
76 | php-version: 7.4
77 | extensions: mbstring, xml
78 |
79 | - name: Test cli [php]
80 | run: |
81 | set -eux
82 | npx haxe runBase.hxml -php bin/dox
83 | php bin/dox/index.php --help
84 |
85 | - name: Setup [java]
86 | uses: actions/setup-java@v3 # https://github.com/marketplace/actions/setup-java-jdk
87 | with:
88 | distribution: 'temurin'
89 | java-version: 11
90 |
91 | - name: Test cli [java]
92 | run: |
93 | set -eux
94 | npx haxe runBase.hxml -java bin/java
95 | java -jar bin/java/Dox.jar
96 |
97 | - name: Test cli [jvm]
98 | run: |
99 | set -eux
100 | npx haxe runBase.hxml -java bin/jvm
101 | java -jar bin/jvm/Dox.jar
102 |
103 | - name: Test cli [node]
104 | run: |
105 | set -eux
106 | npx haxe runBase.hxml -lib hxnodejs -js bin/dox.js
107 | node bin/dox.js
108 |
109 | - name: Install hxcpp development version
110 | if: matrix.haxe-version == 'nightly'
111 | run: |
112 | set -eux
113 | npx lix install haxe nightly
114 |
115 | #npx lix install https://github.com/HaxeFoundation/hxcpp
116 | #echo y | npx lix run hxcpp
117 | #--> does not work, so we do instead:
118 | npx haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp
119 | pushd $(npx haxelib config)/hxcpp/git/tools/hxcpp
120 | echo y | npx haxe compile.hxml
121 | popd
122 | npx lix dev hxcpp $(npx haxelib config)/hxcpp/git
123 |
124 | - name: Test cli [cpp]
125 | run: |
126 | set -eux
127 | npx haxe runBase.hxml -cpp bin/cpp -D HXCPP_SILENT
128 | bin/cpp/Dox
129 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | node_modules/
3 | run.js
4 |
5 | # Eclipse
6 | .settings/
7 | .project
8 | **/.*.md.html
9 |
10 | # Eclipse Theia
11 | .theia
12 |
13 | # IntelliJ
14 | .idea
15 | *.iml
16 | *.ipr
17 | *.iws
18 |
19 | # HaxeDevelop/FlashDevelop
20 | *.hxproj
21 |
22 | # Visual Studio Code
23 | .vscode
24 |
25 | # OSX
26 | .DS_Store
27 |
28 | # Vim
29 | *.swo
30 | *.swp
31 |
32 | # Temp files
33 | .history
34 | *.tmp
35 | *.bak
36 | /dump
37 | Thumbs.db
38 |
39 | # Local work folder that is not checked in
40 | _LOCAL/
--------------------------------------------------------------------------------
/.haxerc:
--------------------------------------------------------------------------------
1 | {
2 | "version": "4.2.5",
3 | "resolveLibs": "scoped"
4 | }
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "HookyQR.beautify",
4 | "influrium.templo"
5 | ]
6 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Interp",
6 | "type": "haxe-eval",
7 | "request": "launch",
8 | "args": ["runBase.hxml", "--interp"]
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "haxe.configurations": [
3 | ["runBase.hxml"]
4 | ],
5 | "[haxe]": {
6 | "editor.formatOnSave": true,
7 | "editor.formatOnPaste": true
8 | },
9 | "[css]": {
10 | "editor.formatOnSave": true,
11 | "editor.defaultFormatter": "HookyQR.beautify"
12 | },
13 | "[javascript]": {
14 | "editor.formatOnSave": true,
15 | "editor.defaultFormatter": "vscode.typescript-language-features"
16 | },
17 | "files.associations": {
18 | "*.mtt": "html"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "hxml",
6 | "file": "run.hxml",
7 | "group": {
8 | "kind": "build",
9 | "isDefault": true
10 | }
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 1.6.0 (August 15, 2021)
2 |
3 | - added a dark mode to the default theme ([#287](https://github.com/HaxeFoundation/dox/issues/287))
4 | - fixed packages that only contain hidden types showing up ([#276](https://github.com/HaxeFoundation/dox/issues/276))
5 |
6 | ### 1.5.0 (April 12, 2020)
7 |
8 | - added a "final" prefix for final classes and interfaces
9 | - added support for deprecation messages on types ([#261](https://github.com/HaxeFoundation/dox/issues/261))
10 | - added support for `@:noCompletion` implying `@:dox(hide)` ([#250](https://github.com/HaxeFoundation/dox/issues/250))
11 | - added support for typedefs that are conditionalized by platform ([#257](https://github.com/HaxeFoundation/dox/issues/257))
12 | - added a `--keep-field-order` argument ([#258](https://github.com/HaxeFoundation/dox/issues/258))
13 | - added tooltips with descriptions for compiler metadata ([#240](https://github.com/HaxeFoundation/dox/issues/240))
14 | - added the fully qualified path of types to tooltips
15 | - added support for platform-specific enum constructors ([haxe#3583](https://github.com/HaxeFoundation/haxe/issues/3583))
16 | - improved function type printing to use Haxe 4 syntax ([#273](https://github.com/HaxeFoundation/dox/issues/273))
17 | - improved filtering to show type names first so they're always visible
18 | - fixed JS version being used if the node version is not supported (< 8.10.0)
19 | - fixed interfaces with multiple `extends` only showing the first one ([#260](https://github.com/HaxeFoundation/dox/issues/260))
20 | - fixed overloads not being filtered by visibility / metadata ([#272](https://github.com/HaxeFoundation/dox/issues/272))
21 | - fixed anchor links of variables in abstracts ([#215](https://github.com/HaxeFoundation/dox/issues/215))
22 | - fixed `@author` tags showing up in package overview ([#228](https://github.com/HaxeFoundation/dox/issues/228))
23 | - fixed "View Source" being shown for types not defined in .hx files ([#224](https://github.com/HaxeFoundation/dox/issues/224))
24 |
25 | ### 1.4.0 (March 27, 2020)
26 |
27 | - added support for using dox as an API via `dox.Dox.run()` and `-lib dox`
28 | - added support for automatically using a much faster JS version when `node` is available
29 | - changed the order in which fields are shown (first static, then instance fields)
30 | - changed metadata rendering to only show specific ones (instead of hiding specific ones)
31 | - improved how enum abstracts as well as final and optional fields are rendered
32 | - improved various things in the default theme
33 | - fixed last argument of abstract methods being removed instead of implicit `this` ([#266](https://github.com/HaxeFoundation/dox/issues/266))
34 | - removed support for `--interp`
35 |
36 | ### 1.3.0 (March 11, 2020)
37 |
38 | - added keyboard shortcuts to the filter textbox (ctrl+p, up, down, enter, escape)
39 | - added `--include-private` to show private fields and types ([#267](https://github.com/HaxeFoundation/dox/pull/267))
40 | - fixed header having a white border
41 | - fixed footer not always being positioned at the bottom
42 | - fixed duplicated separators at the top of pages with only one platform
43 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 David Peek, Haxe Foundation
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 |
--------------------------------------------------------------------------------
/Make.hx:
--------------------------------------------------------------------------------
1 | function main() {
2 | var args = Sys.args();
3 | if (args.length == 0) {
4 | args = ["dox", "xml", "pages", "server"];
5 | }
6 | for (arg in args) {
7 | switch arg {
8 | case "dox":
9 | Sys.command("npx haxe run.hxml");
10 |
11 | case "xml":
12 | Sys.command("npx haxe xml.hxml");
13 |
14 | case "pages":
15 | Sys.command("npx lix run dox -o bin/pages -i bin/xml --include dox -D source-path https://github.com/HaxeFoundation/dox/tree/master/test");
16 |
17 | case "server":
18 | Sys.command("npx http-server bin/pages -c-1");
19 |
20 | case "package":
21 | Sys.command("7z a -i!run.n -i!run.js -i!resources -i!themes -i!src -i!haxelib.json -i!README.md -i!LICENSE.md -i!CHANGELOG.md bin/haxelib.zip");
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dox
2 | [](https://github.com/HaxeFoundation/dox/actions?query=workflow%3ACI)
3 | [](https://lib.haxe.org/p/dox)
4 | [](https://lib.haxe.org/p/dox)
5 | [](LICENSE.md)
6 |
7 |
8 | A Haxe documentation generator used by many popular projects such as:
9 |
10 | - [Haxe](https://api.haxe.org/)
11 | - [OpenFL](https://api.openfl.org/)
12 | - [HaxeFlixel](https://api.haxeflixel.com/)
13 | - [Heaps](https://heaps.io/api/)
14 | - [Kha](http://api.kha.tech/)
15 | - [Ceramic](https://ceramic-engine.com/api-docs/)
16 |
17 | 
18 |
19 |
20 | ## Installation
21 |
22 | Install the library via [haxelib](https://lib.haxe.org/p/dox):
23 | ```sh
24 | haxelib install dox
25 | ```
26 |
27 |
28 | ## Usage
29 |
30 | > **Note:** Dox requires Haxe 3.1 or higher due to some minor changes in
31 | abstract rtti xml generation. You'll also need an up-to-date haxelib
32 | (requires support for `classPath` in _haxelib.json_)
33 |
34 | 1. Compile the code to be included in the documentation using:
35 | ```sh
36 | haxe -xml docs/doc.xml -D doc-gen [LIBS]
37 | ```
38 | E.g.
39 | ```sh
40 | haxe -xml docs/doc.xml -D doc-gen --lib hxargs --classpath src -java bin my.aweseome.package
41 | ```
42 | 2. Generate the HTML pages using:
43 | ```sh
44 | haxelib run dox -i
45 | ```
46 | ...where `input_dir` points to the directory containing the generated .xml file(s) of the previous step, i.e.
47 | ```sh
48 | haxelib run dox -i docs
49 | ```
50 |
51 | **:clipboard: For more details, custom theme creation and options [check out the Dox wiki](https://github.com/HaxeFoundation/dox/wiki/)**
52 |
53 |
54 | ## Local development
55 |
56 | To test Dox locally, clone the git repo, run `npm install` in root directory. This installs the correct Haxe version using lix and all required dependencies.
57 |
58 | After that you can run:
59 | ```sh
60 | npx haxe --run Make dox xml pages server
61 | ```
62 | This compiles Dox, creates XML's, generates the pages and starts a local dev server at .
63 |
64 |
65 | ## Local development - testing with nektos/act
66 |
67 | The GitHub workflow can be run locally using Nekto's [act](https://github.com/nektos/act) command-line tool. To use it:
68 |
69 | 1. Install docker
70 | 1. Install [act](https://github.com/nektos/act)
71 | 1. Navigate into the root of your project (where the .github folder is located)
72 | 1. Run the command `act`
73 | 1. On subsequent re-runs you can use `act -r` to reuse previous container which avoids re-installation of components and thus greatly reduces build time.
74 |
--------------------------------------------------------------------------------
/haxe_libraries/dox.hxml:
--------------------------------------------------------------------------------
1 | # @run: haxelib run-dir dox .
--------------------------------------------------------------------------------
/haxe_libraries/hscript.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "haxelib:/hscript#2.5.0" into hscript/2.5.0/haxelib
2 | # @run: haxelib run-dir hscript "${HAXE_LIBCACHE}/hscript/2.5.0/haxelib"
3 | -cp ${HAXE_LIBCACHE}/hscript/2.5.0/haxelib/
4 | -D hscript=2.5.0
5 | --macro keep('IntIterator')
--------------------------------------------------------------------------------
/haxe_libraries/hxargs.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "gh://github.com/Simn/hxargs#1d8ec84f641833edd6f0cb2e4290b7524fd27219" into hxargs/3.0.2/github/1d8ec84f641833edd6f0cb2e4290b7524fd27219
2 | -cp ${HAXE_LIBCACHE}/hxargs/3.0.2/github/1d8ec84f641833edd6f0cb2e4290b7524fd27219/
3 | -D hxargs=dev
--------------------------------------------------------------------------------
/haxe_libraries/hxcpp.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "haxelib:/hxcpp#4.2.1" into hxcpp/4.2.1/haxelib
2 | # @run: haxelib run-dir hxcpp "${HAXE_LIBCACHE}/hxcpp/4.2.1/haxelib"
3 | -cp ${HAXE_LIBCACHE}/hxcpp/4.2.1/haxelib/
4 | -D hxcpp=4.2.1
--------------------------------------------------------------------------------
/haxe_libraries/hxcs.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "haxelib:/hxcs#4.2.0" into hxcs/4.2.0/haxelib
2 | # @run: haxelib run-dir hxcs "${HAXE_LIBCACHE}/hxcs/4.2.0/haxelib"
3 | -cp ${HAXE_LIBCACHE}/hxcs/4.2.0/haxelib/
4 | -D hxcs=4.2.0
--------------------------------------------------------------------------------
/haxe_libraries/hxjava.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "haxelib:/hxjava#4.0.0-alpha" into hxjava/4.0.0-alpha/haxelib
2 | # @run: haxelib run-dir hxjava ${HAXE_LIBCACHE}/hxjava/4.0.0-alpha/haxelib
3 | -cp ${HAXE_LIBCACHE}/hxjava/4.0.0-alpha/haxelib/
4 | -D hxjava=4.0.0-alpha
5 | -java-lib lib/hxjava-std.jar
6 |
--------------------------------------------------------------------------------
/haxe_libraries/hxnodejs.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "haxelib:/hxnodejs#12.1.0" into hxnodejs/12.1.0/haxelib
2 | -cp ${HAXE_LIBCACHE}/hxnodejs/12.1.0/haxelib/src
3 | -D hxnodejs=12.1.0
4 | --macro allowPackage('sys')
5 | # should behave like other target defines and not be defined in macro context
6 | --macro define('nodejs')
7 | --macro _internal.SuppressDeprecated.run()
8 |
--------------------------------------------------------------------------------
/haxe_libraries/hxparse.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "gh://github.com/simn/hxparse#876070ec62a4869de60081f87763e23457a3bda8" into hxparse/4.3.0/github/876070ec62a4869de60081f87763e23457a3bda8
2 | -cp ${HAXE_LIBCACHE}/hxparse/4.3.0/github/876070ec62a4869de60081f87763e23457a3bda8/src
3 | -D hxparse=4.3.0
4 |
--------------------------------------------------------------------------------
/haxe_libraries/hxtemplo.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "gh://github.com/Simn/hxtemplo#4b9ec0c07e9ec619c7c414c3a72af4be63d820cc" into hxtemplo/3.0.0/github/4b9ec0c07e9ec619c7c414c3a72af4be63d820cc
2 | -lib hxparse
3 | -cp ${HAXE_LIBCACHE}/hxtemplo/3.0.0/github/4b9ec0c07e9ec619c7c414c3a72af4be63d820cc/src
4 | -D hxtemplo=3.3.0-dev
--------------------------------------------------------------------------------
/haxe_libraries/markdown.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "haxelib:/markdown#1.1.2" into markdown/1.1.2/haxelib
2 | # @run: haxelib run-dir markdown "${HAXE_LIBCACHE}/markdown/1.1.2/haxelib"
3 | -cp ${HAXE_LIBCACHE}/markdown/1.1.2/haxelib/src
4 | -D markdown=1.1.2
--------------------------------------------------------------------------------
/haxe_libraries/utest.hxml:
--------------------------------------------------------------------------------
1 | # @install: lix --silent download "haxelib:/utest#1.13.2" into utest/1.13.2/haxelib
2 | -cp ${HAXE_LIBCACHE}/utest/1.13.2/haxelib/src
3 | -D utest=1.13.2
4 | --macro utest.utils.Macro.checkHaxe()
5 | --macro utest.utils.Macro.importEnvSettings()
6 |
--------------------------------------------------------------------------------
/haxelib.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dox",
3 | "url" : "https://github.com/HaxeFoundation/dox",
4 | "license": "MIT",
5 | "tags": ["documentation"],
6 | "description": "Haxe documentation generator.",
7 | "version": "1.6.0",
8 | "releasenote": "Added a dark mode to the default theme.",
9 | "contributors": ["HaxeFoundation", "dpeek", "Simn"],
10 | "dependencies": {
11 | "hxargs": "git:https://github.com/Simn/hxargs#1d8ec84f641833edd6f0cb2e4290b7524fd27219",
12 | "hxtemplo": "git:https://github.com/Simn/hxtemplo#4b9ec0c07e9ec619c7c414c3a72af4be63d820cc",
13 | "hxparse": "git:https://github.com/Simn/hxparse#876070ec62a4869de60081f87763e23457a3bda8",
14 | "markdown": ""
15 | },
16 | "classPath": "src"
17 | }
18 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "haxe-dox",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "hasInstallScript": true,
8 | "devDependencies": {
9 | "lix": "^15.12.0"
10 | }
11 | },
12 | "node_modules/lix": {
13 | "version": "15.12.0",
14 | "resolved": "https://registry.npmjs.org/lix/-/lix-15.12.0.tgz",
15 | "integrity": "sha512-FA36oCl+M+3Of8L4eErXw7tAHGOjqEC4IgEvH6oPDsiYd4yN6XpzZGcbLuIyu4PiztjOrr1TKJnpwi32qb2ddw==",
16 | "dev": true,
17 | "hasInstallScript": true,
18 | "bin": {
19 | "haxe": "bin/haxeshim.js",
20 | "haxelib": "bin/haxelibshim.js",
21 | "lix": "bin/lix.js",
22 | "neko": "bin/nekoshim.js"
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "lix": "^15.12.0"
4 | },
5 | "scripts": {
6 | "postinstall": "lix download"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/resources/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HaxeFoundation/dox/e92caadca75c540f77e44e78305e86fcf6347670/resources/screenshot.png
--------------------------------------------------------------------------------
/run.hxml:
--------------------------------------------------------------------------------
1 | runBase.hxml
2 |
3 | --each
4 |
5 | -neko run.n
6 |
7 | --next
8 |
9 | -lib hxnodejs
10 | -js run.js
11 |
--------------------------------------------------------------------------------
/run.n:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HaxeFoundation/dox/e92caadca75c540f77e44e78305e86fcf6347670/run.n
--------------------------------------------------------------------------------
/runBase.hxml:
--------------------------------------------------------------------------------
1 | -lib hxtemplo
2 | -lib hxparse
3 | -lib hxargs
4 | -lib markdown
5 | -cp src
6 | -main dox.Dox
7 | -dce no
8 | # --macro nullSafety("dox")
--------------------------------------------------------------------------------
/src/dox/Api.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import sys.io.File;
4 | import haxe.Json;
5 | import haxe.rtti.CType;
6 |
7 | /**
8 | The Api class is the general interface to the Dox system which can be
9 | accessed in templates from the global `api` instance.
10 | **/
11 | @:keep
12 | class Api {
13 | /**
14 | The Dox configuration, see `Config` for details.
15 | **/
16 | public var config:Config;
17 |
18 | /**
19 | This instance of `Infos` contains various information which is collected
20 | by the Dox processor.
21 | **/
22 | public var infos:Infos;
23 |
24 | /**
25 | The current page name. For types this is the type name, for packages it
26 | is `"package "` followed by the package name.
27 | **/
28 | public var currentPageName:Null;
29 |
30 | /**
31 | The current page relative url.
32 | **/
33 | public var currentPageUrl:Null;
34 |
35 | /**
36 | Expose Std for theme.
37 | **/
38 | public var std = Std;
39 |
40 | /**
41 | Expose Lambda for theme.
42 | **/
43 | public var lambda = Lambda;
44 |
45 | /**
46 | Information about compiler metadata by name.
47 | **/
48 | public var metas:Map;
49 |
50 | public function new(cfg:Config, infos:Infos) {
51 | this.config = cfg;
52 | this.infos = infos;
53 | var metaJson = File.getContent(haxe.io.Path.join([cfg.doxPath, "resources/meta.json"]));
54 | var metas:Array<{metadata:String}> = Json.parse(metaJson);
55 | this.metas = [for (meta in metas) meta.metadata => meta];
56 | }
57 |
58 | /**
59 | Checks if `name` is a known platform name.
60 |
61 | Platform names correspond to the filenames of the consumed .xml files.
62 | For instance, flash.xml defines target "flash".
63 | **/
64 | public function isPlatform(name:String):Bool {
65 | return config.platforms.has(name);
66 | }
67 |
68 | /**
69 | Returns the name of `tree`, which is the unqualified name of the
70 | package of type represented by `tree`.
71 | **/
72 | public function getTreeName(tree:TypeTree):String {
73 | return switch (tree) {
74 | case TPackage(name, _, _): name;
75 | case TClassdecl(t): getPathName(t.path);
76 | case TEnumdecl(t): getPathName(t.path);
77 | case TTypedecl(t): getPathName(t.path);
78 | case TAbstractdecl(t): getPathName(t.path);
79 | }
80 | }
81 |
82 | /**
83 | Returns the type of `tree`.
84 | **/
85 | public function getTreeType(tree:TypeTree):String {
86 | return switch (tree) {
87 | case TPackage(_, _, _): "package";
88 | case TClassdecl(_): "class";
89 | case TEnumdecl(_): "enum";
90 | case TTypedecl(_): "type";
91 | case TAbstractdecl(_): "abstract";
92 | }
93 | }
94 |
95 | /**
96 | Returns the full dot-path of `tree`.
97 | **/
98 | public function getTreePath(tree:TypeTree):String {
99 | return switch (tree) {
100 | case TPackage(_, path, _): path;
101 | case TClassdecl(t): t.path;
102 | case TEnumdecl(t): t.path;
103 | case TTypedecl(t): t.path;
104 | case TAbstractdecl(t): t.path;
105 | }
106 | }
107 |
108 | /**
109 | Returns the package of `tree`, which is the dot-path without the type
110 | name for types and the package itself for packages.
111 | **/
112 | public function getTreePack(tree:TypeTree):String {
113 | return switch (tree) {
114 | case TPackage(_, pack, _): pack;
115 | case TClassdecl(t): getPathPack(t.path);
116 | case TEnumdecl(t): getPathPack(t.path);
117 | case TTypedecl(t): getPathPack(t.path);
118 | case TAbstractdecl(t): getPathPack(t.path);
119 | }
120 | }
121 |
122 | /**
123 | Returns the URL of `tree`, following the conventions of Dox.
124 |
125 | For packages, the returned value is the slash-path of the package
126 | followed by `"/index.html"`.
127 |
128 | For types, `pathToUrl` is called with the type path.
129 | **/
130 | public function getTreeUrl(tree:TypeTree):String {
131 | return switch (tree) {
132 | case TPackage(_, full, _): packageToUrl(full);
133 | case TClassdecl(t): pathToUrl(t.path);
134 | case TEnumdecl(t): pathToUrl(t.path);
135 | case TTypedecl(t): pathToUrl(t.path);
136 | case TAbstractdecl(t): pathToUrl(t.path);
137 | }
138 | }
139 |
140 | /**
141 | Returns the short description of `tree`.
142 |
143 | @todo: Document this properly.
144 | **/
145 | public function getTreeShortDesc(tree:TypeTree):String {
146 | var infos:TypeInfos = switch (tree) {
147 | case TPackage(_, _, _): null;
148 | case TClassdecl(t): t;
149 | case TEnumdecl(t): t;
150 | case TTypedecl(t): t;
151 | case TAbstractdecl(t): t;
152 | }
153 | return getShortDesc(infos);
154 | }
155 |
156 | /**
157 | Returns the short description of `infos`.
158 |
159 | @todo: Document this properly.
160 | **/
161 | public function getShortDesc(infos:TypeInfos):String {
162 | return (infos == null || infos.doc == null) ? "" : infos.doc.substr(0, infos.doc.indexOf('
') + 4);
163 | }
164 |
165 | /**
166 | Returns the first sentence of the documentation belonging to `infos` as well as its dot path.
167 | **/
168 | public function getSentenceDesc(infos:TypeInfos):String {
169 | var path = infos.path;
170 | if (infos == null || infos.doc == null) {
171 | return path;
172 | }
173 | var stripped = ~/<.+?>/g.replace(infos.doc, "").replace("\n", " ");
174 | var sentence = ~/^(.*?[.?!]+)/;
175 | return sentence.match(stripped) ? path + " - " + sentence.matched(1) : path;
176 | }
177 |
178 | public function getMetaDesc(meta:String):String {
179 | if (!metas.exists(meta)) {
180 | return "";
181 | }
182 | var doc = metas[meta].doc;
183 | return if (doc == null) "" else doc;
184 | }
185 |
186 | /**
187 | Turns a dot-path into a slash-path and appends ".html".
188 | **/
189 | public function pathToUrl(path:Path):String {
190 | return config.rootPath + sanitizePath(path).split(".").join("/") + ".html";
191 | }
192 |
193 | /**
194 | Turns a package-path into a slash-path and appends "/index.html".
195 | **/
196 | public function packageToUrl(full:String):String {
197 | if (full == config.toplevelPackage) {
198 | return config.rootPath + "index.html";
199 | }
200 | return config.rootPath + full.split(".").join("/") + "/index.html";
201 | }
202 |
203 | /**
204 | Checks if `t` corresponds to a core type.
205 | **/
206 | public function isCoreType(t:TypeInfos):Bool {
207 | return t.meta.exists(m -> m.name == ":coreType");
208 | }
209 |
210 | /**
211 | Checks if `t` corresponds to an enum abstract.
212 | **/
213 | public function isEnumAbstract(t:TypeInfos):Bool {
214 | return t.meta.exists(m -> m.name == ":enum");
215 | }
216 |
217 | /**
218 | Checks if `path` corresponds to a known type.
219 | **/
220 | public function isKnownType(path:Path):Bool {
221 | return infos.typeMap.exists(path);
222 | }
223 |
224 | /**
225 | Resolves a type by its dot-path `path`.
226 | **/
227 | public function resolveType(path:Path):Null {
228 | return infos.typeMap.get(path);
229 | }
230 |
231 | /**
232 | Returns the dot-path of type `ctype`.
233 |
234 | If `ctype` does not have a real path, `null` is returned.
235 | **/
236 | public function getTypePath(ctype:CType):Null {
237 | return switch (ctype) {
238 | case CClass(path, _): path;
239 | case CEnum(path, _): path;
240 | case CTypedef(path, _): path;
241 | case CAbstract(path, _): path;
242 | case _: null;
243 | }
244 | }
245 |
246 | /**
247 | Returns the last part of dot-path `path`.
248 | **/
249 | public function getPathName(path:Path):String {
250 | var name = path.split(".").pop();
251 | return name == null ? "" : name;
252 | }
253 |
254 | /**
255 | Returns the package part of dot-path `path`.
256 |
257 | If `path` does not have a package, the empty string `""` is returned.
258 | **/
259 | public function getPathPack(path:Path):String {
260 | var parts = path.split(".");
261 | parts.pop();
262 | return parts.length == 0 ? "" : parts.join(".");
263 | }
264 |
265 | /**
266 | Traces `e` for debug purposes.
267 | **/
268 | public function debug(e:Dynamic):Void {
269 | trace(Std.string(e));
270 | }
271 |
272 | /**
273 | Traces `e` as pretty-printed Json for debug purposes.
274 | **/
275 | public function debugJson(e:Dynamic) {
276 | trace(Json.stringify(e, null, " "));
277 | }
278 |
279 | /**
280 | Checks if `field` is an abstract implementation field.
281 |
282 | Abstract implementation fields are abstract fields which are not static
283 | in the original definition.
284 | **/
285 | public function isAbstractImplementationField(field:ClassField):Bool {
286 | return field.meta.exists(m -> m.name == ":impl");
287 | }
288 |
289 | /**
290 | Returns the CSS class string corresponding to `platforms`. If
291 | `platforms` is empty, `null` is returned.
292 | **/
293 | public function getPlatformClassString(platforms:List):Null {
294 | if (platforms.isEmpty())
295 | return null;
296 | return "platform " + platforms.map(p -> "platform-" + p).join(" ");
297 | }
298 |
299 | /**
300 | Checks if `key` was defined from command line argument `-D key value`.
301 | **/
302 | public function isDefined(key:String):Bool {
303 | return config.defines.exists(key);
304 | }
305 |
306 | /**
307 | Returns the value of `key` as defined by command line argument
308 | `-D key value`. If no value is defined, `null` is returned.
309 | **/
310 | public function getValue(key:String):Null {
311 | return config.defines[key];
312 | }
313 |
314 | /**
315 | Returns the path to the source code of `type`. This method assumes that
316 | `source-path` was defined from command line (`-D source-path url`) and
317 | then appends the path of `type` to it.
318 | **/
319 | public function getSourceLink(type:TypeInfos):Null {
320 | var sourcePath = getValue("source-path");
321 | if (sourcePath == null) {
322 | return null;
323 | }
324 | var module = type.module != null ? type.module : type.path;
325 | return haxe.io.Path.join([sourcePath, module.replace(".", "/") + ".hx"]);
326 | }
327 |
328 | /**
329 | Whether the "View Source" button should be shown.
330 | **/
331 | public function hasSourceLink(type:TypeInfos):Bool {
332 | return isDefined("source-path") && type.file != null && type.file.endsWith(".hx");
333 | }
334 |
335 | /**
336 | Returns additional field information which is not available on the
337 | `ClassField` type. See `FieldInfo` for more information.
338 | **/
339 | public function getFieldInfo(cf:ClassField):FieldInfo {
340 | var modifiers = {
341 | isInline: false,
342 | isDynamic: false,
343 | isOptional: false,
344 | isFinal: false
345 | }
346 | var isMethod = false;
347 | var get = "default";
348 | var set = "default";
349 | switch (cf.set) {
350 | case RNo:
351 | set = "null";
352 | case RCall(_):
353 | set = "set";
354 | case RMethod:
355 | isMethod = true;
356 | case RDynamic:
357 | set = "dynamic";
358 | isMethod = true;
359 | modifiers.isDynamic = true;
360 | default:
361 | }
362 | switch (cf.get) {
363 | case RNo:
364 | get = "null";
365 | case RCall(_):
366 | get = "get";
367 | case RDynamic:
368 | get = "dynamic";
369 | case RInline:
370 | modifiers.isInline = true;
371 | default:
372 | }
373 | modifiers.isOptional = cf.meta.exists(m -> m.name == ":optional");
374 | modifiers.isFinal = cf.isFinal;
375 | function varOrProperty() {
376 | return if (get == "default" && set == "default") {
377 | Variable;
378 | } else {
379 | Property(get, set);
380 | }
381 | }
382 | var kind = if (isMethod || modifiers.isInline) {
383 | switch (cf.type) {
384 | case CFunction(args, ret):
385 | Method(args, ret);
386 | default:
387 | varOrProperty();
388 | }
389 | } else {
390 | varOrProperty();
391 | }
392 | return {
393 | kind: kind,
394 | modifiers: modifiers
395 | }
396 | }
397 |
398 | /**
399 | Checks whether `cf` is a method using `getFieldInfo()`.
400 | **/
401 | public function isMethod(cf:ClassField) {
402 | return getFieldInfo(cf).kind.match(Method(_, _));
403 | }
404 |
405 | /**
406 | Returns an array of all member fields of `c` respecting the inheritance chain.
407 | **/
408 | public function getAllFields(c:Classdef):Array {
409 | var allFields = [];
410 | var fieldMap = new Map();
411 | function loop(c:Classdef) {
412 | for (cf in c.fields) {
413 | if (!fieldMap.exists(cf.name) || cf.overloads != null) {
414 | allFields.push({field: cf, definedBy: c});
415 | fieldMap[cf.name] = true;
416 | }
417 | }
418 | if (c.superClass != null) {
419 | var cSuper:Classdef = cast infos.typeMap[c.superClass.path];
420 | if (cSuper != null) { // class is not part of documentation
421 | loop(cSuper);
422 | }
423 | }
424 | }
425 | loop(c);
426 | if (!config.keepFieldOrder) {
427 | allFields.sort((f1, f2) -> Reflect.compare(f1.field.name, f2.field.name));
428 | }
429 | return allFields;
430 | }
431 |
432 | /**
433 | Returns inherited fields/methods of `c`. Sorted by field-name ordered by type in a map.
434 | **/
435 | public function getInheritedFields(c:Classdef):InheritedFields {
436 | var oc = c;
437 |
438 | var inheritedFields:InheritedFields = {
439 | methods: new Map>(),
440 | fields: new Map>(),
441 | types: [],
442 | }
443 |
444 | var allFields = [];
445 | var fieldMap = new Map();
446 | function loop(c:Classdef) {
447 | for (cf in c.fields) {
448 | if (!fieldMap.exists(cf.name) || cf.overloads != null) {
449 | if (c != oc)
450 | allFields.push({field: cf, definedBy: c});
451 | fieldMap[cf.name] = true;
452 | }
453 | }
454 | if (c.superClass != null) {
455 | var cSuper:Classdef = cast infos.typeMap[c.superClass.path];
456 | if (cSuper != null) { // class is not part of documentation
457 | inheritedFields.types.push(cSuper);
458 | inheritedFields.methods.set(cSuper, []);
459 | inheritedFields.fields.set(cSuper, []);
460 | loop(cSuper);
461 | }
462 | }
463 | }
464 | loop(c);
465 |
466 | function addFieldTo(f, map) {
467 | var fields = map.exists(f.definedBy) ? map.get(f.definedBy) : [];
468 | fields.push(f.field);
469 | map.set(f.definedBy, fields);
470 | }
471 | for (f in allFields) {
472 | if (isMethod(f.field))
473 | addFieldTo(f, inheritedFields.methods);
474 | else
475 | addFieldTo(f, inheritedFields.fields);
476 | }
477 |
478 | if (!config.keepFieldOrder) {
479 | for (fields in inheritedFields.methods) {
480 | fields.sort((f1, f2) -> Reflect.compare(f1.name, f2.name));
481 | }
482 | for (fields in inheritedFields.fields) {
483 | fields.sort((f1, f2) -> Reflect.compare(f1.name, f2.name));
484 | }
485 | }
486 |
487 | return inheritedFields;
488 | }
489 |
490 | private function sanitizePath(path:String) {
491 | return ~/Index$/.replace(path, "$$Index");
492 | }
493 | }
494 |
495 | /**
496 | Additional information on class fields
497 | **/
498 | typedef FieldInfo = {
499 | /**
500 | The kind of the field. See `FieldKind`.
501 | **/
502 | var kind:FieldKind;
503 |
504 | /**
505 | The field modifiers. See `FieldModifiers`.
506 | **/
507 | var modifiers:FieldModifiers;
508 | }
509 |
510 | /**
511 | Describes the kind of a class field.
512 | **/
513 | enum FieldKind {
514 | /**
515 | Field is a variable. Properties with `default, default` access are
516 | also considered variables.
517 | **/
518 | Variable;
519 |
520 | /**
521 | Field is a property. The arguments `get` and `set` correspond to the
522 | accessor.
523 | **/
524 | Property(get:String, set:String);
525 |
526 | /**
527 | Field is a method with arguments `args` and return type `ret`.
528 | **/
529 | Method(args:Array, ret:CType);
530 | }
531 |
532 | /**
533 | The modifiers of a field.
534 | **/
535 | typedef FieldModifiers = {
536 | isInline:Bool,
537 | isDynamic:Bool,
538 | isOptional:Bool,
539 | isFinal:Bool
540 | }
541 |
542 | typedef MemberField = {
543 | field:ClassField,
544 | definedBy:Classdef
545 | }
546 |
547 | typedef InheritedFields = {
548 | methods:Map>,
549 | fields:Map>,
550 | // defines order of the types since keys in maps arent ordered
551 | types:Array,
552 | }
553 |
--------------------------------------------------------------------------------
/src/dox/Config.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import haxe.ds.GenericStack;
4 | import templo.Template;
5 | import sys.FileSystem;
6 |
7 | @:enum abstract Define(String) from String to String {
8 | var Version = "version";
9 | var SourcePath = "source-path";
10 | var ThemeColor = "themeColor";
11 | var TextColor = "textColor";
12 | var Website = "website";
13 | var Logo = "logo";
14 | var Description = "description";
15 | }
16 |
17 | @:keep
18 | class Config {
19 | public final doxPath:String;
20 | public var theme:Null;
21 | public var rootPath:String;
22 | public var toplevelPackage:String;
23 | public var useMarkdown:Bool;
24 | public var includePrivate:Bool;
25 | public var keepFieldOrder:Bool;
26 | public var date:Date;
27 | public var outputPath(default, set):String = "pages";
28 | public var inputPath(default, set):String = "xml";
29 | public var pathFilters(default, null):GenericStack;
30 | public var platforms:Array;
31 | public var resourcePaths:Array;
32 | public var templatePaths(default, null):GenericStack;
33 | public var defines:Map;
34 | public var pageTitle:Null;
35 |
36 | function set_outputPath(v) {
37 | return outputPath = Path.removeTrailingSlashes(v);
38 | }
39 |
40 | function set_inputPath(v) {
41 | return inputPath = Path.removeTrailingSlashes(v);
42 | }
43 |
44 | public function new(doxPath:String) {
45 | this.doxPath = doxPath;
46 | rootPath = "";
47 | platforms = [];
48 | resourcePaths = [];
49 | toplevelPackage = "";
50 | useMarkdown = true;
51 | includePrivate = false;
52 | keepFieldOrder = false;
53 | defines = new Map();
54 | pathFilters = new GenericStack();
55 | templatePaths = new GenericStack();
56 | date = Date.now();
57 | }
58 |
59 | public function addFilter(pattern:String, isIncludeFilter:Bool) {
60 | pathFilters.add(new Filter(pattern, isIncludeFilter));
61 | }
62 |
63 | public function addTemplatePath(path:String) {
64 | templatePaths.add(Path.removeTrailingSlashes(path));
65 | }
66 |
67 | public function loadTemplates(path:String) {
68 | addTemplatePath(path);
69 | if (!FileSystem.exists(path)) {
70 | return;
71 | }
72 | for (file in FileSystem.readDirectory(path)) {
73 | var path = new Path(file);
74 | if (path.ext == "mtt") {
75 | loadTemplate(file);
76 | }
77 | }
78 | }
79 |
80 | public function loadTemplate(name:String) {
81 | for (tp in templatePaths) {
82 | if (FileSystem.exists(tp + "/" + name))
83 | return Template.fromFile(tp + "/" + name);
84 | }
85 | throw "Could not resolve template: " + name;
86 | }
87 |
88 | public function loadTheme(path:String) {
89 | if (path.indexOf("/") == -1 && path.indexOf("\\") == -1) {
90 | path = Path.normalize(Path.join([doxPath, "themes", path]));
91 | }
92 | var configPath = Path.join([path, "config.json"]);
93 | var themeConfig = try sys.io.File.getContent(configPath) catch (e:Dynamic) {
94 | throw 'Could not load $configPath';
95 | }
96 | var theme:Theme = haxe.Json.parse(themeConfig);
97 | if (theme.parentTheme != null) {
98 | loadTheme(theme.parentTheme);
99 | }
100 | var resourcesPath = Path.join([path, "resources"]);
101 | if (FileSystem.exists(resourcesPath))
102 | resourcePaths.push(resourcesPath);
103 |
104 | loadTemplates(Path.join([path, "templates"]));
105 | this.theme = theme;
106 | }
107 |
108 | public function setRootPath(path:String) {
109 | var depth = path.split(".").length - 1;
110 | rootPath = "";
111 | for (_ in 0...depth) {
112 | rootPath += "../";
113 | }
114 | if (rootPath == "")
115 | rootPath = "./";
116 | }
117 |
118 | public function getHeaderIncludes() {
119 | var buf = new StringBuf();
120 | var headerIncludes = [];
121 | if (theme != null && theme.headerIncludes != null) {
122 | headerIncludes = theme.headerIncludes;
123 | }
124 | for (include in headerIncludes) {
125 | var path = new Path(include);
126 | var s = switch (path.ext) {
127 | case 'css': '';
128 | case 'js': '';
129 | case 'ico': '';
130 | case s: throw 'Unknown header include extension: $s';
131 | }
132 | buf.add(s);
133 | }
134 | return buf.toString();
135 | }
136 | }
137 |
138 | private class Filter {
139 | public var r(default, null):EReg;
140 | public var isIncludeFilter(default, null):Bool;
141 |
142 | public function new(pattern:String, isIncludeFilter:Bool) {
143 | r = new EReg(pattern, "");
144 | this.isIncludeFilter = isIncludeFilter;
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/dox/Dox.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import sys.FileSystem;
4 |
5 | class Dox {
6 | static function main() {
7 | var args = Sys.args();
8 |
9 | #if neko
10 | // use the faster JS version if possible
11 | try {
12 | var process = new sys.io.Process("node", ["-v"]);
13 | var isValidNode = false;
14 | if (process.exitCode() == 0) {
15 | var output = process.stdout.readAll().toString();
16 | output = output.substr(1); // remove leading "v"
17 | var parts = output.split(".").map(Std.parseInt);
18 | // min supported node version is 8.10.0 due to usage of regex dotall flag
19 | isValidNode = parts[0] > 8 || parts[0] == 8 && parts[1] >= 10;
20 | }
21 | process.close();
22 | if (isValidNode && FileSystem.exists("run.js")) {
23 | Sys.println("Using Node.js version of dox...");
24 | var exitCode = Sys.command("node", ["run.js"].concat(args));
25 | Sys.exit(exitCode);
26 | }
27 | } catch (e:Any) {}
28 | #end
29 |
30 | var owd = Sys.getCwd();
31 | owd = Path.addTrailingSlash(owd);
32 |
33 | // check if we're running from haxelib (last arg is original working dir)
34 | var last = args[args.length - 1];
35 | if (last != null && Sys.getEnv("HAXELIB_RUN") == "1") {
36 | var path = new Path(last).toString();
37 | if (FileSystem.exists(path) && FileSystem.isDirectory(path)) {
38 | args.pop();
39 | Sys.setCwd(path);
40 | }
41 | }
42 |
43 | var cfg = new Config(owd);
44 | var help = false;
45 |
46 | // @formatter:off
47 | var argHandler = hxargs.Args.generate([
48 | @doc("Set the output path for generated pages (directory or a .zip file)")
49 | ["-o", "--output-path"] => function(path:String) cfg.outputPath = path,
50 |
51 | @doc("Set the xml input path (file names correspond to platform names)")
52 | ["-i", "--input-path"] => function(path:String) cfg.inputPath = path,
53 |
54 | @doc("Add template directory")
55 | ["-t", "--template-path"] => function(path:String) cfg.loadTemplates(path),
56 |
57 | @doc("Add a resource directory whose contents are copied to the output directory")
58 | ["-res", "--resource-path"] => function(dir:String) cfg.resourcePaths.push(dir),
59 |
60 | @doc("Add a path include filter")
61 | ["-in", "--include"] => function(regex:String) cfg.addFilter(regex, true),
62 |
63 | @doc("Add a path exclude filter")
64 | ["-ex", "--exclude"] => function(regex:String) cfg.addFilter(regex, false),
65 |
66 | @doc("Set the page main title")
67 | ["--title"] => function(name:String) cfg.pageTitle = name,
68 |
69 | @doc("Set the package which serves as top-level")
70 | ["--toplevel-package"] => function(dotPath:String) cfg.toplevelPackage = dotPath,
71 |
72 | @doc("Disable markdown rendering")
73 | ["--no-markdown"] => function() cfg.useMarkdown = false,
74 |
75 | @doc("Include private fields and types")
76 | ["--include-private"] => function() cfg.includePrivate = true,
77 |
78 | @doc("Don't sort fields alphabetically")
79 | ["--keep-field-order"] => function() cfg.keepFieldOrder = true,
80 |
81 | @doc("Set the theme name or path")
82 | ["-theme"] => function(name:String) cfg.loadTheme(name),
83 |
84 | @doc("Defines key = value
85 | Available defines for the default theme:
86 | -D version : Version shown on the default theme's index.html
87 | -D source-path : The base URL used for 'View Source' buttons
88 | -D themeColor : Background color of header/footer - default value 0xFAFAFA
89 | -D textColor : Text color of header/footer - defaults to a contrasting color to the themeColor
90 | -D website : URL to website for logo and footer
91 | -D logo : Path to logo image for the header
92 | -D description : A paragraph at the landing page / toplevel package view")
93 | ["-D", "--define"] => function(key:String, value:String) cfg.defines[key] = value,
94 |
95 | @doc("Display this list of options")
96 | ["-help", "--help"] => function() help = true,
97 |
98 | _ => function(arg:String) throw "Unknown command: " +arg
99 | ]);
100 | // @formatter:on
101 | function printHelp() {
102 | Sys.println("Dox 1.6.0");
103 | Sys.println(argHandler.getDoc());
104 | Sys.exit(0);
105 | }
106 |
107 | if (args.length == 0) {
108 | printHelp();
109 | }
110 |
111 | argHandler.parse(args);
112 | if (help) {
113 | printHelp();
114 | }
115 |
116 | if (cfg.theme == null) {
117 | cfg.loadTheme("default");
118 | }
119 |
120 | run(cfg, Api.new);
121 | }
122 |
123 | public static function run(cfg:Config, createApi:Config->Infos->Api) {
124 | if (cfg.theme == null) {
125 | throw "cfg does not have a theme";
126 | }
127 |
128 | var writer = new Writer(cfg);
129 |
130 | if (!FileSystem.exists(cfg.inputPath)) {
131 | Sys.println('Could not read input path ${cfg.inputPath}');
132 | Sys.exit(1);
133 | }
134 | var parser = new haxe.rtti.XmlParser();
135 |
136 | var tStart = haxe.Timer.stamp();
137 |
138 | function parseFile(path) {
139 | var name = new Path(path).file;
140 | Sys.println('Parsing $path');
141 |
142 | var hashPaths = cfg.outputPath + "/hashes/";
143 |
144 | if (!FileSystem.exists(hashPaths))
145 | FileSystem.createDirectory(hashPaths);
146 |
147 | var data = sys.io.File.getContent(path);
148 | var md5Hash = haxe.crypto.Md5.encode(data);
149 |
150 | if (sys.FileSystem.exists(hashPaths + name + ".md5")) {
151 | var previousHash = sys.io.File.getContent(hashPaths + name + ".md5");
152 | if (md5Hash == previousHash) {
153 | Sys.println('Skipping $path, no file changes detected');
154 | return;
155 | }
156 | }
157 |
158 | sys.io.File.saveContent(hashPaths + name + ".md5", md5Hash);
159 |
160 | var xml = try Xml.parse(data).firstElement() catch (err:Dynamic) {
161 | trace('Error while parsing $path');
162 | throw err;
163 | };
164 | parser.process(xml, name);
165 | cfg.platforms.push(name);
166 | }
167 |
168 | if (FileSystem.isDirectory(cfg.inputPath)) {
169 | for (file in FileSystem.readDirectory(cfg.inputPath)) {
170 | if (!file.endsWith(".xml"))
171 | continue;
172 | parseFile(cfg.inputPath + "/" + file);
173 | }
174 | } else {
175 | parseFile(cfg.inputPath);
176 | }
177 |
178 | Sys.println("Processing types");
179 | var proc = new Processor(cfg);
180 | var root = proc.process(parser.root);
181 |
182 | var api = createApi(cfg, proc.infos);
183 | var gen = new Generator(api, writer);
184 |
185 | Sys.println("");
186 | Sys.println("Generating navigation");
187 | gen.generateNavigation(root);
188 |
189 | Sys.println("Generating 404 page");
190 | gen.generateErrorPage(root);
191 |
192 | Sys.println('Generating to ${cfg.outputPath}');
193 | gen.generate(root);
194 |
195 | Sys.println("");
196 | Sys.println('Generated ${api.infos.numGeneratedTypes} types in ${api.infos.numGeneratedPackages} packages');
197 |
198 | for (dir in cfg.resourcePaths) {
199 | Sys.println('Copying resources from $dir');
200 | writer.copyFrom(dir);
201 | }
202 |
203 | writer.finalize();
204 |
205 | var elapsed = Std.string(haxe.Timer.stamp() - tStart).substr(0, 5);
206 | Sys.println('Done (${elapsed}s)');
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/src/dox/Generator.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import haxe.rtti.CType;
4 | import templo.Template;
5 |
6 | class Generator {
7 | var api:Api;
8 | var writer:Writer;
9 | var tplNav:Template;
10 | var tplPackage:Template;
11 | var tplClass:Template;
12 | var tplEnum:Template;
13 | var tplTypedef:Template;
14 | var tplAbstract:Template;
15 | var tplError:Template;
16 |
17 | public function new(api:Api, writer:Writer) {
18 | this.api = api;
19 | this.writer = writer;
20 | var config = api.config;
21 | tplNav = config.loadTemplate("nav.mtt");
22 | tplPackage = config.loadTemplate("package.mtt");
23 | tplClass = config.loadTemplate("class.mtt");
24 | tplEnum = config.loadTemplate("enum.mtt");
25 | tplTypedef = config.loadTemplate("typedef.mtt");
26 | tplAbstract = config.loadTemplate("abstract.mtt");
27 | tplError = config.loadTemplate("404.mtt");
28 | }
29 |
30 | public function generate(root:TypeRoot) {
31 | root.iter(generateTree);
32 | }
33 |
34 | public function generateNavigation(root:TypeRoot) {
35 | api.config.rootPath = "::rootPath::";
36 | var s = tplNav.execute({
37 | api: api,
38 | root: switch (root) {
39 | case [TPackage(_, '', subs)]: subs;
40 | default: throw "root should be [top level package]";
41 | }
42 | });
43 | writer.saveContent("nav.js", ~/[\r\n\t]/g.replace(s, ""));
44 | }
45 |
46 | public function generateErrorPage(root:TypeRoot) {
47 | var full = 'File not found';
48 | var name = 'File not found';
49 | api.config.setRootPath('');
50 | api.currentPageName = full == "" ? name : full;
51 | var s = tplError.execute({
52 | api: api,
53 | name: name,
54 | full: full,
55 | root: switch (root) {
56 | case [TPackage(_, '', subs)]: subs;
57 | default: throw "root should be [top level package]";
58 | }
59 | });
60 | writer.saveContent("404.html", ~/[\r\n\t]/g.replace(s, ""));
61 | }
62 |
63 | @:access(dox.Api.sanitizePath)
64 | function generateTree(tree:TypeTree) {
65 | switch (tree) {
66 | case TPackage(name, full, subs):
67 | if (name.charAt(0) == "_")
68 | return;
69 | api.currentPageName = full == "" ? name : full;
70 | api.config.setRootPath(full == '' ? full : full + ".pack");
71 | api.currentPageUrl = fileUrl(full == '' ? 'index' : full + '.index');
72 | var s = tplPackage.execute({
73 | api: api,
74 | name: name,
75 | full: full,
76 | subs: subs,
77 | });
78 | write(api.currentPageUrl, s);
79 | api.infos.numGeneratedPackages++;
80 | subs.iter(generateTree);
81 | case TClassdecl(c):
82 | api.currentPageName = c.path;
83 | api.config.setRootPath(c.path);
84 | api.currentPageUrl = fileUrl(api.sanitizePath(c.path));
85 | var s = tplClass.execute({
86 | api: api,
87 | "type": c,
88 | "subClasses": api.infos.subClasses.get(c.path),
89 | "implementors": api.infos.implementors.get(c.path)
90 | });
91 | write(api.currentPageUrl, s);
92 | api.infos.numGeneratedTypes++;
93 | case TEnumdecl(e):
94 | api.currentPageName = e.path;
95 | api.config.setRootPath(e.path);
96 | api.currentPageUrl = fileUrl(api.sanitizePath(e.path));
97 | var s = tplEnum.execute({
98 | api: api,
99 | "type": e,
100 | });
101 | write(api.currentPageUrl, s);
102 | api.infos.numGeneratedTypes++;
103 | case TTypedecl(t):
104 | api.currentPageName = t.path;
105 | api.config.setRootPath(t.path);
106 | api.currentPageUrl = fileUrl(api.sanitizePath(t.path));
107 | var s = tplTypedef.execute({
108 | api: api,
109 | "type": t,
110 | });
111 | write(api.currentPageUrl, s);
112 | api.infos.numGeneratedTypes++;
113 | case TAbstractdecl(a):
114 | api.currentPageName = a.path;
115 | api.config.setRootPath(a.path);
116 | api.currentPageUrl = fileUrl(api.sanitizePath(a.path));
117 | var s = tplAbstract.execute({
118 | api: api,
119 | "type": a,
120 | });
121 | write(api.currentPageUrl, s);
122 | api.infos.numGeneratedTypes++;
123 | }
124 | }
125 |
126 | function fileUrl(path:String) {
127 | return path.replace(".", "/").replace("<", "_").replace(">", "_") + '.html';
128 | }
129 |
130 | function write(fileUrl:String, content:String) {
131 | writer.saveContent(fileUrl, content);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/dox/Infos.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import haxe.rtti.CType;
4 |
5 | /**
6 | Infos is a collection of information collected by Dox during processing.
7 |
8 | An instance can be accessed as `api.infos` in templates.
9 | **/
10 | class Infos {
11 | /**
12 | A map of dot-paths to their corresponding `TypeInfos` objects.
13 | **/
14 | public var typeMap:Map;
15 |
16 | /**
17 | A map of dot-path classes to their sub classes.
18 | **/
19 | public var subClasses:Map>;
20 |
21 | /**
22 | A map of dot-path interfaces to their implementors.
23 | **/
24 | public var implementors:Map>;
25 |
26 | /**
27 | The number of generated types.
28 | **/
29 | public var numGeneratedTypes(default, set):Int = 0;
30 |
31 | /**
32 | The number of generated packages.
33 | **/
34 | public var numGeneratedPackages:Int;
35 |
36 | var packages:Map>;
37 | var names:Map;
38 | var numProcessedTypes:Int;
39 |
40 | function set_numGeneratedTypes(v) {
41 | if (v & 16 == 0)
42 | Sys.print(".");
43 | return numGeneratedTypes = v;
44 | }
45 |
46 | public function new() {
47 | typeMap = new Map();
48 | subClasses = new Map();
49 | implementors = new Map();
50 | packages = new Map();
51 | names = new Map();
52 | numGeneratedPackages = 0;
53 | numProcessedTypes = 0;
54 | }
55 |
56 | function resolveType(path:String, type:String):Null {
57 | // direct match
58 | if (typeMap.exists(type))
59 | return type;
60 |
61 | // same package
62 | var parts = path.split('.');
63 | parts.pop();
64 | var pack = parts.join('.');
65 | var types = packages[pack];
66 | if (types != null && types[type] != null) {
67 | return types[type];
68 | }
69 |
70 | // last ditch attempt, by name (first match wins)
71 | return names[type];
72 | }
73 |
74 | @:allow(dox.Processor)
75 | function addType(path:String, typeInfos:TypeInfos) {
76 | var parts = path.split('.');
77 | var name = parts.pop();
78 | if (name == null)
79 | return;
80 |
81 | var pack = parts.join('.');
82 | var packMap = packages[pack];
83 | if (packMap != null)
84 | packMap.set(name, path);
85 | else
86 | packages.set(pack, [name => path]);
87 |
88 | if (!names.exists(name))
89 | names.set(name, path);
90 |
91 | typeMap.set(path, typeInfos);
92 | numProcessedTypes++;
93 | if (numProcessedTypes & 16 == 0)
94 | Sys.print(".");
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/dox/JavadocHandler.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | class JavadocHandler {
4 | var config:Config;
5 | var infos:Infos;
6 | var markdown:MarkdownHandler;
7 |
8 | public function new(cfg:Config, inf:Infos, mdown:MarkdownHandler) {
9 | config = cfg;
10 | infos = inf;
11 | markdown = mdown;
12 | }
13 |
14 | public function parse(path:String, doc:String):DocInfos {
15 | var tags = [];
16 | // TODO: need to parse this better as haxe source might have this sort of meta
17 | var ereg = ~/^@(param|default|exception|throws|deprecated|return|returns|since|see|event|author)\s+([^@]+)/gm;
18 |
19 | doc = ereg.map(doc, function(e) {
20 | var name = e.matched(1);
21 | var doc = e.matched(2);
22 | var value:Null = null;
23 |
24 | switch (name) {
25 | case 'param', 'exception', 'throws', 'event':
26 | var ereg = ~/([^\s]+)\s+(.*)/gs;
27 | if (ereg.match(doc)) {
28 | value = ereg.matched(1);
29 | doc = ereg.matched(2);
30 | }
31 | default:
32 | }
33 | doc = trimDoc(doc);
34 | tags.push({
35 | name: name,
36 | doc: config.useMarkdown ? markdown.markdownToHtml(path, doc) : doc,
37 | value: value
38 | });
39 | return '';
40 | });
41 |
42 | var infos:DocInfos = {
43 | doc: config.useMarkdown ? markdown.markdownToHtml(path, doc) : doc,
44 | throws: [],
45 | params: [],
46 | sees: [],
47 | events: [],
48 | tags: tags
49 | };
50 | for (tag in tags)
51 | switch (tag.name) {
52 | case 'param':
53 | infos.params.push(tag);
54 | case 'exception', 'throws':
55 | infos.throws.push(tag);
56 | case 'deprecated':
57 | infos.deprecated = tag;
58 | case 'return', 'returns':
59 | infos.returns = tag;
60 | case 'since':
61 | infos.since = tag;
62 | case 'default':
63 | infos.defaultValue = tag;
64 | case 'see':
65 | infos.sees.push(tag);
66 | case 'event':
67 | infos.events.push(tag);
68 | default:
69 | }
70 | return infos;
71 | }
72 |
73 | function trimDoc(doc:String) {
74 | var ereg = ~/^\s+/m;
75 | if (ereg.match(doc)) {
76 | var space = new EReg('^' + ereg.matched(0), 'mg');
77 | doc = space.replace(doc, '');
78 | }
79 | return doc;
80 | }
81 | }
82 |
83 | typedef DocInfos = {
84 | doc:String,
85 | ?returns:DocTag,
86 | ?deprecated:DocTag,
87 | ?since:DocTag,
88 | ?defaultValue:DocTag,
89 | sees:Array,
90 | params:Array,
91 | throws:Array,
92 | events:Array,
93 | tags:Array
94 | }
95 |
96 | typedef DocTag = {
97 | name:String,
98 | doc:String,
99 | ?value:String
100 | }
101 |
--------------------------------------------------------------------------------
/src/dox/MarkdownHandler.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import Markdown;
4 | import markdown.AST;
5 | import markdown.InlineParser;
6 |
7 | class MarkdownHandler {
8 | var config:Config;
9 | var infos:Infos;
10 |
11 | public function new(cfg:Config, inf:Infos) {
12 | config = cfg;
13 | infos = inf;
14 | }
15 |
16 | public function markdownToHtml(path:String, markdown:String) {
17 | // create document
18 | var document = new Document();
19 | document.inlineSyntaxes.push(new MagicCodeSyntax(processCode.bind(path)));
20 |
21 | // replace windows line endings with unix, and split
22 | var lines = markdown.replace("\r\n", "\n").split("\n");
23 |
24 | // parse ref links
25 | document.parseRefLinks(lines);
26 |
27 | // parse ast
28 | try {
29 | var blocks = document.parseLines(lines);
30 | return Markdown.renderHtml(blocks);
31 | } catch (err:Dynamic) {
32 | trace("Parsing warning: " + err);
33 | return markdown;
34 | }
35 | }
36 |
37 | @:access(dox.Infos.resolveType)
38 | function processCode(path:String, source:String) {
39 | source = source.htmlEscape();
40 |
41 | // this.field => #field
42 | source = ~/this\.(\w+)/g.map(source, function(e) {
43 | var field = e.matched(1);
44 | return 'this.$field';
45 | });
46 |
47 | // Type, pack.Type, pack.Type.field => pack/Type.html#field
48 | source = ~/\b([A-Za-z_$][\w\.$]+)\b/g.map(source, function(e) {
49 | var match = e.matched(0);
50 |
51 | var tmp1 = match.split(".");
52 | var field = tmp1.pop();
53 | var type = tmp1.join(".");
54 |
55 | var tmp2 = match.split(".");
56 | tmp2.pop(); // split possible field
57 | var type2 = tmp2.pop();
58 | var path2 = tmp2.join(".");
59 |
60 | var possibleTypes = [infos.resolveType(path, type)];
61 | if (field != null) {
62 | possibleTypes.push(infos.resolveType(type, field));
63 | possibleTypes.push(infos.resolveType(path, field));
64 | }
65 |
66 | if (type2 != null)
67 | possibleTypes.push(infos.resolveType(path, type2));
68 | if (path2 != null)
69 | possibleTypes.push(infos.resolveType(path, path2));
70 | if (type2 != null && path2 != null)
71 | possibleTypes.push(infos.resolveType(type2, path2));
72 |
73 | while (possibleTypes.length > 0) {
74 | var type = possibleTypes.pop();
75 | if (type != null) {
76 | var href = resolveTypeLink(type, field);
77 | return '$match';
78 | }
79 | }
80 |
81 | return match;
82 | });
83 |
84 | // true|false => Bool
85 | source = ~/\b(true|false)\b/g.map(source, function(e) {
86 | var field = e.matched(1);
87 | var path = "Bool";
88 | var type = infos.resolveType(path, path);
89 | return if (type != null) '$field' else field;
90 | });
91 |
92 | return source;
93 | }
94 |
95 | public function pathHref(path:String) {
96 | return config.rootPath + path.split(".").join("/") + ".html";
97 | }
98 |
99 | public function resolveTypeLink(type:String, ?field:String) {
100 | if (field == null)
101 | return pathHref(type);
102 | return if (type != field) pathHref(type) + "#" + field else pathHref(type);
103 | }
104 | }
105 |
106 | class MagicCodeSyntax extends CodeSyntax {
107 | var callback:String->String;
108 |
109 | public function new(callback:String->String) {
110 | this.callback = callback;
111 | super('`([^`]*)`');
112 | }
113 |
114 | override function onMatch(parser:InlineParser):Bool {
115 | var source = pattern.matched(1);
116 | parser.addNode(ElementNode.text('code', callback(source)));
117 | return true;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/dox/Processor.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import haxe.Serializer;
4 | import haxe.rtti.CType;
5 |
6 | @:allow(dox.test)
7 | class Processor {
8 | public var infos:Infos;
9 |
10 | var tplDoc:templo.Template;
11 | var config:Config;
12 | var markdownHandler:MarkdownHandler;
13 | var javadocHandler:JavadocHandler;
14 |
15 | public function new(cfg:Config) {
16 | config = cfg;
17 | infos = new Infos();
18 | tplDoc = config.loadTemplate("doc.mtt");
19 | markdownHandler = new MarkdownHandler(cfg, infos);
20 | javadocHandler = new JavadocHandler(cfg, infos, markdownHandler);
21 | }
22 |
23 | public function process(root:TypeRoot) {
24 | root = filter(root);
25 | sort(root);
26 | return processRoot(root);
27 | }
28 |
29 | function filter(root:TypeRoot) {
30 | var newRoot = [];
31 | if (config.toplevelPackage != "") {
32 | var found = false;
33 | function filter(toplevelFilter:String, tree) {
34 | switch (tree) {
35 | case TPackage(name, _, subs):
36 | var split = toplevelFilter.split(".");
37 | if (split[0] != name) {
38 | return;
39 | }
40 | split.shift();
41 | if (split.length == 0) {
42 | root = subs;
43 | found = true;
44 | return;
45 | }
46 | subs.iter(filter.bind(split.join(".")));
47 | case _:
48 | }
49 | }
50 | root.iter(filter.bind(config.toplevelPackage));
51 | if (!found) {
52 | throw 'Could not find toplevel package ${config.toplevelPackage}';
53 | }
54 | }
55 | function filter(parent:Array, tree:TypeTree):Void {
56 | return switch (tree) {
57 | case TPackage(name, full, subs):
58 | var acc = [];
59 | subs.iter(filter.bind(acc));
60 | if (acc.length > 0 && !isPathFiltered(full)) {
61 | parent.push(TPackage(name, full, acc));
62 | }
63 | case TClassdecl(t):
64 | t.fields = filterFields(t.fields);
65 | t.statics = filterFields(t.statics);
66 | if (!isTypeFiltered(t)) {
67 | parent.push(tree);
68 | infos.addType(t.path, t);
69 | }
70 | case TEnumdecl(t):
71 | if (!isTypeFiltered(t)) {
72 | t.constructors = filterEnumFields(t.constructors);
73 | parent.push(tree);
74 | infos.addType(t.path, t);
75 | }
76 | case TTypedecl(t):
77 | if (!isTypeFiltered(t)) {
78 | switch (t.type) {
79 | case CAnonymous(fields):
80 | t.type = CAnonymous(filterFields(fields));
81 | default:
82 | }
83 | parent.push(tree);
84 | infos.addType(t.path, t);
85 | }
86 | case TAbstractdecl(t):
87 | if (t.impl != null) {
88 | populateFieldsOfAbstract(t, root);
89 | }
90 | if (!isTypeFiltered(t)) {
91 | parent.push(tree);
92 | infos.addType(t.path, t);
93 | }
94 | }
95 | }
96 | root.iter(filter.bind(newRoot));
97 | return newRoot;
98 | }
99 |
100 | function populateFieldsOfAbstract(theAbstract:Abstractdef, root:TypeRoot) {
101 | if (hasDoxMetadata(theAbstract.impl.meta, "is-populated")) {
102 | return; // nothing to do
103 | }
104 |
105 | var statics = new Array();
106 | var fields = new Array();
107 |
108 | // collect direct members
109 | for (cf in theAbstract.impl.statics) {
110 | switch (cf.type) {
111 | // handling functions
112 | case CFunction(args, _):
113 | if (cf.name == "_new") { // constructor
114 | cf.name = "new";
115 | // the Haxe compiler automatically adds a ":noCompletion"
116 | // so we remove the first auto-generated occurrence
117 | var noCompletionMeta = cf.meta.find(m -> m.name == ":noCompletion");
118 | if (noCompletionMeta != null) cf.meta.remove(noCompletionMeta);
119 | fields.push(cf);
120 | } else if (args.length == 0 || args[0].name != "this") {
121 | statics.push(cf);
122 | } else
123 | fields.push(cf);
124 | // handling variables (declared with get and/or set accessor)
125 | case CAbstract(name, params):
126 | var isStatic = true;
127 | switch(cf.get) {
128 | case RCall("accessor"):
129 | final accessor = theAbstract.impl.statics.find(f -> f.name == "get_" + cf.name);
130 | if (accessor != null) {
131 | switch (accessor.type) {
132 | case CFunction(args, _):
133 | if (args.length > 0 && args[0].name == "this")
134 | isStatic = false;
135 | case _:
136 | }
137 | }
138 | case _:
139 | switch(cf.set) {
140 | case RCall("accessor"):
141 | final accessor = theAbstract.impl.statics.find(f -> f.name == "set_" + cf.name);
142 | if (accessor != null) {
143 | switch (accessor.type) {
144 | case CFunction(args, _):
145 | if (args.length > 0 && args[0].name == "this")
146 | isStatic = false;
147 | case _:
148 | }
149 | }
150 | case _:
151 | }
152 | }
153 | if (isStatic)
154 | statics.push(cf);
155 | else {
156 | fields.push(cf);
157 | }
158 | case _:
159 | statics.push(cf);
160 | }
161 | }
162 |
163 | // collect forwarded static fields
164 | final forwardStaticsMeta = findMeta(theAbstract.meta, ":forwardStatics");
165 | if (forwardStaticsMeta != null) {
166 | switch (theAbstract.athis) {
167 | case CClass(name, params):
168 | switch (findInTrees(name, root)) {
169 | case TClassdecl(realType):
170 | if (forwardStaticsMeta.params == null || forwardStaticsMeta.params.length == 0) {
171 | statics = statics.concat(realType.statics);
172 | } else {
173 | for (classStatic in realType.statics) {
174 | if (forwardStaticsMeta.params.contains(classStatic.name))
175 | statics.push(classStatic);
176 | }
177 | }
178 | case _:
179 | }
180 | case CAbstract(name, _):
181 | switch (findInTrees(name, root)) {
182 | case TAbstractdecl(realType):
183 | populateFieldsOfAbstract(realType, root);
184 | if (forwardStaticsMeta.params == null || forwardStaticsMeta.params.length == 0) {
185 | statics = statics.concat(realType.impl.statics);
186 | } else {
187 | for (classStatic in realType.impl.statics) {
188 | if (forwardStaticsMeta.params.contains(classStatic.name))
189 | statics.push(classStatic);
190 | }
191 | }
192 | case _:
193 | }
194 | case _:
195 | }
196 | }
197 |
198 | // collect forwarded instance fields
199 | final forwardMeta = findMeta(theAbstract.meta, ":forward");
200 | if (forwardMeta != null) {
201 | switch (theAbstract.athis) {
202 | case CClass(name, _):
203 | switch (findInTrees(name, root)) {
204 | case TClassdecl(realType):
205 | if (forwardMeta.params == null || forwardMeta.params.length == 0) {
206 | fields = fields.concat(realType.fields);
207 | } else {
208 | for (classField in realType.fields) {
209 | if (forwardMeta.params.contains(classField.name))
210 | fields.push(classField);
211 | }
212 | }
213 | case _:
214 | }
215 | case CAbstract(name, _):
216 | switch (findInTrees(name, root)) {
217 | case TAbstractdecl(realType):
218 | populateFieldsOfAbstract(realType, root);
219 | if (forwardMeta.params == null || forwardMeta.params.length == 0) {
220 | fields = fields.concat(realType.impl.fields);
221 | } else {
222 | for (classField in realType.impl.fields) {
223 | if (forwardMeta.params.contains(classField.name))
224 | fields.push(classField);
225 | }
226 | }
227 | case _:
228 | }
229 | case _:
230 | }
231 | }
232 | theAbstract.impl.statics = filterFields(statics);
233 | theAbstract.impl.fields = filterFields(fields);
234 | setMetaParam(theAbstract.impl.meta, ":dox", "is-populated");
235 | }
236 |
237 | function filterFields(fields:Array) {
238 | return fields.filter(function(cf) {
239 | if (cf.overloads != null) {
240 | cf.overloads = filterFields(cf.overloads);
241 | }
242 | var hide = hasHideMetadata(cf.meta);
243 | var show = hasShowMetadata(cf.meta);
244 | return ((cf.isPublic || config.includePrivate) && !hide) || show;
245 | });
246 | }
247 |
248 | function filterEnumFields(fields:Array) {
249 | return fields.filter(ef -> !hasHideMetadata(ef.meta) || hasShowMetadata(ef.meta));
250 | }
251 |
252 | /** Searches for a TClassdecl or TAbstractdecl in the given trees */
253 | static function findInTrees(path:String, trees:Array):Null {
254 | for (tree in trees) {
255 | final result = findInTree(path, tree);
256 | if (result != null) return result;
257 | }
258 | return null;
259 | }
260 |
261 | /** Searches for a TClassdecl or TAbstractdecl in the given tree */
262 | static function findInTree(path:String, tree:TypeTree):Null {
263 | switch (tree) {
264 | case TPackage(_, full, subs):
265 | return findInTrees(path, subs);
266 | case TClassdecl(t):
267 | if (t.path == path) return tree;
268 | case TAbstractdecl(t):
269 | if (t.path == path) return tree;
270 | case _: return null;
271 | }
272 | return null;
273 | }
274 |
275 | function sort(root:TypeRoot) {
276 | function getName(t:TypeTree) {
277 | return switch (t) {
278 | case TEnumdecl(t): t.path;
279 | case TTypedecl(t): t.path;
280 | case TClassdecl(t): t.path;
281 | case TAbstractdecl(t): t.path;
282 | case TPackage(n, _, _): n;
283 | }
284 | }
285 |
286 | function compare(t1, t2) {
287 | return switch [t1, t2] {
288 | case [TPackage(n1, _, _), TPackage(n2, _, _)]: n1 < n2 ? -1 : 1;
289 | case [TPackage(_), _]: -1;
290 | case [_, TPackage(_)]: 1;
291 | case [t1, t2]:
292 | getName(t1) < getName(t2) ? -1 : 1;
293 | }
294 | }
295 |
296 | function compareFields(cf1, cf2)
297 | return switch [cf1.type, cf2.type] {
298 | case [CFunction(_), CFunction(_)]:
299 | cf1.name == "new" ? -1 : cf2.name == "new" ? 1 : cf1.name < cf2.name ? -1 : 1;
300 | case [CFunction(_), _]: 1;
301 | case [_, CFunction(_)]: -1;
302 | case [_, _]:
303 | cf1.name < cf2.name ? -1 : 1;
304 | }
305 |
306 | inline function sortFields(fields:Array) {
307 | if (!config.keepFieldOrder) {
308 | fields.sort(compareFields);
309 | }
310 | }
311 |
312 | function sort(t:TypeTree) {
313 | switch (t) {
314 | case TPackage(_, _, subs):
315 | subs.sort(compare);
316 | subs.iter(sort);
317 | case TClassdecl(c) | TAbstractdecl({impl: c}) if (c != null):
318 | sortFields(c.fields);
319 | sortFields(c.statics);
320 | case TTypedecl(t):
321 | switch (t.type) {
322 | case CAnonymous(fields):
323 | sortFields(fields);
324 | t.type = CAnonymous(fields);
325 | default:
326 | }
327 | case _:
328 | }
329 | }
330 | root.sort(compare);
331 | root.iter(sort);
332 | }
333 |
334 | function processRoot(root:TypeRoot):TypeRoot {
335 | var newRoot = [
336 | TPackage(config.toplevelPackage == '' ? 'top level' : config.toplevelPackage, '', root)
337 | ];
338 | newRoot.iter(processTree);
339 | return newRoot;
340 | }
341 |
342 | function makeFilePathRelative(t:TypeInfos) {
343 | if (t.file != null && t.file.endsWith(".hx")) {
344 | var path = t.module == null ? t.path : t.module;
345 | t.file = path.replace(".", "/") + ".hx";
346 | }
347 | }
348 |
349 | function processTree(tree:TypeTree) {
350 | switch (tree) {
351 | case TPackage(_, full, subs):
352 | config.setRootPath(full + ".pack");
353 | subs.iter(processTree);
354 |
355 | case TEnumdecl(t):
356 | config.setRootPath(t.path);
357 | t.doc = processDoc(t.path, t.doc);
358 | t.constructors.iter(processEnumField.bind(t.path));
359 | makeFilePathRelative(t);
360 |
361 | case TTypedecl(t):
362 | config.setRootPath(t.path);
363 | t.doc = processDoc(t.path, t.doc);
364 |
365 | var mergedTypes = new Map();
366 | for (platform => typeA in t.types) {
367 | var found = false;
368 | for (platforms => typeB in mergedTypes) {
369 | if (Serializer.run(typeA) == Serializer.run(typeB)) {
370 | mergedTypes.remove(platforms);
371 | mergedTypes[platforms + ", " + platform] = typeB;
372 | found = true;
373 | break;
374 | }
375 | }
376 | if (!found) {
377 | mergedTypes[platform] = typeA;
378 | }
379 | }
380 | t.types = mergedTypes;
381 |
382 | function processFields(type:CType) {
383 | switch (t.type) {
384 | case CAnonymous(fields):
385 | fields.iter(processClassField.bind(t.path));
386 | default:
387 | }
388 | }
389 | processFields(t.type);
390 | t.types.iter(processFields);
391 |
392 | makeFilePathRelative(t);
393 |
394 | case TClassdecl(t):
395 | config.setRootPath(t.path);
396 | t.doc = processDoc(t.path, t.doc);
397 | t.fields.iter(processClassField.bind(t.path));
398 | t.statics.iter(processClassField.bind(t.path));
399 | if (t.superClass != null) {
400 | var subClasses = infos.subClasses[t.superClass.path];
401 | if (subClasses == null)
402 | infos.subClasses[t.superClass.path] = [t];
403 | else
404 | subClasses.push(t);
405 | }
406 | for (i in t.interfaces) {
407 | var implementors = infos.implementors[i.path];
408 | if (implementors == null)
409 | infos.implementors[i.path] = [t];
410 | else
411 | implementors.push(t);
412 | }
413 | makeFilePathRelative(t);
414 |
415 | case TAbstractdecl(t):
416 | config.setRootPath(t.path);
417 | if (t.impl != null) {
418 | t.impl.fields.iter(processClassField.bind(t.path));
419 | t.impl.statics.iter(processClassField.bind(t.path));
420 | }
421 | t.doc = processDoc(t.path, t.doc);
422 | makeFilePathRelative(t);
423 | }
424 | }
425 |
426 | function processClassField(path:String, field:ClassField) {
427 | field.doc = processDoc(path, field.doc);
428 | removeEnumAbstractCast(field);
429 | }
430 |
431 | function removeEnumAbstractCast(field:ClassField) {
432 | // remove `cast` from the expression of enum abstract values (#146)
433 | if (field.type.match(CAbstract(_, _)) && hasMeta(field.meta, ":impl") && hasMeta(field.meta, ":enum") && field.get == RInline && field.set == RNo
434 | && field.expr != null && field.expr.startsWith("cast ")) {
435 | field.expr = field.expr.substr("cast ".length);
436 | }
437 | }
438 |
439 | function processEnumField(path:String, field:EnumField) {
440 | field.doc = processDoc(path, field.doc);
441 | }
442 |
443 | function trimDoc(doc:String) {
444 | // trim leading asterixes
445 | while (doc.charAt(0) == '*')
446 | doc = doc.substr(1);
447 |
448 | // trim trailing asterixes
449 | while (doc.charAt(doc.length - 1) == '*')
450 | doc = doc.substr(0, doc.length - 1);
451 |
452 | // trim additional whitespace
453 | doc = doc.trim();
454 |
455 | // detect doc comment style/indent
456 | var ereg = ~/^([ \t]+(\* )?)[^\s\*]/m;
457 | var matched = ereg.match(doc);
458 |
459 | if (matched) {
460 | var string = ereg.matched(1);
461 |
462 | // escape asterix and allow one optional space after it
463 | string = string.split('* ').join('\\* ?');
464 |
465 | var indent = new EReg("^" + string, "gm");
466 | doc = indent.replace(doc, "");
467 | }
468 |
469 | return doc;
470 | }
471 |
472 | function processDoc(path:String, doc:Null) {
473 | if (doc == null)
474 | return '';
475 | doc = trimDoc(doc);
476 | var info = javadocHandler.parse(path, doc);
477 | return tplDoc.execute({info: info});
478 | }
479 |
480 | function isTypeFiltered(type:{path:Path, meta:MetaData, isPrivate:Bool}) {
481 | if (hasShowMetadata(type.meta))
482 | return false;
483 | if (hasHideMetadata(type.meta))
484 | return true;
485 | if (type.isPrivate)
486 | return !config.includePrivate;
487 | return isPathFiltered(type.path);
488 | }
489 |
490 | function isPathFiltered(path:Path) {
491 | var hasInclusionFilter = false;
492 | for (filter in config.pathFilters) {
493 | if (filter.isIncludeFilter)
494 | hasInclusionFilter = true;
495 | if (filter.r.match(path))
496 | return !filter.isIncludeFilter;
497 | }
498 | return hasInclusionFilter;
499 | }
500 |
501 | function findMeta(meta:MetaData, name:String):Null<{name:String, params:Array}> {
502 | return meta.find(meta -> meta.name == name);
503 | }
504 |
505 | function hasMeta(meta:MetaData, name:String) {
506 | return meta.exists(meta -> meta.name == name);
507 | }
508 |
509 | function hasDoxMetadata(meta:MetaData, ?parameterName:String):Bool {
510 | return meta.exists(m -> m.name == ":dox" && parameterName == null || m.params.has(parameterName));
511 | }
512 |
513 | function hasShowMetadata(meta:MetaData):Bool {
514 | return hasDoxMetadata(meta, "show");
515 | }
516 |
517 | function hasHideMetadata(meta:MetaData):Bool {
518 | return hasDoxMetadata(meta, "hide") || hasMeta(meta, ":compilerGenerated") || hasMeta(meta, ":noCompletion");
519 | }
520 |
521 | function setMetaParam(meta:MetaData, name:String, param:String) {
522 | var doxMeta = findMeta(meta, name);
523 | if (doxMeta == null)
524 | meta.push({name: name, params: [param]});
525 | else if (!doxMeta.params.contains(param))
526 | doxMeta.params.push(param);
527 | }
528 | }
529 |
--------------------------------------------------------------------------------
/src/dox/Theme.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | typedef Theme = {
4 | name:String,
5 | author:String,
6 | ?parentTheme:String,
7 | ?headerIncludes:Array
8 | }
9 |
--------------------------------------------------------------------------------
/src/dox/Writer.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import haxe.zip.Entry;
4 | import haxe.io.Bytes;
5 | import sys.FileSystem;
6 | import sys.io.File;
7 |
8 | class Writer {
9 | var config:Config;
10 | var zipEntries:Null>;
11 |
12 | public function new(config:Config) {
13 | this.config = config;
14 | if (!config.outputPath.endsWith(".zip")) {
15 | try {
16 | if (!FileSystem.exists(config.outputPath)) {
17 | FileSystem.createDirectory(config.outputPath);
18 | }
19 | } catch (e:Dynamic) {
20 | Sys.println('Could not create output directory ${config.outputPath}');
21 | Sys.println(Std.string(e));
22 | Sys.exit(1);
23 | }
24 | } else {
25 | zipEntries = new List();
26 | }
27 | }
28 |
29 | public function saveContent(path:String, content:String) {
30 | if (zipEntries == null) {
31 | var path = Path.join([config.outputPath, path]);
32 | var dir = new Path(path).dir;
33 | if (dir != null && !FileSystem.exists(dir)) {
34 | FileSystem.createDirectory(dir);
35 | }
36 | File.saveContent(path, content);
37 | } else {
38 | zipEntries.push(makeEntry(path, Bytes.ofString(content)));
39 | }
40 | }
41 |
42 | public function copyFrom(dir:String) {
43 | function loop(rel) {
44 | var dir = Path.join([dir, rel]);
45 | for (file in FileSystem.readDirectory(dir)) {
46 | var path = Path.join([dir, file]);
47 | if (FileSystem.isDirectory(path)) {
48 | var outDir = Path.join([config.outputPath, rel, file]);
49 | if (zipEntries == null && !FileSystem.exists(outDir))
50 | FileSystem.createDirectory(outDir);
51 | loop(Path.join([rel, file]));
52 | } else {
53 | if (zipEntries != null) {
54 | makeEntry(Path.join([rel, file]), File.getBytes(path));
55 | } else {
56 | File.copy(path, Path.join([config.outputPath, rel, file]));
57 | }
58 | }
59 | }
60 | }
61 | loop("");
62 | }
63 |
64 | public function finalize() {
65 | if (zipEntries != null) {
66 | var output = File.write(config.outputPath);
67 | var zip = new haxe.zip.Writer(output);
68 | zip.write(zipEntries);
69 | }
70 | }
71 |
72 | function makeEntry(path:String, bytes:Bytes) {
73 | var entry = {
74 | fileName: path,
75 | fileSize: bytes.length,
76 | fileTime: Date.now(),
77 | compressed: false,
78 | dataSize: bytes.length,
79 | data: bytes,
80 | crc32: haxe.crypto.Crc32.make(bytes),
81 | extraFields: null
82 | };
83 | haxe.zip.Tools.compress(entry, 1);
84 | return entry;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/dox/import.hx:
--------------------------------------------------------------------------------
1 | package dox;
2 |
3 | import haxe.io.Path;
4 |
5 | using StringTools;
6 | using Lambda;
7 |
--------------------------------------------------------------------------------
/test/dox/sample/DoxTest.hx:
--------------------------------------------------------------------------------
1 | package dox.sample;
2 |
3 | /**
4 | *
A node in the entity hierarchy, and a collection of components.
5 | *
6 | *
To iterate over the hierarchy, use the parent, firstChild, next and firstComponent fields. For
7 | * example:
8 | *
9 | *
10 | * // Iterate over entity's children
11 | * var child = entity.firstChild;
12 | * while (child != null) {
13 | * var next = child.next; // Store in case the child is removed in process()
14 | * process(child);
15 | * child = next;
16 | * }
17 | *
18 | *
19 | * Working link
20 | * Doesnt work link http://google.com
21 | *
22 | * Previously broken link
23 | * Previously broken link http://php.net/manual/en/function.mail.php
24 | *
25 | * ```haxe
26 | * var foo = bar;
27 | * ```
28 | */
29 | class TestClass {
30 | /**
31 | The value of something.
32 |
33 | @default 10
34 | **/
35 | public var value:Int = 10;
36 |
37 | /**
38 | Tests things.
39 |
40 | @param foo Uh, the foo to test?
41 | Also some newlines.
42 |
43 | Even a paragraph.
44 |
45 | trace("Even a freaking code sample")
46 |
47 | @return The result of the `foo` test.
48 | **/
49 | public static function test(foo:String):String {
50 | return "foo";
51 | }
52 |
53 | /**
54 | Simple doc
55 | **/
56 | public static function haxe_doc_style() {}
57 |
58 | /**
59 | * Javadoc style
60 | *
61 | * Asterix on previous line should still be stripped (no trailing space)
62 | */
63 | public static function java_doc_style() {}
64 |
65 | /**
66 | * Adds a child to this entity.
67 | * @param append Whether to add the entity to the end or beginning of the child list.
68 | * @returns This instance, for chaining.
69 | */
70 | public static function java_doc_style_spaces() {}
71 |
72 | /**
73 | Tests @returns without @param
74 |
75 | @return A random String.
76 | **/
77 | public static function java_doc_return_without_param() {}
78 |
79 | /**
80 | * Creates a FlxSprite at a specified position with a specified one-frame graphic.
81 | * If none is provided, a 16x16 image of the HaxeFlixel logo is used.
82 | *
83 | * @param X The initial X position of the sprite.
84 | * @param Y The initial Y position of the sprite.
85 | * @param SimpleGraphic The graphic you want to display (OPTIONAL - for simple stuff only, do NOT use for animated images!).
86 | *
87 | * @see http://google.com
88 | * @see http://haxe.org
89 | *
90 | * @event added Dispatched when something is added.
91 | * @event removed Dispatched when something is removed.
92 | */
93 | public static function java_doc_multiline_comments() {}
94 |
95 | /**
96 | @param x some documentation for parameters.
97 | **/
98 | public function indentation_space_cadets(x:Int):Void {}
99 |
100 | @:dox(hide) public var hiddenField:String;
101 |
102 | public static var hexInit = 0xFF0000;
103 | public static var nonHexInit = 12;
104 |
105 | /**
106 | Arguments should look like this:
107 |
108 | - `a:Int = 0x00FF00`
109 | - `b:String = "foo"`
110 | - `c:Bool = true`
111 | - `?d:String`
112 | - `?e:String`
113 | - `?f:String`
114 | **/
115 | public static function defaultValues(a = 0x00FF00, ?b = "foo", c = true, ?d:String = null, e:String = null, ?f:String) {}
116 |
117 | /**
118 | There should not be a line break between `FlipX:Bool =` and `false`.
119 | **/
120 | public function add(Name:String, Frames:Array, FrameRate:Int = 30, Looped:Bool = true, FlipX:Bool = false, FlipY:Bool = false):Void {}
121 |
122 | /**
123 | There should not be a line break between `?` and `NotifyCallback`.
124 | **/
125 | public static inline function collide(?ObjectOrGroup1:TestClass, ?ObjectOrGroup2:TestClass, ?NotifyCallback:Dynamic->Dynamic->Void):Void {}
126 |
127 | /**
128 | There should not be a line break between `,` and `Looped`.
129 | **/
130 | public function addByIndices(Name:String, Prefix:String, Indices:Array, Postfix:String, VeryVeryVeryLongArgumentName:Int = 30,
131 | Looped:Bool = true):Void {}
132 |
133 | /**
134 | Should be in the "variables" section as `callback:String -> Int -> Void`.
135 | **/
136 | public var callback:String->Int->Void;
137 |
138 | extern public function externFunction():Void;
139 |
140 | public final finalVar = "";
141 |
142 | public final function finalFunction() {}
143 |
144 | #if cs
145 | public var platformSpecificField:Int;
146 | #end
147 | }
148 |
149 | /**
150 | Some *documentation*.
151 | **/
152 | typedef TestTypedef = {
153 | /**
154 | Some description with a type reference `Foo`.
155 |
156 | And a Second Line `"type":"ItemDetail"`
157 | **/
158 | var myField:String;
159 |
160 | @:dox(hide) var hiddenField:Bool;
161 | var ?optionalField:String;
162 | }
163 |
164 | /**
165 | Some *documentation*.
166 | **/
167 | enum TestEnum {
168 | /**
169 | Some *documentation*.
170 | **/
171 | @:dox(hide) HiddenConstructor;
172 |
173 | /**
174 | Some *documentation*.
175 | **/
176 | VisibleConstructor;
177 |
178 | #if cs
179 | PlatformSpecificConstructor;
180 | #end
181 | }
182 |
183 | @:dox(hide) enum TestHiddenEnum {}
184 |
185 | /**
186 | Use this type to have access to the bitwise operators of C# enums that have a `cs.system.FlagsAttribute` attribute.
187 |
188 | ```haxe
189 | import cs.system.reflection.BindingFlags;
190 |
191 | var binding = new Flags(BindingFlags.Public) | BindingFlags.Static | BindingFlags.NonPublic;
192 | ```
193 | **/
194 | class TestCodeBlock {}
195 |
196 | @:enum
197 | abstract TestEnumAbstract(Int) {
198 | /**
199 | Expression shouldn't have a `cast`.
200 | **/
201 | var value = 0;
202 | }
203 |
204 | abstract TestAbstract(Int) {
205 | /**
206 | Method should have `a:Int` and `b:String` arguments, but the implicit `this` argument shouldn't show up.
207 | **/
208 | public function foo(a:Int, b:String) {}
209 |
210 | @:from public static function from(i:Int):TestAbstract {
211 | return cast i;
212 | }
213 |
214 | @:to public function to():String {
215 | return Std.string(this);
216 | }
217 |
218 | @:op(A + B) public function add(b:Int):Int {
219 | return this + b;
220 | }
221 | }
222 |
223 | /**
224 | Properties should be sorted alphabetically.
225 | **/
226 | typedef TestTypedefOrder = {
227 | var z:Int;
228 | var a:Int;
229 | var b:Int;
230 | var q:Int;
231 | }
232 |
233 | class TestFieldVisibility {
234 | static public var staticVarPublic:Int;
235 | static private var staticVarPrivate:Int;
236 | @:dox(hide)
237 | static public var staticVarPublicHiden:Int;
238 | @:dox(show)
239 | static private var staticVarPrivateShown:Int;
240 |
241 | public var varPublic:Int;
242 |
243 | private var varPrivate:Int;
244 |
245 | @:dox(hide)
246 | public var varPublicHidden:Int;
247 |
248 | @:dox(show)
249 | private var varPrivateShown:Int;
250 |
251 | static public function staticFunctionPublic() {}
252 |
253 | static private function staticFunctionPrivate() {}
254 |
255 | @:dox(hide)
256 | static public function staticFunctionPublicHidden() {}
257 |
258 | @:dox(show)
259 | static private function staticFunctionTest4() {}
260 |
261 | public function functionPublic() {}
262 |
263 | private function functionPrivate() {}
264 |
265 | @:dox(hide)
266 | public function functionPublicHidden() {}
267 |
268 | @:dox(show)
269 | private function functionPrivateShown() {}
270 | }
271 |
272 | /**
273 | * `obj`
274 | * `this.obj`,
275 | * `this.OBJ`
276 | * `TestLinks.obj`
277 | * `TestLinks.OBJ`
278 | * `Std.int`
279 | * `haxe.io.Eof.new`
280 | * `true` or `false`
281 | * `Type`
282 | * `TestMetaData.RED`
283 | * `TestEnum.VisibleConstructor`
284 | * `this.obj + this.OBJ`
285 | * `TestLinks.obj + TestLinks.OBJ`
286 | **/
287 | class TestLinks {
288 | public var obj:Int;
289 | public var OBJ = {x: 10, y: 10};
290 | }
291 |
292 | @:using(StringTools)
293 | class TestMetaData {
294 | /**
295 | Deprecated red color value
296 | **/
297 | @:metaWithParams("123", 123)
298 | @:deprecated("Use `red` instead")
299 | @:metaWithoutParams
300 | public static var deprecatedRed = 0xFF0000;
301 |
302 | @:meta
303 | @:to
304 | public static var red = 12;
305 | public static var RED = 12;
306 | }
307 |
308 | /**
309 | This should not be visible on the index page.
310 | **/
311 | class Index {}
312 |
313 | @:generic class GenericClass {}
314 | class ChildOfGeneric extends GenericClass {}
315 |
316 | /**
317 | This interface shouldn't show compiler-generated `get_` / `set_` fields (#239).
318 | **/
319 | interface TestInterface {
320 | public var active(get, set):Bool;
321 | }
322 |
323 | class ClassWithImplements implements TestInterface {
324 | public var active(get, set):Bool;
325 |
326 | inline function get_active()
327 | return false;
328 |
329 | inline function set_active(active)
330 | return false;
331 | }
332 |
333 | final class FinalClass {}
334 | final interface FinalInterface {}
335 |
336 | // #260
337 | interface MultiExtends extends A extends B {}
338 | interface A {}
339 | interface B {}
340 |
341 | @:deprecated("This class is deprecated in favor of something else")
342 | class DeprecatedClass {}
343 |
344 | @:deprecated("This enum is deprecated in favor of something else")
345 | enum DeprecatedEnum {}
346 |
347 | @:deprecated("This abstract is deprecated in favor of something else")
348 | abstract DeprecatedAbstract(Int) {}
349 |
350 | @:deprecated("This typedef is deprecated in favor of something else")
351 | typedef DeprecatedTypedef = {}
352 |
353 | class Overloads {
354 | #if cs
355 | /** only this constructor overload should show up **/
356 | @:overload public function new() {}
357 |
358 | /** this one shouldn't **/
359 | @:overload private function new(i:Int) {}
360 | #end
361 | }
362 |
363 | /**
364 | @author some guy
365 | **/
366 | class JavaDocTag {}
367 |
368 | @:publicFields
369 | class FunctionType {
370 | var a:() -> Void;
371 | var b:Void->Void;
372 | var c:Int->Void;
373 | var d:(i:Int) -> Void;
374 | var e:(i:Int, s:String) -> Void;
375 | var f:(Int, s:String) -> Void;
376 | }
377 |
378 | class NoCompletion {
379 | @:noCompletion public var noCompletion:Int;
380 | @:noCompletion @:dox(show) public var noCompletionButDoxShow:Int;
381 | }
382 |
383 | @:noCompletion class NoCompletionType {}
384 | @:noCompletion @:dox(show) class NoCompletionButDoxShowType {}
385 |
386 | /**
387 | This typedef has different implementations per target.
388 | **/
389 | typedef PlatformConditionalized = #if cpp {
390 | /**
391 | It a int
392 | **/
393 | var i:Int;
394 |
395 | /**
396 | It a string
397 | **/
398 | var s:String;
399 | } #elseif cs(a:String, b:Int) -> Void #elseif neko Array #else {} #end;
400 |
401 | /**
402 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
403 | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
404 | ex ea commodo consequat. Duis aute irure dolor in reprehenderit
405 | **/
406 | typedef Alias = (a:String, b:Int) -> Void;
407 |
--------------------------------------------------------------------------------
/test/dox/sample/emptyPackage/HiddenClass.hx:
--------------------------------------------------------------------------------
1 | package dox.sample.emptyPackage;
2 |
3 | @:dox(hide)
4 | class HiddenClass {}
5 |
--------------------------------------------------------------------------------
/test/dox/test/TestRunner.hx:
--------------------------------------------------------------------------------
1 | package dox.test;
2 |
3 | import dox.test.processor.ProcessorTest;
4 | import utest.ui.Report;
5 | import utest.Runner;
6 |
7 | class TestRunner {
8 | public static function main():Void {
9 | var runner = new Runner();
10 | runner.addCase(new ProcessorTest());
11 | Report.create(runner);
12 | runner.run();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/dox/test/processor/ProcessorTest.hx:
--------------------------------------------------------------------------------
1 | package dox.test.processor;
2 |
3 | import utest.Assert;
4 |
5 | using Lambda;
6 | using StringTools;
7 |
8 | class ProcessorTest extends utest.Test {
9 | public function testProcessor() {
10 | var cfg = new Config(Sys.getCwd());
11 | cfg.inputPath = "bin/doc.xml";
12 |
13 | cfg.outputPath = "bin/pages";
14 | cfg.toplevelPackage = "dox.test";
15 | cfg.loadTheme("default");
16 |
17 | var xml = try Xml.parse(sys.io.File.getContent("bin/doc.xml")).firstElement() catch (err:Dynamic) throw err;
18 | var parser = new haxe.rtti.XmlParser();
19 |
20 | parser.process(xml, "unused");
21 |
22 | var root = new Processor(cfg).process(parser.root);
23 | var testAbstract = Processor.findInTrees("dox.test.processor.TestAbstract", root);
24 | Assert.notNull(testAbstract);
25 | switch (testAbstract) {
26 | case TAbstractdecl(realType):
27 | var fields = realType.impl.fields.map(cf -> cf.name);
28 | fields.sort(Reflect.compare);
29 | var statics = realType.impl.statics.map(cf -> cf.name);
30 | statics.sort(Reflect.compare);
31 | Assert.same([
32 | "abstract_instance_func_no_args",
33 | "abstract_instance_func_with_args",
34 | "abstract_instance_ro_var",
35 | "abstract_instance_wo_var",
36 | "impl_instance_func",
37 | "impl_instance_var",
38 | "new"
39 | ], fields);
40 | Assert.same([
41 | "abstract_static_func_no_args",
42 | "abstract_static_func_with_args",
43 | "abstract_static_ro_var",
44 | "abstract_static_wo_var",
45 | "impl_static_func",
46 | "impl_static_var"
47 | ], statics);
48 | case _:
49 | throw "Type TestAbstract is not an abstract!";
50 | }
51 | var testAbstractOfAbstract = Processor.findInTrees("dox.test.processor.TestAbstractOfAbstract", root);
52 | Assert.notNull(testAbstractOfAbstract);
53 | switch (testAbstractOfAbstract) {
54 | case TAbstractdecl(realType):
55 | var fields = realType.impl.fields.map(cf -> cf.name);
56 | fields.sort(Reflect.compare);
57 | var statics = realType.impl.statics.map(cf -> cf.name);
58 | statics.sort(Reflect.compare);
59 | Assert.same([
60 | "abstract_instance_func_no_args",
61 | "abstract_instance_func_with_args",
62 | "abstract_instance_ro_var",
63 | "abstract_instance_wo_var",
64 | "impl_instance_func",
65 | "impl_instance_var",
66 | "new"
67 | ], fields);
68 | Assert.same([
69 | "abstract_static_func_no_args",
70 | "abstract_static_func_with_args",
71 | "abstract_static_ro_var",
72 | "abstract_static_wo_var",
73 | "impl_static_func",
74 | "impl_static_var"
75 | ], statics);
76 | case _:
77 | throw "Type TestAbstractOfAbstract is not an abstract!";
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/test/dox/test/processor/TestAbstract.hx:
--------------------------------------------------------------------------------
1 | package dox.test.processor;
2 |
3 | @:keep
4 | @:forward
5 | @:forwardStatics
6 | abstract TestAbstractOfAbstract(TestAbstract) from TestAbstract to TestAbstract {}
7 |
8 | @:keep
9 | @:forward(impl_instance_var, impl_instance_func)
10 | @:forwardStatics(impl_static_var, impl_static_func)
11 | abstract TestAbstract(TestAbstractImpl) from TestAbstractImpl to TestAbstractImpl {
12 | /**
13 | * static members
14 | */
15 | public static var abstract_static_ro_var(get, never):TestAbstract;
16 |
17 | private static function get_abstract_static_ro_var():TestAbstract {
18 | return null;
19 | }
20 |
21 | public static var abstract_static_wo_var(never, set):TestAbstract;
22 |
23 | private static function set_abstract_static_wo_var(value:TestAbstract):TestAbstract {
24 | return value;
25 | }
26 |
27 | public static function abstract_static_func_no_args():Void {};
28 |
29 | public static function abstract_static_func_with_args(unused:Int):Void {};
30 |
31 | /**
32 | * instance members
33 | */
34 | public function new(someValue:Int) {
35 | this = new TestAbstractImpl();
36 | }
37 |
38 | public var abstract_instance_ro_var(get, never):TestAbstract;
39 |
40 | private function get_abstract_instance_ro_var():TestAbstract {
41 | return this;
42 | };
43 |
44 | public var abstract_instance_wo_var(never, set):TestAbstract;
45 |
46 | private function set_abstract_instance_wo_var(value:TestAbstract):TestAbstract {
47 | return value;
48 | };
49 |
50 | public function abstract_instance_func_no_args():Void {};
51 |
52 | public function abstract_instance_func_with_args(unused:Int):Void {};
53 | }
54 |
55 | @:keep
56 | private class TestAbstractImpl {
57 | /*
58 | * fields that should be forwarded
59 | */
60 | public static var impl_static_var:Int = 10;
61 |
62 | public static function impl_static_func():Void {};
63 |
64 | public var impl_instance_var:Int;
65 |
66 | public function impl_instance_func():Void {};
67 |
68 | /*
69 | * public fields that should not be forwarded
70 | */
71 | public static var hidden_impl_static_var:Int;
72 |
73 | public static function hidden_impl_static_func():Void {};
74 |
75 | public var hidden_impl_instance_var:Int;
76 |
77 | public function hidden_impl_instance_func():Void {};
78 |
79 | /*
80 | * private fields that should not be forwarded
81 | */
82 | private static var private_impl_static_var:Int;
83 |
84 | private static function private_impl_static_func():Void {};
85 |
86 | private var private_impl_instance_var:Int;
87 |
88 | private function private_impl_instance_func():Void {};
89 |
90 | public function new() {}
91 | }
92 |
--------------------------------------------------------------------------------
/tests.hxml:
--------------------------------------------------------------------------------
1 | -lib hxtemplo
2 | -lib hxparse
3 | -lib hxargs
4 | -lib markdown
5 | -lib utest
6 | -cp src
7 | -cp test
8 | --macro include('dox.test')
9 | -xml bin/doc.xml
10 | --run dox.test.TestRunner
11 |
--------------------------------------------------------------------------------
/themes/default/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Default Haxe Theme",
3 | "author": "Haxe Foundation"
4 | }
5 |
--------------------------------------------------------------------------------
/themes/default/resources/bootstrap/css/bootstrap-responsive.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.2
3 | *
4 | * Copyright 2013 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world by @mdo and @fat.
9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
10 |
--------------------------------------------------------------------------------
/themes/default/resources/bootstrap/css/bootstrap-select.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap-select v1.6.3 (http://silviomoreto.github.io/bootstrap-select/)
3 | *
4 | * Copyright 2013-2014 bootstrap-select
5 | * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
6 | */.bootstrap-select{width:220px \0}.bootstrap-select>.btn{width:100%;padding-right:25px}.error .bootstrap-select .btn{border:1px solid #b94a48}.control-group.error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .btn:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.btn-group.dropdown-menu-right,.bootstrap-select.btn-group[class*=col-].dropdown-menu-right,.row-fluid .bootstrap-select.btn-group[class*=col-].dropdown-menu-right{float:right}.form-search .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group,.form-group .bootstrap-select.btn-group{margin-bottom:0}.form-group-lg .bootstrap-select.btn-group.form-control,.form-group-sm .bootstrap-select.btn-group.form-control{padding:0}.form-inline .bootstrap-select.btn-group .form-control{width:100%}.input-append .bootstrap-select.btn-group{margin-left:-1px}.input-prepend .bootstrap-select.btn-group{margin-right:-1px}.bootstrap-select.btn-group>.disabled{cursor:not-allowed}.bootstrap-select.btn-group>.disabled:focus{outline:0!important}.bootstrap-select.btn-group .btn .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .btn .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group[class*=col-] .btn{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;z-index:1035;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li:not(.disabled) a:hover small,.bootstrap-select.btn-group .dropdown-menu li:not(.disabled) a:focus small,.bootstrap-select.btn-group .dropdown-menu li.active:not(.disabled) a small{color:#64b1d8;color:rgba(100,177,216,.4)}.bootstrap-select.btn-group .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select.btn-group .dropdown-menu li a{cursor:pointer}.bootstrap-select.btn-group .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select.btn-group .dropdown-menu li a span.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px}.bootstrap-select.btn-group.fit-width .btn .filter-option{position:static}.bootstrap-select.btn-group.fit-width .btn .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{position:absolute;display:inline-block;right:15px;margin-top:5px}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.show-menu-arrow.open>.btn{z-index:1035+1}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom-width:7px;border-bottom-style:solid;border-bottom-color:#ccc;border-bottom-color:rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-bottom:0;border-top-width:7px;border-top-style:solid;border-top-color:#ccc;border-top-color:rgba(204,204,204,.2)}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after{display:block}.bs-searchbox,.bs-actionsbox{padding:4px 8px}.bs-actionsbox{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox input.form-control{margin-bottom:0;width:100%}.mobile-device{position:absolute;top:0;left:0;display:block!important;width:100%;height:100%!important;opacity:0}
--------------------------------------------------------------------------------
/themes/default/resources/bootstrap/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HaxeFoundation/dox/e92caadca75c540f77e44e78305e86fcf6347670/themes/default/resources/bootstrap/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/themes/default/resources/bootstrap/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HaxeFoundation/dox/e92caadca75c540f77e44e78305e86fcf6347670/themes/default/resources/bootstrap/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/themes/default/resources/bootstrap/js/bootstrap-select.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap-select v1.6.3 (http://silviomoreto.github.io/bootstrap-select/)
3 | *
4 | * Copyright 2013-2014 bootstrap-select
5 | * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
6 | */
7 | !function(a){"use strict";function b(a,b){return a.toUpperCase().indexOf(b.toUpperCase())>-1}function c(b){var c=[{re:/[\xC0-\xC6]/g,ch:"A"},{re:/[\xE0-\xE6]/g,ch:"a"},{re:/[\xC8-\xCB]/g,ch:"E"},{re:/[\xE8-\xEB]/g,ch:"e"},{re:/[\xCC-\xCF]/g,ch:"I"},{re:/[\xEC-\xEF]/g,ch:"i"},{re:/[\xD2-\xD6]/g,ch:"O"},{re:/[\xF2-\xF6]/g,ch:"o"},{re:/[\xD9-\xDC]/g,ch:"U"},{re:/[\xF9-\xFC]/g,ch:"u"},{re:/[\xC7-\xE7]/g,ch:"c"},{re:/[\xD1]/g,ch:"N"},{re:/[\xF1]/g,ch:"n"}];return a.each(c,function(){b=b.replace(this.re,this.ch)}),b}function d(a){var b={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},c="(?:"+Object.keys(b).join("|")+")",d=new RegExp(c),e=new RegExp(c,"g"),f=null==a?"":""+a;return d.test(f)?f.replace(e,function(a){return b[a]}):f}function e(b,c){var d=arguments,e=b,b=d[0],c=d[1];[].shift.apply(d),"undefined"==typeof b&&(b=e);var g,h=this.each(function(){var e=a(this);if(e.is("select")){var h=e.data("selectpicker"),i="object"==typeof b&&b;if(h){if(i)for(var j in i)i.hasOwnProperty(j)&&(h.options[j]=i[j])}else{var k=a.extend({},f.DEFAULTS,a.fn.selectpicker.defaults||{},e.data(),i);e.data("selectpicker",h=new f(this,k,c))}"string"==typeof b&&(g=h[b]instanceof Function?h[b].apply(h,d):h.options[b])}});return"undefined"!=typeof g?g:h}a.expr[":"].icontains=function(c,d,e){return b(a(c).text(),e[3])},a.expr[":"].aicontains=function(c,d,e){return b(a(c).data("normalizedText")||a(c).text(),e[3])};var f=function(b,c,d){d&&(d.stopPropagation(),d.preventDefault()),this.$element=a(b),this.$newElement=null,this.$button=null,this.$menu=null,this.$lis=null,this.options=c,null===this.options.title&&(this.options.title=this.$element.attr("title")),this.val=f.prototype.val,this.render=f.prototype.render,this.refresh=f.prototype.refresh,this.setStyle=f.prototype.setStyle,this.selectAll=f.prototype.selectAll,this.deselectAll=f.prototype.deselectAll,this.destroy=f.prototype.remove,this.remove=f.prototype.remove,this.show=f.prototype.show,this.hide=f.prototype.hide,this.init()};f.VERSION="1.6.3",f.DEFAULTS={noneSelectedText:"Nothing selected",noneResultsText:"No results match",countSelectedText:function(a){return 1==a?"{0} item selected":"{0} items selected"},maxOptionsText:function(a,b){var c=[];return c[0]=1==a?"Limit reached ({n} item max)":"Limit reached ({n} items max)",c[1]=1==b?"Group limit reached ({n} item max)":"Group limit reached ({n} items max)",c},selectAllText:"Select All",deselectAllText:"Deselect All",multipleSeparator:", ",style:"btn-default",size:"auto",title:null,selectedTextFormat:"values",width:!1,container:!1,hideDisabled:!1,showSubtext:!1,showIcon:!0,showContent:!0,dropupAuto:!0,header:!1,liveSearch:!1,actionsBox:!1,iconBase:"glyphicon",tickIcon:"glyphicon-ok",maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,searchAccentInsensitive:!1},f.prototype={constructor:f,init:function(){var b=this,c=this.$element.attr("id");this.$element.hide(),this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.$newElement=this.createView(),this.$element.after(this.$newElement),this.$menu=this.$newElement.find("> .dropdown-menu"),this.$button=this.$newElement.find("> button"),this.$searchbox=this.$newElement.find("input"),this.options.dropdownAlignRight&&this.$menu.addClass("dropdown-menu-right"),"undefined"!=typeof c&&(this.$button.attr("data-id",c),a('label[for="'+c+'"]').click(function(a){a.preventDefault(),b.$button.focus()})),this.checkDisabled(),this.clickListener(),this.options.liveSearch&&this.liveSearchListener(),this.render(),this.liHeight(),this.setStyle(),this.setWidth(),this.options.container&&this.selectPosition(),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile()},createDropdown:function(){var b=this.multiple?" show-tick":"",c=this.$element.parent().hasClass("input-group")?" input-group-btn":"",d=this.autofocus?" autofocus":"",e=this.$element.parents().hasClass("form-group-lg")?" btn-lg":this.$element.parents().hasClass("form-group-sm")?" btn-sm":"",f=this.options.header?'