undo wip: fixes + smoke test
[fnpeditor.git] / libs / vkbeautify.js
1 /**
2 * vkBeautify - javascript plugin to pretty-print or minify text in XML, JSON, CSS and SQL formats.
3 *  
4 * Version - 0.99.00.beta 
5 * Copyright (c) 2012 Vadim Kiryukhin
6 * vkiryukhin @ gmail.com
7 * http://www.eslinstructor.net/vkbeautify/
8
9 * Dual licensed under the MIT and GPL licenses:
10 *   http://www.opensource.org/licenses/mit-license.php
11 *   http://www.gnu.org/licenses/gpl.html
12 *
13 *   Pretty print
14 *
15 *        vkbeautify.xml(text [,indent_pattern]);
16 *        vkbeautify.json(text [,indent_pattern]);
17 *        vkbeautify.css(text [,indent_pattern]);
18 *        vkbeautify.sql(text [,indent_pattern]);
19 *
20 *        @text - String; text to beatufy;
21 *        @indent_pattern - Integer | String;
22 *                Integer:  number of white spaces;
23 *                String:   character string to visualize indentation ( can also be a set of white spaces )
24 *   Minify
25 *
26 *        vkbeautify.xmlmin(text [,preserve_comments]);
27 *        vkbeautify.jsonmin(text);
28 *        vkbeautify.cssmin(text [,preserve_comments]);
29 *        vkbeautify.sqlmin(text);
30 *
31 *        @text - String; text to minify;
32 *        @preserve_comments - Bool; [optional];
33 *                Set this flag to true to prevent removing comments from @text ( minxml and mincss functions only. )
34 *
35 *   Examples:
36 *        vkbeautify.xml(text); // pretty print XML
37 *        vkbeautify.json(text, 4 ); // pretty print JSON
38 *        vkbeautify.css(text, '. . . .'); // pretty print CSS
39 *        vkbeautify.sql(text, '----'); // pretty print SQL
40 *
41 *        vkbeautify.xmlmin(text, true);// minify XML, preserve comments
42 *        vkbeautify.jsonmin(text);// minify JSON
43 *        vkbeautify.cssmin(text);// minify CSS, remove comments ( default )
44 *        vkbeautify.sqlmin(text);// minify SQL
45 *
46 */
47
48 (function() {
49
50 function createShiftArr(step) {
51
52         var space = '    ';
53         
54         if ( isNaN(parseInt(step)) ) {  // argument is string
55                 space = step;
56         } else { // argument is integer
57                 switch(step) {
58                         case 1: space = ' '; break;
59                         case 2: space = '  '; break;
60                         case 3: space = '   '; break;
61                         case 4: space = '    '; break;
62                         case 5: space = '     '; break;
63                         case 6: space = '      '; break;
64                         case 7: space = '       '; break;
65                         case 8: space = '        '; break;
66                         case 9: space = '         '; break;
67                         case 10: space = '          '; break;
68                         case 11: space = '           '; break;
69                         case 12: space = '            '; break;
70                 }
71         }
72
73         var shift = ['\n']; // array of shifts
74         for(ix=0;ix<100;ix++){
75                 shift.push(shift[ix]+space); 
76         }
77         return shift;
78 }
79
80 function vkbeautify(){
81         this.step = '    '; // 4 spaces
82         this.shift = createShiftArr(this.step);
83 };
84
85 vkbeautify.prototype.xml = function(text,step) {
86
87         var ar = text.replace(/>\s{0,}</g,"><")
88                                  .replace(/</g,"~::~<")
89                                  .replace(/\s*xmlns\:/g,"~::~xmlns:")
90                                  .replace(/\s*xmlns\=/g,"~::~xmlns=")
91                                  .split('~::~'),
92                 len = ar.length,
93                 inComment = false,
94                 deep = 0,
95                 str = '',
96                 ix = 0,
97                 shift = step ? createShiftArr(step) : this.shift;
98
99                 for(ix=0;ix<len;ix++) {
100                         // start comment or <![CDATA[...]]> or <!DOCTYPE //
101                         if(ar[ix].search(/<!/) > -1) { 
102                                 str += shift[deep]+ar[ix];
103                                 inComment = true; 
104                                 // end comment  or <![CDATA[...]]> //
105                                 if(ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1 || ar[ix].search(/!DOCTYPE/) > -1 ) { 
106                                         inComment = false; 
107                                 }
108                         } else 
109                         // end comment  or <![CDATA[...]]> //
110                         if(ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1) { 
111                                 str += ar[ix];
112                                 inComment = false; 
113                         } else 
114                         // <elm></elm> //
115                         if( /^<\w/.exec(ar[ix-1]) && /^<\/\w/.exec(ar[ix]) &&
116                                 /^<[\w:\-\.\,]+/.exec(ar[ix-1]) == /^<\/[\w:\-\.\,]+/.exec(ar[ix])[0].replace('/','')) { 
117                                 str += ar[ix];
118                                 if(!inComment) deep--;
119                         } else
120                          // <elm> //
121                         if(ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) == -1 && ar[ix].search(/\/>/) == -1 ) {
122                                 str = !inComment ? str += shift[deep++]+ar[ix] : str += ar[ix];
123                         } else 
124                          // <elm>...</elm> //
125                         if(ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) > -1) {
126                                 str = !inComment ? str += shift[deep]+ar[ix] : str += ar[ix];
127                         } else 
128                         // </elm> //
129                         if(ar[ix].search(/<\//) > -1) { 
130                                 str = !inComment ? str += shift[--deep]+ar[ix] : str += ar[ix];
131                         } else 
132                         // <elm/> //
133                         if(ar[ix].search(/\/>/) > -1 ) { 
134                                 str = !inComment ? str += shift[deep]+ar[ix] : str += ar[ix];
135                         } else 
136                         // <? xml ... ?> //
137                         if(ar[ix].search(/<\?/) > -1) { 
138                                 str += shift[deep]+ar[ix];
139                         } else 
140                         // xmlns //
141                         if( ar[ix].search(/xmlns\:/) > -1  || ar[ix].search(/xmlns\=/) > -1) { 
142                                 str += shift[deep]+ar[ix];
143                         } 
144                         
145                         else {
146                                 str += ar[ix];
147                         }
148                 }
149                 
150         return  (str[0] == '\n') ? str.slice(1) : str;
151 }
152
153 vkbeautify.prototype.json = function(text,step) {
154
155         var step = step ? step : this.step;
156         
157         if (typeof JSON === 'undefined' ) return text; 
158         
159         if ( typeof text === "string" ) return JSON.stringify(JSON.parse(text), null, step);
160         if ( typeof text === "object" ) return JSON.stringify(text, null, step);
161                 
162         return text; // text is not string nor object
163 }
164
165 vkbeautify.prototype.css = function(text, step) {
166
167         var ar = text.replace(/\s{1,}/g,' ')
168                                 .replace(/\{/g,"{~::~")
169                                 .replace(/\}/g,"~::~}~::~")
170                                 .replace(/\;/g,";~::~")
171                                 .replace(/\/\*/g,"~::~/*")
172                                 .replace(/\*\//g,"*/~::~")
173                                 .replace(/~::~\s{0,}~::~/g,"~::~")
174                                 .split('~::~'),
175                 len = ar.length,
176                 deep = 0,
177                 str = '',
178                 ix = 0,
179                 shift = step ? createShiftArr(step) : this.shift;
180                 
181                 for(ix=0;ix<len;ix++) {
182
183                         if( /\{/.exec(ar[ix]))  { 
184                                 str += shift[deep++]+ar[ix];
185                         } else 
186                         if( /\}/.exec(ar[ix]))  { 
187                                 str += shift[--deep]+ar[ix];
188                         } else
189                         if( /\*\\/.exec(ar[ix]))  { 
190                                 str += shift[deep]+ar[ix];
191                         }
192                         else {
193                                 str += shift[deep]+ar[ix];
194                         }
195                 }
196                 return str.replace(/^\n{1,}/,'');
197 }
198
199 //----------------------------------------------------------------------------
200
201 function isSubquery(str, parenthesisLevel) {
202         return  parenthesisLevel - (str.replace(/\(/g,'').length - str.replace(/\)/g,'').length )
203 }
204
205 function split_sql(str, tab) {
206
207         return str.replace(/\s{1,}/g," ")
208
209                                 .replace(/ AND /ig,"~::~"+tab+tab+"AND ")
210                                 .replace(/ BETWEEN /ig,"~::~"+tab+"BETWEEN ")
211                                 .replace(/ CASE /ig,"~::~"+tab+"CASE ")
212                                 .replace(/ ELSE /ig,"~::~"+tab+"ELSE ")
213                                 .replace(/ END /ig,"~::~"+tab+"END ")
214                                 .replace(/ FROM /ig,"~::~FROM ")
215                                 .replace(/ GROUP\s{1,}BY/ig,"~::~GROUP BY ")
216                                 .replace(/ HAVING /ig,"~::~HAVING ")
217                                 //.replace(/ SET /ig," SET~::~")
218                                 .replace(/ IN /ig," IN ")
219                                 
220                                 .replace(/ JOIN /ig,"~::~JOIN ")
221                                 .replace(/ CROSS~::~{1,}JOIN /ig,"~::~CROSS JOIN ")
222                                 .replace(/ INNER~::~{1,}JOIN /ig,"~::~INNER JOIN ")
223                                 .replace(/ LEFT~::~{1,}JOIN /ig,"~::~LEFT JOIN ")
224                                 .replace(/ RIGHT~::~{1,}JOIN /ig,"~::~RIGHT JOIN ")
225                                 
226                                 .replace(/ ON /ig,"~::~"+tab+"ON ")
227                                 .replace(/ OR /ig,"~::~"+tab+tab+"OR ")
228                                 .replace(/ ORDER\s{1,}BY/ig,"~::~ORDER BY ")
229                                 .replace(/ OVER /ig,"~::~"+tab+"OVER ")
230
231                                 .replace(/\(\s{0,}SELECT /ig,"~::~(SELECT ")
232                                 .replace(/\)\s{0,}SELECT /ig,")~::~SELECT ")
233                                 
234                                 .replace(/ THEN /ig," THEN~::~"+tab+"")
235                                 .replace(/ UNION /ig,"~::~UNION~::~")
236                                 .replace(/ USING /ig,"~::~USING ")
237                                 .replace(/ WHEN /ig,"~::~"+tab+"WHEN ")
238                                 .replace(/ WHERE /ig,"~::~WHERE ")
239                                 .replace(/ WITH /ig,"~::~WITH ")
240                                 
241                                 //.replace(/\,\s{0,}\(/ig,",~::~( ")
242                                 //.replace(/\,/ig,",~::~"+tab+tab+"")
243
244                                 .replace(/ ALL /ig," ALL ")
245                                 .replace(/ AS /ig," AS ")
246                                 .replace(/ ASC /ig," ASC ")     
247                                 .replace(/ DESC /ig," DESC ")   
248                                 .replace(/ DISTINCT /ig," DISTINCT ")
249                                 .replace(/ EXISTS /ig," EXISTS ")
250                                 .replace(/ NOT /ig," NOT ")
251                                 .replace(/ NULL /ig," NULL ")
252                                 .replace(/ LIKE /ig," LIKE ")
253                                 .replace(/\s{0,}SELECT /ig,"SELECT ")
254                                 .replace(/\s{0,}UPDATE /ig,"UPDATE ")
255                                 .replace(/ SET /ig," SET ")
256                                                         
257                                 .replace(/~::~{1,}/g,"~::~")
258                                 .split('~::~');
259 }
260
261 vkbeautify.prototype.sql = function(text,step) {
262
263         var ar_by_quote = text.replace(/\s{1,}/g," ")
264                                                         .replace(/\'/ig,"~::~\'")
265                                                         .split('~::~'),
266                 len = ar_by_quote.length,
267                 ar = [],
268                 deep = 0,
269                 tab = this.step,//+this.step,
270                 inComment = true,
271                 inQuote = false,
272                 parenthesisLevel = 0,
273                 str = '',
274                 ix = 0,
275                 shift = step ? createShiftArr(step) : this.shift;;
276
277                 for(ix=0;ix<len;ix++) {
278                         if(ix%2) {
279                                 ar = ar.concat(ar_by_quote[ix]);
280                         } else {
281                                 ar = ar.concat(split_sql(ar_by_quote[ix], tab) );
282                         }
283                 }
284                 
285                 len = ar.length;
286                 for(ix=0;ix<len;ix++) {
287                         
288                         parenthesisLevel = isSubquery(ar[ix], parenthesisLevel);
289                         
290                         if( /\s{0,}\s{0,}SELECT\s{0,}/.exec(ar[ix]))  { 
291                                 ar[ix] = ar[ix].replace(/\,/g,",\n"+tab+tab+"")
292                         } 
293                         
294                         if( /\s{0,}\s{0,}SET\s{0,}/.exec(ar[ix]))  { 
295                                 ar[ix] = ar[ix].replace(/\,/g,",\n"+tab+tab+"")
296                         } 
297                         
298                         if( /\s{0,}\(\s{0,}SELECT\s{0,}/.exec(ar[ix]))  { 
299                                 deep++;
300                                 str += shift[deep]+ar[ix];
301                         } else 
302                         if( /\'/.exec(ar[ix]) )  { 
303                                 if(parenthesisLevel<1 && deep) {
304                                         deep--;
305                                 }
306                                 str += ar[ix];
307                         }
308                         else  { 
309                                 str += shift[deep]+ar[ix];
310                                 if(parenthesisLevel<1 && deep) {
311                                         deep--;
312                                 }
313                         } 
314                         var junk = 0;
315                 }
316
317                 str = str.replace(/^\n{1,}/,'').replace(/\n{1,}/g,"\n");
318                 return str;
319 }
320
321
322 vkbeautify.prototype.xmlmin = function(text, preserveComments) {
323
324         var str = preserveComments ? text
325                                                            : text.replace(/\<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)\>/g,"")
326                                                                          .replace(/[ \r\n\t]{1,}xmlns/g, ' xmlns');
327         return  str.replace(/>\s{0,}</g,"><"); 
328 }
329
330 vkbeautify.prototype.jsonmin = function(text) {
331
332         if (typeof JSON === 'undefined' ) return text; 
333         
334         return JSON.stringify(JSON.parse(text), null, 0); 
335                                 
336 }
337
338 vkbeautify.prototype.cssmin = function(text, preserveComments) {
339         
340         var str = preserveComments ? text
341                                                            : text.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g,"") ;
342
343         return str.replace(/\s{1,}/g,' ')
344                           .replace(/\{\s{1,}/g,"{")
345                           .replace(/\}\s{1,}/g,"}")
346                           .replace(/\;\s{1,}/g,";")
347                           .replace(/\/\*\s{1,}/g,"/*")
348                           .replace(/\*\/\s{1,}/g,"*/");
349 }
350
351 vkbeautify.prototype.sqlmin = function(text) {
352         return text.replace(/\s{1,}/g," ").replace(/\s{1,}\(/,"(").replace(/\s{1,}\)/,")");
353 }
354
355 window.vkbeautify = new vkbeautify();
356
357 })();
358