├── .gitignore ├── test ├── full │ ├── mlkit.png │ ├── sml.pkg │ └── Makefile ├── sigs.sml ├── about.md └── Makefile ├── PARSE_ARG.sig ├── sigdoc.mlb ├── sml.pkg ├── Makefile ├── LICENSE ├── jslib ├── style.css └── jquery-ui.css ├── ParseArg.sml ├── README.md └── sigdoc.sml /.gitignore: -------------------------------------------------------------------------------- 1 | MLB 2 | sigdoc -------------------------------------------------------------------------------- /test/full/mlkit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diku-dk/sigdoc/HEAD/test/full/mlkit.png -------------------------------------------------------------------------------- /PARSE_ARG.sig: -------------------------------------------------------------------------------- 1 | signature PARSE_ARG = sig 2 | datatype t = Nullary of string * (unit -> unit) 3 | | Unary of string * (string -> unit) 4 | | Multi of string * (string list -> unit) 5 | val run : t list -> string list 6 | end 7 | -------------------------------------------------------------------------------- /sigdoc.mlb: -------------------------------------------------------------------------------- 1 | local 2 | $(SML_LIB)/basis/basis.mlb 3 | lib/github.com/diku-dk/sml-regexp/regexp.mlb 4 | lib/github.com/diku-dk/sml-sort/listsort.mlb 5 | lib/github.com/diku-dk/sml-cstring/cstring.mlb 6 | lib/github.com/diku-dk/sml-setmap/string_map.mlb 7 | PARSE_ARG.sig 8 | ParseArg.sml 9 | in sigdoc.sml 10 | end 11 | -------------------------------------------------------------------------------- /sml.pkg: -------------------------------------------------------------------------------- 1 | require { 2 | github.com/diku-dk/sml-cstring 0.1.0 #261ac586ddad1d9976a34f86bd12214940799fa7 3 | github.com/diku-dk/sml-setmap 0.1.1 #28f95bb08ecd19f719967c945f63f594b3c9fbc6 4 | github.com/diku-dk/sml-regexp 0.1.0 #f0d0891e746255d701cd9b17e8a6a6b76799c4f9 5 | github.com/diku-dk/sml-sort 0.2.0 #2cb5a201e9ad26e124b66aaafa57352c49f1405a 6 | } 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | MLCOMP ?= mlkit 3 | SMLPKG ?= smlpkg 4 | 5 | all: sigdoc 6 | 7 | sigdoc: sigdoc.mlb sigdoc.sml Makefile lib 8 | $(MLCOMP) -output sigdoc sigdoc.mlb 9 | 10 | install: 11 | cp -p sigdoc $(DESTDIR)/bin/ 12 | 13 | clean: 14 | rm -rf *~ MLB run sigdoc *.html 15 | $(MAKE) -C test clean 16 | $(MAKE) -C test/full clean 17 | 18 | realclean: 19 | $(MAKE) clean 20 | rm -rf lib 21 | 22 | lib: sml.pkg 23 | $(SMLPKG) sync 24 | -------------------------------------------------------------------------------- /test/sigs.sml: -------------------------------------------------------------------------------- 1 | 2 | (** A signature *) 3 | 4 | signature A = sig 5 | val a : int 6 | end 7 | 8 | (** 9 | 10 | [a] an integer. 11 | 12 | *) 13 | 14 | val hi = 45 (* a top-level binding *) 15 | 16 | structure K : A = 17 | struct val a = 6 end 18 | 19 | (** Another signature *) 20 | 21 | signature B = sig 22 | val b : int 23 | end 24 | 25 | (** 26 | 27 | [b] another integer. 28 | 29 | *) 30 | 31 | structure J : A = 32 | K 33 | 34 | structure S : B = 35 | struct val b = 34 end 36 | -------------------------------------------------------------------------------- /test/about.md: -------------------------------------------------------------------------------- 1 | This documentation tool provides documentation for the basis library 2 | subset implemented by the [MLKit](http://elsman.com/mlkit) and 3 | available packages, which may be fetched with the 4 | [smlpkg](http://github.com/diku-dk/smlpkg) package manager. 5 | 6 | Some of the documentation specified for modules in the basis library 7 | is borrowed from the [Standard ML Basis 8 | Library](https://smlfamily.github.io/Basis/index.html) 9 | documentation. A physical book specifying the Standard ML Basis 10 | Library is also available [1]. 11 | 12 | [1] Gansner, E., & Reppy, J. (Eds.). (2004). The Standard ML Basis 13 | Library. Cambridge: Cambridge University 14 | Press. [10.1017/CBO9780511546846](http://doi.org/10.1017/CBO9780511546846). 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Martin Elsman 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 | 23 | -------------------------------------------------------------------------------- /test/full/sml.pkg: -------------------------------------------------------------------------------- 1 | require { 2 | github.com/diku-dk/sml-uref 0.0.2 #6881e4ab970febf55d4e684753dfd173b2fb033d 3 | github.com/diku-dk/sml-pickle 0.1.0 #e9a405deb48865cc5ca1cea6fd8e79fbf9fc8851 4 | github.com/diku-dk/sml-setmap 0.1.2 #8549d0d64a4b63b1e7fba3e545c5cac89c2ff447 5 | github.com/diku-dk/sml-sort 0.2.1 #9efbabfd57e2db2514a07e69156d6857efe1a99f 6 | github.com/diku-dk/sml-sobol 0.1.1 #23584dec7d165bf977c55d5aad1056fb585f642d 7 | github.com/diku-dk/sml-random 0.1.1 #0a0f79c857207f948e89e18b320f162e30e1fdaf 8 | github.com/diku-dk/sml-md5 0.1.1 #438d4a6ca20fd6a29c4dd45f247fe073432318d2 9 | github.com/diku-dk/sml-sha256 0.1.0 #e11278f732df62a4ce3e0a7c17e173feb90b3d65 10 | github.com/diku-dk/sml-base64 0.1.1 #575a413424c0a9ba8d3ea4397cb5116dd522cfdc 11 | github.com/diku-dk/sml-hashtable 0.1.1 #2e98df6c57effdbc8c47fa1335e4e543ab412cc2 12 | github.com/diku-dk/sml-cstring 0.1.0 #261ac586ddad1d9976a34f86bd12214940799fa7 13 | github.com/diku-dk/sml-json 0.1.1 #724043b6d05791a62f91ce999e9ebbed22dd227d 14 | github.com/diku-dk/sml-parse 0.1.5 #5693148ec0e4cbba1a1664170d9814f3b63267d9 15 | github.com/diku-dk/sml-regexp 0.1.1 #b91e46945d68c3cb72b6bb002d562a1f57ecc01e 16 | } 17 | -------------------------------------------------------------------------------- /jslib/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Trebuchet MS", "Helvetica", "Arial", "Verdana", "sans-serif"; 3 | } 4 | 5 | .button1 { 6 | background-color: white; 7 | border: none; 8 | padding: 0px; 9 | text-align: bottom; 10 | text-decoration: none; 11 | display: inline-block; 12 | font-size: 16px; 13 | margin: 0px 2px; 14 | cursor: pointer; 15 | border-radius: 10px; 16 | bottom: -5px; 17 | vertical-align:bottom; 18 | position:relative; 19 | } 20 | 21 | .button1:active { 22 | transform: translateY(2px); 23 | } 24 | 25 | .tooltip { 26 | position: relative; 27 | display: inline-block; 28 | } 29 | 30 | .tooltip .tooltiptext { 31 | visibility: hidden; 32 | width: 140px; 33 | background-color: #888; 34 | color: #fff; 35 | text-align: center; 36 | border-radius: 6px; 37 | padding: 4px 0; 38 | position: absolute; 39 | z-index: 1; 40 | bottom: 125%; 41 | left: 50%; 42 | margin-left: -70px; 43 | opacity: 0; 44 | transition: opacity 0.3s; 45 | } 46 | 47 | .tooltip .tooltiptext::after { 48 | content: ""; 49 | position: absolute; 50 | top: 100%; 51 | left: 50%; 52 | margin-left: -5px; 53 | border-width: 5px; 54 | border-style: solid; 55 | border-color: #555 transparent transparent transparent; 56 | } 57 | 58 | .tooltip:hover .tooltiptext { 59 | visibility: visible; 60 | opacity: 1; 61 | } 62 | 63 | .vbadge{ 64 | background: #11bb44; 65 | font-family: Verdana, sans-serif; 66 | font-style: normal; 67 | padding: 6px 10px 2px 10px; 68 | font-size: 10px; 69 | color: #ffffff; 70 | border-radius: 3px; 71 | line-height: 0; 72 | vertical-align: center; 73 | } 74 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | 2 | SIGDOC ?= ../sigdoc 3 | 4 | BASIS ?= ~/gits/mlkit/basis 5 | 6 | BASISFILES0 = GENERAL.sig General.sml OPTION.sig Option.sml LIST.sig List.sml LIST_PAIR.sml \ 7 | ListPair.sml VECTOR.sig Vector.sml VECTOR_SLICE.sml VectorSlice.sml ARRAY.sig \ 8 | Array.sml ARRAY_SLICE.sml ArraySlice.sml ARRAY2.sig Array2.sml MONO_VECTOR.sml \ 9 | MONO_ARRAY.sml ByteTable.sml MONO_VECTOR_SLICE.sml MONO_ARRAY_SLICE.sml ByteSlice.sml \ 10 | MONO_ARRAY2.sml STRING_CVT.sml StringCvt.sml Char.sml String.sml CHAR.sig \ 11 | STRING.sig SUBSTRING.sig Substring.sml TEXT.sig Text.sml BOOL.sig Bool.sml \ 12 | WORD.sig Word.sml Word64.sml Word63.sml Word32.sml Word31.sml Word8.sml Word16.sml \ 13 | PACK_WORD.sml Pack32Little.sml Pack32Big.sml BYTE.sig Byte.sml INTEGER.sml Int.sml \ 14 | Int32.sml Int31.sml Int63.sml Int64.sml MATH.sig Math.sml REAL.sig Real.sml \ 15 | PACK_REAL.sml PackRealLittle.sml PackRealBig.sml RealArrayVector.sml INT_INF.sml \ 16 | IntInf.sml IO.sml TIME.sig Time.sml OS_PATH.sml Path.sml \ 17 | OS_FILE_SYS.sml FileSys.sml OS_PROCESS.sml Process.sml OS_IO.sml OS.sml \ 18 | COMMAND_LINE.sml CommandLine.sml DATE.sig Date.sml TIMER.sig Timer.sml \ 19 | NET_HOST_DB.sig NetHostDB.sml SOCKET.sig Socket.sml INET_SOCK.sig BIT_FLAGS.sml \ 20 | POSIX_IO.sml POSIX_PROCESS.sml POSIX_PROCENV.sml POSIX_FILE_SYS.sml POSIX_SIGNAL.sml \ 21 | POSIX_ERROR.sml POSIX_SYS_DB.sml POSIX_TTY.sml POSIX.sig Posix.sml io/stream-io.sig \ 22 | io/stream-io.sml io/imperative-io.sig io/imperative-io.sml io/text-stream-io.sig \ 23 | io/text-io.sig io/text-io.sml io/bin-stream-io.sig io/bin-io.sig io/io-close.sml \ 24 | UNIX.sig Unix.sml io/prim-io.sig io/text-prim-io.sml io/bin-prim-io.sml 25 | 26 | BASISFILES:=$(BASISFILES0:%=$(BASIS)/%) 27 | 28 | .PHONY: test 29 | test: about0.html 30 | $(SIGDOC) -about about0.html -libpath ../jslib ../lib/github.com/diku-dk/*/*.{sig,sml} ../lib/github.com/diku-dk/*/*/*.{sig,sml} $(BASISFILES) 31 | 32 | about0.html: about.md 33 | pandoc -o $@ $< 34 | 35 | test0: 36 | $(SIGDOC) -libpath ../jslib $(BASISFILES) 37 | 38 | .PHONY: clean 39 | clean: 40 | rm -rf *~ *.html generated_tags.js 41 | -------------------------------------------------------------------------------- /test/full/Makefile: -------------------------------------------------------------------------------- 1 | SMLPKG ?= smlpkg 2 | 3 | SIGDOC ?= ../../sigdoc 4 | 5 | BASIS ?= ~/gits/mlkit/basis 6 | 7 | BASISFILES0 = GENERAL.sig General.sml OPTION.sig Option.sml LIST.sig List.sml LIST_PAIR.sml \ 8 | ListPair.sml VECTOR.sig Vector.sml VECTOR_SLICE.sml VectorSlice.sml ARRAY.sig \ 9 | Array.sml ARRAY_SLICE.sml ArraySlice.sml ARRAY2.sig Array2.sml MONO_VECTOR.sml \ 10 | MONO_ARRAY.sml ByteTable.sml MONO_VECTOR_SLICE.sml MONO_ARRAY_SLICE.sml ByteSlice.sml \ 11 | MONO_ARRAY2.sml STRING_CVT.sml StringCvt.sml Char.sml String.sml CHAR.sig \ 12 | STRING.sig SUBSTRING.sig Substring.sml TEXT.sig Text.sml BOOL.sig Bool.sml \ 13 | WORD.sig Word.sml Word64.sml Word63.sml Word32.sml Word31.sml Word8.sml Word16.sml \ 14 | PACK_WORD.sml Pack32Little.sml Pack32Big.sml BYTE.sig Byte.sml INTEGER.sml Int.sml \ 15 | Int32.sml Int31.sml Int63.sml Int64.sml MATH.sig Math.sml REAL.sig Real.sml IEEE_REAL.sig IEEEReal.sml \ 16 | PACK_REAL.sml PackRealLittle.sml PackRealBig.sml RealArrayVector.sml INT_INF.sml \ 17 | IntInf.sml IO.sml TIME.sig Time.sml OS_PATH.sml Path.sml \ 18 | OS_FILE_SYS.sml FileSys.sml OS_PROCESS.sml Process.sml OS_IO.sml OS.sml \ 19 | COMMAND_LINE.sml CommandLine.sml DATE.sig Date.sml TIMER.sig Timer.sml \ 20 | NET_HOST_DB.sig NetHostDB.sml SOCKET.sig Socket.sml INET_SOCK.sig BIT_FLAGS.sml \ 21 | POSIX_IO.sml POSIX_PROCESS.sml POSIX_PROCENV.sml POSIX_FILE_SYS.sml POSIX_SIGNAL.sml \ 22 | POSIX_ERROR.sml POSIX_SYS_DB.sml POSIX_TTY.sml POSIX.sig Posix.sml io/stream-io.sig \ 23 | io/stream-io.sml io/imperative-io.sig io/imperative-io.sml io/text-stream-io.sig \ 24 | io/text-io.sig io/text-io.sml io/bin-stream-io.sig io/bin-io.sig io/io-close.sml \ 25 | UNIX.sig Unix.sml io/prim-io.sig io/text-prim-io.sml io/bin-prim-io.sml 26 | 27 | BASISFILES:=$(BASISFILES0:%=$(BASIS)/%) 28 | 29 | LOGO = '' 30 | 31 | .PHONY: test 32 | test: about0.html lib jslib 33 | $(SIGDOC) -pkg sml.pkg -logo $(LOGO) -about about0.html -libpath jslib lib/github.com/diku-dk/*/*.{sig,sml} lib/github.com/diku-dk/*/*/*.{sig,sml} $(BASISFILES) 34 | 35 | jslib: 36 | cp -pa ../../jslib . 37 | 38 | about0.html: ../about.md 39 | pandoc -o $@ $< 40 | 41 | lib: sml.pkg 42 | $(SMLPKG) sync 43 | 44 | .PHONY: clean 45 | clean: 46 | rm -rf *~ *.html generated_tags.js jslib 47 | 48 | .PHONY: realclean 49 | realclean: 50 | $(MAKE) clean 51 | rm -rf lib 52 | -------------------------------------------------------------------------------- /ParseArg.sml: -------------------------------------------------------------------------------- 1 | structure ParseArg : PARSE_ARG = struct 2 | exception Exit of string list 3 | datatype t = Nullary of string * (unit -> unit) 4 | | Unary of string * (string -> unit) 5 | | Multi of string * (string list -> unit) 6 | fun isFlag x = (String.sub(x,0) = #"-") handle _ => false 7 | fun read_args xs = 8 | let fun loop nil a = (rev a, nil) 9 | | loop (x::xs) a = 10 | if isFlag x then (rev a, x::xs) 11 | else loop xs (x::a) 12 | in loop xs nil 13 | end 14 | fun getNullary x (Nullary(y,f)::ts) = 15 | if x = y then SOME f 16 | else getNullary x ts 17 | | getNullary x (t::ts) = getNullary x ts 18 | | getNullary x nil = NONE 19 | fun getUnary x (Unary(y,f)::ts) = 20 | if x = y then SOME f 21 | else getUnary x ts 22 | | getUnary x (t::ts) = getUnary x ts 23 | | getUnary x nil = NONE 24 | fun getMulti x (Multi(y,f)::ts) = 25 | if x = y then SOME f 26 | else getMulti x ts 27 | | getMulti x (t::ts) = getMulti x ts 28 | | getMulti x nil = NONE 29 | 30 | fun err s = (print (s ^ "\n"); raise Fail "ParseArg error") 31 | fun unknown x = err ("Unknown argument '" ^ x ^ "'") 32 | fun run ts = 33 | let 34 | fun loop nil = [] 35 | | loop (x::xs) = 36 | if isFlag x then 37 | let val (args, xs) = read_args xs 38 | val () = 39 | case args of 40 | nil => 41 | (case getNullary x ts of 42 | SOME f => f() 43 | | NONE => unknown x) 44 | | [a] => 45 | (case getUnary x ts of 46 | SOME f => f a 47 | | NONE => 48 | (case getMulti x ts of 49 | SOME f => f [a] 50 | | NONE => 51 | (case getNullary x ts of 52 | SOME f => (f(); raise Exit(a::xs)) 53 | | NONE => unknown x))) 54 | | a::aa => 55 | (case getMulti x ts of 56 | SOME f => f args 57 | | NONE => 58 | (case getUnary x ts of 59 | SOME f => (f a; raise Exit(aa@xs)) 60 | | NONE => 61 | (case getNullary x ts of 62 | SOME f => (f(); raise Exit(args@xs)) 63 | | NONE => unknown x))) 64 | in loop xs 65 | end 66 | else raise Exit(x::xs) 67 | in loop (CommandLine.arguments()) handle Exit ys => ys 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## sigdoc 2 | 3 | _A signature documentation tool for Standard ML_ 4 | 5 | Sigdoc is a command-line tool (named `sigdoc`) for generating HTML 6 | documentation for Standard ML signatures. It takes as arguments a set 7 | of signature files, containing single signatures, and a set of 8 | implementation files. Sigdoc scans the signature files to obtain the 9 | set of signature identifiers. Then, for each signature identifier, the 10 | tool generates a documentation page with documentation for each 11 | specified identifier. Also, the tool scans the implementation files 12 | for structure identifiers, identifying implementations of the 13 | signatures and generates a documentation page with an index over 14 | structure identifiers (with links to the signature documentation). 15 | 16 | The generated HTML documentation embeds an auto-completing search 17 | field that depends on the jquery library placed in the `jslib/` 18 | subdirectory. For the generated code to work, copy the content of the 19 | `jslib/` directory into the target directory for the documentation. 20 | 21 | Based on the file paths, the tool also tries to identify if the 22 | signature or structure stem from an `smlpkg` package, and if so, a link to the package is embedded in the generated HTML documentation. 23 | 24 | The result of running `sigdoc` on a series of (signature and 25 | implementation) files, is a set of files written to the working 26 | directory. 27 | 28 | Signatures within files must conform to the following snippet 29 | structure: 30 | 31 | (** Header 32 | 33 | General comment, which may be multi-line or empty... 34 | 35 | *) 36 | 37 | signature A = sig 38 | type a 39 | ... 40 | val b : a -> unit 41 | end 42 | 43 | (** 44 | 45 | [type a] is a type. 46 | 47 | [b a] returns unit. 48 | 49 | [Discussion] 50 | 51 | This last entry can be omitted. Also, above it is 52 | possible to include text-blocks using indentation and 53 | it is possible to refer to exception specifications, 54 | structure specifications, include specifications, and 55 | datatype specifications. 56 | *) 57 | 58 | Structure declarations within files are documented if they take one of 59 | the following forms: 60 | 61 | (** SigDoc *) 62 | structure A : B ... 63 | 64 | (** SigDoc *) 65 | structure A :> B ... 66 | 67 | In both cases `B` must be an identified signature identifier. A 68 | structure declaration that occur first in a file (perhaps after a 69 | single comment) is also documented provided the ascribed signature 70 | identifier identifies a signature in the provided files. 71 | 72 | ### Usage 73 | 74 | bash-3.2$ ./sigdoc 75 | Usage: sigdoc [-libpath p] [-about f] [-logo s] 76 | [-pkg f] FILES 77 | 78 | -libpath p : specify the path to the js-library and 79 | style files, relative to the working 80 | directory. 81 | -about f : specify a file with HTML to embed in an 82 | About tab. 83 | -logo s : specify html that presents a logo. 84 | -pkg f : specify path to smlpkg package file to 85 | read package versions from. 86 | FILES include .sml and .sig files, which may contain 87 | signatures and structures. 88 | For further information, please consult the Sigdoc 89 | documentation at http://github.com/diku-dk/sigdoc 90 | -------------------------------------------------------------------------------- /jslib/jquery-ui.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.10.3 - 2013-05-03 2 | * http://jqueryui.com 3 | * Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css 4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px 5 | * Copyright 2013 jQuery Foundation and other contributors Licensed MIT */ 6 | 7 | /* Layout helpers 8 | ----------------------------------*/ 9 | .ui-helper-hidden { 10 | display: none; 11 | } 12 | .ui-helper-hidden-accessible { 13 | border: 0; 14 | clip: rect(0 0 0 0); 15 | height: 1px; 16 | margin: -1px; 17 | overflow: hidden; 18 | padding: 0; 19 | position: absolute; 20 | width: 1px; 21 | } 22 | .ui-helper-reset { 23 | margin: 0; 24 | padding: 0; 25 | border: 0; 26 | outline: 0; 27 | line-height: 1.3; 28 | text-decoration: none; 29 | font-size: 100%; 30 | list-style: none; 31 | } 32 | .ui-helper-clearfix:before, 33 | .ui-helper-clearfix:after { 34 | content: ""; 35 | display: table; 36 | border-collapse: collapse; 37 | } 38 | .ui-helper-clearfix:after { 39 | clear: both; 40 | } 41 | .ui-helper-clearfix { 42 | min-height: 0; /* support: IE7 */ 43 | } 44 | .ui-helper-zfix { 45 | width: 100%; 46 | height: 100%; 47 | top: 0; 48 | left: 0; 49 | position: absolute; 50 | opacity: 0; 51 | filter:Alpha(Opacity=0); 52 | } 53 | 54 | .ui-front { 55 | z-index: 100; 56 | } 57 | 58 | 59 | /* Interaction Cues 60 | ----------------------------------*/ 61 | .ui-state-disabled { 62 | cursor: default !important; 63 | } 64 | 65 | 66 | /* Icons 67 | ----------------------------------*/ 68 | 69 | /* states and images */ 70 | .ui-icon { 71 | display: block; 72 | text-indent: -99999px; 73 | overflow: hidden; 74 | background-repeat: no-repeat; 75 | } 76 | 77 | 78 | /* Misc visuals 79 | ----------------------------------*/ 80 | 81 | /* Overlays */ 82 | .ui-widget-overlay { 83 | position: fixed; 84 | top: 0; 85 | left: 0; 86 | width: 100%; 87 | height: 100%; 88 | } 89 | .ui-accordion .ui-accordion-header { 90 | display: block; 91 | cursor: pointer; 92 | position: relative; 93 | margin-top: 2px; 94 | padding: .5em .5em .5em .7em; 95 | min-height: 0; /* support: IE7 */ 96 | } 97 | .ui-accordion .ui-accordion-icons { 98 | padding-left: 2.2em; 99 | } 100 | .ui-accordion .ui-accordion-noicons { 101 | padding-left: .7em; 102 | } 103 | .ui-accordion .ui-accordion-icons .ui-accordion-icons { 104 | padding-left: 2.2em; 105 | } 106 | .ui-accordion .ui-accordion-header .ui-accordion-header-icon { 107 | position: absolute; 108 | left: .5em; 109 | top: 50%; 110 | margin-top: -8px; 111 | } 112 | .ui-accordion .ui-accordion-content { 113 | padding: 1em 2.2em; 114 | border-top: 0; 115 | overflow: auto; 116 | } 117 | .ui-autocomplete { 118 | position: absolute; 119 | top: 0; 120 | left: 0; 121 | cursor: default; 122 | } 123 | .ui-button { 124 | display: inline-block; 125 | position: relative; 126 | padding: 0; 127 | line-height: normal; 128 | margin-right: .1em; 129 | cursor: pointer; 130 | vertical-align: middle; 131 | text-align: center; 132 | overflow: visible; /* removes extra width in IE */ 133 | } 134 | .ui-button, 135 | .ui-button:link, 136 | .ui-button:visited, 137 | .ui-button:hover, 138 | .ui-button:active { 139 | text-decoration: none; 140 | } 141 | /* to make room for the icon, a width needs to be set here */ 142 | .ui-button-icon-only { 143 | width: 2.2em; 144 | } 145 | /* button elements seem to need a little more width */ 146 | button.ui-button-icon-only { 147 | width: 2.4em; 148 | } 149 | .ui-button-icons-only { 150 | width: 3.4em; 151 | } 152 | button.ui-button-icons-only { 153 | width: 3.7em; 154 | } 155 | 156 | /* button text element */ 157 | .ui-button .ui-button-text { 158 | display: block; 159 | line-height: normal; 160 | } 161 | .ui-button-text-only .ui-button-text { 162 | padding: .4em 1em; 163 | } 164 | .ui-button-icon-only .ui-button-text, 165 | .ui-button-icons-only .ui-button-text { 166 | padding: .4em; 167 | text-indent: -9999999px; 168 | } 169 | .ui-button-text-icon-primary .ui-button-text, 170 | .ui-button-text-icons .ui-button-text { 171 | padding: .4em 1em .4em 2.1em; 172 | } 173 | .ui-button-text-icon-secondary .ui-button-text, 174 | .ui-button-text-icons .ui-button-text { 175 | padding: .4em 2.1em .4em 1em; 176 | } 177 | .ui-button-text-icons .ui-button-text { 178 | padding-left: 2.1em; 179 | padding-right: 2.1em; 180 | } 181 | /* no icon support for input elements, provide padding by default */ 182 | input.ui-button { 183 | padding: .4em 1em; 184 | } 185 | 186 | /* button icon element(s) */ 187 | .ui-button-icon-only .ui-icon, 188 | .ui-button-text-icon-primary .ui-icon, 189 | .ui-button-text-icon-secondary .ui-icon, 190 | .ui-button-text-icons .ui-icon, 191 | .ui-button-icons-only .ui-icon { 192 | position: absolute; 193 | top: 50%; 194 | margin-top: -8px; 195 | } 196 | .ui-button-icon-only .ui-icon { 197 | left: 50%; 198 | margin-left: -8px; 199 | } 200 | .ui-button-text-icon-primary .ui-button-icon-primary, 201 | .ui-button-text-icons .ui-button-icon-primary, 202 | .ui-button-icons-only .ui-button-icon-primary { 203 | left: .5em; 204 | } 205 | .ui-button-text-icon-secondary .ui-button-icon-secondary, 206 | .ui-button-text-icons .ui-button-icon-secondary, 207 | .ui-button-icons-only .ui-button-icon-secondary { 208 | right: .5em; 209 | } 210 | 211 | /* button sets */ 212 | .ui-buttonset { 213 | margin-right: 7px; 214 | } 215 | .ui-buttonset .ui-button { 216 | margin-left: 0; 217 | margin-right: -.3em; 218 | } 219 | 220 | /* workarounds */ 221 | /* reset extra padding in Firefox, see h5bp.com/l */ 222 | input.ui-button::-moz-focus-inner, 223 | button.ui-button::-moz-focus-inner { 224 | border: 0; 225 | padding: 0; 226 | } 227 | .ui-datepicker { 228 | width: 17em; 229 | padding: .2em .2em 0; 230 | display: none; 231 | } 232 | .ui-datepicker .ui-datepicker-header { 233 | position: relative; 234 | padding: .2em 0; 235 | } 236 | .ui-datepicker .ui-datepicker-prev, 237 | .ui-datepicker .ui-datepicker-next { 238 | position: absolute; 239 | top: 2px; 240 | width: 1.8em; 241 | height: 1.8em; 242 | } 243 | .ui-datepicker .ui-datepicker-prev-hover, 244 | .ui-datepicker .ui-datepicker-next-hover { 245 | top: 1px; 246 | } 247 | .ui-datepicker .ui-datepicker-prev { 248 | left: 2px; 249 | } 250 | .ui-datepicker .ui-datepicker-next { 251 | right: 2px; 252 | } 253 | .ui-datepicker .ui-datepicker-prev-hover { 254 | left: 1px; 255 | } 256 | .ui-datepicker .ui-datepicker-next-hover { 257 | right: 1px; 258 | } 259 | .ui-datepicker .ui-datepicker-prev span, 260 | .ui-datepicker .ui-datepicker-next span { 261 | display: block; 262 | position: absolute; 263 | left: 50%; 264 | margin-left: -8px; 265 | top: 50%; 266 | margin-top: -8px; 267 | } 268 | .ui-datepicker .ui-datepicker-title { 269 | margin: 0 2.3em; 270 | line-height: 1.8em; 271 | text-align: center; 272 | } 273 | .ui-datepicker .ui-datepicker-title select { 274 | font-size: 1em; 275 | margin: 1px 0; 276 | } 277 | .ui-datepicker select.ui-datepicker-month-year { 278 | width: 100%; 279 | } 280 | .ui-datepicker select.ui-datepicker-month, 281 | .ui-datepicker select.ui-datepicker-year { 282 | width: 49%; 283 | } 284 | .ui-datepicker table { 285 | width: 100%; 286 | font-size: .9em; 287 | border-collapse: collapse; 288 | margin: 0 0 .4em; 289 | } 290 | .ui-datepicker th { 291 | padding: .7em .3em; 292 | text-align: center; 293 | font-weight: bold; 294 | border: 0; 295 | } 296 | .ui-datepicker td { 297 | border: 0; 298 | padding: 1px; 299 | } 300 | .ui-datepicker td span, 301 | .ui-datepicker td a { 302 | display: block; 303 | padding: .2em; 304 | text-align: right; 305 | text-decoration: none; 306 | } 307 | .ui-datepicker .ui-datepicker-buttonpane { 308 | background-image: none; 309 | margin: .7em 0 0 0; 310 | padding: 0 .2em; 311 | border-left: 0; 312 | border-right: 0; 313 | border-bottom: 0; 314 | } 315 | .ui-datepicker .ui-datepicker-buttonpane button { 316 | float: right; 317 | margin: .5em .2em .4em; 318 | cursor: pointer; 319 | padding: .2em .6em .3em .6em; 320 | width: auto; 321 | overflow: visible; 322 | } 323 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { 324 | float: left; 325 | } 326 | 327 | /* with multiple calendars */ 328 | .ui-datepicker.ui-datepicker-multi { 329 | width: auto; 330 | } 331 | .ui-datepicker-multi .ui-datepicker-group { 332 | float: left; 333 | } 334 | .ui-datepicker-multi .ui-datepicker-group table { 335 | width: 95%; 336 | margin: 0 auto .4em; 337 | } 338 | .ui-datepicker-multi-2 .ui-datepicker-group { 339 | width: 50%; 340 | } 341 | .ui-datepicker-multi-3 .ui-datepicker-group { 342 | width: 33.3%; 343 | } 344 | .ui-datepicker-multi-4 .ui-datepicker-group { 345 | width: 25%; 346 | } 347 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, 348 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { 349 | border-left-width: 0; 350 | } 351 | .ui-datepicker-multi .ui-datepicker-buttonpane { 352 | clear: left; 353 | } 354 | .ui-datepicker-row-break { 355 | clear: both; 356 | width: 100%; 357 | font-size: 0; 358 | } 359 | 360 | /* RTL support */ 361 | .ui-datepicker-rtl { 362 | direction: rtl; 363 | } 364 | .ui-datepicker-rtl .ui-datepicker-prev { 365 | right: 2px; 366 | left: auto; 367 | } 368 | .ui-datepicker-rtl .ui-datepicker-next { 369 | left: 2px; 370 | right: auto; 371 | } 372 | .ui-datepicker-rtl .ui-datepicker-prev:hover { 373 | right: 1px; 374 | left: auto; 375 | } 376 | .ui-datepicker-rtl .ui-datepicker-next:hover { 377 | left: 1px; 378 | right: auto; 379 | } 380 | .ui-datepicker-rtl .ui-datepicker-buttonpane { 381 | clear: right; 382 | } 383 | .ui-datepicker-rtl .ui-datepicker-buttonpane button { 384 | float: left; 385 | } 386 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, 387 | .ui-datepicker-rtl .ui-datepicker-group { 388 | float: right; 389 | } 390 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, 391 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { 392 | border-right-width: 0; 393 | border-left-width: 1px; 394 | } 395 | .ui-dialog { 396 | position: absolute; 397 | top: 0; 398 | left: 0; 399 | padding: .2em; 400 | outline: 0; 401 | } 402 | .ui-dialog .ui-dialog-titlebar { 403 | padding: .4em 1em; 404 | position: relative; 405 | } 406 | .ui-dialog .ui-dialog-title { 407 | float: left; 408 | margin: .1em 0; 409 | white-space: nowrap; 410 | width: 90%; 411 | overflow: hidden; 412 | text-overflow: ellipsis; 413 | } 414 | .ui-dialog .ui-dialog-titlebar-close { 415 | position: absolute; 416 | right: .3em; 417 | top: 50%; 418 | width: 21px; 419 | margin: -10px 0 0 0; 420 | padding: 1px; 421 | height: 20px; 422 | } 423 | .ui-dialog .ui-dialog-content { 424 | position: relative; 425 | border: 0; 426 | padding: .5em 1em; 427 | background: none; 428 | overflow: auto; 429 | } 430 | .ui-dialog .ui-dialog-buttonpane { 431 | text-align: left; 432 | border-width: 1px 0 0 0; 433 | background-image: none; 434 | margin-top: .5em; 435 | padding: .3em 1em .5em .4em; 436 | } 437 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { 438 | float: right; 439 | } 440 | .ui-dialog .ui-dialog-buttonpane button { 441 | margin: .5em .4em .5em 0; 442 | cursor: pointer; 443 | } 444 | .ui-dialog .ui-resizable-se { 445 | width: 12px; 446 | height: 12px; 447 | right: -5px; 448 | bottom: -5px; 449 | background-position: 16px 16px; 450 | } 451 | .ui-draggable .ui-dialog-titlebar { 452 | cursor: move; 453 | } 454 | .ui-menu { 455 | list-style: none; 456 | padding: 2px; 457 | margin: 0; 458 | display: block; 459 | outline: none; 460 | } 461 | .ui-menu .ui-menu { 462 | margin-top: -3px; 463 | position: absolute; 464 | } 465 | .ui-menu .ui-menu-item { 466 | margin: 0; 467 | padding: 0; 468 | width: 100%; 469 | /* support: IE10, see #8844 */ 470 | list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); 471 | } 472 | .ui-menu .ui-menu-divider { 473 | margin: 5px -2px 5px -2px; 474 | height: 0; 475 | font-size: 0; 476 | line-height: 0; 477 | border-width: 1px 0 0 0; 478 | } 479 | .ui-menu .ui-menu-item a { 480 | text-decoration: none; 481 | display: block; 482 | padding: 2px .4em; 483 | line-height: 1.5; 484 | min-height: 0; /* support: IE7 */ 485 | font-weight: normal; 486 | } 487 | .ui-menu .ui-menu-item a.ui-state-focus, 488 | .ui-menu .ui-menu-item a.ui-state-active { 489 | font-weight: normal; 490 | margin: -1px; 491 | } 492 | 493 | .ui-menu .ui-state-disabled { 494 | font-weight: normal; 495 | margin: .4em 0 .2em; 496 | line-height: 1.5; 497 | } 498 | .ui-menu .ui-state-disabled a { 499 | cursor: default; 500 | } 501 | 502 | /* icon support */ 503 | .ui-menu-icons { 504 | position: relative; 505 | } 506 | .ui-menu-icons .ui-menu-item a { 507 | position: relative; 508 | padding-left: 2em; 509 | } 510 | 511 | /* left-aligned */ 512 | .ui-menu .ui-icon { 513 | position: absolute; 514 | top: .2em; 515 | left: .2em; 516 | } 517 | 518 | /* right-aligned */ 519 | .ui-menu .ui-menu-icon { 520 | position: static; 521 | float: right; 522 | } 523 | .ui-progressbar { 524 | height: 2em; 525 | text-align: left; 526 | overflow: hidden; 527 | } 528 | .ui-progressbar .ui-progressbar-value { 529 | margin: -1px; 530 | height: 100%; 531 | } 532 | .ui-progressbar .ui-progressbar-overlay { 533 | background: url("images/animated-overlay.gif"); 534 | height: 100%; 535 | filter: alpha(opacity=25); 536 | opacity: 0.25; 537 | } 538 | .ui-progressbar-indeterminate .ui-progressbar-value { 539 | background-image: none; 540 | } 541 | .ui-resizable { 542 | position: relative; 543 | } 544 | .ui-resizable-handle { 545 | position: absolute; 546 | font-size: 0.1px; 547 | display: block; 548 | } 549 | .ui-resizable-disabled .ui-resizable-handle, 550 | .ui-resizable-autohide .ui-resizable-handle { 551 | display: none; 552 | } 553 | .ui-resizable-n { 554 | cursor: n-resize; 555 | height: 7px; 556 | width: 100%; 557 | top: -5px; 558 | left: 0; 559 | } 560 | .ui-resizable-s { 561 | cursor: s-resize; 562 | height: 7px; 563 | width: 100%; 564 | bottom: -5px; 565 | left: 0; 566 | } 567 | .ui-resizable-e { 568 | cursor: e-resize; 569 | width: 7px; 570 | right: -5px; 571 | top: 0; 572 | height: 100%; 573 | } 574 | .ui-resizable-w { 575 | cursor: w-resize; 576 | width: 7px; 577 | left: -5px; 578 | top: 0; 579 | height: 100%; 580 | } 581 | .ui-resizable-se { 582 | cursor: se-resize; 583 | width: 12px; 584 | height: 12px; 585 | right: 1px; 586 | bottom: 1px; 587 | } 588 | .ui-resizable-sw { 589 | cursor: sw-resize; 590 | width: 9px; 591 | height: 9px; 592 | left: -5px; 593 | bottom: -5px; 594 | } 595 | .ui-resizable-nw { 596 | cursor: nw-resize; 597 | width: 9px; 598 | height: 9px; 599 | left: -5px; 600 | top: -5px; 601 | } 602 | .ui-resizable-ne { 603 | cursor: ne-resize; 604 | width: 9px; 605 | height: 9px; 606 | right: -5px; 607 | top: -5px; 608 | } 609 | .ui-selectable-helper { 610 | position: absolute; 611 | z-index: 100; 612 | border: 1px dotted black; 613 | } 614 | .ui-slider { 615 | position: relative; 616 | text-align: left; 617 | } 618 | .ui-slider .ui-slider-handle { 619 | position: absolute; 620 | z-index: 2; 621 | width: 1.2em; 622 | height: 1.2em; 623 | cursor: default; 624 | } 625 | .ui-slider .ui-slider-range { 626 | position: absolute; 627 | z-index: 1; 628 | font-size: .7em; 629 | display: block; 630 | border: 0; 631 | background-position: 0 0; 632 | } 633 | 634 | /* For IE8 - See #6727 */ 635 | .ui-slider.ui-state-disabled .ui-slider-handle, 636 | .ui-slider.ui-state-disabled .ui-slider-range { 637 | filter: inherit; 638 | } 639 | 640 | .ui-slider-horizontal { 641 | height: .8em; 642 | } 643 | .ui-slider-horizontal .ui-slider-handle { 644 | top: -.3em; 645 | margin-left: -.6em; 646 | } 647 | .ui-slider-horizontal .ui-slider-range { 648 | top: 0; 649 | height: 100%; 650 | } 651 | .ui-slider-horizontal .ui-slider-range-min { 652 | left: 0; 653 | } 654 | .ui-slider-horizontal .ui-slider-range-max { 655 | right: 0; 656 | } 657 | 658 | .ui-slider-vertical { 659 | width: .8em; 660 | height: 100px; 661 | } 662 | .ui-slider-vertical .ui-slider-handle { 663 | left: -.3em; 664 | margin-left: 0; 665 | margin-bottom: -.6em; 666 | } 667 | .ui-slider-vertical .ui-slider-range { 668 | left: 0; 669 | width: 100%; 670 | } 671 | .ui-slider-vertical .ui-slider-range-min { 672 | bottom: 0; 673 | } 674 | .ui-slider-vertical .ui-slider-range-max { 675 | top: 0; 676 | } 677 | .ui-spinner { 678 | position: relative; 679 | display: inline-block; 680 | overflow: hidden; 681 | padding: 0; 682 | vertical-align: middle; 683 | } 684 | .ui-spinner-input { 685 | border: none; 686 | background: none; 687 | color: inherit; 688 | padding: 0; 689 | margin: .2em 0; 690 | vertical-align: middle; 691 | margin-left: .4em; 692 | margin-right: 22px; 693 | } 694 | .ui-spinner-button { 695 | width: 16px; 696 | height: 50%; 697 | font-size: .5em; 698 | padding: 0; 699 | margin: 0; 700 | text-align: center; 701 | position: absolute; 702 | cursor: default; 703 | display: block; 704 | overflow: hidden; 705 | right: 0; 706 | } 707 | /* more specificity required here to overide default borders */ 708 | .ui-spinner a.ui-spinner-button { 709 | border-top: none; 710 | border-bottom: none; 711 | border-right: none; 712 | } 713 | /* vertical centre icon */ 714 | .ui-spinner .ui-icon { 715 | position: absolute; 716 | margin-top: -8px; 717 | top: 50%; 718 | left: 0; 719 | } 720 | .ui-spinner-up { 721 | top: 0; 722 | } 723 | .ui-spinner-down { 724 | bottom: 0; 725 | } 726 | 727 | /* TR overrides */ 728 | .ui-spinner .ui-icon-triangle-1-s { 729 | /* need to fix icons sprite */ 730 | background-position: -65px -16px; 731 | } 732 | .ui-tabs { 733 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ 734 | padding: .2em; 735 | } 736 | .ui-tabs .ui-tabs-nav { 737 | margin: 0; 738 | padding: .2em .2em 0; 739 | } 740 | .ui-tabs .ui-tabs-nav li { 741 | list-style: none; 742 | float: left; 743 | position: relative; 744 | top: 0; 745 | margin: 1px .2em 0 0; 746 | border-bottom-width: 0; 747 | padding: 0; 748 | white-space: nowrap; 749 | } 750 | .ui-tabs .ui-tabs-nav li a { 751 | float: left; 752 | padding: .5em 1em; 753 | text-decoration: none; 754 | } 755 | .ui-tabs .ui-tabs-nav li.ui-tabs-active { 756 | margin-bottom: -1px; 757 | padding-bottom: 1px; 758 | } 759 | .ui-tabs .ui-tabs-nav li.ui-tabs-active a, 760 | .ui-tabs .ui-tabs-nav li.ui-state-disabled a, 761 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading a { 762 | cursor: text; 763 | } 764 | .ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ 765 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a { 766 | cursor: pointer; 767 | } 768 | .ui-tabs .ui-tabs-panel { 769 | display: block; 770 | border-width: 0; 771 | padding: 1em 1.4em; 772 | background: none; 773 | } 774 | .ui-tooltip { 775 | padding: 8px; 776 | position: absolute; 777 | z-index: 9999; 778 | max-width: 300px; 779 | -webkit-box-shadow: 0 0 5px #aaa; 780 | box-shadow: 0 0 5px #aaa; 781 | } 782 | body .ui-tooltip { 783 | border-width: 2px; 784 | } 785 | 786 | /* Component containers 787 | ----------------------------------*/ 788 | .ui-widget { 789 | font-family: Verdana,Arial,sans-serif; 790 | font-size: 1.1em; 791 | } 792 | .ui-widget .ui-widget { 793 | font-size: 1em; 794 | } 795 | .ui-widget input, 796 | .ui-widget select, 797 | .ui-widget textarea, 798 | .ui-widget button { 799 | font-family: Verdana,Arial,sans-serif; 800 | font-size: 1em; 801 | } 802 | .ui-widget-content { 803 | border: 1px solid #aaaaaa; 804 | background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; 805 | color: #222222; 806 | } 807 | .ui-widget-content a { 808 | color: #222222; 809 | } 810 | .ui-widget-header { 811 | border: 1px solid #aaaaaa; 812 | background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; 813 | color: #222222; 814 | font-weight: bold; 815 | } 816 | .ui-widget-header a { 817 | color: #222222; 818 | } 819 | 820 | /* Interaction states 821 | ----------------------------------*/ 822 | .ui-state-default, 823 | .ui-widget-content .ui-state-default, 824 | .ui-widget-header .ui-state-default { 825 | border: 1px solid #d3d3d3; 826 | background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; 827 | font-weight: normal; 828 | color: #555555; 829 | } 830 | .ui-state-default a, 831 | .ui-state-default a:link, 832 | .ui-state-default a:visited { 833 | color: #555555; 834 | text-decoration: none; 835 | } 836 | .ui-state-hover, 837 | .ui-widget-content .ui-state-hover, 838 | .ui-widget-header .ui-state-hover, 839 | .ui-state-focus, 840 | .ui-widget-content .ui-state-focus, 841 | .ui-widget-header .ui-state-focus { 842 | border: 1px solid #999999; 843 | background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; 844 | font-weight: normal; 845 | color: #212121; 846 | } 847 | .ui-state-hover a, 848 | .ui-state-hover a:hover, 849 | .ui-state-hover a:link, 850 | .ui-state-hover a:visited { 851 | color: #212121; 852 | text-decoration: none; 853 | } 854 | .ui-state-active, 855 | .ui-widget-content .ui-state-active, 856 | .ui-widget-header .ui-state-active { 857 | border: 1px solid #aaaaaa; 858 | background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; 859 | font-weight: normal; 860 | color: #212121; 861 | } 862 | .ui-state-active a, 863 | .ui-state-active a:link, 864 | .ui-state-active a:visited { 865 | color: #212121; 866 | text-decoration: none; 867 | } 868 | 869 | /* Interaction Cues 870 | ----------------------------------*/ 871 | .ui-state-highlight, 872 | .ui-widget-content .ui-state-highlight, 873 | .ui-widget-header .ui-state-highlight { 874 | border: 1px solid #fcefa1; 875 | background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; 876 | color: #363636; 877 | } 878 | .ui-state-highlight a, 879 | .ui-widget-content .ui-state-highlight a, 880 | .ui-widget-header .ui-state-highlight a { 881 | color: #363636; 882 | } 883 | .ui-state-error, 884 | .ui-widget-content .ui-state-error, 885 | .ui-widget-header .ui-state-error { 886 | border: 1px solid #cd0a0a; 887 | background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; 888 | color: #cd0a0a; 889 | } 890 | .ui-state-error a, 891 | .ui-widget-content .ui-state-error a, 892 | .ui-widget-header .ui-state-error a { 893 | color: #cd0a0a; 894 | } 895 | .ui-state-error-text, 896 | .ui-widget-content .ui-state-error-text, 897 | .ui-widget-header .ui-state-error-text { 898 | color: #cd0a0a; 899 | } 900 | .ui-priority-primary, 901 | .ui-widget-content .ui-priority-primary, 902 | .ui-widget-header .ui-priority-primary { 903 | font-weight: bold; 904 | } 905 | .ui-priority-secondary, 906 | .ui-widget-content .ui-priority-secondary, 907 | .ui-widget-header .ui-priority-secondary { 908 | opacity: .7; 909 | filter:Alpha(Opacity=70); 910 | font-weight: normal; 911 | } 912 | .ui-state-disabled, 913 | .ui-widget-content .ui-state-disabled, 914 | .ui-widget-header .ui-state-disabled { 915 | opacity: .35; 916 | filter:Alpha(Opacity=35); 917 | background-image: none; 918 | } 919 | .ui-state-disabled .ui-icon { 920 | filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ 921 | } 922 | 923 | /* Icons 924 | ----------------------------------*/ 925 | 926 | /* states and images */ 927 | .ui-icon { 928 | width: 16px; 929 | height: 16px; 930 | } 931 | .ui-icon, 932 | .ui-widget-content .ui-icon { 933 | background-image: url(images/ui-icons_222222_256x240.png); 934 | } 935 | .ui-widget-header .ui-icon { 936 | background-image: url(images/ui-icons_222222_256x240.png); 937 | } 938 | .ui-state-default .ui-icon { 939 | background-image: url(images/ui-icons_888888_256x240.png); 940 | } 941 | .ui-state-hover .ui-icon, 942 | .ui-state-focus .ui-icon { 943 | background-image: url(images/ui-icons_454545_256x240.png); 944 | } 945 | .ui-state-active .ui-icon { 946 | background-image: url(images/ui-icons_454545_256x240.png); 947 | } 948 | .ui-state-highlight .ui-icon { 949 | background-image: url(images/ui-icons_2e83ff_256x240.png); 950 | } 951 | .ui-state-error .ui-icon, 952 | .ui-state-error-text .ui-icon { 953 | background-image: url(images/ui-icons_cd0a0a_256x240.png); 954 | } 955 | 956 | /* positioning */ 957 | .ui-icon-blank { background-position: 16px 16px; } 958 | .ui-icon-carat-1-n { background-position: 0 0; } 959 | .ui-icon-carat-1-ne { background-position: -16px 0; } 960 | .ui-icon-carat-1-e { background-position: -32px 0; } 961 | .ui-icon-carat-1-se { background-position: -48px 0; } 962 | .ui-icon-carat-1-s { background-position: -64px 0; } 963 | .ui-icon-carat-1-sw { background-position: -80px 0; } 964 | .ui-icon-carat-1-w { background-position: -96px 0; } 965 | .ui-icon-carat-1-nw { background-position: -112px 0; } 966 | .ui-icon-carat-2-n-s { background-position: -128px 0; } 967 | .ui-icon-carat-2-e-w { background-position: -144px 0; } 968 | .ui-icon-triangle-1-n { background-position: 0 -16px; } 969 | .ui-icon-triangle-1-ne { background-position: -16px -16px; } 970 | .ui-icon-triangle-1-e { background-position: -32px -16px; } 971 | .ui-icon-triangle-1-se { background-position: -48px -16px; } 972 | .ui-icon-triangle-1-s { background-position: -64px -16px; } 973 | .ui-icon-triangle-1-sw { background-position: -80px -16px; } 974 | .ui-icon-triangle-1-w { background-position: -96px -16px; } 975 | .ui-icon-triangle-1-nw { background-position: -112px -16px; } 976 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; } 977 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; } 978 | .ui-icon-arrow-1-n { background-position: 0 -32px; } 979 | .ui-icon-arrow-1-ne { background-position: -16px -32px; } 980 | .ui-icon-arrow-1-e { background-position: -32px -32px; } 981 | .ui-icon-arrow-1-se { background-position: -48px -32px; } 982 | .ui-icon-arrow-1-s { background-position: -64px -32px; } 983 | .ui-icon-arrow-1-sw { background-position: -80px -32px; } 984 | .ui-icon-arrow-1-w { background-position: -96px -32px; } 985 | .ui-icon-arrow-1-nw { background-position: -112px -32px; } 986 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; } 987 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } 988 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; } 989 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } 990 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; } 991 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; } 992 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; } 993 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; } 994 | .ui-icon-arrowthick-1-n { background-position: 0 -48px; } 995 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } 996 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; } 997 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; } 998 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; } 999 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } 1000 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; } 1001 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } 1002 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } 1003 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } 1004 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } 1005 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } 1006 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } 1007 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } 1008 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } 1009 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } 1010 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } 1011 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } 1012 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } 1013 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } 1014 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } 1015 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } 1016 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } 1017 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } 1018 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } 1019 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } 1020 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } 1021 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } 1022 | .ui-icon-arrow-4 { background-position: 0 -80px; } 1023 | .ui-icon-arrow-4-diag { background-position: -16px -80px; } 1024 | .ui-icon-extlink { background-position: -32px -80px; } 1025 | .ui-icon-newwin { background-position: -48px -80px; } 1026 | .ui-icon-refresh { background-position: -64px -80px; } 1027 | .ui-icon-shuffle { background-position: -80px -80px; } 1028 | .ui-icon-transfer-e-w { background-position: -96px -80px; } 1029 | .ui-icon-transferthick-e-w { background-position: -112px -80px; } 1030 | .ui-icon-folder-collapsed { background-position: 0 -96px; } 1031 | .ui-icon-folder-open { background-position: -16px -96px; } 1032 | .ui-icon-document { background-position: -32px -96px; } 1033 | .ui-icon-document-b { background-position: -48px -96px; } 1034 | .ui-icon-note { background-position: -64px -96px; } 1035 | .ui-icon-mail-closed { background-position: -80px -96px; } 1036 | .ui-icon-mail-open { background-position: -96px -96px; } 1037 | .ui-icon-suitcase { background-position: -112px -96px; } 1038 | .ui-icon-comment { background-position: -128px -96px; } 1039 | .ui-icon-person { background-position: -144px -96px; } 1040 | .ui-icon-print { background-position: -160px -96px; } 1041 | .ui-icon-trash { background-position: -176px -96px; } 1042 | .ui-icon-locked { background-position: -192px -96px; } 1043 | .ui-icon-unlocked { background-position: -208px -96px; } 1044 | .ui-icon-bookmark { background-position: -224px -96px; } 1045 | .ui-icon-tag { background-position: -240px -96px; } 1046 | .ui-icon-home { background-position: 0 -112px; } 1047 | .ui-icon-flag { background-position: -16px -112px; } 1048 | .ui-icon-calendar { background-position: -32px -112px; } 1049 | .ui-icon-cart { background-position: -48px -112px; } 1050 | .ui-icon-pencil { background-position: -64px -112px; } 1051 | .ui-icon-clock { background-position: -80px -112px; } 1052 | .ui-icon-disk { background-position: -96px -112px; } 1053 | .ui-icon-calculator { background-position: -112px -112px; } 1054 | .ui-icon-zoomin { background-position: -128px -112px; } 1055 | .ui-icon-zoomout { background-position: -144px -112px; } 1056 | .ui-icon-search { background-position: -160px -112px; } 1057 | .ui-icon-wrench { background-position: -176px -112px; } 1058 | .ui-icon-gear { background-position: -192px -112px; } 1059 | .ui-icon-heart { background-position: -208px -112px; } 1060 | .ui-icon-star { background-position: -224px -112px; } 1061 | .ui-icon-link { background-position: -240px -112px; } 1062 | .ui-icon-cancel { background-position: 0 -128px; } 1063 | .ui-icon-plus { background-position: -16px -128px; } 1064 | .ui-icon-plusthick { background-position: -32px -128px; } 1065 | .ui-icon-minus { background-position: -48px -128px; } 1066 | .ui-icon-minusthick { background-position: -64px -128px; } 1067 | .ui-icon-close { background-position: -80px -128px; } 1068 | .ui-icon-closethick { background-position: -96px -128px; } 1069 | .ui-icon-key { background-position: -112px -128px; } 1070 | .ui-icon-lightbulb { background-position: -128px -128px; } 1071 | .ui-icon-scissors { background-position: -144px -128px; } 1072 | .ui-icon-clipboard { background-position: -160px -128px; } 1073 | .ui-icon-copy { background-position: -176px -128px; } 1074 | .ui-icon-contact { background-position: -192px -128px; } 1075 | .ui-icon-image { background-position: -208px -128px; } 1076 | .ui-icon-video { background-position: -224px -128px; } 1077 | .ui-icon-script { background-position: -240px -128px; } 1078 | .ui-icon-alert { background-position: 0 -144px; } 1079 | .ui-icon-info { background-position: -16px -144px; } 1080 | .ui-icon-notice { background-position: -32px -144px; } 1081 | .ui-icon-help { background-position: -48px -144px; } 1082 | .ui-icon-check { background-position: -64px -144px; } 1083 | .ui-icon-bullet { background-position: -80px -144px; } 1084 | .ui-icon-radio-on { background-position: -96px -144px; } 1085 | .ui-icon-radio-off { background-position: -112px -144px; } 1086 | .ui-icon-pin-w { background-position: -128px -144px; } 1087 | .ui-icon-pin-s { background-position: -144px -144px; } 1088 | .ui-icon-play { background-position: 0 -160px; } 1089 | .ui-icon-pause { background-position: -16px -160px; } 1090 | .ui-icon-seek-next { background-position: -32px -160px; } 1091 | .ui-icon-seek-prev { background-position: -48px -160px; } 1092 | .ui-icon-seek-end { background-position: -64px -160px; } 1093 | .ui-icon-seek-start { background-position: -80px -160px; } 1094 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ 1095 | .ui-icon-seek-first { background-position: -80px -160px; } 1096 | .ui-icon-stop { background-position: -96px -160px; } 1097 | .ui-icon-eject { background-position: -112px -160px; } 1098 | .ui-icon-volume-off { background-position: -128px -160px; } 1099 | .ui-icon-volume-on { background-position: -144px -160px; } 1100 | .ui-icon-power { background-position: 0 -176px; } 1101 | .ui-icon-signal-diag { background-position: -16px -176px; } 1102 | .ui-icon-signal { background-position: -32px -176px; } 1103 | .ui-icon-battery-0 { background-position: -48px -176px; } 1104 | .ui-icon-battery-1 { background-position: -64px -176px; } 1105 | .ui-icon-battery-2 { background-position: -80px -176px; } 1106 | .ui-icon-battery-3 { background-position: -96px -176px; } 1107 | .ui-icon-circle-plus { background-position: 0 -192px; } 1108 | .ui-icon-circle-minus { background-position: -16px -192px; } 1109 | .ui-icon-circle-close { background-position: -32px -192px; } 1110 | .ui-icon-circle-triangle-e { background-position: -48px -192px; } 1111 | .ui-icon-circle-triangle-s { background-position: -64px -192px; } 1112 | .ui-icon-circle-triangle-w { background-position: -80px -192px; } 1113 | .ui-icon-circle-triangle-n { background-position: -96px -192px; } 1114 | .ui-icon-circle-arrow-e { background-position: -112px -192px; } 1115 | .ui-icon-circle-arrow-s { background-position: -128px -192px; } 1116 | .ui-icon-circle-arrow-w { background-position: -144px -192px; } 1117 | .ui-icon-circle-arrow-n { background-position: -160px -192px; } 1118 | .ui-icon-circle-zoomin { background-position: -176px -192px; } 1119 | .ui-icon-circle-zoomout { background-position: -192px -192px; } 1120 | .ui-icon-circle-check { background-position: -208px -192px; } 1121 | .ui-icon-circlesmall-plus { background-position: 0 -208px; } 1122 | .ui-icon-circlesmall-minus { background-position: -16px -208px; } 1123 | .ui-icon-circlesmall-close { background-position: -32px -208px; } 1124 | .ui-icon-squaresmall-plus { background-position: -48px -208px; } 1125 | .ui-icon-squaresmall-minus { background-position: -64px -208px; } 1126 | .ui-icon-squaresmall-close { background-position: -80px -208px; } 1127 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } 1128 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } 1129 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; } 1130 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } 1131 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } 1132 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; } 1133 | 1134 | 1135 | /* Misc visuals 1136 | ----------------------------------*/ 1137 | 1138 | /* Corner radius */ 1139 | .ui-corner-all, 1140 | .ui-corner-top, 1141 | .ui-corner-left, 1142 | .ui-corner-tl { 1143 | border-top-left-radius: 4px; 1144 | } 1145 | .ui-corner-all, 1146 | .ui-corner-top, 1147 | .ui-corner-right, 1148 | .ui-corner-tr { 1149 | border-top-right-radius: 4px; 1150 | } 1151 | .ui-corner-all, 1152 | .ui-corner-bottom, 1153 | .ui-corner-left, 1154 | .ui-corner-bl { 1155 | border-bottom-left-radius: 4px; 1156 | } 1157 | .ui-corner-all, 1158 | .ui-corner-bottom, 1159 | .ui-corner-right, 1160 | .ui-corner-br { 1161 | border-bottom-right-radius: 4px; 1162 | } 1163 | 1164 | /* Overlays */ 1165 | .ui-widget-overlay { 1166 | background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; 1167 | opacity: .3; 1168 | filter: Alpha(Opacity=30); 1169 | } 1170 | .ui-widget-shadow { 1171 | margin: -8px 0 0 -8px; 1172 | padding: 8px; 1173 | background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; 1174 | opacity: .3; 1175 | filter: Alpha(Opacity=30); 1176 | border-radius: 8px; 1177 | } 1178 | -------------------------------------------------------------------------------- /sigdoc.sml: -------------------------------------------------------------------------------- 1 | (* Utilities *) 2 | 3 | val sigdoc_url = "http://github.com/diku-dk/sigdoc" 4 | 5 | fun println s = print (s ^"\n") 6 | 7 | fun readFile f = 8 | let val () = print ("Reading file: " ^ f ^ "\n") 9 | val is = TextIO.openIn f 10 | in let val s = TextIO.inputAll is 11 | in TextIO.closeIn is; s 12 | end handle ? => (TextIO.closeIn is; raise ?) 13 | end 14 | 15 | structure Map = struct 16 | open StringMap 17 | fun argsForWhich (m:'a map) (p : 'a -> bool) : string list = 18 | Fold (fn ((s,v),a) => if p v then s::a else a) nil m 19 | end 20 | 21 | (* Catenable strings *) 22 | structure CS = CString 23 | infix 6 & (* as ^ *) 24 | val op & = CS.& 25 | val op $ = CS.$ 26 | 27 | (* Lexing of sml tokens *) 28 | signature LEX = sig 29 | type id = string 30 | type excon = id 31 | type tycon = id 32 | type strid = id 33 | type sigid = id 34 | datatype t = ID of id 35 | | SEP of string 36 | | SPACE of string 37 | | COMMENT of string 38 | | KW of string 39 | | LINK of {href:string,elem:string} 40 | val lex : string -> t list 41 | val defs : t list -> id list * excon list * tycon list * (strid*sigid) list 42 | val conv : (strid -> sigid option) -> t list -> t list 43 | val isKw : string -> bool 44 | val pp : t list -> CS.t 45 | end 46 | 47 | fun htmlencode s : string = 48 | let fun enc #"<" = "<" 49 | | enc #">" = ">" 50 | | enc #"&" = "&" 51 | | enc #"\"" = """ 52 | | enc c = String.str c 53 | in String.translate enc s 54 | end 55 | 56 | fun taga t a e = $("<" ^ t ^ a ^ ">") & e & $("") 57 | fun tag t e = taga t "" e 58 | fun taga0 t a = taga t a ($"") 59 | fun tag0 t = $("<" ^ t ^ " />") 60 | fun tdwl w e = taga "td" (" width='" ^ Int.toString w ^ "%' align='left'") e 61 | 62 | fun encode id = 63 | let val chars = "#%&/<>-_+'~^:!@$=?|" 64 | fun i_to_s i = if i < 10 then "0" ^ Int.toString i else Int.toString i 65 | fun f c = case CharVector.findi (fn (_,e) => c = e) chars of 66 | SOME (i,_) => "$" ^ i_to_s i 67 | | NONE => String.str c 68 | in String.translate f id 69 | end 70 | 71 | fun plingencode id = 72 | String.translate (fn #"'" => "\\'" | c => String.str c) id 73 | 74 | fun init_space s = 75 | Char.isSpace(String.sub(s,0)) 76 | handle _ => false 77 | 78 | fun remove_init_ws s = 79 | let fun loop n = 80 | if Char.isSpace(String.sub(s,n)) then loop (n+1) 81 | else n 82 | in String.extract(s,loop 0,NONE) 83 | end handle _ => "" 84 | 85 | structure Lex : LEX = 86 | struct 87 | type id = string 88 | type excon = id 89 | type tycon = id 90 | type strid = id 91 | type sigid = id 92 | datatype t = ID of id 93 | | SEP of string 94 | | SPACE of string 95 | | COMMENT of string 96 | | KW of string 97 | | LINK of {href:string,elem:string} 98 | 99 | fun sOf (ID s) = $ s 100 | | sOf (SEP s) = $ s 101 | | sOf (SPACE s) = $ s 102 | | sOf (COMMENT s) = $ s 103 | | sOf (KW s) = tag "b" ($s) 104 | | sOf (LINK {href,elem}) = taga "a" (" href='" ^ href ^ "'") ($elem) 105 | 106 | (* signature level key words *) 107 | fun isKw s = 108 | case s of 109 | "and" => true 110 | | "eqtype" => true 111 | | "end" => true 112 | | "exception" => true 113 | | "sharing" => true 114 | | "sig" => true 115 | | "signature" => true 116 | | "structure" => true 117 | | "type" => true 118 | | "datatype" => true 119 | | "val" => true 120 | | "include" => true 121 | | "where" => true 122 | | _ => false 123 | 124 | fun spaces t = 125 | case t of 126 | SPACE s :: rest => 127 | (case spaces rest of 128 | NONE => SOME(s,rest) 129 | | SOME (sp,r) => SOME(s^sp,r)) 130 | | _ => NONE 131 | 132 | fun valId t = 133 | case t of 134 | ID "val" :: t => 135 | (case spaces t of 136 | SOME(space, ID id :: rest) => SOME (SPACE space,id,rest) 137 | | _ => NONE) 138 | | _ => NONE 139 | 140 | fun structureSigid t = 141 | case t of 142 | ID "structure" :: t => 143 | (case spaces t of 144 | SOME(space, ID strid :: rest) => 145 | (case spaces rest of 146 | SOME(space2, ID symb :: rest2) => 147 | if symb = ":" orelse symb = ":>" then 148 | (case spaces rest2 of 149 | SOME(space3, ID sigid :: rest) => 150 | if sigid <> "sig" then 151 | SOME (SPACE space, strid, SPACE space2, ID ":", SPACE space3, sigid, rest) 152 | else NONE 153 | | _ => NONE) 154 | else NONE 155 | | _ => NONE) 156 | | _ => NONE) 157 | | _ => NONE 158 | 159 | fun includeSigid t = 160 | case t of 161 | ID "include" :: t => 162 | (case spaces t of 163 | SOME(sp, ID sigid :: rest) => SOME(SPACE sp,sigid,rest) 164 | | _ => NONE) 165 | | _ => NONE 166 | 167 | fun longid id = 168 | case String.fields (fn c => c = #".") id of 169 | [id1,id2] => SOME (id1,id2) 170 | | _ => NONE 171 | 172 | fun conv lookupStructure t = 173 | let fun conv0 t = 174 | case valId t of 175 | SOME(sp,id,rest) => 176 | KW "val" :: sp :: LINK{href="#" ^ encode id, 177 | elem=id} :: conv0 rest 178 | | NONE => 179 | case structureSigid t of 180 | SOME (sp,strid,sp2,colon,sp3,sigid,rest) => 181 | (KW "structure" :: sp :: ID strid :: sp2 :: colon :: sp3 :: 182 | LINK{href=sigid ^ ".sml.html", 183 | elem=sigid} :: conv0 rest) 184 | | NONE => 185 | case includeSigid t of 186 | SOME(sp,sigid,rest) => 187 | (KW "include" :: sp :: 188 | LINK{href=sigid ^ ".sml.html", 189 | elem=sigid} :: conv0 rest) 190 | | NONE => 191 | case t of 192 | nil => nil 193 | | ID id :: rest => 194 | if isKw id then KW id :: conv0 rest 195 | else (case longid id of 196 | SOME(id1,id2) => 197 | (case lookupStructure id1 of 198 | SOME sigid => 199 | let val file = sigid ^ ".sml.html" 200 | in LINK{href=file,elem=id1} :: ID "." :: ID id2 :: conv0 rest 201 | end 202 | | NONE => 203 | ID id1 :: ID "." :: ID id2 :: conv0 rest) 204 | | NONE => ID id :: conv0 rest) 205 | | e :: rest => e :: conv0 rest 206 | in conv0 t 207 | end 208 | 209 | fun pp ts = CS.concat(map sOf ts) 210 | 211 | type pos = int 212 | type level = int 213 | 214 | type stream = string * pos 215 | 216 | fun getc (s,p) = 217 | if String.size s > p then 218 | SOME(String.sub(s,p),(s,p+1)) 219 | else NONE 220 | 221 | fun pr_error_pos (s,p) = 222 | "Error at position " ^ Int.toString p ^ ": " ^ String.extract(s,p,NONE) 223 | 224 | fun read_chars (f:char->bool) (is:stream) (C:string -> t) = 225 | let fun read is a = 226 | case getc is of 227 | SOME(c,is') => 228 | if f c then 229 | read is' (c::a) 230 | else if a <> [] then 231 | SOME(C(String.implode(rev a)),is) 232 | else NONE 233 | | NONE => 234 | if a <> [] then 235 | SOME(C(String.implode(rev a)),is) 236 | else NONE 237 | in read is [] 238 | end 239 | 240 | fun read_id is = 241 | let fun isIdChar c = 242 | Char.isAlpha c orelse Char.isDigit c orelse c = #"'" orelse c = #"_" orelse c = #"." 243 | in read_chars isIdChar is ID 244 | end 245 | 246 | fun read_symb is = 247 | let val symbolChars = "@$/*-+<>!#%?^~:|=&" 248 | fun isSymbolChar c = CharVector.exists (fn c' => c=c') symbolChars 249 | in read_chars isSymbolChar is ID 250 | end 251 | 252 | fun read_sep is = 253 | let val sepChars = ",;{}[]()" 254 | fun isSepChar c = CharVector.exists (fn c' => c=c') sepChars 255 | in case getc is of 256 | SOME(c,is') => 257 | if isSepChar c then SOME(SEP(String.str c),is') 258 | else NONE 259 | | NONE => NONE 260 | end 261 | 262 | fun read_space is = 263 | read_chars Char.isSpace is SPACE 264 | 265 | fun read_comment is = 266 | let fun read lev is a = 267 | case getc is of 268 | SOME(#"(",is1) => 269 | (case getc is1 of 270 | SOME(#"*",is2) => 271 | read (lev+1) is2 (#"*":: #"("::a) 272 | | _ => read lev is1 (#"("::a)) 273 | | SOME(#"*",is1) => 274 | (case getc is1 of 275 | SOME(#")",is2) => 276 | let val a = #")":: #"*"::a 277 | in if lev=1 then 278 | SOME(COMMENT(implode(rev a)),is2) 279 | else read (lev-1) is2 a 280 | end 281 | | _ => read lev is1 (#"*"::a)) 282 | | SOME(c,is1) => read lev is1 (c::a) 283 | | NONE => raise Fail "immature end of comment" 284 | in case getc is of 285 | SOME(#"(",is1) => 286 | (case getc is1 of 287 | SOME(#"*",is2) => 288 | read 1 is2 [#"*", #"("] 289 | | _ => NONE) 290 | | _ => NONE 291 | end 292 | 293 | fun eos (s,i) = i >= String.size s 294 | 295 | fun lex s = 296 | let fun read (is:stream) (a:t list) : t list = 297 | case read_space is of 298 | SOME(t,is) => read is (t::a) 299 | | NONE => 300 | case read_comment is of 301 | SOME(t,is) => read is (t::a) 302 | | NONE => 303 | case read_sep is of 304 | SOME(t,is) => read is (t::a) 305 | | NONE => 306 | case read_symb is of 307 | SOME(t,is) => read is (t::a) 308 | | NONE => 309 | case read_id is of 310 | SOME(t,is) => read is (t::a) 311 | | NONE => 312 | if eos is then rev a 313 | else raise Fail ("Error reading string: " ^ pr_error_pos is) 314 | in read (s,0) nil 315 | end 316 | 317 | fun defs ts = 318 | let fun tyvar id = 319 | case explode id of 320 | #"'" :: _ => true 321 | | _ => false 322 | fun eat_tyvars ts = 323 | case ts of 324 | ID id :: SPACE _ :: ts' => 325 | if tyvar id then ts' else ts 326 | | _ => ts 327 | fun typespec "type" = true 328 | | typespec "eqtype" = true 329 | | typespec "datatype" = true 330 | | typespec _ = false 331 | 332 | fun loop (acc as (ids,tycons,excons,strs)) ts = 333 | case ts of 334 | ID "val" :: SPACE _ :: ID id :: ts => 335 | loop (id::ids,tycons,excons,strs) ts 336 | | ID "exception" :: SPACE _ :: ID excon :: ts => 337 | loop (ids,tycons,excon::excons,strs) ts 338 | | ID "structure" :: SPACE _ :: ID strid :: 339 | SPACE _ :: ID ":" :: SPACE _ :: ID sigid :: ts => 340 | loop (ids,tycons,excons,(strid,sigid)::strs) ts 341 | | ID "structure" :: SPACE _ :: ID strid :: 342 | SPACE _ :: ID ":>" :: SPACE _ :: ID sigid :: ts => 343 | loop (ids,tycons,excons,(strid,sigid)::strs) ts 344 | | ID kw :: SPACE _ :: ts => 345 | if typespec kw then 346 | (case eat_tyvars ts of 347 | ID tycon :: ts => 348 | loop (ids,tycon::tycons,excons,strs) ts 349 | | _ => loop acc ts) 350 | else loop acc ts 351 | | _ :: ts => loop acc ts 352 | | nil => acc 353 | in 354 | loop ([],[],[],[]) ts 355 | end 356 | end 357 | 358 | structure R = RegExp 359 | 360 | type sigid = string 361 | type strid = string 362 | type id = string 363 | 364 | datatype origin = ORIGIN_BASIS 365 | | ORIGIN_PKG of string * string 366 | | ORIGIN_NONE 367 | 368 | type sigmap = {short_comment:string, long_comment:string, 369 | src:string, origin:origin, comments:string} Map.map (* dom=sigid *) 370 | 371 | fun pkg_id p = 372 | let val re = RegExp.fromString ".*github.com/.*/(.*)" 373 | in case RegExp.extract re p of 374 | SOME [id] => id 375 | | _ => p 376 | end 377 | 378 | fun find_origin pkgvmap s = 379 | let val re_pkg = RegExp.fromString ".*(github.com/.*/.*)/.*" 380 | val re_bas = RegExp.fromString ".*basis.*" 381 | in case RegExp.extract re_pkg s of 382 | SOME [p] => 383 | (case Map.lookup pkgvmap p of 384 | SOME v => ORIGIN_PKG (p,v) 385 | | NONE => ORIGIN_PKG (p,"")) 386 | | _ => if RegExp.match re_bas s then ORIGIN_BASIS 387 | else ORIGIN_NONE 388 | end 389 | 390 | exception SigFormatError of string 391 | fun read_sig pkgvmap (f:string) (s:string) : (sigmap * string) option = 392 | let val re = ".*\\(\\*\\*(.*)\\*\\).*(signature ([0-9a-zA-Z_]+) .*end[\n ]+(where .*)?)[\n ]*\\(\\*\\*(.*)\\*\\)(.*)" 393 | fun doit (c,sigid,src,cs,rest) = 394 | let val origin = find_origin pkgvmap f 395 | val (shortc,longc) = 396 | case R.extract (R.fromString "([^\n]*)\n[ ]*\n(.*)") c of 397 | SOME [shortc,longc] => (shortc,longc) 398 | | _ => if CharVector.exists (fn c => c = #"\n") c then ("",c) else (c,"") 399 | val m = Map.singleton(sigid, {short_comment=shortc, long_comment=longc, 400 | src=src, comments=cs, origin=origin}) 401 | in SOME (m,rest) 402 | end 403 | in case R.extract (R.fromString re) s of 404 | SOME [c,sigid,src,cs,rest] => doit(c,sigid,src,cs,rest) 405 | | SOME [c,sigid,whe,src,cs,rest] => doit(c,sigid,src,cs,rest) 406 | | SOME ss => (List.app (fn s => print(s ^ "\n")) ss; 407 | raise Fail "read_sig wrong format 0") 408 | | NONE => 409 | case R.extract (R.fromString ".*signature ([0-9a-zA-Z_]+)[ \n]*=.*") s of 410 | SOME [sigid] => 411 | ( print("Warning: Signature binding for '" ^ sigid ^ "' not on the form " ^ 412 | "'(**...*)...signature XXX =...sig...end...(**...*)'\n") 413 | ; NONE) 414 | | SOME _ => raise Fail "read_sig wrong format 1" 415 | | NONE => NONE 416 | end 417 | 418 | fun read_sigs pkgvmap (f:string) : sigmap = 419 | let fun loop s = 420 | case read_sig pkgvmap f s of 421 | SOME(m,rest) => Map.plus(m,loop rest) 422 | | NONE => Map.empty 423 | val s = readFile f 424 | in loop s 425 | end 426 | 427 | type strmap = {sigid:sigid,short_comment:string,origin:origin} Map.map (* StrId -> SigId *) 428 | 429 | fun read_impl pkgvmap (sigmap:sigmap) (f:string) : strmap = 430 | let val origin = find_origin pkgvmap f 431 | fun start acc s = 432 | let fun doit (c,strid,sigid,rest) = 433 | let val c = case R.extract (R.fromString "\\(\\*(\\*)?(.*)\\*\\)") c of 434 | SOME [_,c] => c 435 | | _ => "" 436 | val acc = case Map.lookup sigmap sigid of 437 | NONE => (print ("Skipping " ^ strid ^ " : " ^ sigid ^ 438 | ", as " ^ sigid ^ " is not known.\n"); 439 | acc) 440 | | SOME _ => Map.add(strid,{sigid=sigid,short_comment=c,origin=origin},acc) 441 | in cont acc rest 442 | end 443 | in case R.extract (R.fromString "[ \n]*(\\(\\*\\*?[^*]*\\*\\))?[ \n]*structure ([0-9a-zA-Z_]+) :>? ([0-9a-zA-Z_]+)[\n ]+(.*)") s of 444 | SOME [c,strid,sigid,rest] => doit (c,strid,sigid,rest) 445 | | SOME [strid,sigid,rest] => doit ("",strid,sigid,rest) 446 | | SOME ss => (List.app (fn s => print(s ^ "\n")) ss; 447 | raise Fail "read_impl wrong format 1") 448 | | NONE => cont acc s 449 | end 450 | and cont acc s = 451 | case R.extract (R.fromString ".*\\(\\*\\* SigDoc \\*\\)[ \n]*structure ([0-9a-zA-Z_]+) :>? ([0-9a-zA-Z_]+)[\n ]+(.*)") s of 452 | SOME [strid,sigid,rest] => 453 | let val acc = case Map.lookup sigmap sigid of 454 | NONE => (print ("Skipping " ^ strid ^ " : " ^ sigid ^ 455 | ", as " ^ sigid ^ " is not known.\n"); 456 | acc) 457 | | SOME _ => Map.add(strid,{sigid=sigid,short_comment="",origin=origin},acc) 458 | in cont acc rest 459 | end 460 | | SOME _ => raise Fail "cont" 461 | | NONE => acc 462 | val m = start Map.empty (readFile f) 463 | in if Map.isEmpty m then print ("No structures to document in " ^ f ^ "\n") else () 464 | ; m 465 | end 466 | 467 | fun match_id nil h = NONE 468 | | match_id (id::ids) h = 469 | if h = id orelse String.isPrefix (id ^ " ") h orelse 470 | String.isPrefix (id ^ "(") h orelse 471 | String.isSubstring (" " ^ id ^ " ") h then 472 | SOME id 473 | else match_id ids h 474 | 475 | fun pp_comments (ids,tycons,excons,strs) s = 476 | let (*val () = print ("ids = " ^ Int.toString (length ids) ^"\n")*) 477 | (*(*(* 478 | fun loop cs s = 479 | case R.extract (R.fromString "(.*)\\n\\[([\\-0-9a-zA-Z_:<>=&%#@|'!*/$(), ]+)\\](.*)") s of 480 | SOME [rest,h,b] => 481 | loop ((h,b)::cs) rest 482 | | SOME [h,b] => ((h,b)::cs) 483 | | SOME ss => (List.app (fn s => print(s ^ "\n")) ss; 484 | raise Fail "pp_comments wrong format 1") 485 | | NONE => cs 486 | *) 487 | val lines = String.fields (fn #"\n" => true | _ => false) s 488 | val lines = map (fn s => s ^ "\n") lines 489 | fun read_head s = 490 | (case CharVector.findi (fn (_,#"]") => true | _ => false) s of 491 | SOME (i,_) => 492 | (String.extract(s,1,SOME (i-1)), 493 | String.extract(s,i+1,NONE) handle _ => "") 494 | | NONE => raise Fail "read_head") 495 | handle _ => raise Fail ("read_head2:" ^ s) 496 | fun only_ws s = CharVector.all Char.isSpace s 497 | fun eat_wss (s::ss) = if only_ws s then eat_wss ss 498 | else s::ss 499 | | eat_wss nil = nil 500 | fun finalize a cs = 501 | case eat_wss(rev a) of 502 | l0::ls => 503 | let val (h,r) = read_head l0 504 | in (h, concat (r::ls))::cs 505 | end 506 | | nil => cs 507 | fun loop cs a (l::ls) = 508 | if String.isPrefix "[" l then 509 | loop (finalize a cs) [l] ls 510 | else loop cs (l::a) ls 511 | | loop cs a nil = rev(finalize a cs) 512 | val cs = loop [] [] lines 513 | fun layout_head h = if CS.toString h = "Discussion" 514 | then tag "i" h 515 | else ($"[") & tag "tt" h & ($"]") 516 | fun layout_body b = 517 | let 518 | val b = remove_init_ws b 519 | val lines = String.fields (fn #"\n" => true | _ => false) b 520 | fun next_init_space ls = 521 | (init_space(hd ls)) 522 | handle _ => false 523 | fun loop nil true acc = "\n"::acc 524 | | loop nil false acc = acc 525 | | loop (l0::ls) ispre acc = 526 | let val l = l0 ^ "\n" 527 | in 528 | if ispre then 529 | if init_space l0 then 530 | if next_init_space ls then 531 | loop ls true (l::acc) 532 | else loop ls true (l::acc) (* was acc *) 533 | else loop ls false (l::"\n"::acc) 534 | else if only_ws l then 535 | loop ls false acc 536 | else if init_space l then 537 | loop ls true (l::"
\n"::acc)
538 |                   else loop ls false (l::acc)
539 |                 end
540 |           in $(concat(rev(loop lines false nil)))
541 |           end
542 |     in tag "dl"
543 |            (CS.concat
544 |                 (map (fn (h,b) =>
545 |                          let
546 |                            val h2 = htmlencode h
547 |                            val h3 =
548 |                                case match_id ids h of
549 |                                  SOME id =>
550 |                                  let val name = encode id
551 |                                  in taga "a" (" name='" ^ name ^ "'") ($h2)
552 |                                  end
553 |                                | NONE => $ h2
554 |                            val b = htmlencode b
555 |                          in if only_ws b then tag "dt" (tag "b" (layout_head h3))
556 |                             else tag "dt" (tag "b" (layout_head h3)) & ($" ")
557 |                                      & tag "dd" (layout_body b) & tag0 "br"
558 |                          end) cs))
559 |     end
560 | 
561 | val libpath : string ref = ref ""
562 | val about_html : string ref = ref ""
563 | val logo_html : string ref = ref ""
564 | val pkgfile : string ref = ref ""
565 | 
566 | fun load_about () =
567 |     case !about_html of
568 |         "" => NONE
569 |       | f => SOME (readFile f)
570 |              handle _ => raise Fail ("Failed to read file " ^ f)
571 | 
572 | fun page h idx b =
573 |     let val str_idx_html = "str_idx.html"
574 |         val sig_idx_html = "sig_idx.html"
575 |         val id_idx_html = "id_idx.html"
576 |         val pkg_idx_html = "pkg_idx.html"
577 |         val generated_tags_js = "generated_tags.js"
578 | 
579 |         val style_css = OS.Path.concat(!libpath,"style.css")
580 |         val jquery_ui_css = OS.Path.concat(!libpath,"jquery-ui.css")
581 |         val jquery_1_9_1_js = OS.Path.concat(!libpath,"jquery-1.9.1.js")
582 |         val jquery_ui_js = OS.Path.concat(!libpath,"jquery-ui.js")
583 | 
584 |         val str_idx_link = taga "a" (" href='" ^ str_idx_html ^ "'") ($"Structures")
585 |         val sig_idx_link = taga "a" (" href='" ^ sig_idx_html ^ "'") ($"Signatures")
586 |         val id_idx_link = taga "a" (" href='" ^ id_idx_html ^ "'") ($"Identifiers")
587 |         val pkg_idx_link = taga "a" (" href='" ^ pkg_idx_html ^ "'") ($"Packages")
588 |         val search = taga0 "input" " id='tags' placeholder='Search' style='width:100%; margin-right:20px;'"
589 |         val head =
590 |           tag "head"
591 |             (taga0 "link" (" rel='stylesheet' href='" ^ jquery_ui_css ^ "'") &
592 |              taga0 "script" (" src='" ^ jquery_1_9_1_js ^ "'") &
593 |              taga0 "script" (" src='" ^ jquery_ui_js ^ "'") &
594 |              taga0 "script" (" src='" ^ generated_tags_js ^ "'") &
595 |              taga0 "link" (" rel='stylesheet' href='" ^ style_css ^ "'") &
596 |              tag "script" ($"function copyText(text) { console.log('hi'); try { navigator.clipboard.writeText(text); console.log('Text copied!'); } catch (err) { console.error('error copying text', err); } }") &
597 |              tag "script"
598 |                ($ "$(function() { \
599 |                   \  $( '#tags' ).autocomplete({\
600 |                   \    source: availableTags,\
601 |                   \    select: function(event,ui){ window.location = ui.item.value; }\
602 |                   \  });\
603 |                   \});")
604 |             )
605 |         fun tr e = tag "tr" e
606 |         fun tdr e = taga "td" " align='right'" e
607 |         fun tdc e = taga "td" " align='center'" e
608 |         fun td e = tag "td" e
609 | 
610 |         val about_link =
611 |             if !about_html = ""
612 |             then $""
613 |             else tdr (taga "a" (" href='about.html'") ($"About"))
614 | 
615 |         val logo =
616 |             if !logo_html = ""
617 |             then $""
618 |             else td ($(!logo_html))
619 |     in
620 |       tag "html"
621 |           (tag "head" head &
622 |            tag "body"
623 |            (taga "table" " width='100%'"
624 |                  (tr (logo & td ($"Library Documentation")
625 |                            & tdc str_idx_link
626 |                            & tdc sig_idx_link
627 |                            & tdc id_idx_link
628 |                            & tdc pkg_idx_link
629 |                            & about_link)) &
630 |               taga "p" " style='width:100%'" search &
631 |               tag "h4" h &
632 |               tag "p" idx &
633 |               b &
634 |               tag0 "hr" &
635 |               tag "i" ($"Generated by " & taga "a" (" href='" ^ sigdoc_url ^ "'") ($"SigDoc"))))
636 |     end
637 | 
638 | fun strs_for_sigid sigid (strmap:strmap) =
639 |     Map.argsForWhich strmap (fn {sigid=x,...} => sigid=x)
640 | 
641 | fun pp_version v =
642 |     $" " & taga "span" " class='vbadge'" ($("v" ^ v))
643 | 
644 | fun pp_origin origin =
645 |     case origin of
646 |         ORIGIN_BASIS => tag "i" ($ "(basis)")
647 |       | ORIGIN_PKG (p,v) =>
648 |         let val version = case v of
649 |                               "" => $""
650 |                             | _ => pp_version v
651 |         in tag "i"
652 |                ($"(pkg " &
653 |                  taga "a" (" href='http://" ^ p ^ "'") ($p) &
654 |                  version & $")")
655 |         end
656 |       | ORIGIN_NONE => tag "i" ($"(none)")
657 | 
658 | fun pp strmap (sigid, {short_comment,long_comment,src,comments,origin}) =
659 |     let val ts = Lex.lex src
660 |       val ids = Lex.defs ts
661 |       val idmap = Map.singleton(sigid,ids)
662 |       val ts = Lex.conv (fn x =>
663 |                             case Map.lookup strmap x of
664 |                                 SOME {sigid,...} => SOME sigid
665 |                               | NONE => NONE) ts
666 |       val src2 = Lex.pp ts
667 |       open Lex
668 |       fun layout_struct x =
669 |           tag "b" ($("structure " ^ x ^ " : " ^ sigid ^ " ") & pp_origin origin & ($"\n"))
670 |       val space = $" "
671 |       val comments =
672 |           pp_comments ids comments
673 |           handle Fail s => (print ("Warning: Failed to print comments for " ^ sigid ^ " - " ^ s ^ "\n"); $"")
674 |       val structures =
675 |           let val strs = strs_for_sigid sigid strmap
676 |           in case strs of
677 |                  nil => $""
678 |                | _ => tag "pre" (CS.concat (map layout_struct strs))
679 |                           & tag0 "hr"
680 |           end
681 |       val output =
682 |           page
683 |               ($"Signature " & tag "code" ($sigid) & space & tag "tt" (pp_origin origin))
684 |               ($(htmlencode short_comment))
685 |               ($(htmlencode long_comment) &
686 |                 tag0 "hr" &
687 |                 structures &
688 |                 tag "pre" src2 &
689 |                 tag0 "hr" &
690 |                 comments)
691 |     in (output, idmap)
692 |     end
693 | 
694 | fun writeFile f a =
695 |     let val () = print ("Writing file: " ^ f ^ "\n")
696 |         val os = TextIO.openOut f
697 |     in (TextIO.output(os,a);
698 |         TextIO.closeOut os)
699 |        handle ? => (TextIO.closeOut os; raise ?)
700 |     end
701 | 
702 | fun gen_idx {head: string,
703 |              lines: 'a list,
704 |              line_id: 'a -> string,
705 |              line_entry: 'a -> CS.t,
706 |              line_bodies: 'a -> CS.t list,
707 |              sep: string} =
708 |     let
709 |       fun section ch part =
710 |           if CS.toString part = "" then $""
711 |           else
712 |           let val h =
713 |                   if Char.isAlpha ch then String.str (Char.toUpper ch)
714 |                   else "Symbols"
715 |               val h = taga "a" (" name='"^h^"'") ($h)
716 |           in tag "h4" h & taga "table" " width='100%'" part
717 |           end
718 |       val (ch,ch_acc,acc,chs) =
719 |           foldl (fn (line,(ch,ch_acc,acc,chs)) =>
720 |                     let
721 |                       val bodies = line_bodies line
722 |                       val id = line_id line
723 |                       val ch2 = String.sub(id,0)
724 |                       val e = line_entry line
725 |                       val n = List.length bodies
726 |                       val bodies = List.map (tdwl (75 div n)) bodies
727 |                       val entry = tag "tr" (tdwl 20 (tag "tt" e) & tdwl 5 ($sep) & (CS.concat bodies))
728 |                     in if not(Char.isAlpha ch2) orelse ch=ch2 then
729 |                          (ch,ch_acc & entry,acc,chs)
730 |                        else (ch2,entry,acc & section ch ch_acc,ch2::chs)
731 |                     end)
732 |                 (#"*",$"",$"",nil) lines
733 |       val cs = acc & section ch ch_acc
734 |       val idx =
735 |           CS.concatWith " | "
736 |           (map (fn c =>
737 |                    let val h = String.str (Char.toUpper c)
738 |                    in taga "a" (" href='#"^h^"'") ($h)
739 |                    end) (rev chs))
740 |     in page ($head) idx cs
741 |     end
742 | 
743 | fun gen_sig_idx (sigmap:sigmap, strmap) =
744 |     let val sigs = Map.list sigmap
745 |         val sigs = ListSort.sort (fn (x,y) => String.compare (#1 x,#1 y)) sigs
746 |         val im = List.map (fn (s,{short_comment,...}) =>
747 |                               let val t = s ^ ".sml.html"
748 |                                   val strs = map (fn s => tag "tt" ($s)) (strs_for_sigid s strmap)
749 |                                   val strs = CS.concatWith ", " strs
750 |                               in (s, taga "a" (" href='" ^ t ^ "'")
751 |                                           (tag "tt" ($s)), [strs, tag "i" ($short_comment)])
752 |                               end) sigs
753 |         val cs = gen_idx {head="Signatures",
754 |                           lines=im,
755 |                           line_id= #1,
756 |                           line_entry= #2,
757 |                           line_bodies= #3,
758 |                           sep=" "}
759 |     in writeFile "sig_idx.html" (CS.toString cs)
760 |     end
761 | 
762 | fun pp_sigid s sigid =
763 |     let val t = sigid ^ ".sml.html"
764 |     in taga "a" (" href='" ^ t ^ "'") (tag "tt" ($s))
765 |     end
766 | 
767 | fun gen_str_idx (sigmap:sigmap, strmap) =
768 |     let val strs = Map.list strmap
769 |         val strs = ListSort.sort (fn ((x,y),(x1,y1)) => String.compare(x,x1)) strs
770 |         val im = List.map (fn (strid,{sigid,short_comment,origin}) =>
771 |                               let val c =
772 |                                     if short_comment <> "" then short_comment
773 |                                     else
774 |                                       case Map.lookup sigmap sigid of
775 |                                         SOME {short_comment,...} => short_comment
776 |                                       | NONE => ""
777 |                               in (strid, tag "div" ($strid), [pp_sigid sigid sigid, tag "i" ($c)])
778 |                               end) strs
779 |         val cs = gen_idx {head="Structures",
780 |                           lines=im,
781 |                           line_id= #1,
782 |                           line_entry = #2,
783 |                           line_bodies= #3,
784 |                           sep=":"}
785 |     in writeFile "str_idx.html" (CS.toString cs)
786 |     end
787 | 
788 | type pkgversion = string
789 | 
790 | fun read_pkgfile () : pkgversion Map.map =
791 |     case !pkgfile of
792 |         "" => Map.empty
793 |       | pkgf => let val re = RegExp.fromString ".*([^ ]+)[ \n]+([.0-9]+)[ \n]+(.*)"
794 |                     fun loop s m =
795 |                         case RegExp.extract re s of
796 |                             SOME [p, v, rest] => loop rest (Map.add(p,v,m))
797 |                           | _ => m
798 |                 in loop (readFile pkgf) Map.empty
799 |                 end
800 | 
801 | type pkgmap = {full:string, sigs:unit Map.map, impls:string Map.map, version:string} Map.map
802 | 
803 | fun copyText s = taga "span" " class='tooltip'" (taga "span" " class='tooltiptext'" ($"Copy Package URL") & taga "button" (" class='button1' onclick=\"copyText('http://" ^ s ^ "')\"") ($"❐"))
804 | 
805 | fun gen_pkg_idx (sigmap:sigmap, strmap:strmap, pkgvmap:pkgversion Map.map) =
806 |     let
807 |       fun insert_sig (p,sigid,m) =
808 |           let val id = pkg_id p
809 |           in case Map.lookup m id of
810 |                  SOME {full,sigs,impls} =>
811 |                  Map.add(id,{full=full,sigs=Map.add(sigid,(),sigs),impls=impls},m)
812 |                | NONE => Map.add(id,{full=p,sigs=Map.singleton(sigid,()),impls=Map.empty},m)
813 |           end
814 |       fun insert_str (p,strid,sigid,m) =
815 |           let val id = pkg_id p
816 |           in case Map.lookup m id of
817 |                  SOME {full,sigs,impls} =>
818 |                  Map.add(id,{full=full,sigs=sigs,impls=Map.add(strid,sigid,impls)},m)
819 |                | NONE => Map.add(id,{full=p,sigs=Map.empty,impls=Map.singleton(strid,sigid)},m)
820 |           end
821 | 
822 |       val pkgmap = Map.Fold (fn ((sigid,{origin,...}),a) =>
823 |                                 case origin of
824 |                                     ORIGIN_PKG (p,_) => insert_sig(p,sigid,a)
825 |                                   | ORIGIN_BASIS => insert_sig("basis",sigid,a)
826 |                                   | ORIGIN_NONE => insert_sig("unknown",sigid,a)) Map.empty sigmap
827 |       val pkgmap = Map.Fold (fn ((strid,{origin,sigid,...}),a) =>
828 |                                 case origin of
829 |                                     ORIGIN_PKG (p,_) => insert_str(p,strid,sigid,a)
830 |                                   | ORIGIN_BASIS => insert_str("basis",strid,sigid,a)
831 |                                   | ORIGIN_NONE => insert_str("unknown",strid,sigid,a)) pkgmap strmap
832 | 
833 |       val im = List.map (fn (p,{full,sigs,impls}) =>
834 |                             let val sigs = List.foldr (fn (s,a) => pp_sigid s s & ($" ") & a) ($"") (Map.dom sigs)
835 |                                 val strs = List.foldr (fn ((s,sigid),a) => pp_sigid s sigid & ($" ") & a) ($"") (Map.list impls)
836 |                                 val link = if p = "basis" then $"Basis Library"
837 |                                            else taga "a" (" href='http://" ^ full ^ "' title='" ^ full ^ "'")
838 |                                                      (tag "tt" ($p)) & $" " & copyText full
839 |                                 val info =
840 |                                     if p = "basis" then $""
841 |                                     else
842 |                                       let val pkgversion =
843 |                                               case Map.lookup pkgvmap full of
844 |                                                   SOME v => pp_version v
845 |                                                 | NONE => $""
846 |                                           val badge = "https://" ^ full ^ "/workflows/CI/badge.svg"
847 |                                       in
848 |                                         pkgversion & $" " &
849 |                                          taga "a" (" href='https://" ^ full ^ "/actions'")
850 |                                          (taga0 "img" (" style='vertical-align:bottom' src='"
851 |                                                        ^ badge ^ "'"))
852 |                                       end
853 |                             in (p, link,
854 |                                 [info, $"Signatures: " & sigs,
855 |                                  $"Structures: " & strs]
856 |                                )
857 |                             end) (Map.list pkgmap)
858 |       val cs = gen_idx {head="Packages",
859 |                         lines=im,
860 |                         line_id= #1,
861 |                         line_entry= #2,
862 |                         line_bodies= #3,
863 |                         sep=" "}
864 |     in writeFile "pkg_idx.html" (CS.toString cs)
865 |     end
866 | 
867 | fun gen_id_idx (idmap, sigmap, strmap) =
868 |     let val im = Map.Fold (fn ((sigid, (ids,_,_,_)),a) =>
869 |                            let val strs = strs_for_sigid sigid strmap
870 |                            in map (fn id => (id,sigid,strs)) ids @ a
871 |                            end) nil idmap
872 |       val im = ListSort.sort (fn((id,_,_),(id2,_,_)) => String.compare (id,id2)) im
873 |       fun compact nil a = rev a
874 |         | compact ((id,sigid,strs)::rest) nil =
875 |           compact rest [(id,[(sigid,strs)])]
876 |         | compact ((id,sigid,strs)::rest) (acc as ((id2,args)::acc2)) =
877 |           if id = id2
878 |           then compact rest ((id,(sigid,strs)::args)::acc2)
879 |           else compact rest ((id,[(sigid,strs)])::acc)
880 |       val im = compact im nil
881 |       fun layout_impls (id, nil) = $""
882 |         | layout_impls (id, (sigid,strs)::rest) =
883 |           List.foldl (fn (s,a) => a & taga "a" (" href='" ^ sigid ^ ".sml.html'") (tag "tt" ($s))
884 |                                     & (tag "tt" ($("." ^ id))) & ($" "))
885 |                      ($"") strs & layout_impls (id, rest)
886 |       val cs =
887 |           gen_idx {head="Identifiers",
888 |                    lines=im,
889 |                    line_id= #1,
890 |                    line_entry = $ o #1,
891 |                    line_bodies=fn x => [layout_impls x],
892 |                    sep=" "}
893 |       fun qq s = "'" ^ s ^ "'"
894 |       fun pair e1 e2 = "{label:" ^ e1 ^ ",value:" ^ e2 ^ "}"
895 |       fun prtag id strid sigid = pair (qq (plingencode(strid ^ "." ^ id))) (qq(sigid ^ ".sml.html"))
896 |       fun tags nil = $""
897 |         | tags ((id, nil)::rest) = tags rest
898 |         | tags ([(id, [(sigid,[strid])])]) = $(prtag id strid sigid)
899 |         | tags ((id, (sigid,nil)::es)::rest) = tags ((id,es)::rest)
900 |         | tags ((id, (sigid,strid::strids)::es)::rest) =
901 |           $(prtag id strid sigid) & $"," & tags ((id,(sigid,strids)::es)::rest)
902 |       val alltags = $"var availableTags = [" & tags im & $"];"
903 |     in writeFile "id_idx.html" (CS.toString cs)
904 |      ; writeFile "generated_tags.js" (CS.toString alltags)
905 |     end
906 | 
907 | fun gen (files:string list) =
908 |     (* Look in all files for signature snippets on the form
909 | 
910 |           "(** Header\nLong comment... *) signature A = sig end (** defs... *)"
911 | 
912 |      * Look in all files for structure snippets on the form
913 | 
914 |           1. "(** SigDoc *) structure A : B"
915 |           2. "(** SigDoc *) structure A :> B"
916 |           3. "$(* *) structure A :> B"   (* first occurence in file *)
917 |      *)
918 |     let
919 |       val () = case load_about () of
920 |                    SOME about =>
921 |                    let val p = CS.toString (page ($"About") ($"") ($about))
922 |                    in writeFile "about.html" p
923 |                    end
924 |                  | NONE => ()
925 |       val pkgvmap = read_pkgfile ()
926 |       val sigmap : sigmap =
927 |           foldl (fn (x,a) =>
928 |                     let val m = read_sigs pkgvmap x
929 |                     in Map.plus(a,m)
930 |                     end handle SigFormatError s =>
931 |                                (print ("Skipping file: " ^ s ^ "\n");
932 |                                 a)
933 |                 ) Map.empty files
934 |       val strmap : strmap =
935 |           foldl (fn (x,a) => Map.plus(a,read_impl pkgvmap sigmap x)) Map.empty files
936 |       fun out (arg as (s,a), idmap) =
937 |           let val (cstr, idmap2) = pp strmap arg
938 |               val str = CS.toString cstr
939 |               val file = s ^ ".sml.html"
940 |           in writeFile file str;
941 |              Map.plus(idmap,idmap2)
942 |           end
943 |       val idmap = Map.Fold out Map.empty sigmap
944 |     in
945 |        gen_sig_idx (sigmap, strmap);
946 |        gen_str_idx (sigmap, strmap);
947 |        gen_pkg_idx (sigmap, strmap, pkgvmap);
948 |        gen_id_idx (idmap, sigmap, strmap)
949 |     end handle Fail s => (println ("** Error: " ^ s); OS.Process.exit OS.Process.failure)
950 | 
951 | fun help () =
952 |     (print "Usage: sigdoc [-libpath p] [-about f] [-logo s]\n\
953 |            \              [-pkg f] FILES\n\n";
954 |      print " -libpath p : specify the path to the js-library and\n\
955 |            \              style files, relative to the working\n\
956 |            \              directory.\n";
957 |      print " -about f   : specify a file with HTML to embed in an\n\
958 |            \              About tab.\n";
959 |      print " -logo s    : specify html that presents a logo.\n";
960 |      print " -pkg f     : specify path to smlpkg package file to\n\
961 |            \              read package versions from.\n";
962 |      print "FILES include .sml and .sig files, which may contain\n\
963 |            \signatures and structures.\n";
964 |      print "For further information, please consult the Sigdoc\n";
965 |      print ("documentation at " ^ sigdoc_url ^ "\n"))
966 | 
967 | fun reg r xs = r := xs
968 | 
969 | val () = case ParseArg.run [ParseArg.Unary("-libpath", reg libpath),
970 |                             ParseArg.Unary("-about", reg about_html),
971 |                             ParseArg.Unary("-logo", reg logo_html),
972 |                             ParseArg.Unary("-pkg", reg pkgfile)] of
973 |              [] => help()
974 |            | files => gen files
975 | 


--------------------------------------------------------------------------------