js-beautify 1.9.1 → 1.10.2

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.
@@ -656,6 +656,11 @@ function Options(options, merge_child_field) {
656
656
  this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
657
657
 
658
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']);
659
664
  }
660
665
 
661
666
  Options.prototype._get_array = function(name, default_value) {
@@ -1310,6 +1315,9 @@ Beautifier.prototype.beautify = function() {
1310
1315
  isAfterSpace = whitespace !== '';
1311
1316
  previous_ch = topCharacter;
1312
1317
  this._ch = this._input.next();
1318
+ if (this._ch === '\\' && this._input.hasNext()) {
1319
+ this._ch += this._input.next();
1320
+ }
1313
1321
  topCharacter = this._ch;
1314
1322
 
1315
1323
  if (!this._ch) {
@@ -1442,7 +1450,7 @@ Beautifier.prototype.beautify = function() {
1442
1450
  }
1443
1451
  }
1444
1452
  } else if (this._ch === ":") {
1445
- 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) {
1446
1454
  // 'property: value' delimiter
1447
1455
  // which could be in a conditional group query
1448
1456
  this.print_string(':');
@@ -1474,51 +1482,66 @@ Beautifier.prototype.beautify = function() {
1474
1482
  this.print_string(this._ch + this.eatString(this._ch));
1475
1483
  this.eatWhitespace(true);
1476
1484
  } else if (this._ch === ';') {
1477
- if (insidePropertyValue) {
1478
- this.outdent();
1479
- insidePropertyValue = false;
1480
- }
1481
- insideAtExtend = false;
1482
- insideAtImport = false;
1483
- this.print_string(this._ch);
1484
- this.eatWhitespace(true);
1485
-
1486
- // This maintains single line comments on the same
1487
- // line. Block comments are also affected, but
1488
- // a new line is always output before one inside
1489
- // that section
1490
- if (this._input.peek() !== '/') {
1491
- 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;
1492
1506
  }
1493
1507
  } else if (this._ch === '(') { // may be a url
1494
1508
  if (this._input.lookBack("url")) {
1495
1509
  this.print_string(this._ch);
1496
1510
  this.eatWhitespace();
1511
+ parenLevel++;
1512
+ this.indent();
1497
1513
  this._ch = this._input.next();
1498
1514
  if (this._ch === ')' || this._ch === '"' || this._ch === '\'') {
1499
1515
  this._input.back();
1500
- parenLevel++;
1501
1516
  } else if (this._ch) {
1502
1517
  this.print_string(this._ch + this.eatString(')'));
1518
+ if (parenLevel) {
1519
+ parenLevel--;
1520
+ this.outdent();
1521
+ }
1503
1522
  }
1504
1523
  } else {
1505
- parenLevel++;
1506
1524
  this.preserveSingleSpace(isAfterSpace);
1507
1525
  this.print_string(this._ch);
1508
1526
  this.eatWhitespace();
1527
+ parenLevel++;
1528
+ this.indent();
1509
1529
  }
1510
1530
  } else if (this._ch === ')') {
1531
+ if (parenLevel) {
1532
+ parenLevel--;
1533
+ this.outdent();
1534
+ }
1511
1535
  this.print_string(this._ch);
1512
- parenLevel--;
1513
1536
  } else if (this._ch === ',') {
1514
1537
  this.print_string(this._ch);
1515
1538
  this.eatWhitespace(true);
1516
- if (this._options.selector_separator_newline && !insidePropertyValue && parenLevel < 1 && !insideAtImport) {
1539
+ if (this._options.selector_separator_newline && !insidePropertyValue && parenLevel === 0 && !insideAtImport) {
1517
1540
  this._output.add_new_line();
1518
1541
  } else {
1519
1542
  this._output.space_before_token = true;
1520
1543
  }
1521
- } else if ((this._ch === '>' || this._ch === '+' || this._ch === '~') && !insidePropertyValue && parenLevel < 1) {
1544
+ } else if ((this._ch === '>' || this._ch === '+' || this._ch === '~') && !insidePropertyValue && parenLevel === 0) {
1522
1545
  //handle combinator spacing
1523
1546
  if (this._options.space_around_combinator) {
1524
1547
  this._output.space_before_token = true;
@@ -1543,7 +1566,7 @@ Beautifier.prototype.beautify = function() {
1543
1566
  if (whitespaceChar.test(this._ch)) {
1544
1567
  this._ch = '';
1545
1568
  }
1546
- } else if (this._ch === '!') { // !important
1569
+ } else if (this._ch === '!' && !this._input.lookBack("\\")) { // !important
1547
1570
  this.print_string(' ');
1548
1571
  this.print_string(this._ch);
1549
1572
  } else {
@@ -726,6 +726,11 @@ function Options(options, merge_child_field) {
726
726
  this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
727
727
 
728
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']);
729
734
  }
730
735
 
731
736
  Options.prototype._get_array = function(name, default_value) {
@@ -1623,6 +1628,7 @@ function TemplatablePattern(input_scanner, parent) {
1623
1628
  var pattern = new Pattern(input_scanner);
1624
1629
  this.__patterns = {
1625
1630
  handlebars_comment: pattern.starting_with(/{{!--/).until_after(/--}}/),
1631
+ handlebars_unescaped: pattern.starting_with(/{{{/).until_after(/}}}/),
1626
1632
  handlebars: pattern.starting_with(/{{/).until_after(/}}/),
1627
1633
  php: pattern.starting_with(/<\?(?:[=]|php)/).until_after(/\?>/),
1628
1634
  erb: pattern.starting_with(/<%[^%]/).until_after(/[^%]%>/),
@@ -1649,6 +1655,15 @@ TemplatablePattern.prototype.disable = function(language) {
1649
1655
  return result;
1650
1656
  };
1651
1657
 
1658
+ TemplatablePattern.prototype.read_options = function(options) {
1659
+ var result = this._create();
1660
+ for (var language in template_names) {
1661
+ result._disabled[language] = options.templating.indexOf(language) === -1;
1662
+ }
1663
+ result._update();
1664
+ return result;
1665
+ };
1666
+
1652
1667
  TemplatablePattern.prototype.exclude = function(language) {
1653
1668
  var result = this._create();
1654
1669
  result._excluded[language] = true;
@@ -1724,6 +1739,8 @@ TemplatablePattern.prototype._read_template = function() {
1724
1739
  if (!this._disabled.handlebars && !this._excluded.handlebars) {
1725
1740
  resulting_string = resulting_string ||
1726
1741
  this.__patterns.handlebars_comment.read();
1742
+ resulting_string = resulting_string ||
1743
+ this.__patterns.handlebars_unescaped.read();
1727
1744
  resulting_string = resulting_string ||
1728
1745
  this.__patterns.handlebars.read();
1729
1746
  }
@@ -2249,10 +2266,12 @@ Beautifier.prototype._handle_text = function(printer, raw_token, last_tag_token)
2249
2266
  Beautifier.prototype._print_custom_beatifier_text = function(printer, raw_token, last_tag_token) {
2250
2267
  var local = this;
2251
2268
  if (raw_token.text !== '') {
2252
- printer.print_newline(false);
2269
+
2253
2270
  var text = raw_token.text,
2254
2271
  _beautifier,
2255
- script_indent_level = 1;
2272
+ script_indent_level = 1,
2273
+ pre = '',
2274
+ post = '';
2256
2275
  if (last_tag_token.custom_beautifier_name === 'javascript' && typeof this._js_beautify === 'function') {
2257
2276
  _beautifier = this._js_beautify;
2258
2277
  } else if (last_tag_token.custom_beautifier_name === 'css' && typeof this._css_beautify === 'function') {
@@ -2264,7 +2283,6 @@ Beautifier.prototype._print_custom_beatifier_text = function(printer, raw_token,
2264
2283
  };
2265
2284
  }
2266
2285
 
2267
-
2268
2286
  if (this._options.indent_scripts === "keep") {
2269
2287
  script_indent_level = 0;
2270
2288
  } else if (this._options.indent_scripts === "separate") {
@@ -2277,24 +2295,67 @@ Beautifier.prototype._print_custom_beatifier_text = function(printer, raw_token,
2277
2295
  // we'll be adding one back after the text but before the containing tag.
2278
2296
  text = text.replace(/\n[ \t]*$/, '');
2279
2297
 
2280
- if (_beautifier) {
2298
+ // Handle the case where content is wrapped in a comment or cdata.
2299
+ if (last_tag_token.custom_beautifier_name !== 'html' &&
2300
+ text[0] === '<' && text.match(/^(<!--|<!\[CDATA\[)/)) {
2301
+ var matched = /^(<!--[^\n]*|<!\[CDATA\[)(\n?)([ \t\n]*)([\s\S]*)(-->|]]>)$/.exec(text);
2281
2302
 
2282
- // call the Beautifier if avaliable
2283
- var Child_options = function() {
2284
- this.eol = '\n';
2285
- };
2286
- Child_options.prototype = this._options.raw_options;
2287
- var child_options = new Child_options();
2288
- text = _beautifier(indentation + text, child_options);
2289
- } else {
2290
- // simply indent the string otherwise
2291
- var white = raw_token.whitespace_before;
2292
- if (white) {
2293
- text = text.replace(new RegExp('\n(' + white + ')?', 'g'), '\n');
2303
+ // if we start to wrap but don't finish, print raw
2304
+ if (!matched) {
2305
+ printer.add_raw_token(raw_token);
2306
+ return;
2307
+ }
2308
+
2309
+ pre = indentation + matched[1] + '\n';
2310
+ text = matched[4];
2311
+ if (matched[5]) {
2312
+ post = indentation + matched[5];
2313
+ }
2314
+
2315
+ // if there is at least one empty line at the end of this text, strip it
2316
+ // we'll be adding one back after the text but before the containing tag.
2317
+ text = text.replace(/\n[ \t]*$/, '');
2318
+
2319
+ if (matched[2] || matched[3].indexOf('\n') !== -1) {
2320
+ // if the first line of the non-comment text has spaces
2321
+ // use that as the basis for indenting in null case.
2322
+ matched = matched[3].match(/[ \t]+$/);
2323
+ if (matched) {
2324
+ raw_token.whitespace_before = matched[0];
2325
+ }
2326
+ }
2327
+ }
2328
+
2329
+ if (text) {
2330
+ if (_beautifier) {
2331
+
2332
+ // call the Beautifier if avaliable
2333
+ var Child_options = function() {
2334
+ this.eol = '\n';
2335
+ };
2336
+ Child_options.prototype = this._options.raw_options;
2337
+ var child_options = new Child_options();
2338
+ text = _beautifier(indentation + text, child_options);
2339
+ } else {
2340
+ // simply indent the string otherwise
2341
+ var white = raw_token.whitespace_before;
2342
+ if (white) {
2343
+ text = text.replace(new RegExp('\n(' + white + ')?', 'g'), '\n');
2344
+ }
2345
+
2346
+ text = indentation + text.replace(/\n/g, '\n' + indentation);
2294
2347
  }
2348
+ }
2295
2349
 
2296
- text = indentation + text.replace(/\n/g, '\n' + indentation);
2350
+ if (pre) {
2351
+ if (!text) {
2352
+ text = pre + post;
2353
+ } else {
2354
+ text = pre + text + '\n' + post;
2355
+ }
2297
2356
  }
2357
+
2358
+ printer.print_newline(false);
2298
2359
  if (text) {
2299
2360
  raw_token.text = text;
2300
2361
  raw_token.whitespace_before = '';
@@ -2642,6 +2703,9 @@ var BaseOptions = __webpack_require__(6).Options;
2642
2703
 
2643
2704
  function Options(options) {
2644
2705
  BaseOptions.call(this, options, 'html');
2706
+ if (this.templating.length === 1 && this.templating[0] === 'auto') {
2707
+ this.templating = ['django', 'erb', 'handlebars', 'php'];
2708
+ }
2645
2709
 
2646
2710
  this.indent_inner_html = this._get_boolean('indent_inner_html');
2647
2711
  this.indent_body_inner_html = this._get_boolean('indent_body_inner_html', true);
@@ -2689,6 +2753,7 @@ function Options(options) {
2689
2753
  ]);
2690
2754
  this.unformatted_content_delimiter = this._get_characters('unformatted_content_delimiter');
2691
2755
  this.indent_scripts = this._get_selection('indent_scripts', ['normal', 'keep', 'separate']);
2756
+
2692
2757
  }
2693
2758
  Options.prototype = new BaseOptions();
2694
2759
 
@@ -2760,7 +2825,7 @@ var Tokenizer = function(input_string, options) {
2760
2825
 
2761
2826
  // Words end at whitespace or when a tag starts
2762
2827
  // if we are indenting handlebars, they are considered tags
2763
- var templatable_reader = new TemplatablePattern(this._input);
2828
+ var templatable_reader = new TemplatablePattern(this._input).read_options(this._options);
2764
2829
  var pattern_reader = new Pattern(this._input);
2765
2830
 
2766
2831
  this.__patterns = {
@@ -2775,7 +2840,7 @@ var Tokenizer = function(input_string, options) {
2775
2840
  handlebars_open: pattern_reader.until(/[\n\r\t }]/),
2776
2841
  handlebars_raw_close: pattern_reader.until(/}}/),
2777
2842
  comment: pattern_reader.starting_with(/<!--/).until_after(/-->/),
2778
- cdata: pattern_reader.starting_with(/<!\[cdata\[/).until_after(/]]>/),
2843
+ cdata: pattern_reader.starting_with(/<!\[CDATA\[/).until_after(/]]>/),
2779
2844
  // https://en.wikipedia.org/wiki/Conditional_comment
2780
2845
  conditional_comment: pattern_reader.starting_with(/<!\[/).until_after(/]>/),
2781
2846
  processing: pattern_reader.starting_with(/<\?/).until_after(/\?>/)
@@ -2826,27 +2891,27 @@ Tokenizer.prototype._get_next_token = function(previous_token, open_token) { //
2826
2891
 
2827
2892
  token = token || this._read_open_handlebars(c, open_token);
2828
2893
  token = token || this._read_attribute(c, previous_token, open_token);
2829
- token = token || this._read_raw_content(previous_token, open_token);
2894
+ token = token || this._read_raw_content(c, previous_token, open_token);
2830
2895
  token = token || this._read_close(c, open_token);
2831
2896
  token = token || this._read_content_word(c);
2832
- token = token || this._read_comment(c);
2897
+ token = token || this._read_comment_or_cdata(c);
2898
+ token = token || this._read_processing(c);
2833
2899
  token = token || this._read_open(c, open_token);
2834
2900
  token = token || this._create_token(TOKEN.UNKNOWN, this._input.next());
2835
2901
 
2836
2902
  return token;
2837
2903
  };
2838
2904
 
2839
- Tokenizer.prototype._read_comment = function(c) { // jshint unused:false
2905
+ Tokenizer.prototype._read_comment_or_cdata = function(c) { // jshint unused:false
2840
2906
  var token = null;
2841
2907
  var resulting_string = null;
2842
2908
  var directives = null;
2843
2909
 
2844
2910
  if (c === '<') {
2845
2911
  var peek1 = this._input.peek(1);
2846
- //if we're in a comment, do something special
2847
2912
  // We treat all comments as literals, even more than preformatted tags
2848
- // we just look for the appropriate close tag
2849
- if (c === '<' && (peek1 === '!' || peek1 === '?')) {
2913
+ // we only look for the appropriate closing marker
2914
+ if (peek1 === '!') {
2850
2915
  resulting_string = this.__patterns.comment.read();
2851
2916
 
2852
2917
  // only process directive on html comments
@@ -2857,8 +2922,6 @@ Tokenizer.prototype._read_comment = function(c) { // jshint unused:false
2857
2922
  }
2858
2923
  } else {
2859
2924
  resulting_string = this.__patterns.cdata.read();
2860
- resulting_string = resulting_string || this.__patterns.conditional_comment.read();
2861
- resulting_string = resulting_string || this.__patterns.processing.read();
2862
2925
  }
2863
2926
  }
2864
2927
 
@@ -2871,6 +2934,27 @@ Tokenizer.prototype._read_comment = function(c) { // jshint unused:false
2871
2934
  return token;
2872
2935
  };
2873
2936
 
2937
+ Tokenizer.prototype._read_processing = function(c) { // jshint unused:false
2938
+ var token = null;
2939
+ var resulting_string = null;
2940
+ var directives = null;
2941
+
2942
+ if (c === '<') {
2943
+ var peek1 = this._input.peek(1);
2944
+ if (peek1 === '!' || peek1 === '?') {
2945
+ resulting_string = this.__patterns.conditional_comment.read();
2946
+ resulting_string = resulting_string || this.__patterns.processing.read();
2947
+ }
2948
+
2949
+ if (resulting_string) {
2950
+ token = this._create_token(TOKEN.COMMENT, resulting_string);
2951
+ token.directives = directives;
2952
+ }
2953
+ }
2954
+
2955
+ return token;
2956
+ };
2957
+
2874
2958
  Tokenizer.prototype._read_open = function(c, open_token) {
2875
2959
  var resulting_string = null;
2876
2960
  var token = null;
@@ -2962,19 +3046,27 @@ Tokenizer.prototype._is_content_unformatted = function(tag_name) {
2962
3046
  // script and style tags should always be read as unformatted content
2963
3047
  // finally content_unformatted and unformatted element contents are unformatted
2964
3048
  return this._options.void_elements.indexOf(tag_name) === -1 &&
2965
- (tag_name === 'script' || tag_name === 'style' ||
2966
- this._options.content_unformatted.indexOf(tag_name) !== -1 ||
3049
+ (this._options.content_unformatted.indexOf(tag_name) !== -1 ||
2967
3050
  this._options.unformatted.indexOf(tag_name) !== -1);
2968
3051
  };
2969
3052
 
2970
3053
 
2971
- Tokenizer.prototype._read_raw_content = function(previous_token, open_token) { // jshint unused:false
3054
+ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token) { // jshint unused:false
2972
3055
  var resulting_string = '';
2973
3056
  if (open_token && open_token.text[0] === '{') {
2974
3057
  resulting_string = this.__patterns.handlebars_raw_close.read();
2975
3058
  } else if (previous_token.type === TOKEN.TAG_CLOSE && (previous_token.opened.text[0] === '<')) {
2976
3059
  var tag_name = previous_token.opened.text.substr(1).toLowerCase();
2977
- if (this._is_content_unformatted(tag_name)) {
3060
+ if (tag_name === 'script' || tag_name === 'style') {
3061
+ // Script and style tags are allowed to have comments wrapping their content
3062
+ // or just have regular content.
3063
+ var token = this._read_comment_or_cdata(c);
3064
+ if (token) {
3065
+ token.type = TOKEN.TEXT;
3066
+ return token;
3067
+ }
3068
+ resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
3069
+ } else if (this._is_content_unformatted(tag_name)) {
2978
3070
  resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
2979
3071
  }
2980
3072
  }
@@ -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);
@@ -2395,6 +2410,11 @@ function Options(options, merge_child_field) {
2395
2410
  this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
2396
2411
 
2397
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']);
2398
2418
  }
2399
2419
 
2400
2420
  Options.prototype._get_array = function(name, default_value) {
@@ -2632,10 +2652,8 @@ var Tokenizer = function(input_string, options) {
2632
2652
  /\u2028\u2029/.source);
2633
2653
 
2634
2654
  var pattern_reader = new Pattern(this._input);
2635
- var templatable = new TemplatablePattern(this._input);
2636
- templatable = templatable.disable('handlebars');
2637
- templatable = templatable.disable('django');
2638
-
2655
+ var templatable = new TemplatablePattern(this._input)
2656
+ .read_options(this._options);
2639
2657
 
2640
2658
  this.__patterns = {
2641
2659
  template: templatable,
@@ -2804,7 +2822,7 @@ Tokenizer.prototype._read_non_javascript = function(c) {
2804
2822
 
2805
2823
  this._input.back();
2806
2824
 
2807
- } else if (c === '<') {
2825
+ } else if (c === '<' && this._is_first_token()) {
2808
2826
  resulting_string = this.__patterns.html_comment_start.read();
2809
2827
  if (resulting_string) {
2810
2828
  while (this._input.hasNext() && !this._input.testChar(acorn.newline)) {
@@ -3862,6 +3880,7 @@ function TemplatablePattern(input_scanner, parent) {
3862
3880
  var pattern = new Pattern(input_scanner);
3863
3881
  this.__patterns = {
3864
3882
  handlebars_comment: pattern.starting_with(/{{!--/).until_after(/--}}/),
3883
+ handlebars_unescaped: pattern.starting_with(/{{{/).until_after(/}}}/),
3865
3884
  handlebars: pattern.starting_with(/{{/).until_after(/}}/),
3866
3885
  php: pattern.starting_with(/<\?(?:[=]|php)/).until_after(/\?>/),
3867
3886
  erb: pattern.starting_with(/<%[^%]/).until_after(/[^%]%>/),
@@ -3888,6 +3907,15 @@ TemplatablePattern.prototype.disable = function(language) {
3888
3907
  return result;
3889
3908
  };
3890
3909
 
3910
+ TemplatablePattern.prototype.read_options = function(options) {
3911
+ var result = this._create();
3912
+ for (var language in template_names) {
3913
+ result._disabled[language] = options.templating.indexOf(language) === -1;
3914
+ }
3915
+ result._update();
3916
+ return result;
3917
+ };
3918
+
3891
3919
  TemplatablePattern.prototype.exclude = function(language) {
3892
3920
  var result = this._create();
3893
3921
  result._excluded[language] = true;
@@ -3963,6 +3991,8 @@ TemplatablePattern.prototype._read_template = function() {
3963
3991
  if (!this._disabled.handlebars && !this._excluded.handlebars) {
3964
3992
  resulting_string = resulting_string ||
3965
3993
  this.__patterns.handlebars_comment.read();
3994
+ resulting_string = resulting_string ||
3995
+ this.__patterns.handlebars_unescaped.read();
3966
3996
  resulting_string = resulting_string ||
3967
3997
  this.__patterns.handlebars.read();
3968
3998
  }
package/js/lib/cli.js CHANGED
@@ -93,6 +93,7 @@ var path = require('path'),
93
93
  "comma_first": Boolean,
94
94
  "operator_position": ["before-newline", "after-newline", "preserve-newline"],
95
95
  "indent_empty_lines": Boolean,
96
+ "templating": [String, Array],
96
97
  // CSS-only
97
98
  "selector_separator_newline": Boolean,
98
99
  "newline_between_rules": Boolean,
@@ -179,6 +180,7 @@ var path = require('path'),
179
180
  // no shorthand for "config"
180
181
  // no shorthand for "editorconfig"
181
182
  // no shorthand for "indent_empty_lines"
183
+ // not shorthad for "templating"
182
184
  });
183
185
 
184
186
  function verifyExists(fullPath) {
@@ -349,6 +351,7 @@ function usage(err) {
349
351
  ' [first newline in file, otherwise "\\n]',
350
352
  ' -n, --end-with-newline End output with newline',
351
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',
352
355
  ' --editorconfig Use EditorConfig to set up the options'
353
356
  ];
354
357
 
package/js/src/cli.js CHANGED
@@ -93,6 +93,7 @@ var path = require('path'),
93
93
  "comma_first": Boolean,
94
94
  "operator_position": ["before-newline", "after-newline", "preserve-newline"],
95
95
  "indent_empty_lines": Boolean,
96
+ "templating": [String, Array],
96
97
  // CSS-only
97
98
  "selector_separator_newline": Boolean,
98
99
  "newline_between_rules": Boolean,
@@ -179,6 +180,7 @@ var path = require('path'),
179
180
  // no shorthand for "config"
180
181
  // no shorthand for "editorconfig"
181
182
  // no shorthand for "indent_empty_lines"
183
+ // not shorthad for "templating"
182
184
  });
183
185
 
184
186
  function verifyExists(fullPath) {
@@ -349,6 +351,7 @@ function usage(err) {
349
351
  ' [first newline in file, otherwise "\\n]',
350
352
  ' -n, --end-with-newline End output with newline',
351
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',
352
355
  ' --editorconfig Use EditorConfig to set up the options'
353
356
  ];
354
357
 
@@ -66,6 +66,11 @@ function Options(options, merge_child_field) {
66
66
  this.wrap_line_length = this._get_number('wrap_line_length', this._get_number('max_char'));
67
67
 
68
68
  this.indent_empty_lines = this._get_boolean('indent_empty_lines');
69
+
70
+ // valid templating languages ['django', 'erb', 'handlebars', 'php']
71
+ // For now, 'auto' = all off for javascript, all on for html (and inline javascript).
72
+ // other values ignored
73
+ this.templating = this._get_selection_list('templating', ['auto', 'none', 'django', 'erb', 'handlebars', 'php'], ['auto']);
69
74
  }
70
75
 
71
76
  Options.prototype._get_array = function(name, default_value) {