# DocSets.ts

`

1<comment>/**</comment>

2<comment> * DocSets.ts. Loads the doc.json files to process and display as documentation.</comment>

3<comment> * Processing occurs in these steps:</comment>

4<comment> * 1. Load the index.json file that contains a list of doc.json files to load, one for each library to show</comment>

5<comment> * 2. Load each doc.json file, which describes a library</comment>

6<comment> * 3. Call DocSets.add to add the library name to the registry and create an index of entries for the library</comment>

7<comment> */</comment>

8

9<comment>/** */</comment>

10import { m } from <quote>'hslayout'</quote>;

11import { Log } from <quote>'hsutil'</quote>; const log = new Log(<quote>'DocsSets'</quote>);

12import { DocsNode } from <quote>'./Nodes'</quote>;

13

14const DOCDIR:string = <quote>'./data/'</quote>;

15

16<comment>/**</comment>

17<comment> * the default location for the index .json files, relative to the web page:</comment>

18<comment> * `<quote>'./data/index.json'</quote>`</comment>

19<comment> */</comment>

20const FILE:string = <quote>'./data/index.json'</quote>;

21

22export type json = any;

23

24function matchAll(str:string, re:RegExp): string[] {

25 const result:string[] = [];

26 let partial:string[];

27 while ((partial = re.exec(str)) !== null) {

28 result.push(partial[1]);

29 log.debug(log.inspect(partial));

30 }

31 return result;

32}

33

34<comment>/**</comment>

35<comment> * DocSets object. Keeps a list of registered docsets and </comment>

36<comment> * provides access to elements of each docset.</comment>

37<comment> */</comment>

38export class DocSets {

39 <comment>/** Contains references to the docsets and all elements per docset, accessible per ID. */</comment>

40 <comment>// private static gList:List = {index:{}};</comment>

41<comment></comment>

42 private static libs:string[] = [];

43 private static docs:string[] = [];

44

45 <comment>/** an optional title for the set of DocSets loaded, as specified in the `index.json` file. */</comment>

46 private static gTitle: string = <quote>''</quote>;

47 private static nodeList = <{[id:string]: DocsNode}>{};

48

49 public static nodeCount = 0;

50

51 public static addNode(mdl:json, node:DocsNode) {

52 const id = mdl.id;

53 const lib = mdl.lib;

54 DocSets.nodeList[`${lib}.${id}`] = node;

55 DocSets.nodeList[mdl.fullPath] = node;

56<comment>// if (mdl.fullPath.indexOf(<quote>'hsDocs.DocSets'</quote>)>=0) { log.info(`added node ${id}: <quote>'${mdl.fullPath}'</quote>`); }</comment>

57<comment></comment> DocSets.nodeCount++;

58 }

59

60

61 <comment>/**</comment>

62<comment> * Adds the docset in `content` to the `gList` at the position of `file` in `DocSets.docs`.</comment>

63<comment> * @param content the docSet content to add. </comment>

64<comment> * @param file the file name from which the content was read. </comment>

65<comment> */</comment>

66 public static addDocSet(content:json, file:string):void {

67 const lib = content.name;

68 const i = DocSets.docs.indexOf(file);

69 DocSets.libs[i] = lib;

70 const root = DocsNode.traverse(content, lib);

71 log.info(`traversed <quote>'${root.fullPath}'</quote>`);

72 }

73

74 public static getNode(id:string|number, lib:string):DocsNode {

75 const key = ((typeof id === <quote>'number'</quote>) || (parseInt(<quote>''</quote>+id, 10)>=0))? `${lib}.${id}` : id;

76 <comment>// log.info(`getNode id=${id}, lib=${lib} -> ${key}, typeof=${typeof id}, parseInt=${parseInt(<quote>''</quote>+id, 10)}`);</comment>

77<comment></comment> if (DocSets.nodeList.length && !DocSets.nodeList[key]) {

78 log.warn(`did not find node for key <quote>'${key}'</quote> (id=${id}, lib=${lib})`);

79 log.warn(new Error().stack);

80 }

81 return DocSets.nodeList[key];

82 }

83

84 <comment>/**</comment>

85<comment> * returns the specified documentation element.</comment>

86<comment> * When called without parameters, a `string[lib]` of available docSets is returned.</comment>

87<comment> * When called with only `lib` specified, the corresponding docSet overview is returned.</comment>

88<comment> * @param lib specifies the docset to scan. </comment>

89<comment> * @param id specifies the element within the docSet, either by its id number, or by its fully qualified path, </comment>

90<comment> * e.g. <quote>'hsDocs.DocSets.DocSets.add'</quote> </comment>

91<comment> */</comment>

92 public static getLibs():string[]{

93 return DocSets.libs;

94 }

95

96 <comment>/**</comment>

97<comment> * loads an index set and the docsets it contains from driectory `dir`.</comment>

98<comment> * @param file the optional directory to read from. If unspecified, </comment>

99<comment> * read from the index file specified by {@link DocSets.FILE `FILE`}.</comment>

100<comment> * @return a promise to load the index set</comment>

101<comment> */</comment>

102 public static async loadList(file?:string):Promise<void> {

103 <comment>/** load an `index.json` file that contains references to the DocSets to load. */</comment>

104 async function getIndexFile(url:string):Promise<boolean> {

105 if (!url) { return false; }

106 try {

107 const result = await m.request({ method: <quote>"GET"</quote>, url: file});

108 DocSets.gTitle = result.title;

109 DocSets.docs = result.docs;

110 log.info(`found index file ${url}`);

111 return true;

112 } catch(e) {

113 return false;

114 }

115 }

116 <comment>/** </comment>

117<comment> * get all `jsons` in a directory specified by `url`. </comment>

118<comment> * If the directory contains an `index.json`, calls `getIndexFile` on that file<quote>'s url.</comment>

119<comment> */</comment>

120 async function getDirJSONs(url:string):Promise<boolean> {

121 let result = false;

122 await m.request({ method: <quote>"GET"</quote>, url: url,

123 <comment>// catch extract to avoid attempt to deserialize</comment>

124<comment></comment> extract: async (xhr:any, options:any) => {

125 const jsons = matchAll(xhr.responseText, /<a href=<quote>"(\S*?.json)"</quote>>/g); <comment>// all *.json references</comment>

126<comment></comment> <comment>// check if index.jon exists:</comment>

127<comment></comment> const index = jsons.filter(m => m==='</quote>index.json<quote>')[0];

128 <comment>// const jsons = matches.filter(m => !m.startsWith('</quote>index<quote>'));</comment>

129<comment></comment>

130 if (index) {

131 result = await getIndexFile(index);

132 } else if (jsons.length) {

133 log.info(`found dir list in ${url}`);

134 DocSets.gTitle = '</quote><quote>';

135 DocSets.docs = jsons;

136 result = true;

137 }

138 }

139 });

140 return result;

141 }

142 let i = file.lastIndexOf('</quote>/');

143 const dir = file.substring(0,i+1);

144

145 let found = false;

146 if (!found) { found = await getIndexFile(file); }

147 if (!found) { found = await getDirJSONs(dir); }

148 if (!found) { found = await getDirJSONs(DOCDIR); }

149 log.debug(`found ${DocSets.docs.length} dos sets: ${log.inspect(DocSets.docs, 5)}`);

150 await Promise.all(DocSets.docs.map(async (f:string) => await loadDocSet(dir, f))).catch(log.error);

151 log.info(`found ${DocSets.nodeCount} DocNodes`);

152 m.redraw();

153 }

154}

155

156<comment>/**</comment>

157<comment> * Loads a docset specified by file from the directory `dir`. </comment>

158<comment> * Once received, the docset is registered in `modules` via the `add` method.</comment>

159<comment> * @param dir the directory to read from</comment>

160<comment> * @param file the `json` file to load as docset</comment>

161<comment> */</comment>

162async function loadDocSet(dir:string, file:string):Promise<void> {

163 log.debug(`loading ${dir+file}`);

164 const r:json = await m.request({ method: <quote>"GET"</quote>, url: dir+file });

165 DocSets.addDocSet(r, file);

166}

167

168

`