3 * Copyright 2013 Geraint Luff <http://geraintluff.github.io/tv4/>
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 whitespace: /^([ \r\n\t]| )*/,
24 number: /^-?[0-9]+(\.[0-9]+)?([eE][+\-]?[0-9]+)?/
27 function Highlighter(stringData) {
28 this.remaining = stringData;
31 Highlighter.prototype = {
32 unshift: function (next) {
33 this.remaining = next + this.remaining;
37 return this.nextCharacter();
39 nextCharacter: function () {
40 if (this.remaining.length == 0) {
41 throw new Error("Unexpected end of input");
43 if (this.remaining[0] == "&") {
44 var endIndex = this.remaining.indexOf(";") + 1;
48 var result = this.remaining.substring(0, endIndex);
49 this.remaining = this.remaining.substring(endIndex);
52 var result = this.remaining[0];
53 this.remaining = this.remaining.substring(1);
56 whitespace: function () {
57 var ws = this.remaining.match(REGEX.whitespace)[0];
59 this.remaining = this.remaining.substring(ws.length);
61 highlightJson: function (keywords) {
62 if (keywords != undefined) {
63 this.html += keywords.wrapper[0];
66 var next = this.next();
68 this.highlightObject(keywords);
69 } else if (next == '[') {
70 this.highlightArray(keywords);
71 } else if (next == '"' || next == """) {
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>';
88 this.highlightJson(keywords);
90 if (keywords != undefined) {
91 this.html += keywords.wrapper[1];
94 highlightObject: function (keywords) {
95 this.html += '<span class="json-punctuation">{</span>';
96 var next = this.next();
98 if (next == '"' || next == """) {
101 while (next != '"' && next != '"') {
104 next = this.nextCharacter();
109 if (keywords != undefined && keywords.isKeyword(keyHtml)) {
110 this.html += '<span class="json-keyword">"'
114 this.html += '<span class="json-object-key">"'
119 while (next != ":") {
123 this.html += '<span class="json-punctuation">:</span>';
124 var nextKeywords = null;
125 if (keywords != undefined) {
126 nextKeywords = keywords.forKey(keyHtml);
128 this.highlightJson(nextKeywords);
131 this.html += '<span class="json-punctuation">,</span>';
134 } else while (next != "}") {
143 this.html += '<span class="json-punctuation">}</span>';
145 highlightArray: function (keywords) {
146 this.html += '<span class="json-punctuation">[</span>';
147 var next = this.next();
149 while (next != "]") {
151 this.highlightJson(keywords != undefined ? keywords.forItem(i) : null);
154 this.html += '<span class="json-punctuation">,</span>';
158 } else while (next != "]") {
163 this.html += '<span class="json-punctuation">]</span>';
165 highlightString: function () {
166 this.html += '<span class="json-punctuation">"</span><span class="json-string">';
168 while (next != '"' && next != '"') {
171 next = this.nextCharacter();
176 this.html += '</span><span class="json-punctuation">"</span>';
180 function KeywordMap() {
182 KeywordMap.prototype = {
183 wrapper: ["<span>", "</span>"],
185 isKeyword: function (keyHtml) {
186 return this.keywords[keyHtml] !== undefined;
188 forKey: function (keyHtml) {
189 return this.keywords[keyHtml];
191 forItem: function (keyHtml) {
195 var schema = new KeywordMap();
196 var schemaMedia = new KeywordMap();
197 var mapToSchemas = new KeywordMap();
198 var links = new KeywordMap();
202 properties: mapToSchemas,
203 patternProperties: mapToSchemas,
204 additionalProperties: schema,
206 additionalItems: schema,
208 dependencies: mapToSchemas,
211 exclusiveMinimum: null,
212 exclusiveMaximum: null,
238 definitions: mapToSchemas,
239 // from v4 hyper-schema
243 fragmentResolution: null
245 schema.forItem = function () {
248 schemaMedia.keywords = {
249 binaryEncoding: null,
252 mapToSchemas.wrapper = ['<span class="json-schema-map">', '</span>'];
253 mapToSchemas.forKey = function () {
265 links.forItem = function () {
269 function highlightElement(element, keywords) {
270 var highlighter = new Highlighter(element.innerHTML);
272 highlighter.highlightJson(keywords);
276 element.innerHTML = highlighter.html + highlighter.remaining;
279 if (document.getElementsByClassName == undefined) {
280 document.getElementsByClassName = function(className)
282 var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
283 var allElements = document.getElementsByTagName("*");
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);
297 highlight.highlightSchema = function(element) {
298 highlightElement(element, schema);