1、json2html.js的代码
//Copyright (c) 2013 Crystalline Technologies // // 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 json2html = { /* ---------------------------------------- Public Methods ------------------------------------------------ */ 'transform': function(json,transform,_options) { //create the default output var out = {'events':[],'html':''}; //default options (by default we don't allow events) var options = { 'events':false }; //extend the options options = json2html._extend(options,_options); //Make sure we have a transform & json object if( transform !== undefined || json !== undefined ) { //Normalize strings to JSON objects if necessary var obj = typeof json === 'string' ? JSON.parse(json) : json; //Transform the object (using the options) out = json2html._transform(obj, transform, options); } //determine if we need the events // otherwise return just the html string if(options.events) return(out); else return( out.html ); }, /* ---------------------------------------- Private Methods ------------------------------------------------ */ //Extend options '_extend':function(obj1,obj2){ var obj3 = {}; for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; } for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; } return obj3; }, //Append results '_append':function(obj1,obj2) { var out = {'html':'','event':[]}; if(typeof obj1 !== 'undefined' && typeof obj2 !== 'undefined') { out.html = obj1.html + obj2.html; out.events = obj1.events.concat(obj2.events); } return(out); }, //isArray (fix for IE prior to 9) '_isArray':function(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }, //Transform object '_transform':function(json, transform, options) { var elements = {'events':[],'html':''}; //Determine the type of this object if(json2html._isArray(json)) { //Itterrate through the array and add it to the elements array var len=json.length; for(var j=0;j<len;++j) { //Apply the transform to this object and append it to the results elements = json2html._append(elements,json2html._apply(json[j], transform, j, options)); } } else if(typeof json === 'object') { //Apply the transform to this object and append it to the results elements = json2html._append(elements,json2html._apply(json, transform, undefined, options)); } //Return the resulting elements return(elements); }, //Apply the transform at the second level '_apply':function(obj, transform, index, options) { var element = {'events':[],'html':''}; //Itterate through the transform and create html as needed if(json2html._isArray(transform)) { var t_len = transform.length; for(var t=0; t < t_len; ++t) { //transform the object and append it to the output element = json2html._append(element,json2html._apply(obj, transform[t], index, options)); } } else if(typeof transform === 'object') { //Get the tag element of this transform if( transform.tag !== undefined ) { var tag = json2html._getValue(obj,transform,'tag',index); //Create a new element element.html += '<' + tag; //Create a new object for the children var children = {'events':[],'html':''}; //innerHTML var html; //Look into the properties of this transform for (var key in transform) { switch(key) { case 'tag': //Do nothing as we have already created the element from the tag break; case 'children': //Add the children if(json2html._isArray(transform.children)) { //Apply the transform to the children children = json2html._append(children,json2html._apply(obj, transform.children, index, options)); } else if(typeof transform.children === 'function') { //Get the result from the function var temp = transform.children.call(obj, obj, index); //Make sure we have an object result with the props // html (string), events (array) // OR a string (then just append it to the children if(typeof temp === 'object') { //make sure this object is a valid json2html response object if(temp.html !== undefined && temp.events !== undefined) children = json2html._append(children,temp); } else if(typeof temp === 'string') { //append the result directly to the html of the children children.html += temp; } } break; case 'html': //Create the html attribute for this element html = json2html._getValue(obj,transform,'html',index); break; default: //Add the property as a attribute if it's not a key one var isEvent = false; //Check if the first two characters are 'on' then this is an event if( key.length > 2 ) if(key.substring(0,2).toLowerCase() == 'on') { //Determine if we should add events if(options.events) { //if so then setup the event data var data = { 'action':transform[key], 'obj':obj, 'data':options.eventData, 'index':index }; //create a new id for this event var id = json2html._guid(); //append the new event to this elements events element.events[element.events.length] = {'id':id,'type':key.substring(2),'data':data}; //Insert temporary event property (json2html-event-id) into the element element.html += " json2html-event-id-"+key.substring(2)+"='" + id + "'"; } //this is an event isEvent = true; } //If this wasn't an event AND we actually have a value then add it as a property if( !isEvent){ //Get the value var val = json2html._getValue(obj, transform, key, index); //Make sure we have a value if(val !== undefined) { var out; //Determine the output type of this value (wrap with quotes) if(typeof val === 'string') out = '"' + val.replace(/"/g, '"') + '"'; else out = val; //creat the name value pair element.html += ' ' + key + '=' + out; } } break; } } //close the opening tag element.html += '>'; //add the innerHTML (if we have any) if(html) element.html += html; //add the children (if we have any) element = json2html._append(element,children); //add the closing tag element.html += '</' + tag + '>'; } } //Return the output object return(element); }, //Get a new GUID (used by events) '_guid':function() { var S4 = function() { return (((1+Math.random())*0x10000)|0).toString(16).substring(1); }; return (S4()+S4()+"-"+S4()+S4()+"-"+S4()+S4()); }, //Get the html value of the object '_getValue':function(obj, transform, key,index) { var out = ''; var val = transform[key]; var type = typeof val; if (type === 'function') { return(val.call(obj,obj,index)); } else if (type === 'string') { var _tokenizer = new json2html._tokenizer([ /${([^}{]+)}/ ],function( src, real, re ){ return real ? src.replace(re,function(all,name){ //Split the string into it's seperate components var components = name.split('.'); //Set the object we use to query for this name to be the original object var useObj = obj; //Output value var outVal = ''; //Parse the object components var c_len = components.length; for (var i=0;i<c_len;++i) { if( components[i].length > 0 ) { var newObj = useObj[components[i]]; useObj = newObj; if(useObj === null || useObj === undefined) break; } } //As long as we have an object to use then set the out if(useObj !== null && useObj !== undefined) outVal = useObj; return(outVal); }) : src; }); out = _tokenizer.parse(val).join(''); } else { out = val; } return(out); }, //Tokenizer '_tokenizer':function( tokenizers, doBuild ){ if( !(this instanceof json2html._tokenizer ) ) return new json2html._tokenizer( tokenizers, doBuild ); this.tokenizers = tokenizers.splice ? tokenizers : [tokenizers]; if( doBuild ) this.doBuild = doBuild; this.parse = function( src ){ this.src = src; this.ended = false; this.tokens = [ ]; do { this.next(); } while( !this.ended ); return this.tokens; }; this.build = function( src, real ){ if( src ) this.tokens.push( !this.doBuild ? src : this.doBuild(src,real,this.tkn) ); }; this.next = function(){ var self = this, plain; self.findMin(); plain = self.src.slice(0, self.min); self.build( plain, false ); self.src = self.src.slice(self.min).replace(self.tkn,function( all ){ self.build(all, true); return ''; }); if( !self.src ) self.ended = true; }; this.findMin = function(){ var self = this, i=0, tkn, idx; self.min = -1; self.tkn = ''; while(( tkn = self.tokenizers[i++]) !== undefined ){ idx = self.src[tkn.test?'search':'indexOf'](tkn); if( idx != -1 && (self.min == -1 || idx < self.min )){ self.tkn = tkn; self.min = idx; } } if( self.min == -1 ) self.min = self.src.length; }; } };
2、测试代码
2.1 example.escape.js
(function() { //Test the handling of quoted strings var test_data = {"test1":"'single-quoted'", "test2":""double-quoted""}; var test_data2 = {"test1":"<b>escape HTML test</b>", "test2":"<b>escape HTML test</b>"}; var transform = [ {"tag":"input", "html":"", "value":"${test1}"}, {"tag":"input", "html":"", "value":"${test2}"}, {"tag":"textarea", "html":"${test1}"}, {"tag":"textarea", "html":"${test2}"}, ]; var html = json2html.transform(test_data, transform); var html2 = json2html.transform(test_data2, transform); document.write('<h1>Escape Quotes Test</h1>'+ html); document.write('<h1>Escape HTML Test</h1>'+ html2); })();
2.2 example.nested.js
//Test nested dynamic children // calls json2html recusively(递归的) var nested = {"Name": "Parent", "Children": [{"Name": "Child1"}, {"Name": "Child1"}]}; var transform_parent = [ {"tag":"span", "html":"${Name}"}, {"tag":"ul", children:function(){return(json2html.transform(this.Children, transform_child));}} ]; var transform_child = {"tag":"li", children:[ {"tag": "b", "html":"${Name}"} ]}; var html = json2html.transform(nested,transform_parent); document.write('<h1>Nested Test</h1>'+ html);
2.2 example.shorthand.js
//Test the short hand notation // as well as the direct reference via this var movies = [ { "Name": "The Red Violin", "ReleaseYear": "1998" }, { "Name": "The blue Violin", "ReleaseYear": "1998" }, { "Name": "The yellow Violin", "ReleaseYear": "1998" }, { "Name": "The purple Violin", "ReleaseYear": "1998" }, { "Name": "The orange Violin", "ReleaseYear": "1998" } ]; var transform_shortHand = {"tag": "li", "children": [ {"tag":"b", "html":"${Name}"}, {"tag":"span", "html":"${ReleaseYear}"} ]}; var transform_longHand = {"tag": "li", children: [ {"tag": "b", "html": function(){return(this.Name);}}, {"tag": "span", "html": function(){return(this.ReleaseYear);}} ]}; var html1 = json2html.transform(movies,transform_shortHand); var html2 = json2html.transform(movies,transform_longHand); document.write('<h1>Shorthand Test</h1>'+ html1 + '<br><br>' + html2);
3、测试页面
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title> json2html examples </title> <!-- Add JSON2HTML --> <script type="text/javascript" src="json2html.js"></script> <script type="text/javascript" src="jquery.js"></script> <script> $(function(){ }); </script> </head> <body> </body> </html>