Merge branch 'develop' into features/JAL-2068groovyAnnotationWorker
[jalview.git] / examples / biojson-doc / lib / highlight.js
1
2 /*
3  * Copyright 2013 Geraint Luff <http://geraintluff.github.io/tv4/>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 define(function() {
19
20     var highlight = {};
21
22     var REGEX = {
23         whitespace: /^([ \r\n\t]|&nbsp;)*/,
24         number: /^-?[0-9]+(\.[0-9]+)?([eE][+\-]?[0-9]+)?/
25     };
26
27     function Highlighter(stringData) {
28         this.remaining = stringData;
29         this.html = "";
30     }
31     Highlighter.prototype = {
32         unshift: function (next) {
33             this.remaining = next + this.remaining;
34         },
35         next: function () {
36             this.whitespace();
37             return this.nextCharacter();
38         },
39         nextCharacter: function () {
40             if (this.remaining.length == 0) {
41                 throw new Error("Unexpected end of input");
42             }
43             if (this.remaining[0] == "&") {
44                 var endIndex = this.remaining.indexOf(";") + 1;
45                 if (endIndex == -1) {
46                     endIndex = 1;
47                 }
48                 var result = this.remaining.substring(0, endIndex);
49                 this.remaining = this.remaining.substring(endIndex);
50                 return result;
51             }
52             var result = this.remaining[0];
53             this.remaining = this.remaining.substring(1);
54             return result;
55         },
56         whitespace: function () {
57             var ws = this.remaining.match(REGEX.whitespace)[0];
58             this.html += ws;
59             this.remaining = this.remaining.substring(ws.length);
60         },
61         highlightJson: function (keywords) {
62             if (keywords != undefined) {
63                 this.html += keywords.wrapper[0];
64             }
65             this.whitespace();
66             var next = this.next();
67             if (next == "{") {
68                 this.highlightObject(keywords);
69             } else if (next == '[') {
70                 this.highlightArray(keywords);
71             } else if (next == '"' || next == "&quot;") {
72                 this.highlightString();
73             } else if ((next + this.remaining).match(REGEX.number)) {
74                 var numberString = (next + this.remaining).match(REGEX.number)[0];
75                 this.html += '<span class="json-number">' + numberString + '</span>';
76                 this.remaining = this.remaining.substring(numberString.length - 1);
77             } else if (next == "n" && this.remaining.substring(0, 3) == "ull") {
78                 this.remaining = this.remaining.substring(3);
79                 this.html += '<span class="json-null">null</span>';
80             } else if (next == "t" && this.remaining.substring(0, 3) == "rue") {
81                 this.remaining = this.remaining.substring(3);
82                 this.html += '<span class="json-true">true</span>';
83             } else if (next == "f" && this.remaining.substring(0, 4) == "alse") {
84                 this.remaining = this.remaining.substring(4);
85                 this.html += '<span class="json-false">false</span>';
86             } else {
87                 this.html += next;
88                 this.highlightJson(keywords);
89             }
90             if (keywords != undefined) {
91                 this.html += keywords.wrapper[1];
92             }
93         },
94         highlightObject: function (keywords) {
95             this.html += '<span class="json-punctuation">{</span>';
96             var next = this.next();
97             while (next != "}") {
98                 if (next == '"' || next == "&quot;") {
99                     var keyHtml = "";
100                     next = this.next();
101                     while (next != '"' && next != '&quot') {
102                         if (next == "\\") {
103                             keyHtml += next;
104                             next = this.nextCharacter();
105                         }
106                         keyHtml += next;
107                         next = this.next();
108                     }
109                     if (keywords != undefined && keywords.isKeyword(keyHtml)) {
110                         this.html += '<span class="json-keyword">&quot;'
111                             + keyHtml
112                             + '&quot;</span>';
113                     } else {
114                         this.html += '<span class="json-object-key">&quot;'
115                             + keyHtml
116                             + '&quot;</span>';
117                     }
118                     next = this.next();
119                     while (next != ":") {
120                         this.html += next;
121                         next = this.next();
122                     }
123                     this.html += '<span class="json-punctuation">:</span>';
124                     var nextKeywords = null;
125                     if (keywords != undefined) {
126                         nextKeywords = keywords.forKey(keyHtml);
127                     }
128                     this.highlightJson(nextKeywords);
129                     next = this.next();
130                     if (next == ",") {
131                         this.html += '<span class="json-punctuation">,</span>';
132                         next = this.next();
133                         continue;
134                     } else while (next != "}") {
135                         this.html += next;
136                         next = this.next();
137                     }
138                 } else {
139                     this.html += next;
140                     next = this.next();
141                 }
142             }
143             this.html += '<span class="json-punctuation">}</span>';
144         },
145         highlightArray: function (keywords) {
146             this.html += '<span class="json-punctuation">[</span>';
147             var next = this.next();
148             var i = 0;
149             while (next != "]") {
150                 this.unshift(next);
151                 this.highlightJson(keywords != undefined ? keywords.forItem(i) : null);
152                 next = this.next();
153                 if (next == ",") {
154                     this.html += '<span class="json-punctuation">,</span>';
155                     next = this.next();
156                     i++;
157                     continue;
158                 } else while (next != "]") {
159                     this.html += next;
160                     next = this.next();
161                 }
162             }
163             this.html += '<span class="json-punctuation">]</span>';
164         },
165         highlightString: function () {
166             this.html += '<span class="json-punctuation">&quot;</span><span class="json-string">';
167             next = this.next();
168             while (next != '"' && next != '&quot') {
169                 if (next == "\\") {
170                     this.html += next;
171                     next = this.nextCharacter();
172                 }
173                 this.html += next;
174                 next = this.next();
175             }
176             this.html += '</span><span class="json-punctuation">&quot;</span>';
177         }
178     };
179
180     function KeywordMap() {
181     }
182     KeywordMap.prototype = {
183         wrapper: ["<span>", "</span>"],
184         keywords: {},
185         isKeyword: function (keyHtml) {
186             return this.keywords[keyHtml] !== undefined;
187         },
188         forKey: function (keyHtml) {
189             return this.keywords[keyHtml];
190         },
191         forItem: function (keyHtml) {
192             return null;
193         }
194     };
195     var schema = new KeywordMap();
196     var schemaMedia = new KeywordMap();
197     var mapToSchemas = new KeywordMap();
198     var links = new KeywordMap();
199     schema.keywords = {
200         // from v3
201         type: null,
202         properties: mapToSchemas,
203         patternProperties: mapToSchemas,
204         additionalProperties: schema,
205         items: schema,
206         additionalItems: schema,
207         required: null,
208         dependencies: mapToSchemas,
209         minimum: null,
210         maximum: null,
211         exclusiveMinimum: null,
212         exclusiveMaximum: null,
213         minItems: null,
214         maxItems: null,
215         uniqueItems: null,
216         pattern: null,
217         minLength: null,
218         maxLength: null,
219         "enum": null,
220         "default": null,
221         title: null,
222         description: null,
223         format: null,
224         divisibleBy: null,
225         disallow: schema,
226         "extends": schema,
227         "id": null,
228         "$ref": null,
229         "$schema": null,
230         // from v4 core
231         multipleOf: null,
232         maxProperties: null,
233         minProperties: null,
234         allOf: schema,
235         anyOf: schema,
236         oneOf: schema,
237         not: schema,
238         definitions: mapToSchemas,
239         // from v4 hyper-schema
240         media: schemaMedia,
241         links: links,
242         pathStart: null,
243         fragmentResolution: null
244     };
245     schema.forItem = function () {
246         return schema;
247     };
248     schemaMedia.keywords = {
249         binaryEncoding: null,
250         type: null
251     };
252     mapToSchemas.wrapper = ['<span class="json-schema-map">', '</span>'];
253     mapToSchemas.forKey = function () {
254         return schema;
255     };
256     links.keywords = {
257         rel: null,
258         href:null,
259         method: null,
260         encType: null,
261         pathStart: null,
262         schema: schema,
263         targetSchema: schema
264     };
265     links.forItem = function () {
266         return links;
267     };
268
269     function highlightElement(element, keywords) {
270         var highlighter = new Highlighter(element.innerHTML);
271         try {
272             highlighter.highlightJson(keywords);
273         } catch (e) {
274             throw e;
275         }
276         element.innerHTML = highlighter.html + highlighter.remaining;
277     }
278
279     if (document.getElementsByClassName == undefined) {
280         document.getElementsByClassName = function(className)
281         {
282             var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
283             var allElements = document.getElementsByTagName("*");
284             var results = [];
285
286             var element;
287             for (var i = 0; (element = allElements[i]) != null; i++) {
288                 var elementClass = element.className;
289                 if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass))
290                     results.push(element);
291             }
292
293             return results;
294         }
295     }
296
297     highlight.highlightSchema = function(element) {
298         highlightElement(element, schema);
299     }
300
301     return highlight;
302 });