Repository: SaswatPadhi/pseudocode.js
Branch: master
Commit: 1fbd17c0100f
Files: 29
Total size: 171.1 KB
Directory structure:
gitextract_lpmuwsmr/
├── .eslintrc
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── docs/
│ ├── index.html
│ ├── javascripts/
│ │ └── scale.fix.js
│ ├── katex-samples.html
│ ├── mathjax-v2-samples.html
│ ├── mathjax-v3-samples.html
│ ├── mathjax-v4-samples.html
│ ├── params.json
│ ├── pseudocode.css
│ ├── pseudocode.js
│ └── stylesheets/
│ └── styles.css
├── package.json
├── pseudocode.js
├── src/
│ ├── Lexer.js
│ ├── ParseError.js
│ ├── Parser.js
│ ├── Renderer.js
│ └── utils.js
└── static/
├── body.html.part
├── footer.html.part
├── katex.html.part
├── mathjax-v2.html.part
├── mathjax-v3.html.part
├── mathjax-v4.html.part
└── pseudocode.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc
================================================
{
"rules": {
"arrow-spacing": "error",
"brace-style": ["error", "stroustrup"],
"camelcase": "error",
"comma-dangle": ["error", "always-multiline"],
"comma-spacing": "error",
"constructor-super": "error",
"curly": ["error", "multi-or-nest", "consistent"],
"eol-last": "error",
"eqeqeq": ["error", "always"],
"guard-for-in": "off",
"indent": ["error", 4, {
"CallExpression": {"arguments": "first"},
"FunctionExpression": {"parameters": "first"},
"flatTernaryExpressions": true,
"SwitchCase": 1
}],
"keyword-spacing": "error",
"linebreak-style": ["error", "unix"],
"max-len": ["error", 128, 2, {
"ignoreUrls": true,
"ignorePattern": "\\brequire\\([\"']|eslint-disable",
"ignoreComments": true
}],
"no-alert": "error",
"no-array-constructor": "error",
"no-console": "off",
"no-const-assign": "error",
"no-constant-condition": "off",
"no-debugger": "error",
"no-dupe-class-members": "error",
"no-dupe-keys": "error",
"no-duplicate-imports": "error",
"no-extra-bind": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-object": "error",
"no-spaced-func": "error",
"no-this-before-super": "error",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef": "off",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"no-unused-vars": ["error", {"args": "none", "varsIgnorePattern": "^_*$"}],
"no-useless-call": "error",
"no-with": "error",
"one-var": ["error", "never"],
"prefer-const": "error",
"prefer-spread": "error",
"semi": ["error", "always"],
"space-before-blocks": "error",
"space-before-function-paren": ["error", "always"],
"space-infix-ops": "error",
"space-unary-ops": "error",
"prefer-template": "error",
"no-template-curly-in-string": "error",
"template-curly-spacing": ["error", "never"],
"arrow-parens": ["error", "always"],
"arrow-body-style": "error",
"prefer-arrow-callback": "error",
"object-curly-spacing": ["error", "always"]
},
"env": {
"es6": true,
"node": true,
"browser": true
},
"extends": "eslint:recommended",
"root": true
}
================================================
FILE: .gitignore
================================================
_site
tex/
build/
node_modules/
static/katex/
static/fonts/
npm-debug.log
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2020-2023 Saswat Padhi (saswat.sourav@gmail.com)
Copyright (c) 2015-2019 Tate Tian (tatetian@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
.PHONY: all build clean docs default lint release
VERSION=2.4.1
# Building tools
BROWSERIFY = $(realpath ./node_modules/.bin/browserify)
CLEANCSS = $(realpath ./node_modules/.bin/cleancss)
ESLINT = $(realpath ./node_modules/.bin/eslint)
WATCHIFY = $(realpath ./node_modules/.bin/watchify)
UGLIFYJS = $(realpath ./node_modules/.bin/uglifyjs) \
--mangle \
--beautify \
ascii_only=true,beautify=false
SAMPLES = build/katex-samples.html build/mathjax-v2-samples.html build/mathjax-v3-samples.html build/mathjax-v4-samples.html
default: build
all : clean
@$(MAKE) --no-print-directory release
watch-js: pseudocode.js $(wildcard src/*.js)
$(WATCHIFY) $< --standalone pseudocode -o build/pseudocode.js
build: build/pseudocode.js build/pseudocode.css $(SAMPLES)
@echo "> Building succeeded\n"
build/pseudocode.js: pseudocode.js $(wildcard src/*.js)
@$(MAKE) --no-print-directory lint
$(BROWSERIFY) $< --exclude mathjax --exclude katex --standalone pseudocode -o $@
build/pseudocode.css: static/pseudocode.css
cp static/pseudocode.css build/pseudocode.css
build/%-samples.html: static/%.html.part static/body.html.part static/footer.html.part
cat $^ > $@
lint: pseudocode.js $(wildcard src/*.js)
$(ESLINT) $^
fix-lint: pseudocode.js $(wildcard src/*.js)
$(ESLINT) --fix $^
release: build docs build/pseudocode-js.tar.gz build/pseudocode-js.zip
@echo "> Release package generated\n"
RELEASE_DIR=pseudocode.js-$(VERSION)/
build/pseudocode-js.tar.gz: build/$(RELEASE_DIR)
cd build && tar czf pseudocode-js.tar.gz $(RELEASE_DIR)
build/pseudocode-js.zip: build/$(RELEASE_DIR)
cd build && zip -rq pseudocode-js.zip $(RELEASE_DIR) || \
7z a -r pseudocode-js.zip $(RELEASE_DIR)
build/$(RELEASE_DIR): build/pseudocode.js build/pseudocode.min.js build/pseudocode.css build/pseudocode.min.css $(SAMPLES) README.md
mkdir -p build/$(RELEASE_DIR)
cp -r $^ build/$(RELEASE_DIR)
build/pseudocode.min.js: build/pseudocode.js
$(UGLIFYJS) < $< > $@
build/pseudocode.min.css: build/pseudocode.css
$(CLEANCSS) -o $@ $<
docs: build/pseudocode.min.js build/pseudocode.min.css $(SAMPLES)
cp build/pseudocode.min.css docs/pseudocode.css
cp build/pseudocode.min.js docs/pseudocode.js
cp $(SAMPLES) docs/
clean:
@rm -rf build/*
================================================
FILE: README.md
================================================
# pseudocode.js
**pseudocode.js** is a JavaScript library that typesets pseudocode beautifully to
HTML.
* _Intuitive grammar:_ Pseudocode.js takes a LaTeX-style input that supports
the algorithmic constructs from LaTeX's algorithm packages. With or without
LaTeX experience, a user should find the grammar fairly intuitive.
* _Print quality:_ The HTML output produced by pseudocode.js is (almost)
identical with the pretty algorithms printed on publications that are
typeset by LaTeX.
* _Math formula support:_ Inserting math formulas in pseudocode.js is as easy
as LaTeX. Just enclose math expression in `$...$` or `\(...\)`.
It supports all modern browsers, including Chrome, Safari, Firefox, Edge, and Edge.
Visit the [project website](https://saswatpadhi.github.io/pseudocode.js) for a demo.
## Usage
### Quick Start
pseudocode.js can render math formulas using either
[KaTeX](https://github.com/Khan/KaTeX), or [MathJax](https://www.mathjax.org/).
#### Step 1A · For KaTeX users
Include the following in the `
` of your page:
```html
```
#### Step 1B · For MathJax 2.x users
Include the following in the `` of your page:
```html
```
> **Note**
> The `-full` configuration is larger and loads more extensions,
> but I recommend using it it just to avoid any hiccups later.
> You may want to use the standard configuration instead
> if you do not require additional packages.
#### Step 1C · For MathJax 3.x users
Include the following in the `` of your page:
```html
```
> **Note**
> The `-full` configuration is larger and loads more extensions,
> but I recommend using it it just to avoid any hiccups later.
> You may want to use the standard configuration instead
> if you do not require additional packages, such as `color`.
#### Step 2 · Grab pseudocode.js
Include the following in the `` of your page:
```html
```
You may also use the `latest` tag for pseudocode instead,
but jsDelivr might be delayed in updating the pointer for this tag.
```html
```
#### Step 3 · Write your pseudocode inside a `
`
We assume the pseudocode to be rendered is in a `
` DOM element.
Here is an example that illustrates a quicksort algorithm:
```html
```
#### Step 4A · Render the element using pseudocode.js
Insert the following Javascript snippet at the end of your document:
```html
```
#### Step 4B · Render all elements of the class using pseudocode.js
Insert the following Javascript snippet at the end of your document:
```html
```
### Grammar
There are several packages for typesetting algorithms in LaTeX, among which
[`algorithmic`](http://mirror.ctan.org/tex-archive/macros/latex/contrib/algorithms/algorithms.pdf)
package is the most simple and intuitive, and is chosen by IEEE in its
[LaTeX template file](http://www.ctan.org/tex-archive/macros/latex/contrib/IEEEtran).
The grammar of pseudocode.js is mostly compatible with `algorithmic` package with
a few improvement to make it even more easier to use.
Commands for typesetting algorithms must be enclosed in an `algorithmic` environment:
```tex
\begin{algorithmic}
# A precondition is optional
\REQUIRE
# A postcondition is optional
\ENSURE
# An input is optional
\INPUT
# An output is optional
\OUTPUT
# The body of your code is a
\STATE ...
\end{algorithmic}
```
`` can include zero or more ``, ``, ``
and ``:
```tex
# A can be:
\STATE
\RETURN
\PRINT
# A can be:
# A conditional
\IF{}
\ELIF{}
\ELSE
\ENDIF
# Or a loop: \WHILE, \FOR or \FORALL
\WHILE{}
\ENDWHILE
# Or a repeat: \REPEAT \UNTIL{}
\REPEAT
\UNTIL{}
# A can by defined by either \FUNCTION or \PROCEDURE
# Both are exactly the same
\FUNCTION{}{}
\ENDFUNCTION
# A is:
\COMMENT{}
```
A ``, ``, or `` may include the following:
```tex
% Normal characters
Hello world
% Escaped characters
\\, \{, \}, \$, \&, \#, \% and \_
% Math formula
$i \gets i + 1$
% Function call
\CALL{}{}
% Keywords
\AND, \OR, \XOR, \NOT, \TO, \DOWNTO, \TRUE, \FALSE, \BREAK, \CONTINUE
% LaTeX's sizing commands
\tiny, \scriptsize, \footnotesize, \small \normalsize, \large, \Large, \LARGE,
\huge, \HUGE
% LaTeX's font declarations
\rmfamily, \sffamily, \ttfamily
\upshape, \itshape, \slshape, \scshape
\bfseries, \mdseries, \lfseries
% LaTeX's font commands
\textnormal{}, \textrm{}, \textsf{}, \texttt{}
\textup{}, \textit{}, \textsl{}, \textsc{}
\uppercase{}, \lowercase{}
\textbf, \textmd, \textlf
% And it's possible to group text with braces
normal text {\small the size gets smaller} back to normal again
```
> **Note**
> Although pseudocode.js recognizes some LaTeX commands, it is by no means a full-featured LaTeX implementation in JavaScript.
> It only support a subset of LaTeX commands that are most relevant to typesetting algorithms.
To display the caption of an algorithm, use `algorithm` environment as a 'float' wrapper :
```tex
\begin{algorithm}
\caption{The caption of your algorithm}
\begin{algorithmic}
\STATE ...
\end{algorithmic}
\end{algorithm}
```
### Options
#### Global Options
`pseudocode.renderElement` can accept an option object as the last argument, such as
```js
pseudocode.renderElement(document.getElementById("quicksort"),
{ lineNumber: true });
```
The following options are currently supported:
* `captionCount`:
Reset the caption counter to this number, if defined.
* `commentDelimiter`:
The delimiters used to start and end a comment region.
Note that only line comments are supported.
* `indentSize`:
The indent size of inside a control block, e.g. if, for etc.
The unit must be in 'em'.
* `lineNumber`:
Whether line numbering is enabled.
* `lineNumberPunc`:
The punctuation that follows line number.
* `noEnd`:
Whether block ending, like `end if`, end procedure`, etc.
are shown.
* `scopeLines`:
Whether vertical lines indicating the scopes of nested blocks
are drawn.
* `titlePrefix`:
The title prefix (defaults to "Algorithm") for captions.
The default values of these options are:
```js
var DEFAULT_OPTIONS = {
captionCount: undefined,
commentDelimiter: '//',
indentSize: '1.2em',
lineNumber: false,
lineNumberPunc: ':',
noEnd: false,
scopeLines: false,
titlePrefix: 'Algorithm'
};
```
#### Per-Element Options
The above-mentioned global options may be overridden on a per-element basis
using [HTML `data-*` attributes](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset)
on the `
` DOM element.
The following example demonstrates how to enable line numbers and change title prefix:
```html
...
```
## Build and Test
pseudocode.js is written in JavaScript and built with [Node.js](https://nodejs.org).
So, make sure you have Node.js installed before building pseudocode.js.
To compile the project on Ubuntu Linux, run the following commands in terminal:
```bash
cd pseudocode.js/
npm install
make
```
Then, open one of the sample documents:
- `build/katex-samples.html`, or
- `build/mathjax-v2-samples.html`, or
- `build/mathjax-v3-samples.html`
in your favorite browser to check if the algorithms are typeset correctly.
## Author
pseudocode.js was originally written by Tate Tian ([@tatetian](https://github.com/tatetian)).
Together with [@ZJUGuoShuai](https://github.com/ZJUGuoShuai),
I ([@SaswatPadhi](https://github.com/SaswatPadhi)) added the MathJax support,
and I am the current maintainer of this project.
Suggestions, bug reports and pull requests are most welcome.
## Acknowledgement
pseudocode.js is partially inspired by [KaTeX](http://khan.github.io/KaTeX/).
Thanks Emily Eisenberg([@xymostech](https://github.com/xymostech))
and other contributors for building such a wonderful project.
================================================
FILE: docs/index.html
================================================
pseudocode.js
pseudocode.js enables JavaScript to typeset algorithms as beautifully
as LaTeX does:
The demo above is editable. Feel free to experiment with it by clicking
on the edit button.
Features
pseudocode.js is a JavaScript library that typesets pseudocode
beautifully to HTML:
Intuitive grammar:
pseudocode.js takes a LaTeX-style input that supports the
algorithmic constructs from LaTeX's algorithm packages. With or
without LaTeX experience, a user should find the grammar fairly intuitive.
Print quality:
The HTML output produced by pseudocode.js is (almost) identical
with the pretty algorithms printed on publications that
are typeset by LaTeX.
Math formula support:
Inserting math formulas in pseudocode.js is as easy as LaTeX. Just
enclose math expression in $...$ or
\(...\).
It supports all modern browsers, including Chrome, Safari, Firefox, Opera, and Edge.
Usage
Pseudocode.js supports multiple backends to render math formulas.
If you want to include any math formulas in your pseudocode,
please make sure that either KaTeX
or MathJax
is properly setup in your document.
Download pseudocode.js,
and host the files on your server. And then include the js and css files in your HTML files:
pseudocode.js was originally written by Tate Tian (@tatetian).
Together with @ZJUGuoShuai,
I (@SaswatPadhi) added the MathJax support,
and I am the current maintainer of this project.
Suggestions, bug reports and pull requests are most welcome.
Acknowledgement
Pseudocode.js is partially inspired by KaTeX
and relies on it to render math formulas.
Thanks Emily Eisenberg(@xymostech)
and other contributers for building such a wonderful project.
================================================
FILE: docs/javascripts/scale.fix.js
================================================
var metas = document.getElementsByTagName('meta');
var i;
if (navigator.userAgent.match(/iPhone/i)) {
for (i=0; ipseudocode.js Samples with KaTeX
\begin{algorithm}
\caption{Test text-style}
\begin{algorithmic}
\REQUIRE some preconditions
\ENSURE some postconditions
\INPUT some inputs
\OUTPUT some outputs
\PROCEDURE{Test-Declarations}{}
\STATE font families: {\sffamily sffamily, \ttfamily ttfamily, \normalfont normalfont, \rmfamily rmfamily.}
\STATE font weights: {normal weight, \bfseries bold, \mdseries
medium, \lfseries lighter. }
\STATE font shapes: {\itshape itshape \scshape Small-Caps \slshape slshape \upshape upshape.}
\STATE font sizes: \tiny tiny \scriptsize scriptsize \footnotesize
footnotesize \small small \normalsize normal \large large \Large Large
\LARGE LARGE \huge huge \Huge Huge \normalsize
\ENDPROCEDURE
\PROCEDURE{Test-Commands}{}
\STATE \textnormal{textnormal,} \textrm{textrm,} \textsf{textsf,} \texttt{texttt.}
\STATE \textbf{textbf,} \textmd{textmd,} \textlf{textlf.}
\STATE \textup{textup,} \textit{textit,} \textsc{textsc,} \textsl{textsl.}
\STATE \uppercase{uppercase,} \lowercase{LOWERCASE.}
\ENDPROCEDURE
\PROCEDURE{Test-Colors}{}
\STATE colors: $\color{red}{red}$, $\color{green}{green}$, $\color{blue}{blue}$
\STATE colors: $\color{yellow}{yellow}$, $\color{cyan}{cyan}$, $\color{magenta}{magenta}$
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
\begin{algorithm}
\caption{Test atoms}
\begin{algorithmic}
\STATE \textbf{Specials:} \{ \} \$ \& \# \% \_
\STATE \textbf{Bools:} \AND \OR \NOT \TRUE \FALSE
\STATE \textbf{Carriage return:} first line \\ second line
\STATE \textbf{Text-symbols:} \textbackslash
\STATE \textbf{Quote-symbols:} `single quotes', ``double quotes''
\STATE \textbf{Math:} $(\mathcal{C}_m)$, $i \gets i + 1$, $E=mc^2$, \( x^n + y^n = z^n \), $\$$, \(\$\)
\END{ALGORITHMIC}
\END{ALGORITHM}
\begin{algorithm}
\caption{DBSCAN}
\begin{algorithmic}
\INPUT A dataset $D$, the $\varepsilon$ distance threshold, and the minimum number of points $minPts$
\OUTPUT A set of clusters $K$
\PROCEDURE{DBSCAN}{$D, \varepsilon, minPts$}
\STATE $K \gets \emptyset$
\FORALL{$p \in D$}
\IF{$p$ has not been visited}
\STATE mark $p$ as visited
\STATE $N_{\varepsilon}(p) \gets $ \textsc{RangeQuery}$(p, \varepsilon, D)$
\IF{$N_{\varepsilon}(p) < minPts$}
\STATE mark $p$ as noise
\ELSE
\COMMENT{p is a core object}
\STATE $C \gets $ \textsc{ExpandCluster}$(p, N_{\varepsilon}(p))$
\STATE $K \gets K \cup \{C\}$
\ENDIF
\ENDIF
\ENDFOR
\RETURN $K$
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
================================================
FILE: docs/mathjax-v2-samples.html
================================================
pseudocode.js Samples with MathJax
\begin{algorithm}
\caption{Test text-style}
\begin{algorithmic}
\REQUIRE some preconditions
\ENSURE some postconditions
\INPUT some inputs
\OUTPUT some outputs
\PROCEDURE{Test-Declarations}{}
\STATE font families: {\sffamily sffamily, \ttfamily ttfamily, \normalfont normalfont, \rmfamily rmfamily.}
\STATE font weights: {normal weight, \bfseries bold, \mdseries
medium, \lfseries lighter. }
\STATE font shapes: {\itshape itshape \scshape Small-Caps \slshape slshape \upshape upshape.}
\STATE font sizes: \tiny tiny \scriptsize scriptsize \footnotesize
footnotesize \small small \normalsize normal \large large \Large Large
\LARGE LARGE \huge huge \Huge Huge \normalsize
\ENDPROCEDURE
\PROCEDURE{Test-Commands}{}
\STATE \textnormal{textnormal,} \textrm{textrm,} \textsf{textsf,} \texttt{texttt.}
\STATE \textbf{textbf,} \textmd{textmd,} \textlf{textlf.}
\STATE \textup{textup,} \textit{textit,} \textsc{textsc,} \textsl{textsl.}
\STATE \uppercase{uppercase,} \lowercase{LOWERCASE.}
\ENDPROCEDURE
\PROCEDURE{Test-Colors}{}
\STATE colors: $\color{red}{red}$, $\color{green}{green}$, $\color{blue}{blue}$
\STATE colors: $\color{yellow}{yellow}$, $\color{cyan}{cyan}$, $\color{magenta}{magenta}$
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
\begin{algorithm}
\caption{Test atoms}
\begin{algorithmic}
\STATE \textbf{Specials:} \{ \} \$ \& \# \% \_
\STATE \textbf{Bools:} \AND \OR \NOT \TRUE \FALSE
\STATE \textbf{Carriage return:} first line \\ second line
\STATE \textbf{Text-symbols:} \textbackslash
\STATE \textbf{Quote-symbols:} `single quotes', ``double quotes''
\STATE \textbf{Math:} $(\mathcal{C}_m)$, $i \gets i + 1$, $E=mc^2$, \( x^n + y^n = z^n \), $\$$, \(\$\)
\END{ALGORITHMIC}
\END{ALGORITHM}
\begin{algorithm}
\caption{DBSCAN}
\begin{algorithmic}
\INPUT A dataset $D$, the $\varepsilon$ distance threshold, and the minimum number of points $minPts$
\OUTPUT A set of clusters $K$
\PROCEDURE{DBSCAN}{$D, \varepsilon, minPts$}
\STATE $K \gets \emptyset$
\FORALL{$p \in D$}
\IF{$p$ has not been visited}
\STATE mark $p$ as visited
\STATE $N_{\varepsilon}(p) \gets $ \textsc{RangeQuery}$(p, \varepsilon, D)$
\IF{$N_{\varepsilon}(p) < minPts$}
\STATE mark $p$ as noise
\ELSE
\COMMENT{p is a core object}
\STATE $C \gets $ \textsc{ExpandCluster}$(p, N_{\varepsilon}(p))$
\STATE $K \gets K \cup \{C\}$
\ENDIF
\ENDIF
\ENDFOR
\RETURN $K$
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
================================================
FILE: docs/mathjax-v3-samples.html
================================================
pseudocode.js Samples with MathJax
\begin{algorithm}
\caption{Test text-style}
\begin{algorithmic}
\REQUIRE some preconditions
\ENSURE some postconditions
\INPUT some inputs
\OUTPUT some outputs
\PROCEDURE{Test-Declarations}{}
\STATE font families: {\sffamily sffamily, \ttfamily ttfamily, \normalfont normalfont, \rmfamily rmfamily.}
\STATE font weights: {normal weight, \bfseries bold, \mdseries
medium, \lfseries lighter. }
\STATE font shapes: {\itshape itshape \scshape Small-Caps \slshape slshape \upshape upshape.}
\STATE font sizes: \tiny tiny \scriptsize scriptsize \footnotesize
footnotesize \small small \normalsize normal \large large \Large Large
\LARGE LARGE \huge huge \Huge Huge \normalsize
\ENDPROCEDURE
\PROCEDURE{Test-Commands}{}
\STATE \textnormal{textnormal,} \textrm{textrm,} \textsf{textsf,} \texttt{texttt.}
\STATE \textbf{textbf,} \textmd{textmd,} \textlf{textlf.}
\STATE \textup{textup,} \textit{textit,} \textsc{textsc,} \textsl{textsl.}
\STATE \uppercase{uppercase,} \lowercase{LOWERCASE.}
\ENDPROCEDURE
\PROCEDURE{Test-Colors}{}
\STATE colors: $\color{red}{red}$, $\color{green}{green}$, $\color{blue}{blue}$
\STATE colors: $\color{yellow}{yellow}$, $\color{cyan}{cyan}$, $\color{magenta}{magenta}$
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
\begin{algorithm}
\caption{Test atoms}
\begin{algorithmic}
\STATE \textbf{Specials:} \{ \} \$ \& \# \% \_
\STATE \textbf{Bools:} \AND \OR \NOT \TRUE \FALSE
\STATE \textbf{Carriage return:} first line \\ second line
\STATE \textbf{Text-symbols:} \textbackslash
\STATE \textbf{Quote-symbols:} `single quotes', ``double quotes''
\STATE \textbf{Math:} $(\mathcal{C}_m)$, $i \gets i + 1$, $E=mc^2$, \( x^n + y^n = z^n \), $\$$, \(\$\)
\END{ALGORITHMIC}
\END{ALGORITHM}
\begin{algorithm}
\caption{DBSCAN}
\begin{algorithmic}
\INPUT A dataset $D$, the $\varepsilon$ distance threshold, and the minimum number of points $minPts$
\OUTPUT A set of clusters $K$
\PROCEDURE{DBSCAN}{$D, \varepsilon, minPts$}
\STATE $K \gets \emptyset$
\FORALL{$p \in D$}
\IF{$p$ has not been visited}
\STATE mark $p$ as visited
\STATE $N_{\varepsilon}(p) \gets $ \textsc{RangeQuery}$(p, \varepsilon, D)$
\IF{$N_{\varepsilon}(p) < minPts$}
\STATE mark $p$ as noise
\ELSE
\COMMENT{p is a core object}
\STATE $C \gets $ \textsc{ExpandCluster}$(p, N_{\varepsilon}(p))$
\STATE $K \gets K \cup \{C\}$
\ENDIF
\ENDIF
\ENDFOR
\RETURN $K$
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
================================================
FILE: docs/mathjax-v4-samples.html
================================================
pseudocode.js Samples with MathJax
\begin{algorithm}
\caption{Test text-style}
\begin{algorithmic}
\REQUIRE some preconditions
\ENSURE some postconditions
\INPUT some inputs
\OUTPUT some outputs
\PROCEDURE{Test-Declarations}{}
\STATE font families: {\sffamily sffamily, \ttfamily ttfamily, \normalfont normalfont, \rmfamily rmfamily.}
\STATE font weights: {normal weight, \bfseries bold, \mdseries
medium, \lfseries lighter. }
\STATE font shapes: {\itshape itshape \scshape Small-Caps \slshape slshape \upshape upshape.}
\STATE font sizes: \tiny tiny \scriptsize scriptsize \footnotesize
footnotesize \small small \normalsize normal \large large \Large Large
\LARGE LARGE \huge huge \Huge Huge \normalsize
\ENDPROCEDURE
\PROCEDURE{Test-Commands}{}
\STATE \textnormal{textnormal,} \textrm{textrm,} \textsf{textsf,} \texttt{texttt.}
\STATE \textbf{textbf,} \textmd{textmd,} \textlf{textlf.}
\STATE \textup{textup,} \textit{textit,} \textsc{textsc,} \textsl{textsl.}
\STATE \uppercase{uppercase,} \lowercase{LOWERCASE.}
\ENDPROCEDURE
\PROCEDURE{Test-Colors}{}
\STATE colors: $\color{red}{red}$, $\color{green}{green}$, $\color{blue}{blue}$
\STATE colors: $\color{yellow}{yellow}$, $\color{cyan}{cyan}$, $\color{magenta}{magenta}$
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
\begin{algorithm}
\caption{Test atoms}
\begin{algorithmic}
\STATE \textbf{Specials:} \{ \} \$ \& \# \% \_
\STATE \textbf{Bools:} \AND \OR \NOT \TRUE \FALSE
\STATE \textbf{Carriage return:} first line \\ second line
\STATE \textbf{Text-symbols:} \textbackslash
\STATE \textbf{Quote-symbols:} `single quotes', ``double quotes''
\STATE \textbf{Math:} $(\mathcal{C}_m)$, $i \gets i + 1$, $E=mc^2$, \( x^n + y^n = z^n \), $\$$, \(\$\)
\END{ALGORITHMIC}
\END{ALGORITHM}
\begin{algorithm}
\caption{DBSCAN}
\begin{algorithmic}
\INPUT A dataset $D$, the $\varepsilon$ distance threshold, and the minimum number of points $minPts$
\OUTPUT A set of clusters $K$
\PROCEDURE{DBSCAN}{$D, \varepsilon, minPts$}
\STATE $K \gets \emptyset$
\FORALL{$p \in D$}
\IF{$p$ has not been visited}
\STATE mark $p$ as visited
\STATE $N_{\varepsilon}(p) \gets $ \textsc{RangeQuery}$(p, \varepsilon, D)$
\IF{$N_{\varepsilon}(p) < minPts$}
\STATE mark $p$ as noise
\ELSE
\COMMENT{p is a core object}
\STATE $C \gets $ \textsc{ExpandCluster}$(p, N_{\varepsilon}(p))$
\STATE $K \gets K \cup \{C\}$
\ENDIF
\ENDIF
\ENDFOR
\RETURN $K$
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
================================================
FILE: docs/params.json
================================================
{
"name": "pseudocode.js",
"tagline": "Beautiful TeX-style pseudocode for the Web",
"body": "### Welcome to GitHub Pages.\r\nThis automatic page generator is the easiest way to create beautiful pages for all of your projects. Author your page content here using GitHub Flavored Markdown, select a template crafted by a designer, and publish. After your page is generated, you can check out the new branch:\r\n\r\n```\r\n$ cd your_repo_root/repo_name\r\n$ git fetch origin\r\n$ git checkout gh-pages\r\n```\r\n\r\nIf you're using the GitHub for Mac, simply sync your repository and you'll see the new branch.\r\n\r\n### Designer Templates\r\nWe've crafted some handsome templates for you to use. Go ahead and continue to layouts to browse through them. You can easily go back to edit your page before publishing. After publishing your page, you can revisit the page generator and switch to another theme. Your Page content will be preserved if it remained markdown format.\r\n\r\n### Rather Drive Stick?\r\nIf you prefer to not use the automatic generator, push a branch named `gh-pages` to your repository to create a page manually. In addition to supporting regular HTML content, GitHub Pages support Jekyll, a simple, blog aware static site generator written by our own Tom Preston-Werner. Jekyll makes it easy to create site-wide headers and footers without having to copy them across every page. It also offers intelligent blog support and other advanced templating features.\r\n\r\n### Authors and Contributors\r\nYou can @mention a GitHub username to generate a link to their profile. The resulting `` element will link to the contributor's GitHub Profile. For example: In 2007, Chris Wanstrath (@defunkt), PJ Hyett (@pjhyett), and Tom Preston-Werner (@mojombo) founded GitHub.\r\n\r\n### Support or Contact\r\nHaving trouble with Pages? Check out the documentation at https://help.github.com/pages or contact support@github.com and we’ll help you sort it out.\r\n",
"google": "",
"note": "Don't delete this file! It's used internally to help with page regeneration."
}
================================================
FILE: docs/pseudocode.css
================================================
@import url(https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.css);.ps-root{font-family:KaTeX_Main,'Times New Roman',Times,serif;font-size:1em;font-weight:100;-webkit-font-smoothing:antialiased!important}.ps-root .ps-algorithm{margin:.8em 0;border-top:3px solid #000;border-bottom:2px solid #000}.ps-root .ps-algorithm.with-caption>.ps-line:first-child{border-bottom:2px solid #000}.ps-root .katex{text-indent:0;font-size:1em}.ps-root .MathJax,.ps-root .MathJax_CHTML{text-indent:0;font-size:1em!important}.ps-root .ps-line{margin:0;padding:0;line-height:1.2}.ps-root .ps-funcname{font-family:KaTeX_Main,'Times New Roman',Times,serif;font-weight:400;font-variant:small-caps;font-style:normal;text-transform:none}.ps-root .ps-keyword{font-family:KaTeX_Main,'Times New Roman',Times,serif;font-weight:700;font-variant:normal;font-style:normal;text-transform:none}.ps-root .ps-comment{font-family:KaTeX_Main,'Times New Roman',Times,serif;font-weight:400;font-variant:normal;font-style:normal;text-transform:none}.ps-root .ps-linenum{font-size:.8em;line-height:1em;width:1.6em;text-align:right;display:inline-block;position:relative;padding-right:.3em}.ps-root .ps-algorithmic.with-linenum .ps-line.ps-code{text-indent:-1.6em}.ps-root .ps-algorithmic.with-linenum .ps-line.ps-code>span{text-indent:0}.ps-root .ps-algorithmic.with-scopelines div.ps-block{border-left-style:solid;border-left-width:.1em;padding-left:.6em}.ps-root .ps-algorithmic.with-scopelines>div.ps-block{border-left:none}
================================================
FILE: docs/pseudocode.js
================================================
(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.pseudocode=e()}})(function(){var e,t,n;return function(){function p(o,s,a){function l(n,e){if(!s[n]){if(!o[n]){var t="function"==typeof require&&require;if(!e&&t)return t(n,!0);if(h)return h(n,!0);var i=new Error("Cannot find module '"+n+"'");throw i.code="MODULE_NOT_FOUND",i}var r=s[n]={exports:{}};o[n][0].call(r.exports,function(e){var t=o[n][1][e];return l(t||e)},r,r.exports,p,o,s,a)}return s[n].exports}for(var h="function"==typeof require&&require,e=0;e3)MathJax.typeset()}return r},renderToString:function(e,t){if(e===null||e===undefined)throw new ReferenceError("Input cannot be empty");var n=a(e,t);if(n.backend&&n.backend.name==="mathjax"&&n.backend.version<3){console.warn("`renderToString` is fully supported only on MathJax backend 3.x.\n"+"Math ($...$) will not be rendered to HTML and will be left as is.")}return n.toMarkup()},renderElement:function(e,t){if(!(e instanceof Element))throw new ReferenceError("A DOM element is required");e.style.display="none";var n=JSON.parse(JSON.stringify(t||{}));for(const o in e.dataset)n[o]=e.dataset[o];var i=a(e.textContent,n);var r=i.toDOM();e.replaceWith(r);if(i.backend&&i.backend.name==="mathjax"){if(MathJax.version<3)MathJax.Hub.Queue(["Typeset",MathJax.Hub,e]);else if(MathJax.version>3)MathJax.typeset()}},renderClass:function(e,t){[...document.getElementsByClassName(e)].forEach(e=>this.renderElement(e,t))}}},{"./src/Lexer":2,"./src/ParseError":3,"./src/Parser":4,"./src/Renderer":5}],2:[function(e,t,n){var i=e("./utils");var u=e("./ParseError");var r=function(e){this._input=e;this._remain=e;this._pos=0;this._nextAtom=this._currentAtom=null;this._next()};r.prototype.accept=function(e,t){if(this._nextAtom.type===e&&this._matchText(t)){this._next();return this._currentAtom.text}return null};r.prototype.expect=function(e,t){var n=this._nextAtom;if(n.type!==e){throw new u(`Expected an atom of ${e} but received ${n.type}`,this._pos,this._input)}if(!this._matchText(t)){throw new u(`Expected \`${t}\` but received \`${n.text}\``,this._pos,this._input)}this._next();return this._currentAtom.text};r.prototype.get=function(){return this._currentAtom};var o={exec:function(e){var t=[{start:"$",end:"$"},{start:"\\(",end:"\\)"}];var n=e.length;for(var i=0;i0&&a[l-1]==="\\"){var h=l+o.length;a=a.slice(h);s+=h;continue}var p=[e.slice(0,s+l+o.length),e.slice(r.length,s+l)];return p}}return null}};var p={special:/^(\\\\|\\{|\\}|\\\$|\\&|\\#|\\%|\\_)/,math:o,func:/^\\([a-zA-Z]+)/,open:/^\{/,close:/^\}/,quote:/^(`|``|'|'')/,ordinary:/^[^\\{}$%_\s]+/};var c=/^%.*/;var f=/^\s+/;r.prototype._skip=function(e){this._pos+=e;this._remain=this._remain.slice(e)};r.prototype._next=function(){var e=false;while(1){var t=f.exec(this._remain);if(t){e=true;var n=t[0].length;this._skip(n)}var i=c.exec(this._remain);if(!i)break;var r=i[0].length;this._skip(r)}this._currentAtom=this._nextAtom;if(this._remain===""){this._nextAtom={type:"EOF",text:null,whitespace:false};return false}for(var o in p){var s=p[o];var a=s.exec(this._remain);if(!a)continue;var l=a[0];var h=a[1]?a[1]:l;this._nextAtom={type:o,text:h,whitespace:e};this._pos+=l.length;this._remain=this._remain.slice(a[0].length);return true}throw new u("Unrecoganizable atom",this._pos,this._input)};r.prototype._matchText=function(e){if(e===null||e===undefined)return true;if(i.isString(e))return e.toLowerCase()===this._nextAtom.text.toLowerCase();else return e.some(e=>e.toLowerCase()===this._nextAtom.text.toLowerCase())};t.exports=r},{"./ParseError":3,"./utils":6}],3:[function(e,t,n){function i(e,t,n){var i=`Error: ${e}`;if(t!==undefined&&n!==undefined){i+=` at position ${t}: \``;n=`${n.slice(0,t)}\u21B1${n.slice(t)}`;var r=Math.max(0,t-15);var o=t+15;i+=`${n.slice(r,o)}\``}this.message=i}i.prototype=Object.create(Error.prototype);i.prototype.constructor=i;t.exports=i},{}],4:[function(e,t,n){var s=e("./utils");var r=e("./ParseError");var a=function(e,t){this.type=e;this.value=t;this.children=[]};a.prototype.toString=function(e){if(!e)e=0;var t="";for(var n=0;n`;if(this.value)i+=` (${s.toString(this.value)})`;i+="\n";if(this.children){for(var r=0;r0){e.addChild(t);continue}break}return e};i.prototype._parseCaption=function(){var e=this._lexer;if(!e.accept("func","caption"))return null;var t=new a("caption");e.expect("open");t.addChild(this._parseCloseText());e.expect("close");return t};i.prototype._parseBlock=function(){var e=new a("block");while(true){var t=this._parseControl();if(t){e.addChild(t);continue}var n=this._parseFunction();if(n){e.addChild(n);continue}var i=this._parseStatement(h);if(i){e.addChild(i);continue}var r=this._parseCommand(p);if(r){e.addChild(r);continue}var o=this._parseComment();if(o){e.addChild(o);continue}break}return e};i.prototype._parseControl=function(){var e;if(e=this._parseIf())return e;if(e=this._parseLoop())return e;if(e=this._parseRepeat())return e;if(e=this._parseUpon())return e};i.prototype._parseFunction=function(){var e=this._lexer;if(!e.accept("func",["function","procedure"]))return null;var t=this._lexer.get().text;e.expect("open");var n=e.expect("ordinary");e.expect("close");e.expect("open");var i=this._parseCloseText();e.expect("close");var r=this._parseBlock();e.expect("func",`end${t}`);var o=new a("function",{type:t,name:n});o.addChild(i);o.addChild(r);return o};i.prototype._parseIf=function(){if(!this._lexer.accept("func","if"))return null;var e=new a("if");this._lexer.expect("open");e.addChild(this._parseCond());this._lexer.expect("close");e.addChild(this._parseBlock());var t=0;while(this._lexer.accept("func",["elif","elsif","elseif"])){this._lexer.expect("open");e.addChild(this._parseCond());this._lexer.expect("close");e.addChild(this._parseBlock());t++}var n=false;if(this._lexer.accept("func","else")){n=true;e.addChild(this._parseBlock())}this._lexer.expect("func","endif");e.value={numElif:t,hasElse:n};return e};i.prototype._parseLoop=function(){if(!this._lexer.accept("func",["FOR","FORALL","WHILE"]))return null;var e=this._lexer.get().text.toLowerCase();var t=new a("loop",e);this._lexer.expect("open");t.addChild(this._parseCond());this._lexer.expect("close");t.addChild(this._parseBlock());var n=e!=="forall"?`end${e}`:"endfor";this._lexer.expect("func",n);return t};i.prototype._parseRepeat=function(){if(!this._lexer.accept("func",["REPEAT"]))return null;var e=this._lexer.get().text.toLowerCase();var t=new a("repeat",e);t.addChild(this._parseBlock());this._lexer.expect("func","until");this._lexer.expect("open");t.addChild(this._parseCond());this._lexer.expect("close");return t};i.prototype._parseUpon=function(){if(!this._lexer.accept("func","upon"))return null;var e=new a("upon");this._lexer.expect("open");e.addChild(this._parseCond());this._lexer.expect("close");e.addChild(this._parseBlock());this._lexer.expect("func","endupon");return e};var l=["ensure","require","input","output"];var h=["state","print","return"];i.prototype._parseStatement=function(e){if(!this._lexer.accept("func",e))return null;var t=this._lexer.get().text.toLowerCase();var n=new a("statement",t);n.addChild(this._parseOpenText());return n};var p=["break","continue"];i.prototype._parseCommand=function(e){if(!this._lexer.accept("func",e))return null;var t=this._lexer.get().text.toLowerCase();var n=new a("command",t);return n};i.prototype._parseComment=function(){if(!this._lexer.accept("func","comment"))return null;var e=new a("comment");this._lexer.expect("open");e.addChild(this._parseCloseText());this._lexer.expect("close");return e};i.prototype._parseCall=function(){var e=this._lexer;if(!e.accept("func","call"))return null;var t=e.get().whitespace;e.expect("open");var n=e.expect("ordinary");e.expect("close");var i=new a("call");i.whitespace=t;i.value=n;e.expect("open");var r=this._parseCloseText();i.addChild(r);e.expect("close");return i};i.prototype._parseCond=i.prototype._parseCloseText=function(){return this._parseText("close")};i.prototype._parseOpenText=function(){return this._parseText("open")};i.prototype._parseText=function(e){var t=new a(`${e}-text`);var n=false;var i;while(true){i=this._parseAtom()||this._parseCall();if(i){if(n)i.whitespace|=n;t.addChild(i);continue}if(this._lexer.accept("open")){i=this._parseCloseText();n=this._lexer.get().whitespace;i.whitespace=n;t.addChild(i);this._lexer.expect("close");n=this._lexer.get().whitespace;continue}break}return t};var u={ordinary:{tokenType:"ordinary"},math:{tokenType:"math"},special:{tokenType:"special"},"cond-symbol":{tokenType:"func",tokenValues:["and","or","not","true","false","to","downto"]},"quote-symbol":{tokenType:"quote"},"sizing-dclr":{tokenType:"func",tokenValues:["tiny","scriptsize","footnotesize","small","normalsize","large","Large","LARGE","huge","Huge"]},"font-dclr":{tokenType:"func",tokenValues:["normalfont","rmfamily","sffamily","ttfamily","upshape","itshape","slshape","scshape","bfseries","mdseries","lfseries"]},"font-cmd":{tokenType:"func",tokenValues:["textnormal","textrm","textsf","texttt","textup","textit","textsl","textsc","uppercase","lowercase","textbf","textmd","textlf"]},"text-symbol":{tokenType:"func",tokenValues:["textbackslash"]}};i.prototype._parseAtom=function(){for(var e in u){var t=u[e];var n=this._lexer.accept(t.tokenType,t.tokenValues);if(n===null)continue;var i=this._lexer.get().whitespace;if(e!=="ordinary"&&e!=="math")n=n.toLowerCase();return new o(e,n,i)}return null};t.exports=i},{"./ParseError":3,"./utils":6}],5:[function(n,e,t){var a=n("./utils");function A(e){this._css={};this._fontSize=this._outerFontSize=e!==undefined?e:1}A.prototype.outerFontSize=function(e){if(e!==undefined)this._outerFontSize=e;return this._outerFontSize};A.prototype.fontSize=function(){return this._fontSize};A.prototype._fontCommandTable={normalfont:{"font-family":"KaTeX_Main"},rmfamily:{"font-family":"KaTeX_Main"},sffamily:{"font-family":"KaTeX_SansSerif"},ttfamily:{"font-family":"KaTeX_Typewriter"},bfseries:{"font-weight":"bold"},mdseries:{"font-weight":"medium"},lfseries:{"font-weight":"lighter"},upshape:{"font-style":"normal","font-variant":"normal"},itshape:{"font-style":"italic","font-variant":"normal"},scshape:{"font-style":"normal","font-variant":"small-caps"},slshape:{"font-style":"oblique","font-variant":"normal"},textnormal:{"font-family":"KaTeX_Main"},textrm:{"font-family":"KaTeX_Main"},textsf:{"font-family":"KaTeX_SansSerif"},texttt:{"font-family":"KaTeX_Typewriter"},textbf:{"font-weight":"bold"},textmd:{"font-weight":"medium"},textlf:{"font-weight":"lighter"},textup:{"font-style":"normal","font-variant":"normal"},textit:{"font-style":"italic","font-variant":"normal"},textsc:{"font-style":"normal","font-variant":"small-caps"},textsl:{"font-style":"oblique","font-variant":"normal"},uppercase:{"text-transform":"uppercase"},lowercase:{"text-transform":"lowercase"}};A.prototype._sizingScalesTable={tiny:.68,scriptsize:.8,footnotesize:.85,small:.92,normalsize:1,large:1.17,Large:1.41,LARGE:1.58,huge:1.9,Huge:2.28};A.prototype.updateByCommand=function(e){var t=this._fontCommandTable[e];if(t!==undefined){for(var n in t)this._css[n]=t[n];return}var i=this._sizingScalesTable[e];if(i!==undefined){this._outerFontSize=this._fontSize;this._fontSize=i;return}throw new ParserError("Unrecognized `text-style` command")};A.prototype.toCSS=function(){var e="";for(var t in this._css){var n=this._css[t];if(n===undefined)continue;e+=`${t}:${n};`}if(this._fontSize!==this._outerFontSize)e+=`font-size:${this._fontSize/this._outerFontSize}em;`;return e};function B(e,t){this._nodes=e;this._textStyle=t}B.prototype._renderCloseText=function(e,t){var n=new A(this._textStyle.fontSize());var i=new B(e.children,n);if(e.whitespace)this._html.putText(" ");this._html.putHTML(i.renderToHTML(t))};B.prototype.renderToHTML=function(e){this._html=new _;var t;while((t=this._nodes.shift())!==undefined){var n=t.type;var i=t.value;if(t.whitespace)this._html.putText(" ");switch(n){case"ordinary":this._html.putText(i);break;case"math":if(typeof e==="undefined"){throw EvalError("No math backend found. Please setup KaTeX or MathJax.")}else if(e.name==="katex"){this._html.putHTML(e.driver.renderToString(i))}else if(e.name==="mathjax"){if(e.version===3){this._html.putHTML(e.driver.tex2chtml(i,{display:false}).outerHTML)}else{this._html.putText(`$${i}$`)}}else{throw new EvalError(`Unknown math backend ${e}`)}break;case"cond-symbol":this._html.beginSpan("ps-keyword").putText(i.toLowerCase()).endSpan();break;case"special":if(i==="\\\\"){this._html.putHTML(" ");break}var r={"\\{":"{","\\}":"}","\\$":"$","\\&":"&","\\#":"#","\\%":"%","\\_":"_"};var o=r[i];this._html.putText(o);break;case"text-symbol":var s={textbackslash:"\\"};var a=s[i];this._html.putText(a);break;case"quote-symbol":var l={"`":"\u2018","``":"\u201c","'":"\u2019","''":"\u201d"};var h=l[i];this._html.putText(h);break;case"call":this._html.beginSpan("ps-funcname").putText(i).endSpan();this._html.write("(");var p=t.children[0];this._renderCloseText(p,e);this._html.write(")");break;case"close-text":this._renderCloseText(t,e);break;case"font-dclr":case"sizing-dclr":this._textStyle.updateByCommand(i);this._html.beginSpan(null,this._textStyle.toCSS());var u=new B(this._nodes,this._textStyle);this._html.putHTML(u.renderToHTML(e));this._html.endSpan();break;case"font-cmd":var c=this._nodes[0];if(c.type!=="close-text")continue;var f=new A(this._textStyle.fontSize());f.updateByCommand(i);this._html.beginSpan(null,f.toCSS());var d=new B(c.children,f);this._html.putHTML(d.renderToHTML(e));this._html.endSpan();break;default:throw new ParseError(`Unexpected ParseNode of type ${t.type}`)}}return this._html.toMarkup()};function _(){this._body=[];this._textBuf=[]}_.prototype.beginDiv=function(e,t,n){this._beginTag("div",e,t,n);this._body.push("\n");return this};_.prototype.endDiv=function(){this._endTag("div");this._body.push("\n");return this};_.prototype.beginP=function(e,t,n){this._beginTag("p",e,t,n);this._body.push("\n");return this};_.prototype.endP=function(){this._flushText();this._endTag("p");this._body.push("\n");return this};_.prototype.beginSpan=function(e,t,n){this._flushText();return this._beginTag("span",e,t,n)};_.prototype.endSpan=function(){this._flushText();return this._endTag("span")};_.prototype.putHTML=function(e){this._flushText();this._body.push(e);return this};_.prototype.putText=function(e){this._textBuf.push(e);return this};_.prototype.write=function(e){this._body.push(e)};_.prototype.toMarkup=function(){this._flushText();var e=this._body.join("");return e.trim()};_.prototype.toDOM=function(){var e=this.toMarkup();var t=document.createElement("div");t.innerHTML=e;return t.firstChild};_.prototype._flushText=function(){if(this._textBuf.length===0)return;var e=this._textBuf.join("");this._body.push(this._escapeHtml(e));this._textBuf=[]};_.prototype._beginTag=function(e,t,n,i){var r=`<${e}`;if(t)r+=` class="${t}"`;if(n){var o;if(a.isString(n)){o=n}else{o="";for(var s in n){attrVal=n[s];o+=`${s}:${attrVal};`}}if(i)o+=i;r+=` style="${o}"`}r+=">";this._body.push(r);return this};_.prototype._endTag=function(e){this._body.push(`${e}>`);return this};var i={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};_.prototype._escapeHtml=function(e){return String(e).replace(/[&<>"'/]/g,e=>i[e])};function r(e){e=e||{};this.indentSize=e.indentSize?this._parseEmVal(e.indentSize):1.2;this.commentDelimiter=e.commentDelimiter!==undefined?e.commentDelimiter:" // ";this.lineNumberPunc=e.lineNumberPunc!==undefined?e.lineNumberPunc:":";this.lineNumber=e.lineNumber!==undefined?e.lineNumber:false;this.noEnd=e.noEnd!==undefined?e.noEnd:false;this.scopeLines=e.scopeLines!==undefined?e.scopeLines:false;if(e.captionCount!==undefined)F.captionCount=e.captionCount;this.titlePrefix=e.titlePrefix!==undefined?e.titlePrefix:"Algorithm"}r.prototype._parseEmVal=function(e){e=e.trim();if(e.indexOf("em")!==e.length-2)throw new TypeError("Unit error; expected `em` suffix");return Number(e.substring(0,e.length-2))};function F(e,t){this._root=e.parse();this._options=new r(t);this._openLine=false;this._blockLevel=0;this._textLevel=-1;this._globalTextStyle=new A;this.backend=undefined;try{if(typeof katex==="undefined")katex=n("katex")}catch(e){}try{if(typeof MathJax==="undefined")MathJax=n("mathjax")}catch(e){}if(typeof katex!=="undefined"){this.backend={name:"katex",driver:katex}}else if(typeof MathJax!=="undefined"){this.backend={name:"mathjax",version:parseInt(MathJax.version.split(".")[0]),driver:MathJax}}}F.captionCount=0;F.prototype.toMarkup=function(){var e=this._html=new _;this._buildTree(this._root);delete this._html;return e.toMarkup()};F.prototype.toDOM=function(){var e=this.toMarkup();var t=document.createElement("div");t.innerHTML=e;return t.firstChild};F.prototype._beginGroup=function(e,t,n){this._closeLineIfAny();this._html.beginDiv(`ps-${e}${t?` ${t}`:""}`,n)};F.prototype._endGroup=function(e){this._closeLineIfAny();this._html.endDiv()};F.prototype._beginBlock=function(){var e=this._options.lineNumber&&this._blockLevel===0?.6:0;var t=this._options.indentSize+e;if(this._options.scopeLines)t/=2;this._beginGroup("block",null,{"margin-left":`${t}em`});this._blockLevel++};F.prototype._endBlock=function(){this._closeLineIfAny();this._endGroup();this._blockLevel--};F.prototype._newLine=function(){this._closeLineIfAny();this._openLine=true;this._globalTextStyle.outerFontSize(1);var e=this._options.indentSize;if(this._blockLevel>0){this._numLOC++;this._html.beginP("ps-line ps-code",this._globalTextStyle.toCSS());var t=this._options.lineNumber?e*1.25:0;t+=this._options.scopeLines?e*.1:0;if(this._options.lineNumber){this._html.beginSpan("ps-linenum",{left:`${-((this._blockLevel-1)*t)}em`}).putText(this._numLOC+this._options.lineNumberPunc).endSpan()}}else{this._html.beginP("ps-line",{"text-indent":`${-e}em`,"padding-left":`${e}em`},this._globalTextStyle.toCSS())}};F.prototype._closeLineIfAny=function(){if(!this._openLine)return;this._html.endP();this._openLine=false};F.prototype._typeKeyword=function(e){this._html.beginSpan("ps-keyword").putText(e).endSpan()};F.prototype._typeFuncName=function(e){this._html.beginSpan("ps-funcname").putText(e).endSpan()};F.prototype._typeText=function(e){this._html.write(e)};F.prototype._buildTreeForAllChildren=function(e){var t=e.children;for(var n=0;n0&&t[0].type==="comment"){var n=t.shift();this._buildTree(n)}};F.prototype._buildTree=function(e){var t;var n;var i;switch(e.type){case"root":this._beginGroup("root");this._buildTreeForAllChildren(e);this._endGroup();break;case"algorithm":var r;for(t=0;ttypeof e==="string"||e instanceof String,isObject:e=>typeof e==="object"&&e instanceof Object,toString:function(e){if(!this.isObject(e))return`${e}`;var t=[];for(var n in e)t.push(`${n}: ${this.toString(e[n])}`);return t.join(", ")}}},{}]},{},[1])(1)});
================================================
FILE: docs/stylesheets/styles.css
================================================
body {
padding:50px;
font:14px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
color:#777;
font-weight:300;
}
iframe {
border: none;
width: 115%;
}
h1, h2, h3, h4, h5, h6 {
color:#222;
margin:0 0 20px;
font-weight: bold;
}
p, ul, ol, table, pre, dl {
margin:0 0 20px;
}
h1, h2, h3 {
line-height:1.1;
}
h1 {
font-size:28px;
}
h2 {
color:#393939;
}
h3, h4, h5, h6 {
color:#494949;
}
a {
color:#39c;
font-weight:400;
text-decoration:none;
}
a small {
font-size:11px;
color:#777;
margin-top:-0.6em;
display:block;
}
.wrapper {
width:860px;
margin:0 auto;
}
blockquote {
border-left:1px solid #e5e5e5;
margin:0;
padding:0 0 0 20px;
font-style:italic;
}
code, pre {
font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
color:#333;
font-size:0.85em;
}
pre {
border-radius:5px;
border:1px solid #e5e5e5;
font-size:0.75em;
}
table {
width:100%;
border-collapse:collapse;
}
th, td {
text-align:left;
padding:5px 10px;
border-bottom:1px solid #e5e5e5;
}
dt {
color:#444;
font-weight:700;
}
th {
color:#444;
}
img {
max-width:100%;
}
header {
width:270px;
float:left;
position:fixed;
}
header ul {
list-style:none;
height:40px;
padding:0;
background: #eee;
background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));
background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
border-radius:5px;
border:1px solid #d2d2d2;
box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;
width:270px;
}
header li {
width:89px;
float:left;
border-right:1px solid #d2d2d2;
height:40px;
}
header ul a {
line-height:1.15;
font-size:11px;
color:#999;
display:block;
text-align:center;
padding-top:6px;
height:40px;
}
strong {
color:#222;
font-weight:700;
}
header ul li:first-child{
width:88px;
border-left:1px solid #fff;
}
header ul li:last-child {
border-right:none;
width:89px;
}
header ul a strong {
font-size:14px;
display:block;
color:#222;
}
section {
width:500px;
float:right;
padding-bottom:50px;
}
small {
font-size:11px;
}
hr {
border:0;
background:#e5e5e5;
height:1px;
margin:0 0 20px;
}
footer {
width:270px;
float:left;
position:fixed;
bottom:50px;
}
@media print, screen and (max-width: 960px) {
div.wrapper {
width:auto;
margin:0;
}
header, section, footer {
float:none;
position:static;
width:auto;
}
header {
padding-right:320px;
}
section {
border:1px solid #e5e5e5;
border-width:1px 0;
padding:20px 0;
margin:0 0 20px;
}
header a small {
display:inline;
}
header ul {
position:absolute;
right:50px;
top:52px;
}
}
@media print, screen and (max-width: 720px) {
body {
word-wrap:break-word;
}
header {
padding:0;
}
header ul, header p.view {
position:static;
}
pre, code {
word-wrap:normal;
}
}
@media print, screen and (max-width: 480px) {
body {
padding:15px;
}
header ul {
display:none;
}
}
@media print {
body {
padding:0.4in;
font-size:12pt;
color:#444;
}
}
================================================
FILE: package.json
================================================
{
"name": "pseudocode",
"version": "2.4.1",
"author": {
"name": "Saswat Padhi",
"email": "saswat.sourav@gmail.com",
"url": "https://saswatpadhi.github.io/"
},
"description": "Beautiful pseudocode for the Web",
"main": "pseudocode.js",
"repository": {
"type": "git",
"url": "git://github.com/SaswatPadhi/pseudocode.js"
},
"files": [
"pseudocode.js",
"src/",
"build/pseudocode.min.css",
"build/pseudocode.min.js"
],
"dependencies": {
"katex": "^0.16.7"
},
"devDependencies": {
"browserify": "^17.0.0",
"clean-css-cli": "^5.3.0",
"eslint": "^8.45.0",
"uglify-js": "^3.17.4",
"watchify": "^4.0.0",
"clean-css": "^5.2.4"
},
"optionalDependencies": {
"mathjax": "^3.2.2"
},
"license": "MIT"
}
================================================
FILE: pseudocode.js
================================================
/*
* The entry points of pseudocode-js
**/
var ParseError = require('./src/ParseError');
var Lexer = require('./src/Lexer');
var Parser = require('./src/Parser');
var Renderer = require('./src/Renderer');
function makeRenderer (data, options) {
var lexer = new Lexer(data);
var parser = new Parser(lexer);
return new Renderer(parser, options);
}
module.exports = {
ParseError: ParseError,
render: function (input, baseDomEle, options) {
if (input === null || input === undefined)
throw new ReferenceError('Input cannot be empty');
var R = makeRenderer(input, options);
var elem = R.toDOM();
if (baseDomEle)
baseDomEle.appendChild(elem);
if (R.backend && R.backend.name === 'mathjax') {
if (MathJax.version < 3)
MathJax.Hub.Queue(["Typeset", MathJax.Hub, elem]);
else if (MathJax.version > 3)
MathJax.typeset();
// We use synchronous conversion in MathJax 3.x
}
return elem;
},
renderToString: function (input, options) {
if (input === null || input === undefined)
throw new ReferenceError('Input cannot be empty');
var R = makeRenderer(input, options);
if (R.backend && R.backend.name === 'mathjax' && R.backend.version < 3) {
console.warn('`renderToString` is fully supported only on MathJax backend 3.x.\n' +
'Math ($...$) will not be rendered to HTML and will be left as is.');
}
return R.toMarkup();
},
renderElement: function (elem, options) {
if (!(elem instanceof Element))
throw new ReferenceError('A DOM element is required');
elem.style.display = 'none';
var elemOptions = JSON.parse(JSON.stringify(options || {}));
for (const dataProp in elem.dataset)
elemOptions[dataProp] = elem.dataset[dataProp];
var R = makeRenderer(elem.textContent, elemOptions);
var newElem = R.toDOM();
elem.replaceWith(newElem);
if (R.backend && R.backend.name === 'mathjax') {
if (MathJax.version < 3)
MathJax.Hub.Queue(["Typeset", MathJax.Hub, elem]);
else if (MathJax.version > 3)
MathJax.typeset();
// We use synchronous conversion in MathJax 3.x
}
},
renderClass: function (className, options) {
[...document.getElementsByClassName(className)].forEach(
(el) => this.renderElement(el, options)
);
},
};
================================================
FILE: src/Lexer.js
================================================
/**
* The Lexer class tokenizes the input sequentially, looking ahead only one
* token.
*/
var utils = require('./utils');
var ParseError = require('./ParseError');
var Lexer = function (input) {
this._input = input;
this._remain = input;
this._pos = 0;
this._nextAtom = this._currentAtom = null;
this._next(); // get the next atom
};
Lexer.prototype.accept = function (type, text) {
if (this._nextAtom.type === type && this._matchText(text)) {
this._next();
return this._currentAtom.text;
}
return null;
};
Lexer.prototype.expect = function (type, text) {
var nextAtom = this._nextAtom;
// The next atom is NOT of the right type
if (nextAtom.type !== type) {
throw new ParseError(
`Expected an atom of ${type} but received ${nextAtom.type}`,
this._pos,
this._input
);
}
// Check whether the text is exactly the same
if (!this._matchText(text)) {
throw new ParseError(
`Expected \`${text}\` but received \`${nextAtom.text}\``,
this._pos,
this._input
);
}
this._next();
return this._currentAtom.text;
};
Lexer.prototype.get = function () {
return this._currentAtom;
};
/* Math pattern
Math environtment like $ $ or \( \) cannot be matched using regular
expression. This object simulates a RegEx object
*/
var mathPattern = {
exec: function (str) {
var delimiters = [
{ start: '$', end: '$' },
{ start: '\\(', end: '\\)' },
];
var totalLen = str.length;
for (var di = 0; di < delimiters.length; di++) {
var startDel = delimiters[di].start;
if (str.indexOf(startDel) !== 0) continue;
var endDel = delimiters[di].end;
var endPos = startDel.length;
var remain = str.slice(endPos);
while (endPos < totalLen) {
var pos = remain.indexOf(endDel);
if (pos < 0) {
throw new ParseError('Math environment is not closed',
this._pos, this._input);
}
// false positive, it's escaped, not a match
if (pos > 0 && remain[pos - 1] === '\\') {
var skipLen = pos + endDel.length;
remain = remain.slice(skipLen);
endPos += skipLen;
continue;
}
var res = [str.slice(0, endPos + pos + endDel.length),
str.slice(startDel.length, endPos + pos)];
return res;
}
}
return null;
},
};
var atomRegex = {
// TODO: which is correct? func: /^\\(?:[a-zA-Z]+|.)/,
special: /^(\\\\|\\{|\\}|\\\$|\\&|\\#|\\%|\\_)/,
math: mathPattern, ///^\$.*\$/
func: /^\\([a-zA-Z]+)/,
open: /^\{/,
close: /^\}/,
quote: /^(`|``|'|'')/,
ordinary: /^[^\\{}$%_\s]+/,
};
var commentRegex = /^%.*/;
var whitespaceRegex = /^\s+/;
Lexer.prototype._skip = function (len) {
this._pos += len;
this._remain = this._remain.slice(len);
};
/* Get the next atom */
Lexer.prototype._next = function () {
var anyWhitespace = false;
while (1) {
// Skip whitespace (one or more)
var whitespaceMatch = whitespaceRegex.exec(this._remain);
if (whitespaceMatch) {
anyWhitespace = true;
var whitespaceLen = whitespaceMatch[0].length;
this._skip(whitespaceLen);
}
// Skip comment
var commentMatch = commentRegex.exec(this._remain);
if (!commentMatch) break;
var commentLen = commentMatch[0].length;
this._skip(commentLen);
}
// Remember the current atom
this._currentAtom = this._nextAtom;
// Reach the end of string
if (this._remain === '') {
this._nextAtom = {
type: 'EOF',
text: null,
whitespace: false,
};
return false;
}
// Try all kinds of atoms
for (var type in atomRegex) {
var regex = atomRegex[type];
var match = regex.exec(this._remain);
if (!match) continue; // not matched
// match[1] is the useful part, e.g. '123' of '$123$', 'it' of '\\it'
var matchText = match[0];
var usefulText = match[1] ? match[1] : matchText;
this._nextAtom = {
type: type, /* special, func, open, close, ordinary, math */
text: usefulText, /* the text value of the atom */
whitespace: anyWhitespace, /* any whitespace before the atom */
};
this._pos += matchText.length;
this._remain = this._remain.slice(match[0].length);
return true;
}
throw new ParseError('Unrecoganizable atom', this._pos, this._input);
};
/* Check whether the text of the next atom matches */
Lexer.prototype._matchText = function (text) {
// don't need to match
if (text === null || text === undefined) return true;
// using case-insensitive comparisons,
// check if text is the same as next atom,
// or if text is an array that contains the next atom
if (utils.isString(text))
return text.toLowerCase() === this._nextAtom.text.toLowerCase();
else
return text.some((str) => str.toLowerCase() === this._nextAtom.text.toLowerCase());
};
module.exports = Lexer;
================================================
FILE: src/ParseError.js
================================================
function ParseError (message, pos, input) {
var error = `Error: ${message}`;
// If we have the input and a position, make the error a bit fancier
if (pos !== undefined && input !== undefined) {
error += ` at position ${pos}: \``;
// Insert a combining underscore at the correct position
input = `${input.slice(0, pos)}\u21B1${input.slice(pos)}`;
// Extract some context from the input and add it to the error
var begin = Math.max(0, pos - 15);
var end = pos + 15;
error += `${input.slice(begin, end)}\``;
}
this.message = error;
}
ParseError.prototype = Object.create(Error.prototype);
ParseError.prototype.constructor = ParseError;
module.exports = ParseError;
================================================
FILE: src/Parser.js
================================================
/**
* The Parser class parses the token stream from Lexer into an abstract syntax
* tree, represented by ParseNode.
*
* The grammar of pseudocode required by Pseudocode.js mimics that of TeX/Latex
* and its algorithm packages. It is designed intentionally to be less powerful
* than Tex/LaTeX for the convinience of implementation. As a consequence, the
* grammar is context-free, which can be expressed in production rules:
*
* :== ( | )[0..n]
*
* :== \begin{algorithm}
* (