PageSourceSearch

https://www.vtla.com/assets/common/javascript/prettyprint.js

js vtla.com collected 2026-09-29 08:48:03 UTC 19,948 bytes, 775 lines download raw bytes

1/*
2Copyright (c) 2009 James Padolsey.  All rights reserved.
3
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions
6are met:
7
8   1. Redistributions of source code must retain the above copyright
9	  notice, this list of conditions and the following disclaimer.
10
11   2. Redistributions in binary form must reproduce the above copyright
12	  notice, this list of conditions and the following disclaimer in the
13	  documentation and/or other materials provided with the distribution.
14
15THIS SOFTWARE IS PROVIDED BY James Padolsey ``AS IS'' AND
16ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ARE DISCLAIMED. IN NO EVENT SHALL James Padolsey OR CONTRIBUTORS BE LIABLE
19FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25SUCH DAMAGE.
26
27The views and conclusions contained in the software and documentation are
28those of the authors and should not be interpreted as representing official
29policies, either expressed or implied, of James Padolsey.
30
31 AUTHOR James Padolsey (http://james.padolsey.com)
32 VERSION 1.03.0
33 UPDATED 29-10-2011
34 CONTRIBUTORS
35	David Waller
36    Benjamin Drucker
37
38*/
39
40var prettyPrint = (function(){
41	
42	/* These "util" functions are not part of the core
43	   functionality but are  all necessary - mostly DOM helpers */
44	
45	var util = {
46		
47		el: function(type, attrs) {
48			
49			/* Create new element */
50			var el = document.createElement(type), attr;
51			
52			/*Copy to single object */
53			attrs = util.merge({}, attrs);
54			
55			/* Add attributes to el */
56			if (attrs && attrs.style) {
57				var styles = attrs.style;
58				util.applyCSS( el, attrs.style );
59				delete attrs.style;
60			}
61			for (attr in attrs) {
62				if (attrs.hasOwnProperty(attr)) {
63					el[attr] = attrs[attr];
64				}
65			}
66			
67			return el;
68		
69		},
70		
71		applyCSS: function(el, styles) {
72			/* Applies CSS to a single element */
73			for (var prop in styles) {
74				if (styles.hasOwnProperty(prop)) {
75					try{
76						/* Yes, IE6 SUCKS! */
77						el.style[prop] = styles[prop];
78					}catch(e){}
79				}
80			}
81		},
82		
83		txt: function(t) {
84			/* Create text node */
85			return document.createTextNode(t);
86		},
87		
88		row: function(cells, type, cellType) {
89			
90			/* Creates new <tr> */
91			cellType = cellType || 'td';
92			
93			/* colSpan is calculated by length of null items in array */
94			var colSpan = util.count(cells, null) + 1,
95				tr = util.el('tr'), td,
96				attrs = {
97					style: util.getStyles(cellType, type),
98					colSpan: colSpan,
99					onmouseover: function() {
100						var tds = this.parentNode.childNodes;
101						util.forEach(tds, function(cell){
102							if (cell.nodeName.toLowerCase() !== 'td') { return; }
103							util.applyCSS(cell, util.getStyles('td_hover', type));
104						});
105					},
106					onmouseout: function() {
107						var tds = this.parentNode.childNodes;
108						util.forEach(tds, function(cell){
109							if (cell.nodeName.toLowerCase() !== 'td') { return; }
110							util.applyCSS(cell, util.getStyles('td', type));
111						});
112					}
113				};
114				
115			util.forEach(cells, function(cell){
116				
117				if (cell === null) { return; }
118				/* Default cell type is <td> */
119				td = util.el(cellType, attrs);
120				
121				if (cell.nodeType) {
122					/* IsDomElement */
123					td.appendChild(cell);
124				} else {
125					/* IsString */
126					td.innerHTML = util.shorten(cell.toString());
127				}
128				
129				tr.appendChild(td);
130			});
131			
132			return tr;
133		},
134		
135		hRow: function(cells, type){
136			/* Return new <th> */
137			return util.row(cells, type, 'th');
138		},
139		
140		table: function(headings, type){
141			
142			headings = headings || [];
143			
144			/* Creates new table: */
145			var attrs = {
146					thead: {
147						style:util.getStyles('thead',type)
148					},
149					tbody: {
150						style:util.getStyles('tbody',type)
151					},
152					table: {
153						style:util.getStyles('table',type)
154					}
155				},
156				tbl = util.el('table', attrs.table),
157				thead = util.el('thead', attrs.thead),
158				tbody = util.el('tbody', attrs.tbody);
159				
160			if (headings.length) {
161				tbl.appendChild(thead);
162				thead.appendChild( util.hRow(headings, type) );
163			}
164			tbl.appendChild(tbody);
165			
166			return {
167				/* Facade for dealing with table/tbody
168				   Actual table node is this.node: */
169				node: tbl,
170				tbody: tbody,
171				thead: thead,
172				appendChild: function(node) {
173					this.tbody.appendChild(node);
174				},
175				addRow: function(cells, _type, cellType){
176					this.appendChild(util.row.call(util, cells, (_type || type), cellType));
177					return this;
178				}
179			};
180		},
181		
182		shorten: function(str) {
183			var max = 40;
184			str = str.replace(/^\s\s*|\s\s*$|\n/g,'');
185			return str.length > max ? (str.substring(0, max-1) + '...') : str;
186		},
187		
188		htmlentities: function(str) {
189			return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
190		},
191		
192		merge: function(target, source) {
193			
194			/* Merges two (or more) objects,
195			   giving the last one precedence */
196			
197			if ( typeof target !== 'object' ) {
198				target = {};
199			}
200			
201			for (var property in source) {
202				
203				if ( source.hasOwnProperty(property) ) {
204					
205					var sourceProperty = source[ property ];
206					
207					if ( typeof sourceProperty === 'object' ) {
208						target[ property ] = util.merge( target[ property ], sourceProperty );
209						continue;
210					}
211					
212					target[ property ] = sourceProperty;
213					
214				}
215				
216			}
217			
218			for (var a = 2, l = arguments.length; a < l; a++) {
219				util.merge(target, arguments[a]);
220			}
221			
222			return target;
223		},
224		
225		count: function(arr, item) {
226			var count = 0;
227			for (var i = 0, l = arr.length; i< l; i++) {
228				if (arr[i] === item) {
229					count++;
230				}
231			}
232			return count;
233		},
234		
235		thead: function(tbl) {
236			return tbl.getElementsByTagName('thead')[0];
237		},
238		
239		forEach: function(arr, max, fn) {
240			
241			if (!fn) {
242				fn = max;
243			}
244
245			/* Helper: iteration */
246			var len = arr.length,
247				index = -1;
248			
249			while (++index < len) {
250				if(fn( arr[index], index, arr ) === false) {
251					break;
252				}
253			}
254			
255			return true;
256		},
257		
258		type: function(v){
259			try {
260				/* Returns type, e.g. "string", "number", "array" etc.
261				   Note, this is only used for precise typing. */
262				if (v === null) { return 'null'; }
263				if (v === undefined) { return 'undefined'; }
264				var oType = Object.prototype.toString.call(v).match(/\s(.+?)\]/)[1].toLowerCase();
265				if (v.nodeType) {
266					if (v.nodeType === 1) {
267						return 'domelement';
268					}
269					return 'domnode';
270				}
271				if (/^(string|number|array|regexp|function|date|boolean)$/.test(oType)) {
272					return oType;
273				}
274				if (typeof v === 'object') {
275					return v.jquery && typeof v.jquery === 'string' ? 'jquery' : 'object';
276				}
277				if (v === window || v === document) {
278					return 'object';
279				}
280				return 'default';
281			} catch(e) {
282				return 'default';
283			}
284		},
285		
286		within: function(ref) {
287			/* Check existence of a val within an object
288			   RETURNS KEY */
289			return {
290				is: function(o) {
291					for (var i in ref) {
292						if (ref[i] === o) {
293							return i;
294						}
295					}
296					return '';
297				}
298			};
299		},
300		
301		common: {
302			circRef: function(obj, key, settings) {
303				return util.expander(
304					'[POINTS BACK TO <strong>' + (key) + '</strong>]',
305					'Click to show this item anyway',
306					function() {
307						this.parentNode.appendChild( prettyPrintThis(obj,{maxDepth:1}) );
308					}
309				);
310			},
311			depthReached: function(obj, settings) {
312				return util.expander(
313					'[DEPTH REACHED]',
314					'Click to show this item anyway',
315					function() {
316						try {
317							this.parentNode.appendChild( prettyPrintThis(obj,{maxDepth:1}) );
318						} catch(e) {
319							this.parentNode.appendChild(
320								util.table(['ERROR OCCURED DURING OBJECT RETRIEVAL'],'error').addRow([e.message]).node   
321							);
322						}
323					}
324				);
325			}
326		},
327		
328		getStyles: function(el, type) {
329			type = prettyPrintThis.settings.styles[type] || {};
330			return util.merge(
331				{}, prettyPrintThis.settings.styles['default'][el], type[el]
332			);
333		},
334		
335		expander: function(text, title, clickFn) {
336			return util.el('a', {
337				innerHTML:  util.shorten(text) + ' <b style="visibility:hidden;">[+]</b>',
338				title: title,
339				onmouseover: function() {
340					this.getElementsByTagName('b')[0].style.visibility = 'visible';
341				},
342				onmouseout: function() {
343					this.getElementsByTagName('b')[0].style.visibility = 'hidden';
344				},
345				onclick: function() {
346					this.style.display = 'none';
347					clickFn.call(this);
348					return false;
349				},
350				style: {
351					cursor: 'pointer'
352				}
353			});
354		},
355		
356		stringify: function(obj) {
357			
358			/* Bit of an ugly duckling!
359			   - This fn returns an ATTEMPT at converting an object/array/anyType
360				 into a string, kinda like a JSON-deParser
361			   - This is used for when |settings.expanded === false| */
362			
363			var type = util.type(obj),
364				str, first = true;
365			if ( type === 'array' ) {
366				str = '[';
367				util.forEach(obj, function(item,i){
368					str += (i===0?'':', ') + util.stringify(item);
369				});
370				return str + ']';
371			}
372			if (typeof obj === 'object') {
373				str = '{';
374				for (var i in obj){
375					if (obj.hasOwnProperty(i)) {
376						str += (first?'':', ') + i + ':' + util.stringify(obj[i]);
377						first = false;
378					}
379				}
380				return str + '}';
381			}
382			if (type === 'regexp') {
383				return '/' + obj.source + '/';
384			}
385			if (type === 'string') {
386				return '"' + obj.replace(/"/g,'\\"') + '"';
387			}
388			return obj.toString();
389		},
390		
391		headerGradient: (function(){
392			
393			var canvas = document.createElement('canvas');
394			if (!canvas.getContext) { return ''; }
395			var cx = canvas.getContext('2d');
396			canvas.height = 30;
397			canvas.width = 1;
398			
399			var linearGrad = cx.createLinearGradient(0,0,0,30);
400			linearGrad.addColorStop(0,'rgba(0,0,0,0)');
401			linearGrad.addColorStop(1,'rgba(0,0,0,0.25)');
402			
403			cx.fillStyle = linearGrad;
404			cx.fillRect(0,0,1,30);
405			
406			var dataURL = canvas.toDataURL && canvas.toDataURL();
407			return 'url(' + (dataURL || '') + ')';
408		
409		})()
410		
411	};
412	
413	// Main..
414	var prettyPrintThis = function(obj, options) {
415		
416		 /*
417		 *	  obj :: Object to be printed					
418		 *  options :: Options (merged with config)
419		 */
420		
421		options = options || {};
422		
423		var settings = util.merge( {}, prettyPrintThis.config, options ),
424			container = util.el('div'),
425			config = prettyPrintThis.config,
426			currentDepth = 0,
427			stack = {},
428			hasRunOnce = false;
429		
430		/* Expose per-call settings.
431		   Note: "config" is overwritten (where necessary) by options/"settings"
432		   So, if you need to access/change *DEFAULT* settings then go via ".config" */
433		prettyPrintThis.settings = settings;
434		
435		var typeDealer = {
436			string : function(item){
437				return util.txt('"' + util.shorten(item.replace(/"/g,'\\"')) + '"');
438			},
439			number : function(item) {
440				return util.txt(item);
441			},
442			regexp : function(item) {
443				
444				var miniTable = util.table(['RegExp',null], 'regexp');
445				var flags = util.table();
446				var span = util.expander(
447					'/' + item.source + '/',
448					'Click to show more',
449					function() {
450						this.parentNode.appendChild(miniTable.node);
451					}
452				);
453				
454				flags
455					.addRow(['g', item.global])
456					.addRow(['i', item.ignoreCase])
457					.addRow(['m', item.multiline]);
458				
459				miniTable
460					.addRow(['source', '/' + item.source + '/'])
461					.addRow(['flags', flags.node])
462					.addRow(['lastIndex', item.lastIndex]);
463					
464				return settings.expanded ? miniTable.node : span;
465			},
466			domelement : function(element, depth) {
467				
468				var miniTable = util.table(['DOMElement',null], 'domelement'),
469					props = ['id', 'className', 'innerHTML', 'src', 'href'], elname = element.nodeName || '';
470				
471				miniTable.addRow(['tag', '&lt;' + elname.toLowerCase() + '&gt;']);
472					
473				util.forEach(props, function(prop){
474					if ( element[prop] ) {
475						miniTable.addRow([ prop, util.htmlentities(element[prop]) ]);
476					}
477				});
478				
479				return settings.expanded ? miniTable.node : util.expander(
480					'DOMElement (' + elname.toLowerCase() + ')',
481					'Click to show more',
482					function() {
483						this.parentNode.appendChild(miniTable.node);
484					}
485				);
486			},
487			domnode : function(node){
488				
489				/* Deals with all DOMNodes that aren't elements (nodeType !== 1) */
490				var miniTable = util.table(['DOMNode',null], 'domelement'),
491					data =  util.htmlentities( (node.data || 'UNDEFINED').replace(/\n/g,'\\n') );
492				miniTable
493					.addRow(['nodeType', node.nodeType + ' (' + node.nodeName + ')'])
494					.addRow(['data', data]);
495				
496				return settings.expanded ? miniTable.node : util.expander(
497					'DOMNode',
498					'Click to show more',
499					function() {
500						this.parentNode.appendChild(miniTable.node);
501					}
502				);
503			},
504			jquery : function(obj, depth, key) {
505				return typeDealer['array'](obj, depth, key, true);
506			},
507			object : function(obj, depth, key) {
508				
509				/* Checking depth + circular refs */
510				/* Note, check for circular refs before depth; just makes more sense */
511				var stackKey = util.within(stack).is(obj);
512				if ( stackKey ) {
513					return util.common.circRef(obj, stackKey, settings);
514				}
515				stack[key||'TOP'] = obj;
516				if (depth === settings.maxDepth) {
517					return util.common.depthReached(obj, settings);
518				}
519				
520				var table = util.table(['Object', null],'object'),
521					isEmpty = true;
522				
523				for (var i in obj) {
524					if (!obj.hasOwnProperty || obj.hasOwnProperty(i)) {
525						var item = obj[i],
526							type = util.type(item);
527						isEmpty = false;
528						try {
529							table.addRow([i, typeDealer[ type ](item, depth+1, i)], type);
530						} catch(e) {
531							/* Security errors are thrown on certain Window/DOM properties */
532							if (window.console && window.console.log) {
533								console.log(e.message);
534							}
535						}
536					}
537				}
538				
539				if (isEmpty) {
540					table.addRow(['<small>[empty]</small>']);
541				} else {
542					table.thead.appendChild(
543						util.hRow(['key','value'], 'colHeader')
544					);
545				}
546				
547				var ret = (settings.expanded || hasRunOnce) ? table.node : util.expander(
548					util.stringify(obj),
549					'Click to show more',
550					function() {
551						this.parentNode.appendChild(table.node);
552					}
553				);
554				
555				hasRunOnce = true;
556				
557				return ret;
558				
559			},
560			array : function(arr, depth, key, jquery) {
561				
562				/* Checking depth + circular refs */
563				/* Note, check for circular refs before depth; just makes more sense */
564				var stackKey = util.within(stack).is(arr);
565				if ( stackKey ) {
566					return util.common.circRef(arr, stackKey);
567				}
568				stack[key||'TOP'] = arr;
569				if (depth === settings.maxDepth) {
570					return util.common.depthReached(arr);
571				}
572				
573				/* Accepts a table and modifies it */
574				var me = jquery ? 'jQuery' : 'Array', table = util.table([me + '(' + arr.length + ')', null], jquery ? 'jquery' : me.toLowerCase()),
575					isEmpty = true,
576                    count = 0;
577				
578				if (jquery){
579					table.addRow(['selector',arr.selector]);
580				}
581
582				util.forEach(arr, function(item,i){
583                    if (settings.maxArray >= 0 && ++count > settings.maxArray) {
584                        table.addRow([
585                            i + '..' + (arr.length-1),
586                            typeDealer[ util.type(item) ]('...', depth+1, i)
587                        ]);
588                        return false;
589                    }
590					isEmpty = false;
591					table.addRow([i, typeDealer[ util.type(item) ](item, depth+1, i)]);
592				});
593
594				if (!jquery){
595					if (isEmpty) {
596						table.addRow(['<small>[empty]</small>']);
597					} else {
598						table.thead.appendChild( util.hRow(['index','value'], 'colHeader') );
599					}
600				}
601				
602				return settings.expanded ? table.node : util.expander(
603					util.stringify(arr),
604					'Click to show more',
605					function() {
606						this.parentNode.appendChild(table.node);
607					}
608				);
609				
610			},
611			'function' : function(fn, depth, key) {
612				
613				/* Checking JUST circular refs */
614				var stackKey = util.within(stack).is(fn);
615				if ( stackKey ) { return util.common.circRef(fn, stackKey); }
616				stack[key||'TOP'] = fn;
617				
618				var miniTable = util.table(['Function',null], 'function'),
619					argsTable = util.table(['Arguments']),
620					args = fn.toString().match(/\((.+?)\)/),
621					body = fn.toString().match(/\(.*?\)\s+?\{?([\S\s]+)/)[1].replace(/\}?$/,'');
622					
623				miniTable
624					.addRow(['arguments', args ? args[1].replace(/[^\w_,\s]/g,'') : '<small>[none/native]</small>'])
625					.addRow(['body', body]);
626					
627				return settings.expanded ? miniTable.node : util.expander(
628					'function(){...}',
629					'Click to see more about this function.',
630					function(){
631						this.parentNode.appendChild(miniTable.node);
632					}
633				);
634			},
635			'date' : function(date) {
636				
637				var miniTable = util.table(['Date',null], 'date'),
638					sDate = date.toString().split(/\s/);
639				
640				/* TODO: Make this work well in IE! */
641				miniTable
642					.addRow(['Time', sDate[4]])
643					.addRow(['Date', sDate.slice(0,4).join('-')]);
644					
645				return settings.expanded ? miniTable.node : util.expander(
646					'Date (timestamp): ' + (+date),
647					'Click to see a little more info about this date',
648					function() {
649						this.parentNode.appendChild(miniTable.node);
650					}
651				);
652				
653			},
654			'boolean' : function(bool) {
655				return util.txt( bool.toString().toUpperCase() );
656			},
657			'undefined' : function() {
658				return util.txt('UNDEFINED');
659			},
660			'null' : function() {
661				return util.txt('NULL');
662			},
663			'default' : function() {
664				/* When a type cannot be found */
665				return util.txt('prettyPrint: TypeNotFound Error');
666			}
667		};
668		
669		container.appendChild( typeDealer[ (settings.forceObject) ? 'object' : util.type(obj) ](obj, currentDepth) );
670		
671		return container;
672		
673	};
674	
675	/* Configuration */
676	
677	/* All items can be overwridden by passing an
678	   "options" object when calling prettyPrint */
679	prettyPrintThis.config = {
680		
681		/* Try setting this to false to save space */
682		expanded: true,
683		
684		forceObject: false,
685		maxDepth: 3,
686		maxArray: -1,  // default is unlimited
687		styles: {
688			array: {
689				th: {
690					backgroundColor: '#6DBD2A',
691					color: 'white'
692				}
693			},
694			'function': {
695				th: {
696					backgroundColor: '#D82525'
697				}
698			},
699			regexp: {
700				th: {
701					backgroundColor: '#E2F3FB',
702					color: '#000'
703				}
704			},
705			object: {
706				th: {
707					backgroundColor: '#1F96CF'
708				}
709			},
710			jquery : {
711				th: {
712					backgroundColor: '#FBF315'
713				}
714			},
715			error: {
716				th: {
717					backgroundColor: 'red',
718					color: 'yellow'
719				}
720			},
721			domelement: {
722				th: {
723					backgroundColor: '#F3801E'
724				}
725			},
726			date: {
727				th: {
728					backgroundColor: '#A725D8'
729				}
730			},
731			colHeader: {
732				th: {
733					backgroundColor: '#EEE',
734					color: '#000',
735					textTransform: 'uppercase'
736				}
737			},
738			'default': {
739				table: {
740					borderCollapse: 'collapse',
741					width: '100%'
742				},
743				td: {
744					padding: '5px',
745					fontSize: '12px',
746					backgroundColor: '#FFF',
747					color: '#222',
748					border: '1px solid #000',
749					verticalAlign: 'top',
750					fontFamily: '"Consolas","Lucida Console",Courier,mono',
751					whiteSpace: 'nowrap'
752				},
753				td_hover: {
754					/* Styles defined here will apply to all tr:hover > td,
755						- Be aware that "inheritable" properties (e.g. fontWeight) WILL BE INHERITED */
756				},
757				th: {
758					padding: '5px',
759					fontSize: '12px',
760					backgroundColor: '#222',
761					color: '#EEE',
762					textAlign: 'left',
763					border: '1px solid #000',
764					verticalAlign: 'top',
765					fontFamily: '"Consolas","Lucida Console",Courier,mono',
766					backgroundImage: util.headerGradient,
767					backgroundRepeat: 'repeat-x'
768				}
769			}
770		}
771	};
772	
773	return prettyPrintThis;
774	
775})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.