/*
Requires prototype.js

JSON Voorhees Control - Edit JSON blocks with ease.

Copyright (c) 2006 Bryan English (bryan@bluelinecity.com)

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

var JVoorheesControl = Class.create();

JVoorheesControl._autoinc = 0;
JVoorheesControl._lookup = new Array();
JVoorheesControl._models = new Array();
JVoorheesControl._element = null;
JVoorheesControl.version = '83.05.13';
JVoorheesControl.prototype = {

   	initialize: function( json ){
		this.children = new Array();
		this._parent = null;
		this._id = JVoorheesControl._autoinc++;
		  
		if ( json ){
			//defaults//
			this.name = '';
			this.json = json;
			
			//self-assign any json non-object value//
			for ( attribute in json ){
				if ( typeof(json[attribute]) != 'object' && typeof(json[attribute]) != 'function' && attribute != 'children' ){
					this[attribute] = json[attribute];
				}
			}
			
			//take care of optionals//
			this.control = this.getAttribute('control',this.type);
			this.label = this.getAttribute('label',this.name);
			this.value = this.getAttribute('value',this.getAttribute('def',''))
			this.nullable = this.getAttribute('nullable',true);

			//if this node is to serve as a model for future use//		
			if ( this.model ){
				JVoorheesControl._models[this.model] = json;
			}

            //if not array and has json children, add them as object properties//
			if ( this.type != 'array' && this.control != 'array' && json.children ){
				for ( var cnt=0; cnt < json.children.length; cnt++ ){
        		    this.addChild( this.create( json.children[cnt] ) );
				}
			}	
		}

		JVoorheesControl._lookup[this._id] = this;

   	},

	addChild: function( node ){
		node._parent = this;
		this.children[this.children.length] = node;
		return this.children[this.children.length-1];
	},

    create: function ( json, element ){
        
        if ( typeof(json) == 'string' ){
            json = json.parseJSON();
        }

        if ( !json.type ){
            alert('Problem with JSON schema');
        }
        
        var control = (json.control!==undefined?json.control:json.type)

        if ( JVoorheesControl.factory[control] ){
            var j = new JVoorheesControl.factory[control]( json );
        } else {
            var j = JVoorhees.create(JVoorheesControl._models[control]);
        }

        //if control has setup(), call it//
        if ( j.setup ){
            j.setup();
        }

        //if element set, load, hide and create control//
        if ( element ){
            JVoorheesControl._element = element;

            if ( !/^\s*$/.test($F(element)) ){
                loader = $F(element).parseJSON();
                if ( loader ){
                    j.loadJSON( loader );
                } else {
                    alert('Problem loading JSON block');
                }
            }

            $(element).style.display = 'none';
            new Insertion.After( element,j.toXHTML() );
        }

        return j;    
    },

    getAttribute: function(name,def){
        var def = (def==null?'':def);
        if ( this[name] === undefined || this[name] === null || this[name] === '' ){
            return def;
        } else {
            return this[name];
        }
    },
    
   	updateValues: function(){},

	updateXHTML: function(){
		this.updateValues();
		var node = $('input/' + this._id);
		node.innerHTML = this.getInnerXHTML();
	},
	
	'$': function(id){
		return JVoorheesControl._lookup[id];
	},

    /**
     * Save a JSON string to the element it was taken from.
     */
    saveJSON: function(){
        if ( JVoorheesControl._element ){
            this.updateValues();
            $(JVoorheesControl._element).value = this.toJSON().toJSONString();
        }
    },

    validate: function(){

    },

    jsLink: function(title,attributes){

        var html = '<a href="javascript:void(\'\');" title="'+ ((attributes.status)?attributes.status:title).replace(/\<[^\>]+?\>/g,'') +'"';
        
        attributes.onmouseover = (attributes.onmouseover?attributes.onmouseover:'') + 'window.status=\''+ ((attributes.status)?attributes.status:title).replace(/\<[^\>]+?\>/g,'') +'\';return(true);';
        attributes.onmouseout = (attributes.onmouseoout?attributes.onmouseout:'') + 'window.status=\'\';return(true);';

        for ( attribute in attributes ){
            if ( /^on|class/.test(attribute) ){
                html += ' ' + attribute + '="'+ attributes[attribute] +'"';
            }
        }

        return html + '>'+ title +'</a>';

    }

}

//-----------------------------------------------------//
// BRANCH
//-----------------------------------------------------//

var JVoorheesControlBranch = Class.create();
Object.extend(JVoorheesControlBranch.prototype,JVoorheesControl.prototype);
Object.extend(JVoorheesControlBranch.prototype,{

    /**
     * After creating branch from schema, setup the control
     */
    setup: function(){
        this._status = 'closed';
        this._hasBranches = false;

        for (var cnt=0; cnt<this.children.length; cnt++ ){
            if ( this.children[cnt].control == 'branch'){
                this._hasBranches = true;
            }
        }
    },

    toXHTML: function(){

        var buffer = '';
        buffer  = '<div id="input/'+this._id+'" class="branch'+ ((this._parent.getAttribute('default') == this.name)?'_ghost':'') +
                  '"'+ (this._parent.control=='tree' || this._parent._status == 'opened'?' style="display:block;"':'') +'>';
        buffer += this.getInnerXHTML();
        buffer += '</div>';	        
        return buffer;
    },

    getInnerXHTML: function(){  

        mytree = this.findTree();
        selected = mytree._selected;
        var buffer = '';

        if ( this._parent.getAttribute('default') != this.name ){

            var buffer = '<div class="label">';

            buffer += this.jsLink('<div class="icon" id="input/'+this._id+'/icon">'+ this.getIcon() +'</div>',
                {"onmousedown":"JVoorhees.$('"+ this._id +"').toggleBranch();"});

            buffer += this.jsLink(this.getLabel(),{"onmousedown":"JVoorhees.$('"+ this._id +"').selectBranch();"})
            buffer += '&nbsp;';

            if ( this.type == 'array' ){            
                buffer += this.jsLink(mytree.getAttribute('add_icon','<span>+</span>'),{"class":"control","onclick":"var n = JVoorhees.$('"+ this._id +"').addElement();JVoorhees.$('"+ this._id +"')._parent.updateXHTML();JVoorhees.$('"+ this._id +"').openBranch();n.selectBranch();"});
            }

            if ( this.getAttribute('default') ){
                for ( var cnt=0 ; cnt < this.children.length ; cnt++ ){
                    if ( this.children[cnt].getAttribute('name') == this.getAttribute('default') ){
                        buffer += this.jsLink(mytree.getAttribute('add_icon','<span>+</span>'),{"class":"control","onclick":"var n = JVoorhees.$('"+ this.children[cnt]._id +"').addElement();JVoorhees.$('"+ this.children[cnt]._id +"')._parent.updateXHTML();JVoorhees.$('"+ this.children[cnt]._id +"').openBranch();n.selectBranch();"});                
                    }
                }
            }
            
            if ( this._parent.type == 'array' ){
                buffer += this.jsLink(mytree.getAttribute('remove_icon','<span>-</span>'),{"class":"control","status":"Remove","onclick":"if(confirm(\'sure?\')){var p = JVoorhees.$("+ this._id +")._parent;p.removeElement("+ this._id +");p.updateXHTML();p.openBranch();}"});
                buffer += this.jsLink(mytree.getAttribute('up_icon','<span>&nbsp;/\\&nbsp;</span>'),{"class":"control","status":"Up","onclick":"JVoorhees.$("+ this._id +").moveUp();"});
                buffer += this.jsLink(mytree.getAttribute('down_icon','<span>&nbsp;\\/&nbsp;</span>'),{"class":"control","status":"Down","onclick":"JVoorhees.$("+ this._id +").moveDown();"});
            }

            buffer += '</div>';

        }

        //get sub branches//       
        for (var cnt=0; cnt < this.children.length; cnt++){
            if ( this.children[cnt].control == 'branch'){
                buffer += this.children[cnt].toXHTML();
            }
        }
            
        return buffer;
    },

    /**
     * Find this branches tree object
     *
     * @return object Tree object this branch is a part of
     */
    findTree: function(){

        if ( this._parent.control == 'tree'){
            return this._parent;
        } else if (this._parent.control == 'branch'){
            return this._parent.findTree();
        } else {
            return null;
        }

    },

    updateIcon: function(){

        if ( node = $('input/'+ this._id +'/icon') ){
            node.innerHTML = this.getIcon();
        }

    },

    moveUp: function(){
        
        for ( var cnt=0 ; cnt < this._parent.children.length ; cnt++ ){
            if ( this._parent.children[cnt] === this && cnt != 0){
                var tmp = this._parent.children[cnt-1];
                this._parent.children[cnt-1] = this._parent.children[cnt];
                this._parent.children[cnt] = tmp;
            }
        }

        this._parent.updateXHTML();
    },

    moveDown: function(){
        
        for ( var cnt = (this._parent.children.length-1) ; cnt >= 0 ; cnt-- ){
            if ( this._parent.children[cnt] === this && cnt != (this._parent.children.length-1)){
                var tmp = this._parent.children[cnt+1];
                this._parent.children[cnt+1] = this._parent.children[cnt];
                this._parent.children[cnt] = tmp;
            }
        }

        this._parent.updateXHTML();
    },

    getIcon: function(){

       var mytree = this.findTree();

       if ( this._hasBranches ) {
           if ( this._status == 'opened' ) {
               return this.getAttribute('opened_icon',mytree.getAttribute('opened_icon','-'));
           } else {
               return this.getAttribute('closed_icon',mytree.getAttribute('closed_icon','+'));
           }
       } else {
           return this.getAttribute('icon',mytree.getAttribute('icon','-'));
       }

    },

    getLabel: function(){
        if ( /^\!/.test( this.label ) ){
            label = this.label.replace(/^\!/,'');
            
            for (var cnt=0;cnt<this.children.length;cnt++){
                if ( this.children[cnt].name == label ){
                    return this.children[cnt].getAttribute('value',this.name).HTMLSpecialChars();
                }
            }

            return this.name;
        } else {
            return this.label;
        }
    },

    /**
     * Get the branches child branch types for use as
     * a multiple child selector. Right now only does first branch found.
     */
    getChildBranchTypes: function(){
        var options = new Array();

        this.json.children.each(function(e,i){
            if ( e.control == 'tree'){
                options[options.length] = new Array(((e.label)?e.label:e.name),i);
            }
        });

        return options[0];
    },
    
    /**
     * Select a branch to view
     */
    selectBranch: function(){

        var buffer = '';

        mytree = this.findTree();            
        mytree._selected.updateValues();     
        mytree._selected.updateXHTML();
        Element.removeClassName($('input/' + mytree._selected._id),'selected');

        //get non-branch properties for viewing pane//
        for ( var cnt=0; cnt < this.children.length; cnt++ ){
            if ( this.children[cnt].control != 'branch' ){
                buffer += this.children[cnt].toXHTML();
            }
        }
        
        if ( mytree ){
            var node = $('input/'+ mytree._id +'/right');
            mytree._selected = this;
            node.innerHTML = mytree.getPath() + buffer;
        } else {
            alert("No tree control found");
        }

        Element.addClassName($('input/' + mytree._selected._id),'selected');

        this.updateXHTML();

    },

    toggleBranch: function(){

        var style = (this._status=='opened'?'none':'block');
        this._status = (this._status=='opened'?'closed':'opened');
        for (var cnt=0; cnt < this.children.length; cnt++){
            if ( this.children[cnt].control == 'branch' ){
                var div = $('input/' + this.children[cnt]._id);
                $('input/' + this.children[cnt]._id).style.display = style;
            }
        }

        this.updateIcon();

    },

    /**
     * open this objects branch
     */
    openBranch: function(){

        for (var cnt=0; cnt < this.children.length; cnt++){
            if ( this.children[cnt].control == 'branch' ){
                $('input/' + this.children[cnt]._id).style.display = 'block';
            }
        }
        
        this._status = 'opened';
        this.updateIcon();

    },


    toJSON: function(){

      if ( this.type == 'object' ){
          var node = {};

          for (var cnt=0; cnt < this.children.length; cnt++){
             if ( this.children[cnt].toJSON() != '' ){
                 node[this.children[cnt].name] = this.children[cnt].toJSON();
             }
          }
      } else if ( this.type == 'array' ) {
          var node = new Array();

          for (var cnt=0; cnt < this.children.length; cnt++){
             if ( this.children[cnt].toJSON() != '' ){
                 node[node.length] = this.children[cnt].toJSON();
             }
          }
      }

      return node;
    },
    
    
    loadJSON: function( json ){
        if (json){
            if ( this.type == 'object' && typeof(json) == 'object'){
                for (var cnt=0; cnt<this.children.length; cnt++ ){
                    this.children[cnt].loadJSON( json[this.children[cnt].name] );
                }                
            } else if ( this.type == 'array' && json.length ) {
                for (var cnt=0; cnt < json.length; cnt++ ){
                    this.addElement().loadJSON( json[cnt] );
                }
            }
        }
    },   


    removeElement: function(id){

        for (var cnt=0; cnt < this.children.length ;cnt++ ){
            this.children = this.children.findAll(function(a){
                return (a._id != id);
            });
        }

        this.findTree()._selected = this;

        if ( this.children.length == 0){
            this._hasBranches = false;
        }

    },

    /**
     * Adds a child to the JSON object using a previously saved json object
     * as the model. 
     *
     * @param type Int Defaults to 0, index of the model to use in the saved json object.
     */
    addElement: function(type){
        type = (type?type:0);
        this._hasBranches = true;
        var n = this.addChild( this.create( this.json.children[type] ) );
        return n;
    },
       
    updateValues: function(){
        this.children.each(function(child){
            child.updateValues();
        });
    }

});

//-----------------------------------------------------//
// OBJECT
//-----------------------------------------------------//

var JVoorheesControlObject = Class.create();
Object.extend(JVoorheesControlObject.prototype,JVoorheesControl.prototype);
Object.extend(JVoorheesControlObject.prototype,{

    toXHTML: function(){
		var buffer  = '<div id="input/'+this._id+'" class="object">';
		buffer += this.getInnerXHTML();
		buffer += '</div>';	        
        return buffer;
    },

   getInnerXHTML: function(){

      var nameable = ( this._parent && this._parent.control == 'array' && this._parent.type == 'object' );
      var buffer = '<div class="label">' + (nameable?(this.label?this.label + ': ':'') + '<input type="text" id="input/'+ this._id +'/name" name="input/'+ this._id +'/name" value="'+ this.name +'" />':'<span>'+this.label+'</span>')+ '</div>';
      if ( this._parent && this._parent.control == 'array' ){
         buffer += '<div class="controls"><input type="button" value="X" onClick="if(confirm(\'sure?\')){JVoorhees.$('+ this._id +')._parent.removeElement('+ this._id +');JVoorhees.$('+this._id+')._parent.updateXHTML();}" /></div>';
      }
      for (var cnt=0; cnt < this.children.length; cnt++){
         buffer += this.children[cnt].toXHTML();
      }
      
      buffer += '<div class="field">&nbsp;</div>';
	        
      return buffer;
   },

   toJSON: function(){
      var node = {};

      for (var cnt=0; cnt < this.children.length; cnt++){
         if ( this.children[cnt].toJSON() != '' ){
             node[this.children[cnt].name] = this.children[cnt].toJSON();
         }
      }

      return node;
   },
   
   loadJSON: function(json){
      for (var cnt=0; cnt<this.children.length; cnt++ ){
         this.children[cnt].loadJSON(json[this.children[cnt].name]);
      }
   },   

   updateValues: function(){
      if ( this._parent && this._parent.control == 'array' && this._parent.type == 'object' && $('input/' + this._id) ){
          this.name = $F('input/'+ this._id +'/name');
      }

      this.children.each(function(child){
         child.updateValues();
      });
   }

});

//-----------------------------------------------------//
// ARRAY
//-----------------------------------------------------//

var JVoorheesControlArray = Class.create();
Object.extend(JVoorheesControlArray.prototype,JVoorheesControl.prototype);
Object.extend(JVoorheesControlArray.prototype,{

   toXHTML: function(){
      var buffer = '<div id="input/' + this._id + '" class="array">';
      buffer += this.getInnerXHTML();
      buffer += '</div>';
      return buffer;
   },

   getInnerXHTML: function(){
      var buffer = '<div class="label">';
      if ( this.label ){
         buffer += '<span>'+this.label+'</span>';
      }

      buffer += '</div><div class="controls"><input type="button" onClick="JVoorhees.$('+ this._id +').addElement();JVoorhees.$('+ this._id +').updateXHTML();" value="+" />';

      if ( this._parent && this._parent.control == 'array' ){
         buffer += '<input type="button" value="X" onClick="if(confirm(\'sure?\')){JVoorhees.$('+ this._id +')._parent.removeElement('+ this._id +');JVoorhees.$('+this._id+')._parent.updateXHTML();}" />';
      }

      if ( this._parent && this._parent.type == 'array' ){
         buffer += '<input type="button" value="X" />';
      }

      buffer += '</div>';

      for (var cnt=0; cnt < this.children.length; cnt++){
         buffer += this.children[cnt].toXHTML();
      }

      buffer += '<div class="field">&nbsp;</div>';
      return buffer;
   },

   toJSON: function(){
      if ( this.type == 'object' ){
          var node = {};

          for (var cnt=0; cnt < this.children.length; cnt++){
             if ( this.children[cnt].toJSON() != '' ){
                 node[this.children[cnt].name] = this.children[cnt].toJSON();
             }
          }
      } else if ( this.type == 'array' ) {
          var node = new Array();

          for (var cnt=0; cnt < this.children.length; cnt++){
             if ( this.children[cnt].toJSON() != '' ){
                 node[node.length] = this.children[cnt].toJSON();
             }
          }
      }

      return node;
   },

   removeElement: function(id){
      for (var cnt=0; cnt < this.children.length ;cnt++ ){
         this.children = this.children.findAll(function(a){
             return (a._id != id);
	     });
      }
   },

   addElement: function(){
      return this.addChild(this.create( this.json.children[0] ));
   },

   loadJSON: function(json){
      if (json){
          if ( this.type == 'object' && typeof(json) == 'object'){
              for (prop in json){
                 if ( prop != 'toJSONString' ){
                    var node = this.addElement()
                    node.loadJSON(json[prop]);                 
                    node.name = prop;
                 }
              }
          } else if ( this.type == 'array' && json.length) {
              for (var cnt=0; cnt<json.length; cnt++ ){
                 this.addElement().loadJSON(json[cnt]);
              }
          }
      }
   },

   updateValues: function(){
      this.children.each(function(child){
         child.updateValues();
      });
   }

});


//-----------------------------------------------------//
// TEXT
//-----------------------------------------------------//

var JVoorheesControlText = Class.create();
Object.extend(JVoorheesControlText.prototype,JVoorheesControl.prototype);
Object.extend(JVoorheesControlText.prototype,{

   toXHTML: function(){
      var buffer = '<div id="field/' + this._id + '" class="field'+ (this.nullable?'':' required') + '"><label for="input/'+ this._id +'">'+ this.label +':</label> <input type="text" id="input/'+ this._id +'" name="input/'+ this._id +'" value="'+ this.value.HTMLSpecialChars() +'" /></div>';
	   return buffer;
   },

   toJSON: function(){
      return this.value;
   },

   loadJSON: function(json){
      this.value = (json === undefined?'':json);
   },

   updateValues: function(){
      if ($('input/' + this._id)){
         this.value = $F('input/' + this._id);
      }
   }

});

//-----------------------------------------------------//
// OPTION
//-----------------------------------------------------//

var JVoorheesControlOption = Class.create();
Object.extend(JVoorheesControlOption.prototype,JVoorheesControl.prototype);
Object.extend(JVoorheesControlOption.prototype,{

   toXHTML: function(selected){
		return '<option'+ (selected==this.value?' selected="selected"':'') +' value="'+ this.value +'">'+ this.label +'</option>';
   },

   toJSON: function(){
      return null;
   }
});

//-----------------------------------------------------//
// SELECT
//-----------------------------------------------------//

var JVoorheesControlSelect = Class.create();
Object.extend(JVoorheesControlSelect.prototype,JVoorheesControl.prototype);
Object.extend(JVoorheesControlSelect.prototype,{

   toXHTML: function(){
		var buffer = '<div id="field/' + this._id + '" class="field'+ (this.nullable?'':' required') + '"><label for="input/'+ this._id +'">'+ this.label +':</label> ';
      buffer += '<select id="input/'+ this._id +'" name="input/'+ this._id +'">';
      for (var cnt=0; cnt < this.children.length; cnt++){
         buffer += this.children[cnt].toXHTML(this.value);
      }
      buffer += '</select></div>';
      return buffer;
   },

   toJSON: function(){
      return this.value;
   },

   loadJSON: function(json){
      this.value = (json === undefined?'':json);;
   },

   updateValues: function(){
      if ($('input/' + this._id)){
         this.value = $F('input/' + this._id);
      }
   }

});

//-----------------------------------------------------//
// TREE
//-----------------------------------------------------//

/**
 * The tree component can only have branch child elements
 */
var JVoorheesControlTree = Class.create();
Object.extend(JVoorheesControlTree.prototype,JVoorheesControl.prototype);
Object.extend(JVoorheesControlTree.prototype,{

    setup: function(){
        this._selected = this.children[0];
        this._selected._status = 'opened';
    },
    
    toXHTML: function(){
        var buffer  = '<div id="input/'+this._id+'" class="tree">';
        buffer += this.getInnerXHTML();
        buffer += '</div>';	        
        return buffer;
    },

    getInnerXHTML: function(){
        var buffer = '<h1>'+ this.label +'</h1><div id="input/'+this._id+'/left" class="leftTree">';
        buffer += this.children[0].toXHTML(); //trunk//
        buffer += '</div><div id="input/'+this._id+'/right" class="rightTree"></div>';
        return buffer;
    },
        
    /**
     * Get the full path of the currently selected branch
     *
     * @param string delimiter Optional character for seperating the branches
     * @return string Path representation of currently selected branch
     */
    getPath: function(delimiter){
        var cur = this._selected;
        var path = '';
        delimiter = (delimiter?delimiter:'&nbsp;/&nbsp;');

        while ( cur.control == 'branch' ){
            if (  cur._parent.getAttribute('default') != cur.name ){
                path = delimiter + cur.getLabel().HTMLSpecialChars() + path;
            }
            
            cur = cur._parent;
        }

        return path;
    },

    toJSON: function(){
        return this.children[0].toJSON();
    },

    loadJSON: function(json){
        this.children[0].loadJSON(json);
    },   

    updateValues: function(){
        this.children[0].updateValues();
    }

});

JVoorheesControl.factory = {
   'object' : JVoorheesControlObject,
   'branch' : JVoorheesControlBranch,
   'array'  : JVoorheesControlArray,
   'string' : JVoorheesControlText,
   'tree'   : JVoorheesControlTree,
   'text'   : JVoorheesControlText,
   'select' : JVoorheesControlSelect,
   'option' : JVoorheesControlOption
};
  
var JVoorhees = new JVoorheesControl();

/*
 json.js
 2006-04-28

 This file adds these methods to JavaScript:

     object.toJSONString()

         This method produces a JSON text from an object. The
         object must not contain any cyclical references.

     array.toJSONString()

         This method produces a JSON text from an array. The
         array must not contain any cyclical references.

     string.parseJSON()

         This method parses a JSON text to produce an object or
         array. It will return false if there is an error.
*/
(function () {
    var m = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        s = {
            array: function (x) {
                var a = ['['], b, f, i, l = x.length, v;
                for (i = 0; i < l; i += 1) {
                    v = x[i];
                    f = s[typeof v];
                    if (f) {
                        v = f(v);
                        if (typeof v == 'string') {
                            if (b) {
                                a[a.length] = ',';
                            }
                            a[a.length] = v;
                            b = true;
                        }
                    }
                }
                a[a.length] = ']';
                return a.join('');
            },
            'boolean': function (x) {
                return String(x);
            },
            'null': function (x) {
                return "null";
            },
            number: function (x) {
                return isFinite(x) ? String(x) : 'null';
            },
            object: function (x) {
                if (x) {
                    if (x instanceof Array) {
                        return s.array(x);
                    }
                    var a = ['{'], b, f, i, v;
                    for (i in x) {
                        v = x[i];
                        f = s[typeof v];
                        if (f) {
                            v = f(v);
                            if (typeof v == 'string') {
                                if (b) {
                                    a[a.length] = ',';
                                }
                                a.push(s.string(i), ':', v);
                                b = true;
                            }
                        }
                    }
                    a[a.length] = '}';
                    return a.join('');
                }
                return 'null';
            },
            string: function (x) {
                if (/["\\\x00-\x1f]/.test(x)) {
                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                        var c = m[b];
                        if (c) {
                            return c;
                        }
                        c = b.charCodeAt();
                        return '\\u00' +
                            Math.floor(c / 16).toString(16) +
                            (c % 16).toString(16);
                    });
                }
                return '"' + x + '"';
            }
        };

    Object.prototype.toJSONString = function () {
        return s.object(this);
    };

    Array.prototype.toJSONString = function () {
        return s.array(this);
    };
})();

String.prototype.parseJSON = function () {
    try {
        return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
                this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
            eval('(' + this + ')');
    } catch (e) {
        return false;
    }
};

String.prototype.HTMLSpecialChars = function(){

   if ( this.match(/\&\#?[a-z0-9]+\;/) ){
      return this;
   }

   var sp = {
      "\&":"&amp;",
      "\<":"&lt;",
      "\>":"&gt;",
      "\"":"&quot;",
      "\'":"&squot;"
      }

   var str = this;

   for ( reg in sp ){
      str = str.replace(new RegExp(reg,'g'),sp[reg]);
   }

   return str;

}
