--- /dev/null
+
+/*
+ * Copyright 2013 Geraint Luff <http://geraintluff.github.io/tv4/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+define(function() {
+
+ var highlight = {};
+
+ var REGEX = {
+ whitespace: /^([ \r\n\t]| )*/,
+ number: /^-?[0-9]+(\.[0-9]+)?([eE][+\-]?[0-9]+)?/
+ };
+
+ function Highlighter(stringData) {
+ this.remaining = stringData;
+ this.html = "";
+ }
+ Highlighter.prototype = {
+ unshift: function (next) {
+ this.remaining = next + this.remaining;
+ },
+ next: function () {
+ this.whitespace();
+ return this.nextCharacter();
+ },
+ nextCharacter: function () {
+ if (this.remaining.length == 0) {
+ throw new Error("Unexpected end of input");
+ }
+ if (this.remaining[0] == "&") {
+ var endIndex = this.remaining.indexOf(";") + 1;
+ if (endIndex == -1) {
+ endIndex = 1;
+ }
+ var result = this.remaining.substring(0, endIndex);
+ this.remaining = this.remaining.substring(endIndex);
+ return result;
+ }
+ var result = this.remaining[0];
+ this.remaining = this.remaining.substring(1);
+ return result;
+ },
+ whitespace: function () {
+ var ws = this.remaining.match(REGEX.whitespace)[0];
+ this.html += ws;
+ this.remaining = this.remaining.substring(ws.length);
+ },
+ highlightJson: function (keywords) {
+ if (keywords != undefined) {
+ this.html += keywords.wrapper[0];
+ }
+ this.whitespace();
+ var next = this.next();
+ if (next == "{") {
+ this.highlightObject(keywords);
+ } else if (next == '[') {
+ this.highlightArray(keywords);
+ } else if (next == '"' || next == """) {
+ this.highlightString();
+ } else if ((next + this.remaining).match(REGEX.number)) {
+ var numberString = (next + this.remaining).match(REGEX.number)[0];
+ this.html += '<span class="json-number">' + numberString + '</span>';
+ this.remaining = this.remaining.substring(numberString.length - 1);
+ } else if (next == "n" && this.remaining.substring(0, 3) == "ull") {
+ this.remaining = this.remaining.substring(3);
+ this.html += '<span class="json-null">null</span>';
+ } else if (next == "t" && this.remaining.substring(0, 3) == "rue") {
+ this.remaining = this.remaining.substring(3);
+ this.html += '<span class="json-true">true</span>';
+ } else if (next == "f" && this.remaining.substring(0, 4) == "alse") {
+ this.remaining = this.remaining.substring(4);
+ this.html += '<span class="json-false">false</span>';
+ } else {
+ this.html += next;
+ this.highlightJson(keywords);
+ }
+ if (keywords != undefined) {
+ this.html += keywords.wrapper[1];
+ }
+ },
+ highlightObject: function (keywords) {
+ this.html += '<span class="json-punctuation">{</span>';
+ var next = this.next();
+ while (next != "}") {
+ if (next == '"' || next == """) {
+ var keyHtml = "";
+ next = this.next();
+ while (next != '"' && next != '"') {
+ if (next == "\\") {
+ keyHtml += next;
+ next = this.nextCharacter();
+ }
+ keyHtml += next;
+ next = this.next();
+ }
+ if (keywords != undefined && keywords.isKeyword(keyHtml)) {
+ this.html += '<span class="json-keyword">"'
+ + keyHtml
+ + '"</span>';
+ } else {
+ this.html += '<span class="json-object-key">"'
+ + keyHtml
+ + '"</span>';
+ }
+ next = this.next();
+ while (next != ":") {
+ this.html += next;
+ next = this.next();
+ }
+ this.html += '<span class="json-punctuation">:</span>';
+ var nextKeywords = null;
+ if (keywords != undefined) {
+ nextKeywords = keywords.forKey(keyHtml);
+ }
+ this.highlightJson(nextKeywords);
+ next = this.next();
+ if (next == ",") {
+ this.html += '<span class="json-punctuation">,</span>';
+ next = this.next();
+ continue;
+ } else while (next != "}") {
+ this.html += next;
+ next = this.next();
+ }
+ } else {
+ this.html += next;
+ next = this.next();
+ }
+ }
+ this.html += '<span class="json-punctuation">}</span>';
+ },
+ highlightArray: function (keywords) {
+ this.html += '<span class="json-punctuation">[</span>';
+ var next = this.next();
+ var i = 0;
+ while (next != "]") {
+ this.unshift(next);
+ this.highlightJson(keywords != undefined ? keywords.forItem(i) : null);
+ next = this.next();
+ if (next == ",") {
+ this.html += '<span class="json-punctuation">,</span>';
+ next = this.next();
+ i++;
+ continue;
+ } else while (next != "]") {
+ this.html += next;
+ next = this.next();
+ }
+ }
+ this.html += '<span class="json-punctuation">]</span>';
+ },
+ highlightString: function () {
+ this.html += '<span class="json-punctuation">"</span><span class="json-string">';
+ next = this.next();
+ while (next != '"' && next != '"') {
+ if (next == "\\") {
+ this.html += next;
+ next = this.nextCharacter();
+ }
+ this.html += next;
+ next = this.next();
+ }
+ this.html += '</span><span class="json-punctuation">"</span>';
+ }
+ };
+
+ function KeywordMap() {
+ }
+ KeywordMap.prototype = {
+ wrapper: ["<span>", "</span>"],
+ keywords: {},
+ isKeyword: function (keyHtml) {
+ return this.keywords[keyHtml] !== undefined;
+ },
+ forKey: function (keyHtml) {
+ return this.keywords[keyHtml];
+ },
+ forItem: function (keyHtml) {
+ return null;
+ }
+ };
+ var schema = new KeywordMap();
+ var schemaMedia = new KeywordMap();
+ var mapToSchemas = new KeywordMap();
+ var links = new KeywordMap();
+ schema.keywords = {
+ // from v3
+ type: null,
+ properties: mapToSchemas,
+ patternProperties: mapToSchemas,
+ additionalProperties: schema,
+ items: schema,
+ additionalItems: schema,
+ required: null,
+ dependencies: mapToSchemas,
+ minimum: null,
+ maximum: null,
+ exclusiveMinimum: null,
+ exclusiveMaximum: null,
+ minItems: null,
+ maxItems: null,
+ uniqueItems: null,
+ pattern: null,
+ minLength: null,
+ maxLength: null,
+ "enum": null,
+ "default": null,
+ title: null,
+ description: null,
+ format: null,
+ divisibleBy: null,
+ disallow: schema,
+ "extends": schema,
+ "id": null,
+ "$ref": null,
+ "$schema": null,
+ // from v4 core
+ multipleOf: null,
+ maxProperties: null,
+ minProperties: null,
+ allOf: schema,
+ anyOf: schema,
+ oneOf: schema,
+ not: schema,
+ definitions: mapToSchemas,
+ // from v4 hyper-schema
+ media: schemaMedia,
+ links: links,
+ pathStart: null,
+ fragmentResolution: null
+ };
+ schema.forItem = function () {
+ return schema;
+ };
+ schemaMedia.keywords = {
+ binaryEncoding: null,
+ type: null
+ };
+ mapToSchemas.wrapper = ['<span class="json-schema-map">', '</span>'];
+ mapToSchemas.forKey = function () {
+ return schema;
+ };
+ links.keywords = {
+ rel: null,
+ href:null,
+ method: null,
+ encType: null,
+ pathStart: null,
+ schema: schema,
+ targetSchema: schema
+ };
+ links.forItem = function () {
+ return links;
+ };
+
+ function highlightElement(element, keywords) {
+ var highlighter = new Highlighter(element.innerHTML);
+ try {
+ highlighter.highlightJson(keywords);
+ } catch (e) {
+ throw e;
+ }
+ element.innerHTML = highlighter.html + highlighter.remaining;
+ }
+
+ if (document.getElementsByClassName == undefined) {
+ document.getElementsByClassName = function(className)
+ {
+ var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
+ var allElements = document.getElementsByTagName("*");
+ var results = [];
+
+ var element;
+ for (var i = 0; (element = allElements[i]) != null; i++) {
+ var elementClass = element.className;
+ if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass))
+ results.push(element);
+ }
+
+ return results;
+ }
+ }
+
+ highlight.highlightSchema = function(element) {
+ highlightElement(element, schema);
+ }
+
+ return highlight;
+});