Policy 1707 commit to LF
[policy/engine.git] / POLICY-SDK-APP / src / main / webapp / app / policyApp / CSS / bootstrap / grunt / bs-lessdoc-parser.js
1 /*!
2  * Bootstrap Grunt task for parsing Less docstrings
3  * http://getbootstrap.com
4  * Copyright 2014 Twitter, Inc.
5  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6  */
7 'use strict';
8
9 var Markdown = require('markdown-it');
10
11 function markdown2html(markdownString) {
12   var md = new Markdown();
13
14   // the slice removes the <p>...</p> wrapper output by Markdown processor
15   return md.render(markdownString.trim()).slice(3, -5);
16 }
17
18
19 /*
20 Mini-language:
21   //== This is a normal heading, which starts a section. Sections group variables together.
22   //## Optional description for the heading
23
24   //=== This is a subheading.
25
26   //** Optional description for the following variable. You **can** use Markdown in descriptions to discuss `<html>` stuff.
27   @foo: #fff;
28
29   //-- This is a heading for a section whose variables shouldn't be customizable
30
31   All other lines are ignored completely.
32 */
33
34
35 var CUSTOMIZABLE_HEADING = /^[/]{2}={2}(.*)$/;
36 var UNCUSTOMIZABLE_HEADING = /^[/]{2}-{2}(.*)$/;
37 var SUBSECTION_HEADING = /^[/]{2}={3}(.*)$/;
38 var SECTION_DOCSTRING = /^[/]{2}#{2}(.+)$/;
39 var VAR_ASSIGNMENT = /^(@[a-zA-Z0-9_-]+):[ ]*([^ ;][^;]*);[ ]*$/;
40 var VAR_DOCSTRING = /^[/]{2}[*]{2}(.+)$/;
41
42 function Section(heading, customizable) {
43   this.heading = heading.trim();
44   this.id = this.heading.replace(/\s+/g, '-').toLowerCase();
45   this.customizable = customizable;
46   this.docstring = null;
47   this.subsections = [];
48 }
49
50 Section.prototype.addSubSection = function (subsection) {
51   this.subsections.push(subsection);
52 };
53
54 function SubSection(heading) {
55   this.heading = heading.trim();
56   this.id = this.heading.replace(/\s+/g, '-').toLowerCase();
57   this.variables = [];
58 }
59
60 SubSection.prototype.addVar = function (variable) {
61   this.variables.push(variable);
62 };
63
64 function VarDocstring(markdownString) {
65   this.html = markdown2html(markdownString);
66 }
67
68 function SectionDocstring(markdownString) {
69   this.html = markdown2html(markdownString);
70 }
71
72 function Variable(name, defaultValue) {
73   this.name = name;
74   this.defaultValue = defaultValue;
75   this.docstring = null;
76 }
77
78 function Tokenizer(fileContent) {
79   this._lines = fileContent.split('\n');
80   this._next = undefined;
81 }
82
83 Tokenizer.prototype.unshift = function (token) {
84   if (this._next !== undefined) {
85     throw new Error('Attempted to unshift twice!');
86   }
87   this._next = token;
88 };
89
90 Tokenizer.prototype._shift = function () {
91   // returning null signals EOF
92   // returning undefined means the line was ignored
93   if (this._next !== undefined) {
94     var result = this._next;
95     this._next = undefined;
96     return result;
97   }
98   if (this._lines.length <= 0) {
99     return null;
100   }
101   var line = this._lines.shift();
102   var match = null;
103   match = SUBSECTION_HEADING.exec(line);
104   if (match !== null) {
105     return new SubSection(match[1]);
106   }
107   match = CUSTOMIZABLE_HEADING.exec(line);
108   if (match !== null) {
109     return new Section(match[1], true);
110   }
111   match = UNCUSTOMIZABLE_HEADING.exec(line);
112   if (match !== null) {
113     return new Section(match[1], false);
114   }
115   match = SECTION_DOCSTRING.exec(line);
116   if (match !== null) {
117     return new SectionDocstring(match[1]);
118   }
119   match = VAR_DOCSTRING.exec(line);
120   if (match !== null) {
121     return new VarDocstring(match[1]);
122   }
123   var commentStart = line.lastIndexOf('//');
124   var varLine = (commentStart === -1) ? line : line.slice(0, commentStart);
125   match = VAR_ASSIGNMENT.exec(varLine);
126   if (match !== null) {
127     return new Variable(match[1], match[2]);
128   }
129   return undefined;
130 };
131
132 Tokenizer.prototype.shift = function () {
133   while (true) {
134     var result = this._shift();
135     if (result === undefined) {
136       continue;
137     }
138     return result;
139   }
140 };
141
142 function Parser(fileContent) {
143   this._tokenizer = new Tokenizer(fileContent);
144 }
145
146 Parser.prototype.parseFile = function () {
147   var sections = [];
148   while (true) {
149     var section = this.parseSection();
150     if (section === null) {
151       if (this._tokenizer.shift() !== null) {
152         throw new Error('Unexpected unparsed section of file remains!');
153       }
154       return sections;
155     }
156     sections.push(section);
157   }
158 };
159
160 Parser.prototype.parseSection = function () {
161   var section = this._tokenizer.shift();
162   if (section === null) {
163     return null;
164   }
165   if (!(section instanceof Section)) {
166     throw new Error('Expected section heading; got: ' + JSON.stringify(section));
167   }
168   var docstring = this._tokenizer.shift();
169   if (docstring instanceof SectionDocstring) {
170     section.docstring = docstring;
171   }
172   else {
173     this._tokenizer.unshift(docstring);
174   }
175   this.parseSubSections(section);
176
177   return section;
178 };
179
180 Parser.prototype.parseSubSections = function (section) {
181   while (true) {
182     var subsection = this.parseSubSection();
183     if (subsection === null) {
184       if (section.subsections.length === 0) {
185         // Presume an implicit initial subsection
186         subsection = new SubSection('');
187         this.parseVars(subsection);
188       }
189       else {
190         break;
191       }
192     }
193     section.addSubSection(subsection);
194   }
195
196   if (section.subsections.length === 1 && !(section.subsections[0].heading) && section.subsections[0].variables.length === 0) {
197     // Ignore lone empty implicit subsection
198     section.subsections = [];
199   }
200 };
201
202 Parser.prototype.parseSubSection = function () {
203   var subsection = this._tokenizer.shift();
204   if (subsection instanceof SubSection) {
205     this.parseVars(subsection);
206     return subsection;
207   }
208   this._tokenizer.unshift(subsection);
209   return null;
210 };
211
212 Parser.prototype.parseVars = function (subsection) {
213   while (true) {
214     var variable = this.parseVar();
215     if (variable === null) {
216       return;
217     }
218     subsection.addVar(variable);
219   }
220 };
221
222 Parser.prototype.parseVar = function () {
223   var docstring = this._tokenizer.shift();
224   if (!(docstring instanceof VarDocstring)) {
225     this._tokenizer.unshift(docstring);
226     docstring = null;
227   }
228   var variable = this._tokenizer.shift();
229   if (variable instanceof Variable) {
230     variable.docstring = docstring;
231     return variable;
232   }
233   this._tokenizer.unshift(variable);
234   return null;
235 };
236
237
238 module.exports = Parser;