Improved error handling

This commit is contained in:
josdejong 2013-05-04 11:26:40 +02:00
parent 9d5aab339b
commit a8d26fd113
10 changed files with 272 additions and 194 deletions

View File

@ -6,6 +6,7 @@ http://jsoneditoronline.org
- Unified JSONFormatter and JSONEditor in one editor with a switchable mode. - Unified JSONFormatter and JSONEditor in one editor with a switchable mode.
- Urls are navigable now. - Urls are navigable now.
- Improved error and log handling.
- Added jsoneditor to npm and bower. - Added jsoneditor to npm and bower.

View File

@ -43,7 +43,7 @@ app.CodeToTree = function() {
treeEditor.set(codeEditor.get()); treeEditor.set(codeEditor.get());
} }
catch (err) { catch (err) {
app.notify.showError(err); app.notify.showError(app.formatError(err));
} }
}; };
@ -55,7 +55,7 @@ app.treeToCode = function () {
codeEditor.set(treeEditor.get()); codeEditor.set(treeEditor.get());
} }
catch (err) { catch (err) {
app.notify.showError(err); app.notify.showError(app.formatError(err));
} }
}; };
@ -103,12 +103,12 @@ app.load = function() {
mode: 'code', mode: 'code',
change: function () { change: function () {
app.lastChanged = codeEditor; app.lastChanged = codeEditor;
},
error: function (err) {
app.notify.showError(app.formatError(err));
} }
}); });
codeEditor.set(json); codeEditor.set(json);
codeEditor.onError = function (err) {
app.notify.showError(err);
};
// tree editor // tree editor
container = document.getElementById("treeEditor"); container = document.getElementById("treeEditor");
@ -116,6 +116,9 @@ app.load = function() {
mode: 'tree', mode: 'tree',
change: function () { change: function () {
app.lastChanged = treeEditor; app.lastChanged = treeEditor;
},
error: function (err) {
app.notify.showError(app.formatError(err));
} }
}); });
treeEditor.set(json); treeEditor.set(json);
@ -212,7 +215,7 @@ app.openCallback = function (err, data) {
} }
catch (err) { catch (err) {
treeEditor.set({}); treeEditor.set({});
app.notify.showError(err); app.notify.showError(app.formatError(err));
} }
} }
} }
@ -267,6 +270,22 @@ app.saveFile = function () {
}); });
}; };
/**
* Format a JSON parse/stringify error as HTML
* @param {Error} err
* @returns {string}
*/
app.formatError = function (err) {
var message = '<pre class="error">' + err.toString() + '</pre>';
if (typeof(jsonlint) != 'undefined') {
message +=
'<a class="error" href="http://zaach.github.com/jsonlint/" target="_blank">' +
'validated by jsonlint' +
'</a>';
}
return message;
};
/** /**
* Clear the current file * Clear the current file
*/ */

4
jsoneditor-min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -173,6 +173,7 @@ JSONEditor.prototype.setMode = function (mode) {
options.mode = mode; options.mode = mode;
var config = JSONEditor.modes[mode]; var config = JSONEditor.modes[mode];
if (config) { if (config) {
try {
if (config.data == 'text') { if (config.data == 'text') {
// text // text
name = this.getName(); name = this.getName();
@ -201,14 +202,43 @@ JSONEditor.prototype.setMode = function (mode) {
} }
if (typeof config.load === 'function') { if (typeof config.load === 'function') {
try {
config.load.call(this); config.load.call(this);
} }
catch (err) {}
}
}
catch (err) {
this._onError(err);
}
} }
else { else {
throw new Error('Unknown mode "' + options.mode + '"'); throw new Error('Unknown mode "' + options.mode + '"');
} }
}; };
/**
* Throw an error. If an error callback is configured in options.error, this
* callback will be invoked. Else, a regular error is thrown.
* @param {Error} err
* @private
*/
JSONEditor.prototype._onError = function(err) {
// TODO: onError is deprecated since version 2.2.0. cleanup some day
if (typeof this.onError === 'function') {
util.log('WARNING: JSONEditor.onError is deprecated. ' +
'Use options.error instead.');
this.onError(err);
}
if (typeof this.options.error === 'function') {
this.options.error(err);
}
else {
throw err;
}
};
/** /**
* @constructor TreeEditor * @constructor TreeEditor
* @param {Element} container Container element * @param {Element} container Container element
@ -303,22 +333,22 @@ TreeEditor.prototype._setOptions = function (options) {
if (options['enableSearch']) { if (options['enableSearch']) {
// deprecated since version 1.6.0, 2012-11-03 // deprecated since version 1.6.0, 2012-11-03
this.options.search = options['enableSearch']; this.options.search = options['enableSearch'];
console.log('WARNING: Option "enableSearch" is deprecated. Use "search" instead.'); util.log('WARNING: Option "enableSearch" is deprecated. Use "search" instead.');
} }
if (options['enableHistory']) { if (options['enableHistory']) {
// deprecated since version 1.6.0, 2012-11-03 // deprecated since version 1.6.0, 2012-11-03
this.options.history = options['enableHistory']; this.options.history = options['enableHistory'];
console.log('WARNING: Option "enableHistory" is deprecated. Use "history" instead.'); util.log('WARNING: Option "enableHistory" is deprecated. Use "history" instead.');
} }
if (options['mode'] == 'editor') { if (options['mode'] == 'editor') {
// deprecated since version 2.2.0, 2013-04-30 // deprecated since version 2.2.0, 2013-04-30
this.options.mode = 'tree'; this.options.mode = 'tree';
console.log('WARNING: Mode "editor" is deprecated. Use "tree" instead.'); util.log('WARNING: Mode "editor" is deprecated. Use "tree" instead.');
} }
if (options['mode'] == 'viewer') { if (options['mode'] == 'viewer') {
// deprecated since version 2.2.0, 2013-04-30 // deprecated since version 2.2.0, 2013-04-30
this.options.mode = 'view'; this.options.mode = 'view';
console.log('WARNING: Mode "viewer" is deprecated. Use "view" instead.'); util.log('WARNING: Mode "viewer" is deprecated. Use "view" instead.');
} }
} }
@ -343,7 +373,7 @@ TreeEditor.prototype.set = function (json, name) {
// adjust field name for root node // adjust field name for root node
if (name) { if (name) {
// TODO: deprecated since version 2.2.0. Cleanup some day. // TODO: deprecated since version 2.2.0. Cleanup some day.
console.log('Warning: second parameter "name" is deprecated. ' + util.log('Warning: second parameter "name" is deprecated. ' +
'Use setName(name) instead.'); 'Use setName(name) instead.');
this.options.name = name; this.options.name = name;
} }
@ -528,7 +558,7 @@ TreeEditor.prototype._onAction = function (action, params) {
this.options.change(); this.options.change();
} }
catch (err) { catch (err) {
console.log('Error in change callback: ', err); util.log('Error in change callback: ', err);
} }
} }
}; };
@ -1023,17 +1053,18 @@ TextEditor.prototype._create = function (container, options, json) {
if (options.indentation) { if (options.indentation) {
this.indentation = Number(options.indentation); this.indentation = Number(options.indentation);
} }
this.options = options;
this.mode = (options.mode == 'code') ? 'code' : 'text'; this.mode = (options.mode == 'code') ? 'code' : 'text';
if (this.mode == 'code') { if (this.mode == 'code') {
// verify whether Ace editor is available and supported // verify whether Ace editor is available and supported
if (typeof ace === 'undefined') { if (typeof ace === 'undefined') {
this.mode = 'text'; this.mode = 'text';
console.log('WARNING: Cannot load code editor, Ace library not loaded. ' + util.log('WARNING: Cannot load code editor, Ace library not loaded. ' +
'Falling back to plain text editor'); 'Falling back to plain text editor');
} }
if (util.getInternetExplorerVersion() == 8) { if (util.getInternetExplorerVersion() == 8) {
this.mode = 'text'; this.mode = 'text';
console.log('WARNING: Cannot load code editor, Ace is not supported on IE8. ' + util.log('WARNING: Cannot load code editor, Ace is not supported on IE8. ' +
'Falling back to plain text editor'); 'Falling back to plain text editor');
} }
} }
@ -1067,7 +1098,12 @@ TextEditor.prototype._create = function (container, options, json) {
//buttonFormat.className = 'jsoneditor-button'; //buttonFormat.className = 'jsoneditor-button';
this.menu.appendChild(buttonFormat); this.menu.appendChild(buttonFormat);
buttonFormat.onclick = function () { buttonFormat.onclick = function () {
try {
me.format(); me.format();
}
catch (err) {
me._onError(err);
}
}; };
// create compact button // create compact button
@ -1078,7 +1114,13 @@ TextEditor.prototype._create = function (container, options, json) {
//buttonCompact.className = 'jsoneditor-button'; //buttonCompact.className = 'jsoneditor-button';
this.menu.appendChild(buttonCompact); this.menu.appendChild(buttonCompact);
buttonCompact.onclick = function () { buttonCompact.onclick = function () {
try {
me.compact(); me.compact();
}
catch (err) {
me._onError(err);
}
}; };
this.content = document.createElement('div'); this.content = document.createElement('div');
@ -1166,39 +1208,41 @@ TextEditor.prototype._delete = function () {
}; };
/** /**
* This method is executed on error. * Throw an error. If an error callback is configured in options.error, this
* It can be overwritten for each instance of the TextEditor * callback will be invoked. Else, a regular error is thrown.
* @param {String} err * @param {Error} err
* @private
*/ */
// TODO: replace with an options.error TextEditor.prototype._onError = function(err) {
TextEditor.prototype.onError = function(err) { // TODO: onError is deprecated since version 2.2.0. cleanup some day
// action should be implemented for the instance if (typeof this.onError === 'function') {
util.log('WARNING: JSONEditor.onError is deprecated. ' +
'Use options.error instead.');
this.onError(err);
}
if (typeof this.options.error === 'function') {
this.options.error(err);
}
else {
throw err;
}
}; };
/** /**
* Compact the code in the formatter * Compact the code in the formatter
*/ */
TextEditor.prototype.compact = function () { TextEditor.prototype.compact = function () {
try {
var json = util.parse(this.getText()); var json = util.parse(this.getText());
this.setText(JSON.stringify(json)); this.setText(JSON.stringify(json));
}
catch (err) {
this.onError(err);
}
}; };
/** /**
* Format the code in the formatter * Format the code in the formatter
*/ */
TextEditor.prototype.format = function () { TextEditor.prototype.format = function () {
try {
var json = util.parse(this.getText()); var json = util.parse(this.getText());
this.setText(JSON.stringify(json, null, this.indentation)); this.setText(JSON.stringify(json, null, this.indentation));
}
catch (err) {
this.onError(err);
}
}; };
/** /**
@ -1409,7 +1453,7 @@ Node.prototype.setValue = function(value, type) {
if (typeof(value) == 'string') { if (typeof(value) == 'string') {
var escValue = JSON.stringify(value); var escValue = JSON.stringify(value);
this.value = escValue.substring(1, escValue.length - 1); this.value = escValue.substring(1, escValue.length - 1);
console.log('check', value, this.value); util.log('check', value, this.value);
} }
else { else {
this.value = value; this.value = value;
@ -3179,7 +3223,7 @@ Node.prototype.onKeyDown = function (event) {
var handled = false; var handled = false;
var prevNode, nextNode, nextDom, nextDom2; var prevNode, nextNode, nextDom, nextDom2;
// console.log(ctrlKey, keynum, event.charCode); // TODO: cleanup // util.log(ctrlKey, keynum, event.charCode); // TODO: cleanup
if (keynum == 13) { // Enter if (keynum == 13) { // Enter
if (target == this.dom.value) { if (target == this.dom.value) {
if (!this.editor.mode.edit || event.ctrlKey) { if (!this.editor.mode.edit || event.ctrlKey) {
@ -4979,7 +5023,7 @@ History.prototype.undo = function () {
} }
} }
else { else {
console.log('Error: unknown action "' + obj.action + '"'); util.log('Error: unknown action "' + obj.action + '"');
} }
} }
this.index--; this.index--;
@ -5006,7 +5050,7 @@ History.prototype.redo = function () {
} }
} }
else { else {
console.log('Error: unknown action "' + obj.action + '"'); util.log('Error: unknown action "' + obj.action + '"');
} }
} }
@ -5415,13 +5459,6 @@ if (!Array.prototype.forEach) {
} }
} }
// Old browsers do not have a console, so create a fake one in that case.
if (typeof console === 'undefined') {
console = {
log: function () {}
};
}
/** /**
* Parse JSON using the parser built-in in the browser. * Parse JSON using the parser built-in in the browser.
* On exception, the jsonString is validated and a detailed error is thrown. * On exception, the jsonString is validated and a detailed error is thrown.
@ -5432,9 +5469,9 @@ util.parse = function (jsonString) {
return JSON.parse(jsonString); return JSON.parse(jsonString);
} }
catch (err) { catch (err) {
// get a detailed error message using validate // try to throw a more detailed error message using validate
var message = util.validate(jsonString) || err; util.validate(jsonString);
throw new Error(message); throw err;
} }
}; };
@ -5443,33 +5480,15 @@ util.parse = function (jsonString) {
* This method uses JSONLint to validate the String. If JSONLint is not * This method uses JSONLint to validate the String. If JSONLint is not
* available, the built-in JSON parser of the browser is used. * available, the built-in JSON parser of the browser is used.
* @param {String} jsonString String with an (invalid) JSON object * @param {String} jsonString String with an (invalid) JSON object
* @return {String | undefined} Returns undefined when the string is valid JSON, * @throws Error
* returns a string with an error message when
* the data is invalid. This message is HTML
* formatted.
*/ */
util.validate = function (jsonString) { util.validate = function (jsonString) {
var message = undefined;
try {
if (typeof(jsonlint) != 'undefined') { if (typeof(jsonlint) != 'undefined') {
jsonlint.parse(jsonString); jsonlint.parse(jsonString);
} }
else { else {
JSON.parse(jsonString); JSON.parse(jsonString);
} }
}
catch (err) {
message = '<pre class="error">' + err.toString() + '</pre>';
if (typeof(jsonlint) != 'undefined') {
message +=
'<a class="error" href="http://zaach.github.com/jsonlint/" target="_blank">' +
'validated by jsonlint' +
'</a>';
}
}
return message;
}; };
/** /**
@ -5501,6 +5520,16 @@ util.clear = function (a) {
return a; return a;
}; };
/**
* Output text to the console, if console is available
* @param {...*} args
*/
util.log = function(args) {
if (console && typeof console.log === 'function') {
console.log.apply(console, arguments);
}
};
/** /**
* Test whether a text contains a url (matches when a string starts * Test whether a text contains a url (matches when a string starts
* with 'http://*' or 'https://*' and has no whitespace characters) * with 'http://*' or 'https://*' and has no whitespace characters)

View File

@ -181,7 +181,7 @@ History.prototype.undo = function () {
} }
} }
else { else {
console.log('Error: unknown action "' + obj.action + '"'); util.log('Error: unknown action "' + obj.action + '"');
} }
} }
this.index--; this.index--;
@ -208,7 +208,7 @@ History.prototype.redo = function () {
} }
} }
else { else {
console.log('Error: unknown action "' + obj.action + '"'); util.log('Error: unknown action "' + obj.action + '"');
} }
} }

View File

@ -139,6 +139,7 @@ JSONEditor.prototype.setMode = function (mode) {
options.mode = mode; options.mode = mode;
var config = JSONEditor.modes[mode]; var config = JSONEditor.modes[mode];
if (config) { if (config) {
try {
if (config.data == 'text') { if (config.data == 'text') {
// text // text
name = this.getName(); name = this.getName();
@ -167,10 +168,39 @@ JSONEditor.prototype.setMode = function (mode) {
} }
if (typeof config.load === 'function') { if (typeof config.load === 'function') {
try {
config.load.call(this); config.load.call(this);
} }
catch (err) {}
}
}
catch (err) {
this._onError(err);
}
} }
else { else {
throw new Error('Unknown mode "' + options.mode + '"'); throw new Error('Unknown mode "' + options.mode + '"');
} }
}; };
/**
* Throw an error. If an error callback is configured in options.error, this
* callback will be invoked. Else, a regular error is thrown.
* @param {Error} err
* @private
*/
JSONEditor.prototype._onError = function(err) {
// TODO: onError is deprecated since version 2.2.0. cleanup some day
if (typeof this.onError === 'function') {
util.log('WARNING: JSONEditor.onError is deprecated. ' +
'Use options.error instead.');
this.onError(err);
}
if (typeof this.options.error === 'function') {
this.options.error(err);
}
else {
throw err;
}
};

View File

@ -129,7 +129,7 @@ Node.prototype.setValue = function(value, type) {
if (typeof(value) == 'string') { if (typeof(value) == 'string') {
var escValue = JSON.stringify(value); var escValue = JSON.stringify(value);
this.value = escValue.substring(1, escValue.length - 1); this.value = escValue.substring(1, escValue.length - 1);
console.log('check', value, this.value); util.log('check', value, this.value);
} }
else { else {
this.value = value; this.value = value;
@ -1899,7 +1899,7 @@ Node.prototype.onKeyDown = function (event) {
var handled = false; var handled = false;
var prevNode, nextNode, nextDom, nextDom2; var prevNode, nextNode, nextDom, nextDom2;
// console.log(ctrlKey, keynum, event.charCode); // TODO: cleanup // util.log(ctrlKey, keynum, event.charCode); // TODO: cleanup
if (keynum == 13) { // Enter if (keynum == 13) { // Enter
if (target == this.dom.value) { if (target == this.dom.value) {
if (!this.editor.mode.edit || event.ctrlKey) { if (!this.editor.mode.edit || event.ctrlKey) {

View File

@ -41,17 +41,18 @@ TextEditor.prototype._create = function (container, options, json) {
if (options.indentation) { if (options.indentation) {
this.indentation = Number(options.indentation); this.indentation = Number(options.indentation);
} }
this.options = options;
this.mode = (options.mode == 'code') ? 'code' : 'text'; this.mode = (options.mode == 'code') ? 'code' : 'text';
if (this.mode == 'code') { if (this.mode == 'code') {
// verify whether Ace editor is available and supported // verify whether Ace editor is available and supported
if (typeof ace === 'undefined') { if (typeof ace === 'undefined') {
this.mode = 'text'; this.mode = 'text';
console.log('WARNING: Cannot load code editor, Ace library not loaded. ' + util.log('WARNING: Cannot load code editor, Ace library not loaded. ' +
'Falling back to plain text editor'); 'Falling back to plain text editor');
} }
if (util.getInternetExplorerVersion() == 8) { if (util.getInternetExplorerVersion() == 8) {
this.mode = 'text'; this.mode = 'text';
console.log('WARNING: Cannot load code editor, Ace is not supported on IE8. ' + util.log('WARNING: Cannot load code editor, Ace is not supported on IE8. ' +
'Falling back to plain text editor'); 'Falling back to plain text editor');
} }
} }
@ -85,7 +86,12 @@ TextEditor.prototype._create = function (container, options, json) {
//buttonFormat.className = 'jsoneditor-button'; //buttonFormat.className = 'jsoneditor-button';
this.menu.appendChild(buttonFormat); this.menu.appendChild(buttonFormat);
buttonFormat.onclick = function () { buttonFormat.onclick = function () {
try {
me.format(); me.format();
}
catch (err) {
me._onError(err);
}
}; };
// create compact button // create compact button
@ -96,7 +102,13 @@ TextEditor.prototype._create = function (container, options, json) {
//buttonCompact.className = 'jsoneditor-button'; //buttonCompact.className = 'jsoneditor-button';
this.menu.appendChild(buttonCompact); this.menu.appendChild(buttonCompact);
buttonCompact.onclick = function () { buttonCompact.onclick = function () {
try {
me.compact(); me.compact();
}
catch (err) {
me._onError(err);
}
}; };
this.content = document.createElement('div'); this.content = document.createElement('div');
@ -184,39 +196,41 @@ TextEditor.prototype._delete = function () {
}; };
/** /**
* This method is executed on error. * Throw an error. If an error callback is configured in options.error, this
* It can be overwritten for each instance of the TextEditor * callback will be invoked. Else, a regular error is thrown.
* @param {String} err * @param {Error} err
* @private
*/ */
// TODO: replace with an options.error TextEditor.prototype._onError = function(err) {
TextEditor.prototype.onError = function(err) { // TODO: onError is deprecated since version 2.2.0. cleanup some day
// action should be implemented for the instance if (typeof this.onError === 'function') {
util.log('WARNING: JSONEditor.onError is deprecated. ' +
'Use options.error instead.');
this.onError(err);
}
if (typeof this.options.error === 'function') {
this.options.error(err);
}
else {
throw err;
}
}; };
/** /**
* Compact the code in the formatter * Compact the code in the formatter
*/ */
TextEditor.prototype.compact = function () { TextEditor.prototype.compact = function () {
try {
var json = util.parse(this.getText()); var json = util.parse(this.getText());
this.setText(JSON.stringify(json)); this.setText(JSON.stringify(json));
}
catch (err) {
this.onError(err);
}
}; };
/** /**
* Format the code in the formatter * Format the code in the formatter
*/ */
TextEditor.prototype.format = function () { TextEditor.prototype.format = function () {
try {
var json = util.parse(this.getText()); var json = util.parse(this.getText());
this.setText(JSON.stringify(json, null, this.indentation)); this.setText(JSON.stringify(json, null, this.indentation));
}
catch (err) {
this.onError(err);
}
}; };
/** /**

View File

@ -92,22 +92,22 @@ TreeEditor.prototype._setOptions = function (options) {
if (options['enableSearch']) { if (options['enableSearch']) {
// deprecated since version 1.6.0, 2012-11-03 // deprecated since version 1.6.0, 2012-11-03
this.options.search = options['enableSearch']; this.options.search = options['enableSearch'];
console.log('WARNING: Option "enableSearch" is deprecated. Use "search" instead.'); util.log('WARNING: Option "enableSearch" is deprecated. Use "search" instead.');
} }
if (options['enableHistory']) { if (options['enableHistory']) {
// deprecated since version 1.6.0, 2012-11-03 // deprecated since version 1.6.0, 2012-11-03
this.options.history = options['enableHistory']; this.options.history = options['enableHistory'];
console.log('WARNING: Option "enableHistory" is deprecated. Use "history" instead.'); util.log('WARNING: Option "enableHistory" is deprecated. Use "history" instead.');
} }
if (options['mode'] == 'editor') { if (options['mode'] == 'editor') {
// deprecated since version 2.2.0, 2013-04-30 // deprecated since version 2.2.0, 2013-04-30
this.options.mode = 'tree'; this.options.mode = 'tree';
console.log('WARNING: Mode "editor" is deprecated. Use "tree" instead.'); util.log('WARNING: Mode "editor" is deprecated. Use "tree" instead.');
} }
if (options['mode'] == 'viewer') { if (options['mode'] == 'viewer') {
// deprecated since version 2.2.0, 2013-04-30 // deprecated since version 2.2.0, 2013-04-30
this.options.mode = 'view'; this.options.mode = 'view';
console.log('WARNING: Mode "viewer" is deprecated. Use "view" instead.'); util.log('WARNING: Mode "viewer" is deprecated. Use "view" instead.');
} }
} }
@ -132,7 +132,7 @@ TreeEditor.prototype.set = function (json, name) {
// adjust field name for root node // adjust field name for root node
if (name) { if (name) {
// TODO: deprecated since version 2.2.0. Cleanup some day. // TODO: deprecated since version 2.2.0. Cleanup some day.
console.log('Warning: second parameter "name" is deprecated. ' + util.log('Warning: second parameter "name" is deprecated. ' +
'Use setName(name) instead.'); 'Use setName(name) instead.');
this.options.name = name; this.options.name = name;
} }
@ -317,7 +317,7 @@ TreeEditor.prototype._onAction = function (action, params) {
this.options.change(); this.options.change();
} }
catch (err) { catch (err) {
console.log('Error in change callback: ', err); util.log('Error in change callback: ', err);
} }
} }
}; };

View File

@ -26,13 +26,6 @@ if (!Array.prototype.forEach) {
} }
} }
// Old browsers do not have a console, so create a fake one in that case.
if (typeof console === 'undefined') {
console = {
log: function () {}
};
}
/** /**
* Parse JSON using the parser built-in in the browser. * Parse JSON using the parser built-in in the browser.
* On exception, the jsonString is validated and a detailed error is thrown. * On exception, the jsonString is validated and a detailed error is thrown.
@ -43,9 +36,9 @@ util.parse = function (jsonString) {
return JSON.parse(jsonString); return JSON.parse(jsonString);
} }
catch (err) { catch (err) {
// get a detailed error message using validate // try to throw a more detailed error message using validate
var message = util.validate(jsonString) || err; util.validate(jsonString);
throw new Error(message); throw err;
} }
}; };
@ -54,33 +47,15 @@ util.parse = function (jsonString) {
* This method uses JSONLint to validate the String. If JSONLint is not * This method uses JSONLint to validate the String. If JSONLint is not
* available, the built-in JSON parser of the browser is used. * available, the built-in JSON parser of the browser is used.
* @param {String} jsonString String with an (invalid) JSON object * @param {String} jsonString String with an (invalid) JSON object
* @return {String | undefined} Returns undefined when the string is valid JSON, * @throws Error
* returns a string with an error message when
* the data is invalid. This message is HTML
* formatted.
*/ */
util.validate = function (jsonString) { util.validate = function (jsonString) {
var message = undefined;
try {
if (typeof(jsonlint) != 'undefined') { if (typeof(jsonlint) != 'undefined') {
jsonlint.parse(jsonString); jsonlint.parse(jsonString);
} }
else { else {
JSON.parse(jsonString); JSON.parse(jsonString);
} }
}
catch (err) {
message = '<pre class="error">' + err.toString() + '</pre>';
if (typeof(jsonlint) != 'undefined') {
message +=
'<a class="error" href="http://zaach.github.com/jsonlint/" target="_blank">' +
'validated by jsonlint' +
'</a>';
}
}
return message;
}; };
/** /**
@ -112,6 +87,16 @@ util.clear = function (a) {
return a; return a;
}; };
/**
* Output text to the console, if console is available
* @param {...*} args
*/
util.log = function(args) {
if (console && typeof console.log === 'function') {
console.log.apply(console, arguments);
}
};
/** /**
* Test whether a text contains a url (matches when a string starts * Test whether a text contains a url (matches when a string starts
* with 'http://*' or 'https://*' and has no whitespace characters) * with 'http://*' or 'https://*' and has no whitespace characters)