js-beautify 1.9.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -321,7 +321,11 @@ OutputLine.prototype.trim = function() {
321
321
 
322
322
  OutputLine.prototype.toString = function() {
323
323
  var result = '';
324
- if (!this.is_empty()) {
324
+ if (this.is_empty()) {
325
+ if (this.__parent.indent_empty_lines) {
326
+ result = this.__parent.get_indent_string(this.__indent_count);
327
+ }
328
+ } else {
325
329
  result = this.__parent.get_indent_string(this.__indent_count, this.__alignment_count);
326
330
  result += this.__items.join('');
327
331
  }
@@ -398,6 +402,7 @@ function Output(options, baseIndentString) {
398
402
  this._end_with_newline = options.end_with_newline;
399
403
  this.indent_size = options.indent_size;
400
404
  this.wrap_line_length = options.wrap_line_length;
405
+ this.indent_empty_lines = options.indent_empty_lines;
401
406
  this.__lines = [];
402
407
  this.previous_line = null;
403
408
  this.current_line = null;
@@ -650,6 +655,12 @@ function Options(options, merge_child_field) {
650
655
  // Backwards compat with 1.3.x
651
656
  this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
652
657
 
658
+ this.indent_empty_lines = this._get_boolean('indent_empty_lines');
659
+
660
+ // valid templating languages ['django', 'erb', 'handlebars', 'php']
661
+ // For now, 'auto' = all off for javascript, all on for html (and inline javascript).
662
+ // other values ignored
663
+ this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto']);
653
664
  }
654
665
 
655
666
  Options.prototype._get_array = function(name, default_value) {
@@ -1304,6 +1315,9 @@ Beautifier.prototype.beautify = function() {
1304
1315
  isAfterSpace = whitespace !== '';
1305
1316
  previous_ch = topCharacter;
1306
1317
  this._ch = this._input.next();
1318
+ if (this._ch === '\\' && this._input.hasNext()) {
1319
+ this._ch += this._input.next();
1320
+ }
1307
1321
  topCharacter = this._ch;
1308
1322
 
1309
1323
  if (!this._ch) {
@@ -1436,7 +1450,7 @@ Beautifier.prototype.beautify = function() {
1436
1450
  }
1437
1451
  }
1438
1452
  } else if (this._ch === ":") {
1439
- if ((insideRule || enteringConditionalGroup) && !(this._input.lookBack("&") || this.foundNestedPseudoClass()) && !this._input.lookBack("(") && !insideAtExtend) {
1453
+ if ((insideRule || enteringConditionalGroup) && !(this._input.lookBack("&") || this.foundNestedPseudoClass()) && !this._input.lookBack("(") && !insideAtExtend && parenLevel === 0) {
1440
1454
  // 'property: value' delimiter
1441
1455
  // which could be in a conditional group query
1442
1456
  this.print_string(':');
@@ -1468,51 +1482,66 @@ Beautifier.prototype.beautify = function() {
1468
1482
  this.print_string(this._ch + this.eatString(this._ch));
1469
1483
  this.eatWhitespace(true);
1470
1484
  } else if (this._ch === ';') {
1471
- if (insidePropertyValue) {
1472
- this.outdent();
1473
- insidePropertyValue = false;
1474
- }
1475
- insideAtExtend = false;
1476
- insideAtImport = false;
1477
- this.print_string(this._ch);
1478
- this.eatWhitespace(true);
1479
-
1480
- // This maintains single line comments on the same
1481
- // line. Block comments are also affected, but
1482
- // a new line is always output before one inside
1483
- // that section
1484
- if (this._input.peek() !== '/') {
1485
- this._output.add_new_line();
1485
+ if (parenLevel === 0) {
1486
+ if (insidePropertyValue) {
1487
+ this.outdent();
1488
+ insidePropertyValue = false;
1489
+ }
1490
+ insideAtExtend = false;
1491
+ insideAtImport = false;
1492
+ this.print_string(this._ch);
1493
+ this.eatWhitespace(true);
1494
+
1495
+ // This maintains single line comments on the same
1496
+ // line. Block comments are also affected, but
1497
+ // a new line is always output before one inside
1498
+ // that section
1499
+ if (this._input.peek() !== '/') {
1500
+ this._output.add_new_line();
1501
+ }
1502
+ } else {
1503
+ this.print_string(this._ch);
1504
+ this.eatWhitespace(true);
1505
+ this._output.space_before_token = true;
1486
1506
  }
1487
1507
  } else if (this._ch === '(') { // may be a url
1488
1508
  if (this._input.lookBack("url")) {
1489
1509
  this.print_string(this._ch);
1490
1510
  this.eatWhitespace();
1511
+ parenLevel++;
1512
+ this.indent();
1491
1513
  this._ch = this._input.next();
1492
1514
  if (this._ch === ')' || this._ch === '"' || this._ch === '\'') {
1493
1515
  this._input.back();
1494
- parenLevel++;
1495
1516
  } else if (this._ch) {
1496
1517
  this.print_string(this._ch + this.eatString(')'));
1518
+ if (parenLevel) {
1519
+ parenLevel--;
1520
+ this.outdent();
1521
+ }
1497
1522
  }
1498
1523
  } else {
1499
- parenLevel++;
1500
1524
  this.preserveSingleSpace(isAfterSpace);
1501
1525
  this.print_string(this._ch);
1502
1526
  this.eatWhitespace();
1527
+ parenLevel++;
1528
+ this.indent();
1503
1529
  }
1504
1530
  } else if (this._ch === ')') {
1531
+ if (parenLevel) {
1532
+ parenLevel--;
1533
+ this.outdent();
1534
+ }
1505
1535
  this.print_string(this._ch);
1506
- parenLevel--;
1507
1536
  } else if (this._ch === ',') {
1508
1537
  this.print_string(this._ch);
1509
1538
  this.eatWhitespace(true);
1510
- if (this._options.selector_separator_newline && !insidePropertyValue && parenLevel < 1 && !insideAtImport) {
1539
+ if (this._options.selector_separator_newline && !insidePropertyValue && parenLevel === 0 && !insideAtImport) {
1511
1540
  this._output.add_new_line();
1512
1541
  } else {
1513
1542
  this._output.space_before_token = true;
1514
1543
  }
1515
- } else if ((this._ch === '>' || this._ch === '+' || this._ch === '~') && !insidePropertyValue && parenLevel < 1) {
1544
+ } else if ((this._ch === '>' || this._ch === '+' || this._ch === '~') && !insidePropertyValue && parenLevel === 0) {
1516
1545
  //handle combinator spacing
1517
1546
  if (this._options.space_around_combinator) {
1518
1547
  this._output.space_before_token = true;
@@ -1537,7 +1566,7 @@ Beautifier.prototype.beautify = function() {
1537
1566
  if (whitespaceChar.test(this._ch)) {
1538
1567
  this._ch = '';
1539
1568
  }
1540
- } else if (this._ch === '!') { // !important
1569
+ } else if (this._ch === '!' && !this._input.lookBack("\\")) { // !important
1541
1570
  this.print_string(' ');
1542
1571
  this.print_string(this._ch);
1543
1572
  } else {
@@ -331,7 +331,11 @@ OutputLine.prototype.trim = function() {
331
331
 
332
332
  OutputLine.prototype.toString = function() {
333
333
  var result = '';
334
- if (!this.is_empty()) {
334
+ if (this.is_empty()) {
335
+ if (this.__parent.indent_empty_lines) {
336
+ result = this.__parent.get_indent_string(this.__indent_count);
337
+ }
338
+ } else {
335
339
  result = this.__parent.get_indent_string(this.__indent_count, this.__alignment_count);
336
340
  result += this.__items.join('');
337
341
  }
@@ -408,6 +412,7 @@ function Output(options, baseIndentString) {
408
412
  this._end_with_newline = options.end_with_newline;
409
413
  this.indent_size = options.indent_size;
410
414
  this.wrap_line_length = options.wrap_line_length;
415
+ this.indent_empty_lines = options.indent_empty_lines;
411
416
  this.__lines = [];
412
417
  this.previous_line = null;
413
418
  this.current_line = null;
@@ -720,6 +725,12 @@ function Options(options, merge_child_field) {
720
725
  // Backwards compat with 1.3.x
721
726
  this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
722
727
 
728
+ this.indent_empty_lines = this._get_boolean('indent_empty_lines');
729
+
730
+ // valid templating languages ['django', 'erb', 'handlebars', 'php']
731
+ // For now, 'auto' = all off for javascript, all on for html (and inline javascript).
732
+ // other values ignored
733
+ this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto']);
723
734
  }
724
735
 
725
736
  Options.prototype._get_array = function(name, default_value) {
@@ -1643,6 +1654,15 @@ TemplatablePattern.prototype.disable = function(language) {
1643
1654
  return result;
1644
1655
  };
1645
1656
 
1657
+ TemplatablePattern.prototype.read_options = function(options) {
1658
+ var result = this._create();
1659
+ for (var language in template_names) {
1660
+ result._disabled[language] = options.templating.indexOf(language) === -1;
1661
+ }
1662
+ result._update();
1663
+ return result;
1664
+ };
1665
+
1646
1666
  TemplatablePattern.prototype.exclude = function(language) {
1647
1667
  var result = this._create();
1648
1668
  result._excluded[language] = true;
@@ -2243,10 +2263,12 @@ Beautifier.prototype._handle_text = function(printer, raw_token, last_tag_token)
2243
2263
  Beautifier.prototype._print_custom_beatifier_text = function(printer, raw_token, last_tag_token) {
2244
2264
  var local = this;
2245
2265
  if (raw_token.text !== '') {
2246
- printer.print_newline(false);
2266
+
2247
2267
  var text = raw_token.text,
2248
2268
  _beautifier,
2249
- script_indent_level = 1;
2269
+ script_indent_level = 1,
2270
+ pre = '',
2271
+ post = '';
2250
2272
  if (last_tag_token.custom_beautifier_name === 'javascript' && typeof this._js_beautify === 'function') {
2251
2273
  _beautifier = this._js_beautify;
2252
2274
  } else if (last_tag_token.custom_beautifier_name === 'css' && typeof this._css_beautify === 'function') {
@@ -2258,7 +2280,6 @@ Beautifier.prototype._print_custom_beatifier_text = function(printer, raw_token,
2258
2280
  };
2259
2281
  }
2260
2282
 
2261
-
2262
2283
  if (this._options.indent_scripts === "keep") {
2263
2284
  script_indent_level = 0;
2264
2285
  } else if (this._options.indent_scripts === "separate") {
@@ -2271,24 +2292,67 @@ Beautifier.prototype._print_custom_beatifier_text = function(printer, raw_token,
2271
2292
  // we'll be adding one back after the text but before the containing tag.
2272
2293
  text = text.replace(/\n[ \t]*$/, '');
2273
2294
 
2274
- if (_beautifier) {
2295
+ // Handle the case where content is wrapped in a comment or cdata.
2296
+ if (last_tag_token.custom_beautifier_name !== 'html' &&
2297
+ text[0] === '<' && text.match(/^(<!--|<!\[CDATA\[)/)) {
2298
+ var matched = /^(<!--[^\n]*|<!\[CDATA\[)(\n?)([ \t\n]*)([\s\S]*)(-->|]]>)$/.exec(text);
2275
2299
 
2276
- // call the Beautifier if avaliable
2277
- var Child_options = function() {
2278
- this.eol = '\n';
2279
- };
2280
- Child_options.prototype = this._options.raw_options;
2281
- var child_options = new Child_options();
2282
- text = _beautifier(indentation + text, child_options);
2283
- } else {
2284
- // simply indent the string otherwise
2285
- var white = raw_token.whitespace_before;
2286
- if (white) {
2287
- text = text.replace(new RegExp('\n(' + white + ')?', 'g'), '\n');
2300
+ // if we start to wrap but don't finish, print raw
2301
+ if (!matched) {
2302
+ printer.add_raw_token(raw_token);
2303
+ return;
2304
+ }
2305
+
2306
+ pre = indentation + matched[1] + '\n';
2307
+ text = matched[4];
2308
+ if (matched[5]) {
2309
+ post = indentation + matched[5];
2288
2310
  }
2289
2311
 
2290
- text = indentation + text.replace(/\n/g, '\n' + indentation);
2312
+ // if there is at least one empty line at the end of this text, strip it
2313
+ // we'll be adding one back after the text but before the containing tag.
2314
+ text = text.replace(/\n[ \t]*$/, '');
2315
+
2316
+ if (matched[2] || matched[3].indexOf('\n') !== -1) {
2317
+ // if the first line of the non-comment text has spaces
2318
+ // use that as the basis for indenting in null case.
2319
+ matched = matched[3].match(/[ \t]+$/);
2320
+ if (matched) {
2321
+ raw_token.whitespace_before = matched[0];
2322
+ }
2323
+ }
2291
2324
  }
2325
+
2326
+ if (text) {
2327
+ if (_beautifier) {
2328
+
2329
+ // call the Beautifier if avaliable
2330
+ var Child_options = function() {
2331
+ this.eol = '\n';
2332
+ };
2333
+ Child_options.prototype = this._options.raw_options;
2334
+ var child_options = new Child_options();
2335
+ text = _beautifier(indentation + text, child_options);
2336
+ } else {
2337
+ // simply indent the string otherwise
2338
+ var white = raw_token.whitespace_before;
2339
+ if (white) {
2340
+ text = text.replace(new RegExp('\n(' + white + ')?', 'g'), '\n');
2341
+ }
2342
+
2343
+ text = indentation + text.replace(/\n/g, '\n' + indentation);
2344
+ }
2345
+ }
2346
+
2347
+ if (pre) {
2348
+ if (!text) {
2349
+ text = pre + post;
2350
+ } else {
2351
+ text = pre + text + '\n' + post;
2352
+ }
2353
+ }
2354
+
2355
+ printer.print_newline(false);
2292
2356
  if (text) {
2293
2357
  raw_token.text = text;
2294
2358
  raw_token.whitespace_before = '';
@@ -2579,8 +2643,8 @@ Beautifier.prototype._do_optional_end_element = function(parser_token) {
2579
2643
  } else if (parser_token.tag_name === 'th' || parser_token.tag_name === 'td') {
2580
2644
  // A td element’s end tag may be omitted if the td element is immediately followed by a td or th element, or if there is no more content in the parent element.
2581
2645
  // A th element’s end tag may be omitted if the th element is immediately followed by a td or th element, or if there is no more content in the parent element.
2582
- result = result || this._tag_stack.try_pop('td', ['tr']);
2583
- result = result || this._tag_stack.try_pop('th', ['tr']);
2646
+ result = result || this._tag_stack.try_pop('td', ['table', 'thead', 'tbody', 'tfoot', 'tr']);
2647
+ result = result || this._tag_stack.try_pop('th', ['table', 'thead', 'tbody', 'tfoot', 'tr']);
2584
2648
  }
2585
2649
 
2586
2650
  // Start element omission not handled currently
@@ -2636,6 +2700,9 @@ var BaseOptions = __webpack_require__(6).Options;
2636
2700
 
2637
2701
  function Options(options) {
2638
2702
  BaseOptions.call(this, options, 'html');
2703
+ if (this.templating.length === 1 && this.templating[0] === 'auto') {
2704
+ this.templating = ['django', 'erb', 'handlebars', 'php'];
2705
+ }
2639
2706
 
2640
2707
  this.indent_inner_html = this._get_boolean('indent_inner_html');
2641
2708
  this.indent_body_inner_html = this._get_boolean('indent_body_inner_html', true);
@@ -2683,6 +2750,7 @@ function Options(options) {
2683
2750
  ]);
2684
2751
  this.unformatted_content_delimiter = this._get_characters('unformatted_content_delimiter');
2685
2752
  this.indent_scripts = this._get_selection('indent_scripts', ['normal', 'keep', 'separate']);
2753
+
2686
2754
  }
2687
2755
  Options.prototype = new BaseOptions();
2688
2756
 
@@ -2754,7 +2822,7 @@ var Tokenizer = function(input_string, options) {
2754
2822
 
2755
2823
  // Words end at whitespace or when a tag starts
2756
2824
  // if we are indenting handlebars, they are considered tags
2757
- var templatable_reader = new TemplatablePattern(this._input);
2825
+ var templatable_reader = new TemplatablePattern(this._input).read_options(this._options);
2758
2826
  var pattern_reader = new Pattern(this._input);
2759
2827
 
2760
2828
  this.__patterns = {
@@ -2769,7 +2837,7 @@ var Tokenizer = function(input_string, options) {
2769
2837
  handlebars_open: pattern_reader.until(/[\n\r\t }]/),
2770
2838
  handlebars_raw_close: pattern_reader.until(/}}/),
2771
2839
  comment: pattern_reader.starting_with(/<!--/).until_after(/-->/),
2772
- cdata: pattern_reader.starting_with(/<!\[cdata\[/).until_after(/]]>/),
2840
+ cdata: pattern_reader.starting_with(/<!\[CDATA\[/).until_after(/]]>/),
2773
2841
  // https://en.wikipedia.org/wiki/Conditional_comment
2774
2842
  conditional_comment: pattern_reader.starting_with(/<!\[/).until_after(/]>/),
2775
2843
  processing: pattern_reader.starting_with(/<\?/).until_after(/\?>/)
@@ -2820,27 +2888,27 @@ Tokenizer.prototype._get_next_token = function(previous_token, open_token) { //
2820
2888
 
2821
2889
  token = token || this._read_open_handlebars(c, open_token);
2822
2890
  token = token || this._read_attribute(c, previous_token, open_token);
2823
- token = token || this._read_raw_content(previous_token, open_token);
2891
+ token = token || this._read_raw_content(c, previous_token, open_token);
2824
2892
  token = token || this._read_close(c, open_token);
2825
2893
  token = token || this._read_content_word(c);
2826
- token = token || this._read_comment(c);
2894
+ token = token || this._read_comment_or_cdata(c);
2895
+ token = token || this._read_processing(c);
2827
2896
  token = token || this._read_open(c, open_token);
2828
2897
  token = token || this._create_token(TOKEN.UNKNOWN, this._input.next());
2829
2898
 
2830
2899
  return token;
2831
2900
  };
2832
2901
 
2833
- Tokenizer.prototype._read_comment = function(c) { // jshint unused:false
2902
+ Tokenizer.prototype._read_comment_or_cdata = function(c) { // jshint unused:false
2834
2903
  var token = null;
2835
2904
  var resulting_string = null;
2836
2905
  var directives = null;
2837
2906
 
2838
2907
  if (c === '<') {
2839
2908
  var peek1 = this._input.peek(1);
2840
- //if we're in a comment, do something special
2841
2909
  // We treat all comments as literals, even more than preformatted tags
2842
- // we just look for the appropriate close tag
2843
- if (c === '<' && (peek1 === '!' || peek1 === '?')) {
2910
+ // we only look for the appropriate closing marker
2911
+ if (peek1 === '!') {
2844
2912
  resulting_string = this.__patterns.comment.read();
2845
2913
 
2846
2914
  // only process directive on html comments
@@ -2851,8 +2919,6 @@ Tokenizer.prototype._read_comment = function(c) { // jshint unused:false
2851
2919
  }
2852
2920
  } else {
2853
2921
  resulting_string = this.__patterns.cdata.read();
2854
- resulting_string = resulting_string || this.__patterns.conditional_comment.read();
2855
- resulting_string = resulting_string || this.__patterns.processing.read();
2856
2922
  }
2857
2923
  }
2858
2924
 
@@ -2865,6 +2931,27 @@ Tokenizer.prototype._read_comment = function(c) { // jshint unused:false
2865
2931
  return token;
2866
2932
  };
2867
2933
 
2934
+ Tokenizer.prototype._read_processing = function(c) { // jshint unused:false
2935
+ var token = null;
2936
+ var resulting_string = null;
2937
+ var directives = null;
2938
+
2939
+ if (c === '<') {
2940
+ var peek1 = this._input.peek(1);
2941
+ if (peek1 === '!' || peek1 === '?') {
2942
+ resulting_string = this.__patterns.conditional_comment.read();
2943
+ resulting_string = resulting_string || this.__patterns.processing.read();
2944
+ }
2945
+
2946
+ if (resulting_string) {
2947
+ token = this._create_token(TOKEN.COMMENT, resulting_string);
2948
+ token.directives = directives;
2949
+ }
2950
+ }
2951
+
2952
+ return token;
2953
+ };
2954
+
2868
2955
  Tokenizer.prototype._read_open = function(c, open_token) {
2869
2956
  var resulting_string = null;
2870
2957
  var token = null;
@@ -2956,19 +3043,27 @@ Tokenizer.prototype._is_content_unformatted = function(tag_name) {
2956
3043
  // script and style tags should always be read as unformatted content
2957
3044
  // finally content_unformatted and unformatted element contents are unformatted
2958
3045
  return this._options.void_elements.indexOf(tag_name) === -1 &&
2959
- (tag_name === 'script' || tag_name === 'style' ||
2960
- this._options.content_unformatted.indexOf(tag_name) !== -1 ||
3046
+ (this._options.content_unformatted.indexOf(tag_name) !== -1 ||
2961
3047
  this._options.unformatted.indexOf(tag_name) !== -1);
2962
3048
  };
2963
3049
 
2964
3050
 
2965
- Tokenizer.prototype._read_raw_content = function(previous_token, open_token) { // jshint unused:false
3051
+ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token) { // jshint unused:false
2966
3052
  var resulting_string = '';
2967
3053
  if (open_token && open_token.text[0] === '{') {
2968
3054
  resulting_string = this.__patterns.handlebars_raw_close.read();
2969
3055
  } else if (previous_token.type === TOKEN.TAG_CLOSE && (previous_token.opened.text[0] === '<')) {
2970
3056
  var tag_name = previous_token.opened.text.substr(1).toLowerCase();
2971
- if (this._is_content_unformatted(tag_name)) {
3057
+ if (tag_name === 'script' || tag_name === 'style') {
3058
+ // Script and style tags are allowed to have comments wrapping their content
3059
+ // or just have regular content.
3060
+ var token = this._read_comment_or_cdata(c);
3061
+ if (token) {
3062
+ token.type = TOKEN.TEXT;
3063
+ return token;
3064
+ }
3065
+ resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
3066
+ } else if (this._is_content_unformatted(tag_name)) {
2972
3067
  resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
2973
3068
  }
2974
3069
  }
@@ -631,7 +631,7 @@ Beautifier.prototype.print_token_line_indentation = function(current_token) {
631
631
  }
632
632
  };
633
633
 
634
- Beautifier.prototype.print_token = function(current_token, printable_token) {
634
+ Beautifier.prototype.print_token = function(current_token) {
635
635
  if (this._output.raw) {
636
636
  this._output.add_raw_token(current_token);
637
637
  return;
@@ -657,10 +657,9 @@ Beautifier.prototype.print_token = function(current_token, printable_token) {
657
657
  }
658
658
  }
659
659
 
660
- printable_token = printable_token || current_token.text;
661
660
  this.print_token_line_indentation(current_token);
662
661
  this._output.non_breaking_space = true;
663
- this._output.add_token(printable_token);
662
+ this._output.add_token(current_token.text);
664
663
  if (this._output.previous_token_wrapped) {
665
664
  this._flags.multiline_frame = true;
666
665
  }
@@ -914,6 +913,8 @@ Beautifier.prototype.handle_start_block = function(current_token) {
914
913
  if (this._flags.last_word === 'switch' && this._flags.last_token.type === TOKEN.END_EXPR) {
915
914
  this.set_mode(MODE.BlockStatement);
916
915
  this._flags.in_case_statement = true;
916
+ } else if (this._flags.case_body) {
917
+ this.set_mode(MODE.BlockStatement);
917
918
  } else if (second_token && (
918
919
  (in_array(second_token.text, [':', ',']) && in_array(next_token.type, [TOKEN.STRING, TOKEN.WORD, TOKEN.RESERVED])) ||
919
920
  (in_array(next_token.text, ['get', 'set', '...']) && in_array(second_token.type, [TOKEN.WORD, TOKEN.RESERVED]))
@@ -1099,11 +1100,12 @@ Beautifier.prototype.handle_word = function(current_token) {
1099
1100
 
1100
1101
  if (this._flags.in_case_statement && reserved_array(current_token, ['case', 'default'])) {
1101
1102
  this.print_newline();
1102
- if (this._flags.case_body || this._options.jslint_happy) {
1103
+ if (this._flags.last_token.type !== TOKEN.END_BLOCK && (this._flags.case_body || this._options.jslint_happy)) {
1103
1104
  // switch cases following one another
1104
1105
  this.deindent();
1105
- this._flags.case_body = false;
1106
1106
  }
1107
+ this._flags.case_body = false;
1108
+
1107
1109
  this.print_token(current_token);
1108
1110
  this._flags.in_case = true;
1109
1111
  return;
@@ -1401,11 +1403,16 @@ Beautifier.prototype.handle_operator = function(current_token) {
1401
1403
  }
1402
1404
 
1403
1405
  if (current_token.text === ':' && this._flags.in_case) {
1404
- this._flags.case_body = true;
1405
- this.indent();
1406
1406
  this.print_token(current_token);
1407
- this.print_newline();
1407
+
1408
1408
  this._flags.in_case = false;
1409
+ this._flags.case_body = true;
1410
+ if (this._tokens.peek().type !== TOKEN.START_BLOCK) {
1411
+ this.indent();
1412
+ this.print_newline();
1413
+ } else {
1414
+ this._output.space_before_token = true;
1415
+ }
1409
1416
  return;
1410
1417
  }
1411
1418
 
@@ -1568,8 +1575,12 @@ Beautifier.prototype.handle_block_comment = function(current_token, preserve_sta
1568
1575
  this.print_token(current_token);
1569
1576
  this._output.space_before_token = true;
1570
1577
  return;
1578
+ } else {
1579
+ this.print_block_commment(current_token, preserve_statement_flags);
1571
1580
  }
1581
+ };
1572
1582
 
1583
+ Beautifier.prototype.print_block_commment = function(current_token, preserve_statement_flags) {
1573
1584
  var lines = split_linebreaks(current_token.text);
1574
1585
  var j; // iterator for this case
1575
1586
  var javadoc = false;
@@ -1581,7 +1592,8 @@ Beautifier.prototype.handle_block_comment = function(current_token, preserve_sta
1581
1592
  this.print_newline(false, preserve_statement_flags);
1582
1593
 
1583
1594
  // first line always indented
1584
- this.print_token(current_token, lines[0]);
1595
+ this.print_token_line_indentation(current_token);
1596
+ this._output.add_token(lines[0]);
1585
1597
  this.print_newline(false, preserve_statement_flags);
1586
1598
 
1587
1599
 
@@ -1597,10 +1609,12 @@ Beautifier.prototype.handle_block_comment = function(current_token, preserve_sta
1597
1609
  for (j = 0; j < lines.length; j++) {
1598
1610
  if (javadoc) {
1599
1611
  // javadoc: reformat and re-indent
1600
- this.print_token(current_token, ltrim(lines[j]));
1612
+ this.print_token_line_indentation(current_token);
1613
+ this._output.add_token(ltrim(lines[j]));
1601
1614
  } else if (starless && lines[j]) {
1602
1615
  // starless: re-indent non-empty content, avoiding trim
1603
- this.print_token(current_token, lines[j].substring(lastIndentLength));
1616
+ this.print_token_line_indentation(current_token);
1617
+ this._output.add_token(lines[j].substring(lastIndentLength));
1604
1618
  } else {
1605
1619
  // normal comments output raw
1606
1620
  this._output.current_line.set_indent(-1);
@@ -1615,6 +1629,7 @@ Beautifier.prototype.handle_block_comment = function(current_token, preserve_sta
1615
1629
  }
1616
1630
  };
1617
1631
 
1632
+
1618
1633
  Beautifier.prototype.handle_comment = function(current_token, preserve_statement_flags) {
1619
1634
  if (current_token.newlines) {
1620
1635
  this.print_newline(false, preserve_statement_flags);
@@ -1838,7 +1853,11 @@ OutputLine.prototype.trim = function() {
1838
1853
 
1839
1854
  OutputLine.prototype.toString = function() {
1840
1855
  var result = '';
1841
- if (!this.is_empty()) {
1856
+ if (this.is_empty()) {
1857
+ if (this.__parent.indent_empty_lines) {
1858
+ result = this.__parent.get_indent_string(this.__indent_count);
1859
+ }
1860
+ } else {
1842
1861
  result = this.__parent.get_indent_string(this.__indent_count, this.__alignment_count);
1843
1862
  result += this.__items.join('');
1844
1863
  }
@@ -1915,6 +1934,7 @@ function Output(options, baseIndentString) {
1915
1934
  this._end_with_newline = options.end_with_newline;
1916
1935
  this.indent_size = options.indent_size;
1917
1936
  this.wrap_line_length = options.wrap_line_length;
1937
+ this.indent_empty_lines = options.indent_empty_lines;
1918
1938
  this.__lines = [];
1919
1939
  this.previous_line = null;
1920
1940
  this.current_line = null;
@@ -2389,6 +2409,12 @@ function Options(options, merge_child_field) {
2389
2409
  // Backwards compat with 1.3.x
2390
2410
  this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
2391
2411
 
2412
+ this.indent_empty_lines = this._get_boolean('indent_empty_lines');
2413
+
2414
+ // valid templating languages ['django', 'erb', 'handlebars', 'php']
2415
+ // For now, 'auto' = all off for javascript, all on for html (and inline javascript).
2416
+ // other values ignored
2417
+ this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto']);
2392
2418
  }
2393
2419
 
2394
2420
  Options.prototype._get_array = function(name, default_value) {
@@ -2626,10 +2652,8 @@ var Tokenizer = function(input_string, options) {
2626
2652
  /\u2028\u2029/.source);
2627
2653
 
2628
2654
  var pattern_reader = new Pattern(this._input);
2629
- var templatable = new TemplatablePattern(this._input);
2630
- templatable = templatable.disable('handlebars');
2631
- templatable = templatable.disable('django');
2632
-
2655
+ var templatable = new TemplatablePattern(this._input)
2656
+ .read_options(this._options);
2633
2657
 
2634
2658
  this.__patterns = {
2635
2659
  template: templatable,
@@ -2798,7 +2822,7 @@ Tokenizer.prototype._read_non_javascript = function(c) {
2798
2822
 
2799
2823
  this._input.back();
2800
2824
 
2801
- } else if (c === '<') {
2825
+ } else if (c === '<' && this._is_first_token()) {
2802
2826
  resulting_string = this.__patterns.html_comment_start.read();
2803
2827
  if (resulting_string) {
2804
2828
  while (this._input.hasNext() && !this._input.testChar(acorn.newline)) {
@@ -3882,6 +3906,15 @@ TemplatablePattern.prototype.disable = function(language) {
3882
3906
  return result;
3883
3907
  };
3884
3908
 
3909
+ TemplatablePattern.prototype.read_options = function(options) {
3910
+ var result = this._create();
3911
+ for (var language in template_names) {
3912
+ result._disabled[language] = options.templating.indexOf(language) === -1;
3913
+ }
3914
+ result._update();
3915
+ return result;
3916
+ };
3917
+
3885
3918
  TemplatablePattern.prototype.exclude = function(language) {
3886
3919
  var result = this._create();
3887
3920
  result._excluded[language] = true;
package/js/lib/cli.js CHANGED
@@ -92,6 +92,8 @@ var path = require('path'),
92
92
  "end_with_newline": Boolean,
93
93
  "comma_first": Boolean,
94
94
  "operator_position": ["before-newline", "after-newline", "preserve-newline"],
95
+ "indent_empty_lines": Boolean,
96
+ "templating": [String, Array],
95
97
  // CSS-only
96
98
  "selector_separator_newline": Boolean,
97
99
  "newline_between_rules": Boolean,
@@ -177,6 +179,8 @@ var path = require('path'),
177
179
  "q": ["--quiet"]
178
180
  // no shorthand for "config"
179
181
  // no shorthand for "editorconfig"
182
+ // no shorthand for "indent_empty_lines"
183
+ // not shorthad for "templating"
180
184
  });
181
185
 
182
186
  function verifyExists(fullPath) {
@@ -346,6 +350,8 @@ function usage(err) {
346
350
  ' -e, --eol Character(s) to use as line terminators.',
347
351
  ' [first newline in file, otherwise "\\n]',
348
352
  ' -n, --end-with-newline End output with newline',
353
+ ' --indent-empty-lines Keep indentation on empty lines',
354
+ ' --templating List of templating languages (auto,none,django,erb,handlebars,php) ["auto"] auto = none in JavaScript, all in html',
349
355
  ' --editorconfig Use EditorConfig to set up the options'
350
356
  ];
351
357