vue_crud 0.1.9.6 → 0.1.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/vue_crud/froala_generator.rb +12 -0
  3. data/lib/generators/vue_crud/sortable.rb +10 -0
  4. data/lib/generators/vue_crud/templates/assets/images/froala_editor/color_picker.png +0 -0
  5. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/froala_editor.js +11089 -0
  6. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/en_gb.js +178 -0
  7. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/zh_cn.js +234 -0
  8. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/zh_tw.js +234 -0
  9. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/align.js +119 -0
  10. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/char_counter.js +150 -0
  11. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/code_beautifier.js +3271 -0
  12. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/code_view.js +311 -0
  13. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/colors.js +350 -0
  14. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/draggable.js +379 -0
  15. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/emoticons.js +347 -0
  16. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/entities.js +113 -0
  17. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/file.js +609 -0
  18. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/font_family.js +178 -0
  19. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/font_size.js +116 -0
  20. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/forms.js +415 -0
  21. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/fullscreen.js +209 -0
  22. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/image.js +2401 -0
  23. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/image_manager.js +921 -0
  24. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/inline_style.js +86 -0
  25. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/line_breaker.js +435 -0
  26. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/link.js +997 -0
  27. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/lists.js +382 -0
  28. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/paragraph_format.js +293 -0
  29. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/paragraph_style.js +139 -0
  30. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/quick_insert.js +342 -0
  31. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/quote.js +138 -0
  32. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/save.js +176 -0
  33. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/table.js +3123 -0
  34. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/url.js +113 -0
  35. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/video.js +1135 -0
  36. data/lib/generators/vue_crud/templates/assets/javascripts/sortable.js +1249 -0
  37. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/froala_editor.scss +1008 -0
  38. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/froala_style.scss +273 -0
  39. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/char_counter.scss +46 -0
  40. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/code_view.scss +102 -0
  41. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/colors.scss +129 -0
  42. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/draggable.scss +32 -0
  43. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/emoticons.scss +27 -0
  44. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/file.scss +135 -0
  45. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/fullscreen.scss +28 -0
  46. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/image.scss +233 -0
  47. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/image_manager.scss +370 -0
  48. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/line_breaker.scss +26 -0
  49. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/quick_insert.scss +56 -0
  50. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/table.scss +156 -0
  51. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/video.scss +136 -0
  52. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/dark.scss +1087 -0
  53. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/gray.scss +1087 -0
  54. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/red.scss +1087 -0
  55. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/royal.scss +1087 -0
  56. data/lib/generators/vue_crud/templates/vue_crud.html +54 -71
  57. data/lib/vue_crud/version.rb +1 -1
  58. data/vendor/assets/javascripts/vue_crud.js +212 -59
  59. metadata +56 -2
@@ -0,0 +1,3271 @@
1
+ /*!
2
+ * froala_editor v2.3.3 (https://www.froala.com/wysiwyg-editor)
3
+ * License https://froala.com/wysiwyg-editor/terms/
4
+ * Copyright 2014-2016 Froala Labs
5
+ */
6
+
7
+ (function (factory) {
8
+ if (typeof define === 'function' && define.amd) {
9
+ // AMD. Register as an anonymous module.
10
+ define(['jquery'], factory);
11
+ } else if (typeof module === 'object' && module.exports) {
12
+ // Node/CommonJS
13
+ module.exports = function( root, jQuery ) {
14
+ if ( jQuery === undefined ) {
15
+ // require('jQuery') returns a factory that requires window to
16
+ // build a jQuery instance, we normalize how we use modules
17
+ // that require this pattern but the window provided is a noop
18
+ // if it's defined (how jquery works)
19
+ if ( typeof window !== 'undefined' ) {
20
+ jQuery = require('jquery');
21
+ }
22
+ else {
23
+ jQuery = require('jquery')(root);
24
+ }
25
+ }
26
+ factory(jQuery);
27
+ return jQuery;
28
+ };
29
+ } else {
30
+ // Browser globals
31
+ factory(jQuery);
32
+ }
33
+ }(function ($) {
34
+
35
+ 'use strict';
36
+
37
+ $.FE.PLUGINS.codeBeautifier = function () {
38
+ /**
39
+ * HTML BEAUTIFIER
40
+ *
41
+ * LICENSE: The MIT License (MIT)
42
+ *
43
+ * Written by Nochum Sossonko, (nsossonko@hotmail.com)
44
+ *
45
+ * Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
46
+ * http://jsbeautifier.org/
47
+ *
48
+ */
49
+ /* jshint ignore:start */
50
+ /* jscs:disable */
51
+
52
+
53
+ var acorn = {};
54
+ (function(exports) {
55
+ /* jshint curly: false */
56
+ // This section of code is taken from acorn.
57
+ //
58
+ // Acorn was written by Marijn Haverbeke and released under an MIT
59
+ // license. The Unicode regexps (for identifiers and whitespace) were
60
+ // taken from [Esprima](http://esprima.org) by Ariya Hidayat.
61
+ //
62
+ // Git repositories for Acorn are available at
63
+ //
64
+ // http://marijnhaverbeke.nl/git/acorn
65
+ // https://github.com/marijnh/acorn.git
66
+
67
+ // ## Character categories
68
+
69
+ // Big ugly regular expressions that match characters in the
70
+ // whitespace, identifier, and identifier-start categories. These
71
+ // are only applied when a character is found to actually have a
72
+ // code point above 128.
73
+
74
+ var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
75
+ var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
76
+ var nonASCIIidentifierChars = "\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";
77
+ var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
78
+ var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
79
+
80
+ // Whether a single character denotes a newline.
81
+
82
+ exports.newline = /[\n\r\u2028\u2029]/;
83
+
84
+ // Matches a whole line break (where CRLF is considered a single
85
+ // line break). Used to count lines.
86
+
87
+ // in javascript, these two differ
88
+ // in python they are the same, different methods are called on them
89
+ exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
90
+ exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
91
+
92
+
93
+ // Test whether a given character code starts an identifier.
94
+
95
+ exports.isIdentifierStart = function(code) {
96
+ // permit $ (36) and @ (64). @ is used in ES7 decorators.
97
+ if (code < 65) return code === 36 || code === 64;
98
+ // 65 through 91 are uppercase letters.
99
+ if (code < 91) return true;
100
+ // permit _ (95).
101
+ if (code < 97) return code === 95;
102
+ // 97 through 123 are lowercase letters.
103
+ if (code < 123) return true;
104
+ return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
105
+ };
106
+
107
+ // Test whether a given character is part of an identifier.
108
+
109
+ exports.isIdentifierChar = function(code) {
110
+ if (code < 48) return code === 36;
111
+ if (code < 58) return true;
112
+ if (code < 65) return false;
113
+ if (code < 91) return true;
114
+ if (code < 97) return code === 95;
115
+ if (code < 123) return true;
116
+ return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
117
+ };
118
+ })(acorn);
119
+
120
+ function run (html_source, options) {
121
+ function ltrim(s) {
122
+ return s.replace(/^\s+/g, '');
123
+ }
124
+
125
+ function rtrim(s) {
126
+ return s.replace(/\s+$/g, '');
127
+ }
128
+
129
+ var multi_parser;
130
+ var indent_inner_html;
131
+ var indent_size;
132
+ var indent_character;
133
+ var wrap_line_length;
134
+ var brace_style;
135
+ var unformatted;
136
+ var preserve_newlines;
137
+ var max_preserve_newlines;
138
+ var indent_handlebars;
139
+ var wrap_attributes;
140
+ var wrap_attributes_indent_size;
141
+ var end_with_newline;
142
+ var extra_liners;
143
+
144
+ options = options || {};
145
+
146
+ // backwards compatibility to 1.3.4
147
+ if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
148
+ (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
149
+ options.wrap_line_length = options.max_char;
150
+ }
151
+
152
+ indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
153
+ indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
154
+ indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
155
+ brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
156
+ wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
157
+ unformatted = options.unformatted || ['a', 'span', 'img', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd',
158
+ 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike',
159
+ 'font', 'ins', 'del', 'address', 'pre'
160
+ ];
161
+ preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
162
+ max_preserve_newlines = preserve_newlines ?
163
+ (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10)) : 0;
164
+ indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
165
+ wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes;
166
+ wrap_attributes_indent_size = (options.wrap_attributes_indent_size === undefined) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10) || indent_size;
167
+ end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
168
+ extra_liners = Array.isArray(options.extra_liners) ?
169
+ options.extra_liners.concat() : (typeof options.extra_liners == 'string') ?
170
+ options.extra_liners.split(',') : 'head,body,/html'.split(',');
171
+
172
+ if (options.indent_with_tabs) {
173
+ indent_character = '\t';
174
+ indent_size = 1;
175
+ }
176
+
177
+ function Parser() {
178
+
179
+ this.pos = 0; //Parser position
180
+ this.token = '';
181
+ this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
182
+ this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
183
+ parent: 'parent1',
184
+ parentcount: 1,
185
+ parent1: ''
186
+ };
187
+ this.tag_type = '';
188
+ this.token_text = this.last_token = this.last_text = this.token_type = '';
189
+ this.newlines = 0;
190
+ this.indent_content = indent_inner_html;
191
+
192
+ this.Utils = { //Uilities made available to the various functions
193
+ whitespace: "\n\r\t ".split(''),
194
+ single_token: 'br,input,link,meta,source,!doctype,basefont,base,area,hr,wbr,param,img,isindex,embed'.split(','), //all the single tags for HTML
195
+ extra_liners: extra_liners, //for tags that need a line of whitespace before them
196
+ in_array: function(what, arr) {
197
+ for (var i = 0; i < arr.length; i++) {
198
+ if (what == arr[i]) {
199
+ return true;
200
+ }
201
+ }
202
+ return false;
203
+ }
204
+ };
205
+
206
+ // Return true if the given text is composed entirely of whitespace.
207
+ this.is_whitespace = function(text) {
208
+ for (var n = 0; n < text.length; text++) {
209
+ if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) {
210
+ return false;
211
+ }
212
+ }
213
+ return true;
214
+ };
215
+
216
+ this.traverse_whitespace = function() {
217
+ var input_char = '';
218
+
219
+ input_char = this.input.charAt(this.pos);
220
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
221
+ this.newlines = 0;
222
+ while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
223
+ if (preserve_newlines && input_char == '\n' && this.newlines <= max_preserve_newlines) {
224
+ this.newlines += 1;
225
+ }
226
+
227
+ this.pos++;
228
+ input_char = this.input.charAt(this.pos);
229
+ }
230
+ return true;
231
+ }
232
+ return false;
233
+ };
234
+
235
+ // Append a space to the given content (string array) or, if we are
236
+ // at the wrap_line_length, append a newline/indentation.
237
+ this.space_or_wrap = function(content) {
238
+ if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
239
+ this.print_newline(false, content);
240
+ this.print_indentation(content);
241
+ } else {
242
+ this.line_char_count++;
243
+ content.push(' ');
244
+ }
245
+ };
246
+
247
+ this.get_content = function() { //function to capture regular content between tags
248
+ var input_char = '',
249
+ content = [];
250
+
251
+ while (this.input.charAt(this.pos) != '<') {
252
+ if (this.pos >= this.input.length) {
253
+ return content.length ? content.join('') : ['', 'TK_EOF'];
254
+ }
255
+
256
+ if (this.traverse_whitespace()) {
257
+ this.space_or_wrap(content);
258
+ continue;
259
+ }
260
+
261
+ if (indent_handlebars) {
262
+ // Handlebars parsing is complicated.
263
+ // {{#foo}} and {{/foo}} are formatted tags.
264
+ // {{something}} should get treated as content, except:
265
+ // {{else}} specifically behaves like {{#if}} and {{/if}}
266
+ var peek3 = this.input.substr(this.pos, 3);
267
+ if (peek3 == '{{#' || peek3 == '{{/') {
268
+ // These are tags and not content.
269
+ break;
270
+ } else if (peek3 == '{{!') {
271
+ return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT'];
272
+ } else if (this.input.substr(this.pos, 2) == '{{') {
273
+ if (this.get_tag(true) == '{{else}}') {
274
+ break;
275
+ }
276
+ }
277
+ }
278
+
279
+ input_char = this.input.charAt(this.pos);
280
+ this.pos++;
281
+ this.line_char_count++;
282
+ content.push(input_char); //letter at-a-time (or string) inserted to an array
283
+ }
284
+ return content.length ? content.join('') : '';
285
+ };
286
+
287
+ this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
288
+ if (this.pos == this.input.length) {
289
+ return ['', 'TK_EOF'];
290
+ }
291
+ var content = '';
292
+ var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
293
+ reg_match.lastIndex = this.pos;
294
+ var reg_array = reg_match.exec(this.input);
295
+ var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
296
+ if (this.pos < end_script) { //get everything in between the script tags
297
+ content = this.input.substring(this.pos, end_script);
298
+ this.pos = end_script;
299
+ }
300
+ return content;
301
+ };
302
+
303
+ this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
304
+ if (this.tags[tag + 'count']) { //check for the existence of this tag type
305
+ this.tags[tag + 'count']++;
306
+ this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
307
+ } else { //otherwise initialize this tag type
308
+ this.tags[tag + 'count'] = 1;
309
+ this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
310
+ }
311
+ this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
312
+ this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
313
+ };
314
+
315
+ this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
316
+ if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
317
+ var temp_parent = this.tags.parent; //check to see if it's a closable tag.
318
+ while (temp_parent) { //till we reach '' (the initial value);
319
+ if (tag + this.tags[tag + 'count'] == temp_parent) { //if this is it use it
320
+ break;
321
+ }
322
+ temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
323
+ }
324
+ if (temp_parent) { //if we caught something
325
+ this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
326
+ this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
327
+ }
328
+ delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
329
+ delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
330
+ if (this.tags[tag + 'count'] == 1) {
331
+ delete this.tags[tag + 'count'];
332
+ } else {
333
+ this.tags[tag + 'count']--;
334
+ }
335
+ }
336
+ };
337
+
338
+ this.indent_to_tag = function(tag) {
339
+ // Match the indentation level to the last use of this tag, but don't remove it.
340
+ if (!this.tags[tag + 'count']) {
341
+ return;
342
+ }
343
+ var temp_parent = this.tags.parent;
344
+ while (temp_parent) {
345
+ if (tag + this.tags[tag + 'count'] == temp_parent) {
346
+ break;
347
+ }
348
+ temp_parent = this.tags[temp_parent + 'parent'];
349
+ }
350
+ if (temp_parent) {
351
+ this.indent_level = this.tags[tag + this.tags[tag + 'count']];
352
+ }
353
+ };
354
+
355
+ this.get_tag = function(peek) { //function to get a full tag and parse its type
356
+ var input_char = '',
357
+ content = [],
358
+ comment = '',
359
+ space = false,
360
+ first_attr = true,
361
+ tag_start, tag_end,
362
+ tag_start_char,
363
+ orig_pos = this.pos,
364
+ orig_line_char_count = this.line_char_count;
365
+
366
+ peek = peek !== undefined ? peek : false;
367
+
368
+ do {
369
+ if (this.pos >= this.input.length) {
370
+ if (peek) {
371
+ this.pos = orig_pos;
372
+ this.line_char_count = orig_line_char_count;
373
+ }
374
+ return content.length ? content.join('') : ['', 'TK_EOF'];
375
+ }
376
+
377
+ input_char = this.input.charAt(this.pos);
378
+ this.pos++;
379
+
380
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
381
+ space = true;
382
+ continue;
383
+ }
384
+
385
+ if (input_char == "'" || input_char == '"') {
386
+ input_char += this.get_unformatted(input_char);
387
+ space = true;
388
+
389
+ }
390
+
391
+ if (input_char == '=') { //no space before =
392
+ space = false;
393
+ }
394
+
395
+ if (content.length && content[content.length - 1] != '=' && input_char != '>' && space) {
396
+ //no space after = or before >
397
+ this.space_or_wrap(content);
398
+ space = false;
399
+ if (!first_attr && wrap_attributes == 'force' && input_char != '/') {
400
+ this.print_newline(true, content);
401
+ this.print_indentation(content);
402
+ for (var count = 0; count < wrap_attributes_indent_size; count++) {
403
+ content.push(indent_character);
404
+ }
405
+ }
406
+ for (var i = 0; i < content.length; i++) {
407
+ if (content[i] == ' ') {
408
+ first_attr = false;
409
+ break;
410
+ }
411
+ }
412
+ }
413
+
414
+ if (indent_handlebars && tag_start_char == '<') {
415
+ // When inside an angle-bracket tag, put spaces around
416
+ // handlebars not inside of strings.
417
+ if ((input_char + this.input.charAt(this.pos)) == '{{') {
418
+ input_char += this.get_unformatted('}}');
419
+ if (content.length && content[content.length - 1] != ' ' && content[content.length - 1] != '<') {
420
+ input_char = ' ' + input_char;
421
+ }
422
+ space = true;
423
+ }
424
+ }
425
+
426
+ if (input_char == '<' && !tag_start_char) {
427
+ tag_start = this.pos - 1;
428
+ tag_start_char = '<';
429
+ }
430
+
431
+ if (indent_handlebars && !tag_start_char) {
432
+ if (content.length >= 2 && content[content.length - 1] == '{' && content[content.length - 2] == '{') {
433
+ if (input_char == '#' || input_char == '/' || input_char == '!') {
434
+ tag_start = this.pos - 3;
435
+ } else {
436
+ tag_start = this.pos - 2;
437
+ }
438
+ tag_start_char = '{';
439
+ }
440
+ }
441
+
442
+ this.line_char_count++;
443
+ content.push(input_char); //inserts character at-a-time (or string)
444
+
445
+ if (content[1] && (content[1] == '!' || content[1] == '?' || content[1] == '%')) { //if we're in a comment, do something special
446
+ // We treat all comments as literals, even more than preformatted tags
447
+ // we just look for the appropriate close tag
448
+ content = [this.get_comment(tag_start)];
449
+ break;
450
+ }
451
+
452
+ if (indent_handlebars && content[1] && content[1] == '{' && content[2] && content[2] == '!') { //if we're in a comment, do something special
453
+ // We treat all comments as literals, even more than preformatted tags
454
+ // we just look for the appropriate close tag
455
+ content = [this.get_comment(tag_start)];
456
+ break;
457
+ }
458
+
459
+ if (indent_handlebars && tag_start_char == '{' && content.length > 2 && content[content.length - 2] == '}' && content[content.length - 1] == '}') {
460
+ break;
461
+ }
462
+ } while (input_char != '>');
463
+
464
+ var tag_complete = content.join('');
465
+ var tag_index;
466
+ var tag_offset;
467
+
468
+ if (tag_complete.indexOf(' ') != -1) { //if there's whitespace, thats where the tag name ends
469
+ tag_index = tag_complete.indexOf(' ');
470
+ } else if (tag_complete[0] == '{') {
471
+ tag_index = tag_complete.indexOf('}');
472
+ } else { //otherwise go with the tag ending
473
+ tag_index = tag_complete.indexOf('>');
474
+ }
475
+ if (tag_complete[0] == '<' || !indent_handlebars) {
476
+ tag_offset = 1;
477
+ } else {
478
+ tag_offset = tag_complete[2] == '#' ? 3 : 2;
479
+ }
480
+ var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
481
+ if (tag_complete.charAt(tag_complete.length - 2) == '/' ||
482
+ this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
483
+ if (!peek) {
484
+ this.tag_type = 'SINGLE';
485
+ }
486
+ } else if (indent_handlebars && tag_complete[0] == '{' && tag_check == 'else') {
487
+ if (!peek) {
488
+ this.indent_to_tag('if');
489
+ this.tag_type = 'HANDLEBARS_ELSE';
490
+ this.indent_content = true;
491
+ this.traverse_whitespace();
492
+ }
493
+ } else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
494
+ comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
495
+ content.push(comment);
496
+ tag_end = this.pos - 1;
497
+ this.tag_type = 'SINGLE';
498
+ } else if (tag_check == 'script' &&
499
+ (tag_complete.search('type') == -1 ||
500
+ (tag_complete.search('type') > -1 &&
501
+ tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript)/) > -1))) {
502
+ if (!peek) {
503
+ this.record_tag(tag_check);
504
+ this.tag_type = 'SCRIPT';
505
+ }
506
+ } else if (tag_check == 'style' &&
507
+ (tag_complete.search('type') == -1 ||
508
+ (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
509
+ if (!peek) {
510
+ this.record_tag(tag_check);
511
+ this.tag_type = 'STYLE';
512
+ }
513
+ } else if (tag_check.charAt(0) == '!') { //peek for <! comment
514
+ // for comments content is already correct.
515
+ if (!peek) {
516
+ this.tag_type = 'SINGLE';
517
+ this.traverse_whitespace();
518
+ }
519
+ } else if (!peek) {
520
+ if (tag_check.charAt(0) == '/') { //this tag is a double tag so check for tag-ending
521
+ this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
522
+ this.tag_type = 'END';
523
+ } else { //otherwise it's a start-tag
524
+ this.record_tag(tag_check); //push it on the tag stack
525
+ if (tag_check.toLowerCase() != 'html') {
526
+ this.indent_content = true;
527
+ }
528
+ this.tag_type = 'START';
529
+ }
530
+
531
+ // Allow preserving of newlines after a start or end tag
532
+ if (this.traverse_whitespace()) {
533
+ this.space_or_wrap(content);
534
+ }
535
+
536
+ if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
537
+ this.print_newline(false, this.output);
538
+ if (this.output.length && this.output[this.output.length - 2] != '\n') {
539
+ this.print_newline(true, this.output);
540
+ }
541
+ }
542
+ }
543
+
544
+ if (peek) {
545
+ this.pos = orig_pos;
546
+ this.line_char_count = orig_line_char_count;
547
+ }
548
+
549
+ return content.join(''); //returns fully formatted tag
550
+ };
551
+
552
+ this.get_comment = function(start_pos) { //function to return comment content in its entirety
553
+ // this is will have very poor perf, but will work for now.
554
+ var comment = '',
555
+ delimiter = '>',
556
+ matched = false;
557
+
558
+ this.pos = start_pos;
559
+ var input_char = this.input.charAt(this.pos);
560
+ this.pos++;
561
+
562
+ while (this.pos <= this.input.length) {
563
+ comment += input_char;
564
+
565
+ // only need to check for the delimiter if the last chars match
566
+ if (comment[comment.length - 1] == delimiter[delimiter.length - 1] &&
567
+ comment.indexOf(delimiter) != -1) {
568
+ break;
569
+ }
570
+
571
+ // only need to search for custom delimiter for the first few characters
572
+ if (!matched && comment.length < 10) {
573
+ if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
574
+ delimiter = '<![endif]>';
575
+ matched = true;
576
+ } else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
577
+ delimiter = ']]>';
578
+ matched = true;
579
+ } else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
580
+ delimiter = ']>';
581
+ matched = true;
582
+ } else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
583
+ delimiter = '-->';
584
+ matched = true;
585
+ } else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment
586
+ delimiter = '}}';
587
+ matched = true;
588
+ } else if (comment.indexOf('<?') === 0) { // {{! handlebars comment
589
+ delimiter = '?>';
590
+ matched = true;
591
+ } else if (comment.indexOf('<%') === 0) { // {{! handlebars comment
592
+ delimiter = '%>';
593
+ matched = true;
594
+ }
595
+ }
596
+
597
+ input_char = this.input.charAt(this.pos);
598
+ this.pos++;
599
+ }
600
+
601
+ return comment;
602
+ };
603
+
604
+ this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety
605
+
606
+ if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) != -1) {
607
+ return '';
608
+ }
609
+ var input_char = '';
610
+ var content = '';
611
+ var min_index = 0;
612
+ var space = true;
613
+ do {
614
+
615
+ if (this.pos >= this.input.length) {
616
+ return content;
617
+ }
618
+
619
+ input_char = this.input.charAt(this.pos);
620
+ this.pos++;
621
+
622
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
623
+ if (!space) {
624
+ this.line_char_count--;
625
+ continue;
626
+ }
627
+ if (input_char == '\n' || input_char == '\r') {
628
+ content += '\n';
629
+ /* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
630
+ for (var i=0; i<this.indent_level; i++) {
631
+ content += this.indent_string;
632
+ }
633
+ space = false; //...and make sure other indentation is erased
634
+ */
635
+ this.line_char_count = 0;
636
+ continue;
637
+ }
638
+ }
639
+ content += input_char;
640
+ this.line_char_count++;
641
+ space = true;
642
+
643
+ if (indent_handlebars && input_char == '{' && content.length && content[content.length - 2] == '{') {
644
+ // Handlebars expressions in strings should also be unformatted.
645
+ content += this.get_unformatted('}}');
646
+ // These expressions are opaque. Ignore delimiters found in them.
647
+ min_index = content.length;
648
+ }
649
+ } while (content.toLowerCase().indexOf(delimiter, min_index) == -1);
650
+ return content;
651
+ };
652
+
653
+ this.get_token = function() { //initial handler for token-retrieval
654
+ var token;
655
+
656
+ if (this.last_token == 'TK_TAG_SCRIPT' || this.last_token == 'TK_TAG_STYLE') { //check if we need to format javascript
657
+ var type = this.last_token.substr(7);
658
+ token = this.get_contents_to(type);
659
+ if (typeof token != 'string') {
660
+ return token;
661
+ }
662
+ return [token, 'TK_' + type];
663
+ }
664
+ if (this.current_mode == 'CONTENT') {
665
+ token = this.get_content();
666
+ if (typeof token != 'string') {
667
+ return token;
668
+ } else {
669
+ return [token, 'TK_CONTENT'];
670
+ }
671
+ }
672
+
673
+ if (this.current_mode == 'TAG') {
674
+ token = this.get_tag();
675
+ if (typeof token != 'string') {
676
+ return token;
677
+ } else {
678
+ var tag_name_type = 'TK_TAG_' + this.tag_type;
679
+ return [token, tag_name_type];
680
+ }
681
+ }
682
+ };
683
+
684
+ this.get_full_indent = function(level) {
685
+ level = this.indent_level + level || 0;
686
+ if (level < 1) {
687
+ return '';
688
+ }
689
+
690
+ return (new Array(level + 1)).join(this.indent_string);
691
+ };
692
+
693
+ this.is_unformatted = function(tag_check, unformatted) {
694
+ //is this an HTML5 block-level link?
695
+ if (!this.Utils.in_array(tag_check, unformatted)) {
696
+ return false;
697
+ }
698
+
699
+ if (tag_check.toLowerCase() != 'a' || !this.Utils.in_array('a', unformatted)) {
700
+ return true;
701
+ }
702
+
703
+ //at this point we have an tag; is its first child something we want to remain
704
+ //unformatted?
705
+ var next_tag = this.get_tag(true /* peek. */ );
706
+
707
+ // test next_tag to see if it is just html tag (no external content)
708
+ var tag = (next_tag || '').match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);
709
+
710
+ // if next_tag comes back but is not an isolated tag, then
711
+ // let's treat the 'a' tag as having content
712
+ // and respect the unformatted option
713
+ if (!tag || this.Utils.in_array(tag, unformatted)) {
714
+ return true;
715
+ } else {
716
+ return false;
717
+ }
718
+ };
719
+
720
+ this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
721
+
722
+ this.input = js_source || ''; //gets the input for the Parser
723
+ this.output = [];
724
+ this.indent_character = indent_character;
725
+ this.indent_string = '';
726
+ this.indent_size = indent_size;
727
+ this.brace_style = brace_style;
728
+ this.indent_level = 0;
729
+ this.wrap_line_length = wrap_line_length;
730
+ this.line_char_count = 0; //count to see if wrap_line_length was exceeded
731
+
732
+ for (var i = 0; i < this.indent_size; i++) {
733
+ this.indent_string += this.indent_character;
734
+ }
735
+
736
+ this.print_newline = function(force, arr) {
737
+ this.line_char_count = 0;
738
+ if (!arr || !arr.length) {
739
+ return;
740
+ }
741
+ if (force || (arr[arr.length - 1] != '\n')) { //we might want the extra line
742
+ if ((arr[arr.length - 1] != '\n')) {
743
+ arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
744
+ }
745
+ arr.push('\n');
746
+ }
747
+ };
748
+
749
+ this.print_indentation = function(arr) {
750
+ for (var i = 0; i < this.indent_level; i++) {
751
+ arr.push(this.indent_string);
752
+ this.line_char_count += this.indent_string.length;
753
+ }
754
+ };
755
+
756
+ this.print_token = function(text) {
757
+ // Avoid printing initial whitespace.
758
+ if (this.is_whitespace(text) && !this.output.length) {
759
+ return;
760
+ }
761
+ if (text || text !== '') {
762
+ if (this.output.length && this.output[this.output.length - 1] == '\n') {
763
+ this.print_indentation(this.output);
764
+ text = ltrim(text);
765
+ }
766
+ }
767
+ this.print_token_raw(text);
768
+ };
769
+
770
+ this.print_token_raw = function(text) {
771
+ // If we are going to print newlines, truncate trailing
772
+ // whitespace, as the newlines will represent the space.
773
+ if (this.newlines > 0) {
774
+ text = rtrim(text);
775
+ }
776
+
777
+ if (text && text !== '') {
778
+ if (text.length > 1 && text[text.length - 1] == '\n') {
779
+ // unformatted tags can grab newlines as their last character
780
+ this.output.push(text.slice(0, -1));
781
+ this.print_newline(false, this.output);
782
+ } else {
783
+ this.output.push(text);
784
+ }
785
+ }
786
+
787
+ for (var n = 0; n < this.newlines; n++) {
788
+ this.print_newline(n > 0, this.output);
789
+ }
790
+ this.newlines = 0;
791
+ };
792
+
793
+ this.indent = function() {
794
+ this.indent_level++;
795
+ };
796
+
797
+ this.unindent = function() {
798
+ if (this.indent_level > 0) {
799
+ this.indent_level--;
800
+ }
801
+ };
802
+ };
803
+ return this;
804
+ }
805
+
806
+ /*_____________________--------------------_____________________*/
807
+
808
+ multi_parser = new Parser(); //wrapping functions Parser
809
+ multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
810
+
811
+ while (true) {
812
+ var t = multi_parser.get_token();
813
+ multi_parser.token_text = t[0];
814
+ multi_parser.token_type = t[1];
815
+
816
+ if (multi_parser.token_type == 'TK_EOF') {
817
+ break;
818
+ }
819
+
820
+ switch (multi_parser.token_type) {
821
+ case 'TK_TAG_START':
822
+ multi_parser.print_newline(false, multi_parser.output);
823
+ multi_parser.print_token(multi_parser.token_text);
824
+ if (multi_parser.indent_content) {
825
+ multi_parser.indent();
826
+ multi_parser.indent_content = false;
827
+ }
828
+ multi_parser.current_mode = 'CONTENT';
829
+ break;
830
+ case 'TK_TAG_STYLE':
831
+ case 'TK_TAG_SCRIPT':
832
+ multi_parser.print_newline(false, multi_parser.output);
833
+ multi_parser.print_token(multi_parser.token_text);
834
+ multi_parser.current_mode = 'CONTENT';
835
+ break;
836
+ case 'TK_TAG_END':
837
+ // Print new line only if the tag has no content and has child
838
+ if (multi_parser.last_token == 'TK_CONTENT' && multi_parser.last_text === '') {
839
+ var tag_name = multi_parser.token_text.match(/\w+/)[0];
840
+ var tag_extracted_from_last_output = null;
841
+ if (multi_parser.output.length) {
842
+ tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
843
+ }
844
+ if (tag_extracted_from_last_output == null ||
845
+ (tag_extracted_from_last_output[1] != tag_name && !multi_parser.Utils.in_array(tag_extracted_from_last_output[1], unformatted))) {
846
+ multi_parser.print_newline(false, multi_parser.output);
847
+ }
848
+ }
849
+ multi_parser.print_token(multi_parser.token_text);
850
+ multi_parser.current_mode = 'CONTENT';
851
+ break;
852
+ case 'TK_TAG_SINGLE':
853
+ // Don't add a newline before elements that should remain unformatted.
854
+ var tag_check = multi_parser.token_text.match(/^\s*<([a-z-]+)/i);
855
+ if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
856
+ multi_parser.print_newline(false, multi_parser.output);
857
+ }
858
+ multi_parser.print_token(multi_parser.token_text);
859
+ multi_parser.current_mode = 'CONTENT';
860
+ break;
861
+ case 'TK_TAG_HANDLEBARS_ELSE':
862
+ multi_parser.print_token(multi_parser.token_text);
863
+ if (multi_parser.indent_content) {
864
+ multi_parser.indent();
865
+ multi_parser.indent_content = false;
866
+ }
867
+ multi_parser.current_mode = 'CONTENT';
868
+ break;
869
+ case 'TK_TAG_HANDLEBARS_COMMENT':
870
+ multi_parser.print_token(multi_parser.token_text);
871
+ multi_parser.current_mode = 'TAG';
872
+ break;
873
+ case 'TK_CONTENT':
874
+ multi_parser.print_token(multi_parser.token_text);
875
+ multi_parser.current_mode = 'TAG';
876
+ break;
877
+ case 'TK_STYLE':
878
+ case 'TK_SCRIPT':
879
+ if (multi_parser.token_text !== '') {
880
+ multi_parser.print_newline(false, multi_parser.output);
881
+ var text = multi_parser.token_text;
882
+ var _beautifier;
883
+ var script_indent_level = 1;
884
+
885
+ if (multi_parser.token_type == 'TK_SCRIPT') {
886
+ _beautifier = typeof js_beautify == 'function' && js_beautify;
887
+ } else if (multi_parser.token_type == 'TK_STYLE') {
888
+ _beautifier = typeof css_beautify == 'function' && css_beautify;
889
+ }
890
+
891
+ if (options.indent_scripts == 'keep') {
892
+ script_indent_level = 0;
893
+ } else if (options.indent_scripts == 'separate') {
894
+ script_indent_level = -multi_parser.indent_level;
895
+ }
896
+
897
+ var indentation = multi_parser.get_full_indent(script_indent_level);
898
+ if (_beautifier) {
899
+ // call the Beautifier if avaliable
900
+ text = _beautifier(text.replace(/^\s*/, indentation), options);
901
+ } else {
902
+ // simply indent the string otherwise
903
+ var white = text.match(/^\s*/)[0];
904
+ var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
905
+ var reindent = multi_parser.get_full_indent(script_indent_level - _level);
906
+
907
+ text = text.replace(/^\s*/, indentation)
908
+ .replace(/\r\n|\r|\n/g, '\n' + reindent)
909
+ .replace(/\s+$/, '');
910
+ }
911
+ if (text) {
912
+ multi_parser.print_token_raw(text);
913
+ multi_parser.print_newline(true, multi_parser.output);
914
+ }
915
+ }
916
+ multi_parser.current_mode = 'TAG';
917
+ break;
918
+ default:
919
+ // We should not be getting here but we don't want to drop input on the floor
920
+ // Just output the text and move on
921
+ if (multi_parser.token_text !== '') {
922
+ multi_parser.print_token(multi_parser.token_text);
923
+ }
924
+ break;
925
+ }
926
+ multi_parser.last_token = multi_parser.token_type;
927
+ multi_parser.last_text = multi_parser.token_text;
928
+ }
929
+ var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');
930
+ if (end_with_newline) {
931
+ sweet_code += '\n';
932
+ }
933
+ return sweet_code;
934
+ }
935
+
936
+ function css_beautify (source_text, options) {
937
+ var NESTED_AT_RULE = {
938
+ "@page": true,
939
+ "@font-face": true,
940
+ "@keyframes": true,
941
+ // also in CONDITIONAL_GROUP_RULE below
942
+ "@media": true,
943
+ "@supports": true,
944
+ "@document": true
945
+ };
946
+ var CONDITIONAL_GROUP_RULE = {
947
+ "@media": true,
948
+ "@supports": true,
949
+ "@document": true
950
+ };
951
+
952
+ options = options || {};
953
+ source_text = source_text || '';
954
+ // HACK: newline parsing inconsistent. This brute force normalizes the input.
955
+ source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n')
956
+
957
+ var indentSize = options.indent_size || 4;
958
+ var indentCharacter = options.indent_char || ' ';
959
+ var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
960
+ var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
961
+ var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
962
+ var eol = options.eol ? options.eol : '\n';
963
+
964
+ // compatibility
965
+ if (typeof indentSize === "string") {
966
+ indentSize = parseInt(indentSize, 10);
967
+ }
968
+
969
+ if(options.indent_with_tabs){
970
+ indentCharacter = '\t';
971
+ indentSize = 1;
972
+ }
973
+
974
+ eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
975
+
976
+
977
+ // tokenizer
978
+ var whiteRe = /^\s+$/;
979
+ var wordRe = /[\w$\-_]/;
980
+
981
+ var pos = -1,
982
+ ch;
983
+ var parenLevel = 0;
984
+
985
+ function next() {
986
+ ch = source_text.charAt(++pos);
987
+ return ch || '';
988
+ }
989
+
990
+ function peek(skipWhitespace) {
991
+ var result = '';
992
+ var prev_pos = pos;
993
+ if (skipWhitespace) {
994
+ eatWhitespace();
995
+ }
996
+ result = source_text.charAt(pos + 1) || '';
997
+ pos = prev_pos - 1;
998
+ next();
999
+ return result;
1000
+ }
1001
+
1002
+ function eatString(endChars) {
1003
+ var start = pos;
1004
+ while (next()) {
1005
+ if (ch === "\\") {
1006
+ next();
1007
+ } else if (endChars.indexOf(ch) !== -1) {
1008
+ break;
1009
+ } else if (ch === "\n") {
1010
+ break;
1011
+ }
1012
+ }
1013
+ return source_text.substring(start, pos + 1);
1014
+ }
1015
+
1016
+ function peekString(endChar) {
1017
+ var prev_pos = pos;
1018
+ var str = eatString(endChar);
1019
+ pos = prev_pos - 1;
1020
+ next();
1021
+ return str;
1022
+ }
1023
+
1024
+ function eatWhitespace() {
1025
+ var result = '';
1026
+ while (whiteRe.test(peek())) {
1027
+ next();
1028
+ result += ch;
1029
+ }
1030
+ return result;
1031
+ }
1032
+
1033
+ function skipWhitespace() {
1034
+ var result = '';
1035
+ if (ch && whiteRe.test(ch)) {
1036
+ result = ch;
1037
+ }
1038
+ while (whiteRe.test(next())) {
1039
+ result += ch;
1040
+ }
1041
+ return result;
1042
+ }
1043
+
1044
+ function eatComment(singleLine) {
1045
+ var start = pos;
1046
+ singleLine = peek() === "/";
1047
+ next();
1048
+ while (next()) {
1049
+ if (!singleLine && ch === "*" && peek() === "/") {
1050
+ next();
1051
+ break;
1052
+ } else if (singleLine && ch === "\n") {
1053
+ return source_text.substring(start, pos);
1054
+ }
1055
+ }
1056
+
1057
+ return source_text.substring(start, pos) + ch;
1058
+ }
1059
+
1060
+
1061
+ function lookBack(str) {
1062
+ return source_text.substring(pos - str.length, pos).toLowerCase() ===
1063
+ str;
1064
+ }
1065
+
1066
+ // Nested pseudo-class if we are insideRule
1067
+ // and the next special character found opens
1068
+ // a new block
1069
+ function foundNestedPseudoClass() {
1070
+ var openParen = 0;
1071
+ for (var i = pos + 1; i < source_text.length; i++) {
1072
+ var ch = source_text.charAt(i);
1073
+ if (ch === "{") {
1074
+ return true;
1075
+ } else if (ch === '(') {
1076
+ // pseudoclasses can contain ()
1077
+ openParen += 1;
1078
+ } else if (ch === ')') {
1079
+ if (openParen == 0) {
1080
+ return false;
1081
+ }
1082
+ openParen -= 1;
1083
+ } else if (ch === ";" || ch === "}") {
1084
+ return false;
1085
+ }
1086
+ }
1087
+ return false;
1088
+ }
1089
+
1090
+ // printer
1091
+ var basebaseIndentString = source_text.match(/^[\t ]*/)[0];
1092
+ var singleIndent = new Array(indentSize + 1).join(indentCharacter);
1093
+ var indentLevel = 0;
1094
+ var nestedLevel = 0;
1095
+
1096
+ function indent() {
1097
+ indentLevel++;
1098
+ basebaseIndentString += singleIndent;
1099
+ }
1100
+
1101
+ function outdent() {
1102
+ indentLevel--;
1103
+ basebaseIndentString = basebaseIndentString.slice(0, -indentSize);
1104
+ }
1105
+
1106
+ var print = {};
1107
+ print["{"] = function(ch) {
1108
+ print.singleSpace();
1109
+ output.push(ch);
1110
+ print.newLine();
1111
+ };
1112
+ print["}"] = function(ch) {
1113
+ print.newLine();
1114
+ output.push(ch);
1115
+ print.newLine();
1116
+ };
1117
+
1118
+ print._lastCharWhitespace = function() {
1119
+ return whiteRe.test(output[output.length - 1]);
1120
+ };
1121
+
1122
+ print.newLine = function(keepWhitespace) {
1123
+ if (output.length) {
1124
+ if (!keepWhitespace && output[output.length - 1] !== '\n') {
1125
+ print.trim();
1126
+ }
1127
+
1128
+ output.push('\n');
1129
+
1130
+ if (basebaseIndentString) {
1131
+ output.push(basebaseIndentString);
1132
+ }
1133
+ }
1134
+ };
1135
+ print.singleSpace = function() {
1136
+ if (output.length && !print._lastCharWhitespace()) {
1137
+ output.push(' ');
1138
+ }
1139
+ };
1140
+
1141
+ print.preserveSingleSpace = function() {
1142
+ if (isAfterSpace) {
1143
+ print.singleSpace();
1144
+ }
1145
+ };
1146
+
1147
+ print.trim = function() {
1148
+ while (print._lastCharWhitespace()) {
1149
+ output.pop();
1150
+ }
1151
+ };
1152
+
1153
+
1154
+ var output = [];
1155
+ /*_____________________--------------------_____________________*/
1156
+
1157
+ var insideRule = false;
1158
+ var insidePropertyValue = false;
1159
+ var enteringConditionalGroup = false;
1160
+ var top_ch = '';
1161
+ var last_top_ch = '';
1162
+
1163
+ while (true) {
1164
+ var whitespace = skipWhitespace();
1165
+ var isAfterSpace = whitespace !== '';
1166
+ var isAfterNewline = whitespace.indexOf('\n') !== -1;
1167
+ last_top_ch = top_ch;
1168
+ top_ch = ch;
1169
+
1170
+ if (!ch) {
1171
+ break;
1172
+ } else if (ch === '/' && peek() === '*') { /* css comment */
1173
+ var header = indentLevel === 0;
1174
+
1175
+ if (isAfterNewline || header) {
1176
+ print.newLine();
1177
+ }
1178
+
1179
+ output.push(eatComment());
1180
+ print.newLine();
1181
+ if (header) {
1182
+ print.newLine(true);
1183
+ }
1184
+ } else if (ch === '/' && peek() === '/') { // single line comment
1185
+ if (!isAfterNewline && last_top_ch !== '{' ) {
1186
+ print.trim();
1187
+ }
1188
+ print.singleSpace();
1189
+ output.push(eatComment());
1190
+ print.newLine();
1191
+ } else if (ch === '@') {
1192
+ print.preserveSingleSpace();
1193
+ output.push(ch);
1194
+
1195
+ // strip trailing space, if present, for hash property checks
1196
+ var variableOrRule = peekString(": ,;{}()[]/='\"");
1197
+
1198
+ if (variableOrRule.match(/[ :]$/)) {
1199
+ // we have a variable or pseudo-class, add it and insert one space before continuing
1200
+ next();
1201
+ variableOrRule = eatString(": ").replace(/\s$/, '');
1202
+ output.push(variableOrRule);
1203
+ print.singleSpace();
1204
+ }
1205
+
1206
+ variableOrRule = variableOrRule.replace(/\s$/, '')
1207
+
1208
+ // might be a nesting at-rule
1209
+ if (variableOrRule in NESTED_AT_RULE) {
1210
+ nestedLevel += 1;
1211
+ if (variableOrRule in CONDITIONAL_GROUP_RULE) {
1212
+ enteringConditionalGroup = true;
1213
+ }
1214
+ }
1215
+ } else if (ch === '#' && peek() === '{') {
1216
+ print.preserveSingleSpace();
1217
+ output.push(eatString('}'));
1218
+ } else if (ch === '{') {
1219
+ if (peek(true) === '}') {
1220
+ eatWhitespace();
1221
+ next();
1222
+ print.singleSpace();
1223
+ output.push("{}");
1224
+ print.newLine();
1225
+ if (newline_between_rules && indentLevel === 0) {
1226
+ print.newLine(true);
1227
+ }
1228
+ } else {
1229
+ indent();
1230
+ print["{"](ch);
1231
+ // when entering conditional groups, only rulesets are allowed
1232
+ if (enteringConditionalGroup) {
1233
+ enteringConditionalGroup = false;
1234
+ insideRule = (indentLevel > nestedLevel);
1235
+ } else {
1236
+ // otherwise, declarations are also allowed
1237
+ insideRule = (indentLevel >= nestedLevel);
1238
+ }
1239
+ }
1240
+ } else if (ch === '}') {
1241
+ outdent();
1242
+ print["}"](ch);
1243
+ insideRule = false;
1244
+ insidePropertyValue = false;
1245
+ if (nestedLevel) {
1246
+ nestedLevel--;
1247
+ }
1248
+ if (newline_between_rules && indentLevel === 0) {
1249
+ print.newLine(true);
1250
+ }
1251
+ } else if (ch === ":") {
1252
+ eatWhitespace();
1253
+ if ((insideRule || enteringConditionalGroup) &&
1254
+ !(lookBack("&") || foundNestedPseudoClass())) {
1255
+ // 'property: value' delimiter
1256
+ // which could be in a conditional group query
1257
+ insidePropertyValue = true;
1258
+ output.push(':');
1259
+ print.singleSpace();
1260
+ } else {
1261
+ // sass/less parent reference don't use a space
1262
+ // sass nested pseudo-class don't use a space
1263
+ if (peek() === ":") {
1264
+ // pseudo-element
1265
+ next();
1266
+ output.push("::");
1267
+ } else {
1268
+ // pseudo-class
1269
+ output.push(':');
1270
+ }
1271
+ }
1272
+ } else if (ch === '"' || ch === '\'') {
1273
+ print.preserveSingleSpace();
1274
+ output.push(eatString(ch));
1275
+ } else if (ch === ';') {
1276
+ insidePropertyValue = false;
1277
+ output.push(ch);
1278
+ print.newLine();
1279
+ } else if (ch === '(') { // may be a url
1280
+ if (lookBack("url")) {
1281
+ output.push(ch);
1282
+ eatWhitespace();
1283
+ if (next()) {
1284
+ if (ch !== ')' && ch !== '"' && ch !== '\'') {
1285
+ output.push(eatString(')'));
1286
+ } else {
1287
+ pos--;
1288
+ }
1289
+ }
1290
+ } else {
1291
+ parenLevel++;
1292
+ print.preserveSingleSpace();
1293
+ output.push(ch);
1294
+ eatWhitespace();
1295
+ }
1296
+ } else if (ch === ')') {
1297
+ output.push(ch);
1298
+ parenLevel--;
1299
+ } else if (ch === ',') {
1300
+ output.push(ch);
1301
+ eatWhitespace();
1302
+ if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
1303
+ print.newLine();
1304
+ } else {
1305
+ print.singleSpace();
1306
+ }
1307
+ } else if (ch === ']') {
1308
+ output.push(ch);
1309
+ } else if (ch === '[') {
1310
+ print.preserveSingleSpace();
1311
+ output.push(ch);
1312
+ } else if (ch === '=') { // no whitespace before or after
1313
+ eatWhitespace()
1314
+ ch = '=';
1315
+ output.push(ch);
1316
+ } else {
1317
+ print.preserveSingleSpace();
1318
+ output.push(ch);
1319
+ }
1320
+ }
1321
+
1322
+
1323
+ var sweetCode = '';
1324
+ if (basebaseIndentString) {
1325
+ sweetCode += basebaseIndentString;
1326
+ }
1327
+
1328
+ sweetCode += output.join('').replace(/[\r\n\t ]+$/, '');
1329
+
1330
+ // establish end_with_newline
1331
+ if (end_with_newline) {
1332
+ sweetCode += '\n';
1333
+ }
1334
+
1335
+ if (eol != '\n') {
1336
+ sweetCode = sweetCode.replace(/[\n]/g, eol);
1337
+ }
1338
+
1339
+ return sweetCode;
1340
+ }
1341
+
1342
+ function in_array(what, arr) {
1343
+ for (var i = 0; i < arr.length; i += 1) {
1344
+ if (arr[i] === what) {
1345
+ return true;
1346
+ }
1347
+ }
1348
+ return false;
1349
+ }
1350
+
1351
+ function trim(s) {
1352
+ return s.replace(/^\s+|\s+$/g, '');
1353
+ }
1354
+
1355
+ function ltrim(s) {
1356
+ return s.replace(/^\s+/g, '');
1357
+ }
1358
+
1359
+ function rtrim(s) {
1360
+ return s.replace(/\s+$/g, '');
1361
+ }
1362
+
1363
+ function js_beautify(js_source_text, options) {
1364
+ "use strict";
1365
+ var beautifier = new Beautifier(js_source_text, options);
1366
+ return beautifier.beautify();
1367
+ }
1368
+
1369
+ var MODE = {
1370
+ BlockStatement: 'BlockStatement', // 'BLOCK'
1371
+ Statement: 'Statement', // 'STATEMENT'
1372
+ ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
1373
+ ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
1374
+ ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
1375
+ Conditional: 'Conditional', //'(COND-EXPRESSION)',
1376
+ Expression: 'Expression' //'(EXPRESSION)'
1377
+ };
1378
+
1379
+ function Beautifier(js_source_text, options) {
1380
+ "use strict";
1381
+ var output
1382
+ var tokens = [], token_pos;
1383
+ var Tokenizer;
1384
+ var current_token;
1385
+ var last_type, last_last_text, indent_string;
1386
+ var flags, previous_flags, flag_store;
1387
+ var prefix;
1388
+
1389
+ var handlers, opt;
1390
+ var baseIndentString = '';
1391
+
1392
+ handlers = {
1393
+ 'TK_START_EXPR': handle_start_expr,
1394
+ 'TK_END_EXPR': handle_end_expr,
1395
+ 'TK_START_BLOCK': handle_start_block,
1396
+ 'TK_END_BLOCK': handle_end_block,
1397
+ 'TK_WORD': handle_word,
1398
+ 'TK_RESERVED': handle_word,
1399
+ 'TK_SEMICOLON': handle_semicolon,
1400
+ 'TK_STRING': handle_string,
1401
+ 'TK_EQUALS': handle_equals,
1402
+ 'TK_OPERATOR': handle_operator,
1403
+ 'TK_COMMA': handle_comma,
1404
+ 'TK_BLOCK_COMMENT': handle_block_comment,
1405
+ 'TK_COMMENT': handle_comment,
1406
+ 'TK_DOT': handle_dot,
1407
+ 'TK_UNKNOWN': handle_unknown,
1408
+ 'TK_EOF': handle_eof
1409
+ };
1410
+
1411
+ function create_flags(flags_base, mode) {
1412
+ var next_indent_level = 0;
1413
+ if (flags_base) {
1414
+ next_indent_level = flags_base.indentation_level;
1415
+ if (!output.just_added_newline() &&
1416
+ flags_base.line_indent_level > next_indent_level) {
1417
+ next_indent_level = flags_base.line_indent_level;
1418
+ }
1419
+ }
1420
+
1421
+ var next_flags = {
1422
+ mode: mode,
1423
+ parent: flags_base,
1424
+ last_text: flags_base ? flags_base.last_text : '', // last token text
1425
+ last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
1426
+ declaration_statement: false,
1427
+ declaration_assignment: false,
1428
+ multiline_frame: false,
1429
+ if_block: false,
1430
+ else_block: false,
1431
+ do_block: false,
1432
+ do_while: false,
1433
+ in_case_statement: false, // switch(..){ INSIDE HERE }
1434
+ in_case: false, // we're on the exact line with "case 0:"
1435
+ case_body: false, // the indented case-action block
1436
+ indentation_level: next_indent_level,
1437
+ line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
1438
+ start_line_index: output.get_line_number(),
1439
+ ternary_depth: 0
1440
+ };
1441
+ return next_flags;
1442
+ }
1443
+
1444
+ // Some interpreters have unexpected results with foo = baz || bar;
1445
+ options = options ? options : {};
1446
+ opt = {};
1447
+
1448
+ // compatibility
1449
+ if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
1450
+ opt.brace_style = options.braces_on_own_line ? "expand" : "collapse";
1451
+ }
1452
+ opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse");
1453
+
1454
+ // graceful handling of deprecated option
1455
+ if (opt.brace_style === "expand-strict") {
1456
+ opt.brace_style = "expand";
1457
+ }
1458
+
1459
+
1460
+ opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
1461
+ opt.indent_char = options.indent_char ? options.indent_char : ' ';
1462
+ opt.eol = options.eol ? options.eol : '\n';
1463
+ opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
1464
+ opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
1465
+ opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
1466
+ opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
1467
+ opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
1468
+ opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
1469
+ opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
1470
+ opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
1471
+ opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
1472
+ opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
1473
+ opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
1474
+ opt.e4x = (options.e4x === undefined) ? false : options.e4x;
1475
+ opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
1476
+ opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
1477
+
1478
+ // For testing of beautify ignore:start directive
1479
+ opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;
1480
+
1481
+ // force opt.space_after_anon_function to true if opt.jslint_happy
1482
+ if(opt.jslint_happy) {
1483
+ opt.space_after_anon_function = true;
1484
+ }
1485
+
1486
+ if(options.indent_with_tabs){
1487
+ opt.indent_char = '\t';
1488
+ opt.indent_size = 1;
1489
+ }
1490
+
1491
+ opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
1492
+
1493
+ //----------------------------------
1494
+ indent_string = '';
1495
+ while (opt.indent_size > 0) {
1496
+ indent_string += opt.indent_char;
1497
+ opt.indent_size -= 1;
1498
+ }
1499
+
1500
+ var preindent_index = 0;
1501
+ if(js_source_text && js_source_text.length) {
1502
+ while ( (js_source_text.charAt(preindent_index) === ' ' ||
1503
+ js_source_text.charAt(preindent_index) === '\t')) {
1504
+ baseIndentString += js_source_text.charAt(preindent_index);
1505
+ preindent_index += 1;
1506
+ }
1507
+ js_source_text = js_source_text.substring(preindent_index);
1508
+ }
1509
+
1510
+ last_type = 'TK_START_BLOCK'; // last token type
1511
+ last_last_text = ''; // pre-last token text
1512
+ output = new Output(indent_string, baseIndentString);
1513
+
1514
+ // If testing the ignore directive, start with output disable set to true
1515
+ output.raw = opt.test_output_raw;
1516
+
1517
+
1518
+ // Stack of parsing/formatting states, including MODE.
1519
+ // We tokenize, parse, and output in an almost purely a forward-only stream of token input
1520
+ // and formatted output. This makes the beautifier less accurate than full parsers
1521
+ // but also far more tolerant of syntax errors.
1522
+ //
1523
+ // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
1524
+ // MODE.BlockStatement on the the stack, even though it could be object literal. If we later
1525
+ // encounter a ":", we'll switch to to MODE.ObjectLiteral. If we then see a ";",
1526
+ // most full parsers would die, but the beautifier gracefully falls back to
1527
+ // MODE.BlockStatement and continues on.
1528
+ flag_store = [];
1529
+ set_mode(MODE.BlockStatement);
1530
+
1531
+ this.beautify = function() {
1532
+
1533
+ /*jshint onevar:true */
1534
+ var local_token, sweet_code;
1535
+ Tokenizer = new tokenizer(js_source_text, opt, indent_string);
1536
+ tokens = Tokenizer.tokenize();
1537
+ token_pos = 0;
1538
+
1539
+ while (local_token = get_token()) {
1540
+ for(var i = 0; i < local_token.comments_before.length; i++) {
1541
+ // The cleanest handling of inline comments is to treat them as though they aren't there.
1542
+ // Just continue formatting and the behavior should be logical.
1543
+ // Also ignore unknown tokens. Again, this should result in better behavior.
1544
+ handle_token(local_token.comments_before[i]);
1545
+ }
1546
+ handle_token(local_token);
1547
+
1548
+ last_last_text = flags.last_text;
1549
+ last_type = local_token.type;
1550
+ flags.last_text = local_token.text;
1551
+
1552
+ token_pos += 1;
1553
+ }
1554
+
1555
+ sweet_code = output.get_code();
1556
+ if (opt.end_with_newline) {
1557
+ sweet_code += '\n';
1558
+ }
1559
+
1560
+ if (opt.eol != '\n') {
1561
+ sweet_code = sweet_code.replace(/[\n]/g, opt.eol);
1562
+ }
1563
+
1564
+ return sweet_code;
1565
+ };
1566
+
1567
+ function handle_token(local_token) {
1568
+ var newlines = local_token.newlines;
1569
+ var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
1570
+
1571
+ if (keep_whitespace) {
1572
+ for (i = 0; i < newlines; i += 1) {
1573
+ print_newline(i > 0);
1574
+ }
1575
+ } else {
1576
+ if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
1577
+ newlines = opt.max_preserve_newlines;
1578
+ }
1579
+
1580
+ if (opt.preserve_newlines) {
1581
+ if (local_token.newlines > 1) {
1582
+ print_newline();
1583
+ for (var i = 1; i < newlines; i += 1) {
1584
+ print_newline(true);
1585
+ }
1586
+ }
1587
+ }
1588
+ }
1589
+
1590
+ current_token = local_token;
1591
+ handlers[current_token.type]();
1592
+ }
1593
+
1594
+ // we could use just string.split, but
1595
+ // IE doesn't like returning empty strings
1596
+ function split_newlines(s) {
1597
+ //return s.split(/\x0d\x0a|\x0a/);
1598
+
1599
+ s = s.replace(/\x0d/g, '');
1600
+ var out = [],
1601
+ idx = s.indexOf("\n");
1602
+ while (idx !== -1) {
1603
+ out.push(s.substring(0, idx));
1604
+ s = s.substring(idx + 1);
1605
+ idx = s.indexOf("\n");
1606
+ }
1607
+ if (s.length) {
1608
+ out.push(s);
1609
+ }
1610
+ return out;
1611
+ }
1612
+
1613
+ function allow_wrap_or_preserved_newline(force_linewrap) {
1614
+ force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
1615
+
1616
+ // Never wrap the first token on a line
1617
+ if (output.just_added_newline()) {
1618
+ return
1619
+ }
1620
+
1621
+ if ((opt.preserve_newlines && current_token.wanted_newline) || force_linewrap) {
1622
+ print_newline(false, true);
1623
+ } else if (opt.wrap_line_length) {
1624
+ var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
1625
+ (output.space_before_token ? 1 : 0);
1626
+ if (proposed_line_length >= opt.wrap_line_length) {
1627
+ print_newline(false, true);
1628
+ }
1629
+ }
1630
+ }
1631
+
1632
+ function print_newline(force_newline, preserve_statement_flags) {
1633
+ if (!preserve_statement_flags) {
1634
+ if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
1635
+ while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
1636
+ restore_mode();
1637
+ }
1638
+ }
1639
+ }
1640
+
1641
+ if (output.add_new_line(force_newline)) {
1642
+ flags.multiline_frame = true;
1643
+ }
1644
+ }
1645
+
1646
+ function print_token_line_indentation() {
1647
+ if (output.just_added_newline()) {
1648
+ if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
1649
+ output.current_line.push(current_token.whitespace_before);
1650
+ output.space_before_token = false;
1651
+ } else if (output.set_indent(flags.indentation_level)) {
1652
+ flags.line_indent_level = flags.indentation_level;
1653
+ }
1654
+ }
1655
+ }
1656
+
1657
+ function print_token(printable_token) {
1658
+ if (output.raw) {
1659
+ output.add_raw_token(current_token)
1660
+ return;
1661
+ }
1662
+
1663
+ if (opt.comma_first && last_type === 'TK_COMMA'
1664
+ && output.just_added_newline()) {
1665
+ if(output.previous_line.last() === ',') {
1666
+ output.previous_line.pop();
1667
+ print_token_line_indentation();
1668
+ output.add_token(',');
1669
+ output.space_before_token = true;
1670
+ }
1671
+ }
1672
+
1673
+ printable_token = printable_token || current_token.text;
1674
+ print_token_line_indentation();
1675
+ output.add_token(printable_token);
1676
+ }
1677
+
1678
+ function indent() {
1679
+ flags.indentation_level += 1;
1680
+ }
1681
+
1682
+ function deindent() {
1683
+ if (flags.indentation_level > 0 &&
1684
+ ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level))
1685
+ flags.indentation_level -= 1;
1686
+ }
1687
+
1688
+ function set_mode(mode) {
1689
+ if (flags) {
1690
+ flag_store.push(flags);
1691
+ previous_flags = flags;
1692
+ } else {
1693
+ previous_flags = create_flags(null, mode);
1694
+ }
1695
+
1696
+ flags = create_flags(previous_flags, mode);
1697
+ }
1698
+
1699
+ function is_array(mode) {
1700
+ return mode === MODE.ArrayLiteral;
1701
+ }
1702
+
1703
+ function is_expression(mode) {
1704
+ return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
1705
+ }
1706
+
1707
+ function restore_mode() {
1708
+ if (flag_store.length > 0) {
1709
+ previous_flags = flags;
1710
+ flags = flag_store.pop();
1711
+ if (previous_flags.mode === MODE.Statement) {
1712
+ output.remove_redundant_indentation(previous_flags);
1713
+ }
1714
+ }
1715
+ }
1716
+
1717
+ function start_of_object_property() {
1718
+ return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
1719
+ (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
1720
+ }
1721
+
1722
+ function start_of_statement() {
1723
+ if (
1724
+ (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
1725
+ (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
1726
+ (last_type === 'TK_RESERVED' && flags.last_text === 'return' && !current_token.wanted_newline) ||
1727
+ (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(current_token.type === 'TK_RESERVED' && current_token.text === 'if')) ||
1728
+ (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
1729
+ (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement
1730
+ && !flags.in_case
1731
+ && !(current_token.text === '--' || current_token.text === '++')
1732
+ && last_last_text !== 'function'
1733
+ && current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
1734
+ (flags.mode === MODE.ObjectLiteral && (
1735
+ (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
1736
+ ) {
1737
+
1738
+ set_mode(MODE.Statement);
1739
+ indent();
1740
+
1741
+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
1742
+ flags.declaration_statement = true;
1743
+ }
1744
+
1745
+ // Issue #276:
1746
+ // If starting a new statement with [if, for, while, do], push to a new line.
1747
+ // if (a) if (b) if(c) d(); else e(); else f();
1748
+ if (!start_of_object_property()) {
1749
+ allow_wrap_or_preserved_newline(
1750
+ current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
1751
+ }
1752
+
1753
+ return true;
1754
+ }
1755
+ return false;
1756
+ }
1757
+
1758
+ function all_lines_start_with(lines, c) {
1759
+ for (var i = 0; i < lines.length; i++) {
1760
+ var line = trim(lines[i]);
1761
+ if (line.charAt(0) !== c) {
1762
+ return false;
1763
+ }
1764
+ }
1765
+ return true;
1766
+ }
1767
+
1768
+ function each_line_matches_indent(lines, indent) {
1769
+ var i = 0,
1770
+ len = lines.length,
1771
+ line;
1772
+ for (; i < len; i++) {
1773
+ line = lines[i];
1774
+ // allow empty lines to pass through
1775
+ if (line && line.indexOf(indent) !== 0) {
1776
+ return false;
1777
+ }
1778
+ }
1779
+ return true;
1780
+ }
1781
+
1782
+ function is_special_word(word) {
1783
+ return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
1784
+ }
1785
+
1786
+ function get_token(offset) {
1787
+ var index = token_pos + (offset || 0);
1788
+ return (index < 0 || index >= tokens.length) ? null : tokens[index];
1789
+ }
1790
+
1791
+ function handle_start_expr() {
1792
+ if (start_of_statement()) {
1793
+ // The conditional starts the statement if appropriate.
1794
+ }
1795
+
1796
+ var next_mode = MODE.Expression;
1797
+ if (current_token.text === '[') {
1798
+
1799
+ if (last_type === 'TK_WORD' || flags.last_text === ')') {
1800
+ // this is array index specifier, break immediately
1801
+ // a[x], fn()[x]
1802
+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) {
1803
+ output.space_before_token = true;
1804
+ }
1805
+ set_mode(next_mode);
1806
+ print_token();
1807
+ indent();
1808
+ if (opt.space_in_paren) {
1809
+ output.space_before_token = true;
1810
+ }
1811
+ return;
1812
+ }
1813
+
1814
+ next_mode = MODE.ArrayLiteral;
1815
+ if (is_array(flags.mode)) {
1816
+ if (flags.last_text === '[' ||
1817
+ (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
1818
+ // ], [ goes to new line
1819
+ // }, [ goes to new line
1820
+ if (!opt.keep_array_indentation) {
1821
+ print_newline();
1822
+ }
1823
+ }
1824
+ }
1825
+
1826
+ } else {
1827
+ if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
1828
+ next_mode = MODE.ForInitializer;
1829
+ } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
1830
+ next_mode = MODE.Conditional;
1831
+ } else {
1832
+ // next_mode = MODE.Expression;
1833
+ }
1834
+ }
1835
+
1836
+ if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
1837
+ print_newline();
1838
+ } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
1839
+ // TODO: Consider whether forcing this is required. Review failing tests when removed.
1840
+ allow_wrap_or_preserved_newline(current_token.wanted_newline);
1841
+ // do nothing on (( and )( and ][ and ]( and .(
1842
+ } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
1843
+ output.space_before_token = true;
1844
+ } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
1845
+ (flags.last_text === '*' && last_last_text === 'function')) {
1846
+ // function() vs function ()
1847
+ if (opt.space_after_anon_function) {
1848
+ output.space_before_token = true;
1849
+ }
1850
+ } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) {
1851
+ if (opt.space_before_conditional) {
1852
+ output.space_before_token = true;
1853
+ }
1854
+ }
1855
+
1856
+ // Should be a space between await and an IIFE
1857
+ if(current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await'){
1858
+ output.space_before_token = true;
1859
+ }
1860
+
1861
+ // Support of this kind of newline preservation.
1862
+ // a = (b &&
1863
+ // (c || d));
1864
+ if (current_token.text === '(') {
1865
+ if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
1866
+ if (!start_of_object_property()) {
1867
+ allow_wrap_or_preserved_newline();
1868
+ }
1869
+ }
1870
+ }
1871
+
1872
+ set_mode(next_mode);
1873
+ print_token();
1874
+ if (opt.space_in_paren) {
1875
+ output.space_before_token = true;
1876
+ }
1877
+
1878
+ // In all cases, if we newline while inside an expression it should be indented.
1879
+ indent();
1880
+ }
1881
+
1882
+ function handle_end_expr() {
1883
+ // statements inside expressions are not valid syntax, but...
1884
+ // statements must all be closed when their container closes
1885
+ while (flags.mode === MODE.Statement) {
1886
+ restore_mode();
1887
+ }
1888
+
1889
+ if (flags.multiline_frame) {
1890
+ allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
1891
+ }
1892
+
1893
+ if (opt.space_in_paren) {
1894
+ if (last_type === 'TK_START_EXPR' && ! opt.space_in_empty_paren) {
1895
+ // () [] no inner space in empty parens like these, ever, ref #320
1896
+ output.trim();
1897
+ output.space_before_token = false;
1898
+ } else {
1899
+ output.space_before_token = true;
1900
+ }
1901
+ }
1902
+ if (current_token.text === ']' && opt.keep_array_indentation) {
1903
+ print_token();
1904
+ restore_mode();
1905
+ } else {
1906
+ restore_mode();
1907
+ print_token();
1908
+ }
1909
+ output.remove_redundant_indentation(previous_flags);
1910
+
1911
+ // do {} while () // no statement required after
1912
+ if (flags.do_while && previous_flags.mode === MODE.Conditional) {
1913
+ previous_flags.mode = MODE.Expression;
1914
+ flags.do_block = false;
1915
+ flags.do_while = false;
1916
+
1917
+ }
1918
+ }
1919
+
1920
+ function handle_start_block() {
1921
+ // Check if this is should be treated as a ObjectLiteral
1922
+ var next_token = get_token(1)
1923
+ var second_token = get_token(2)
1924
+ if (second_token && (
1925
+ (second_token.text === ':' && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED']))
1926
+ || (in_array(next_token.text, ['get', 'set']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
1927
+ )) {
1928
+ // We don't support TypeScript,but we didn't break it for a very long time.
1929
+ // We'll try to keep not breaking it.
1930
+ if (!in_array(last_last_text, ['class','interface'])) {
1931
+ set_mode(MODE.ObjectLiteral);
1932
+ } else {
1933
+ set_mode(MODE.BlockStatement);
1934
+ }
1935
+ } else {
1936
+ set_mode(MODE.BlockStatement);
1937
+ }
1938
+
1939
+ var empty_braces = !next_token.comments_before.length && next_token.text === '}';
1940
+ var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
1941
+ last_type === 'TK_END_EXPR';
1942
+
1943
+ if (opt.brace_style === "expand" ||
1944
+ (opt.brace_style === "none" && current_token.wanted_newline)) {
1945
+ if (last_type !== 'TK_OPERATOR' &&
1946
+ (empty_anonymous_function ||
1947
+ last_type === 'TK_EQUALS' ||
1948
+ (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
1949
+ output.space_before_token = true;
1950
+ } else {
1951
+ print_newline(false, true);
1952
+ }
1953
+ } else { // collapse
1954
+ if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
1955
+ if (last_type === 'TK_START_BLOCK') {
1956
+ print_newline();
1957
+ } else {
1958
+ output.space_before_token = true;
1959
+ }
1960
+ } else {
1961
+ // if TK_OPERATOR or TK_START_EXPR
1962
+ if (is_array(previous_flags.mode) && flags.last_text === ',') {
1963
+ if (last_last_text === '}') {
1964
+ // }, { in array context
1965
+ output.space_before_token = true;
1966
+ } else {
1967
+ print_newline(); // [a, b, c, {
1968
+ }
1969
+ }
1970
+ }
1971
+ }
1972
+ print_token();
1973
+ indent();
1974
+ }
1975
+
1976
+ function handle_end_block() {
1977
+ // statements must all be closed when their container closes
1978
+ while (flags.mode === MODE.Statement) {
1979
+ restore_mode();
1980
+ }
1981
+ var empty_braces = last_type === 'TK_START_BLOCK';
1982
+
1983
+ if (opt.brace_style === "expand") {
1984
+ if (!empty_braces) {
1985
+ print_newline();
1986
+ }
1987
+ } else {
1988
+ // skip {}
1989
+ if (!empty_braces) {
1990
+ if (is_array(flags.mode) && opt.keep_array_indentation) {
1991
+ // we REALLY need a newline here, but newliner would skip that
1992
+ opt.keep_array_indentation = false;
1993
+ print_newline();
1994
+ opt.keep_array_indentation = true;
1995
+
1996
+ } else {
1997
+ print_newline();
1998
+ }
1999
+ }
2000
+ }
2001
+ restore_mode();
2002
+ print_token();
2003
+ }
2004
+
2005
+ function handle_word() {
2006
+ if (current_token.type === 'TK_RESERVED' && flags.mode !== MODE.ObjectLiteral &&
2007
+ in_array(current_token.text, ['set', 'get'])) {
2008
+ current_token.type = 'TK_WORD';
2009
+ }
2010
+
2011
+ if (current_token.type === 'TK_RESERVED' && flags.mode === MODE.ObjectLiteral) {
2012
+ var next_token = get_token(1);
2013
+ if (next_token.text == ':') {
2014
+ current_token.type = 'TK_WORD';
2015
+ }
2016
+ }
2017
+
2018
+ if (start_of_statement()) {
2019
+ // The conditional starts the statement if appropriate.
2020
+ } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
2021
+ (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
2022
+ last_type !== 'TK_EQUALS' &&
2023
+ (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {
2024
+
2025
+ print_newline();
2026
+ }
2027
+
2028
+ if (flags.do_block && !flags.do_while) {
2029
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
2030
+ // do {} ## while ()
2031
+ output.space_before_token = true;
2032
+ print_token();
2033
+ output.space_before_token = true;
2034
+ flags.do_while = true;
2035
+ return;
2036
+ } else {
2037
+ // do {} should always have while as the next word.
2038
+ // if we don't see the expected while, recover
2039
+ print_newline();
2040
+ flags.do_block = false;
2041
+ }
2042
+ }
2043
+
2044
+ // if may be followed by else, or not
2045
+ // Bare/inline ifs are tricky
2046
+ // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
2047
+ if (flags.if_block) {
2048
+ if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
2049
+ flags.else_block = true;
2050
+ } else {
2051
+ while (flags.mode === MODE.Statement) {
2052
+ restore_mode();
2053
+ }
2054
+ flags.if_block = false;
2055
+ flags.else_block = false;
2056
+ }
2057
+ }
2058
+
2059
+ if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
2060
+ print_newline();
2061
+ if (flags.case_body || opt.jslint_happy) {
2062
+ // switch cases following one another
2063
+ deindent();
2064
+ flags.case_body = false;
2065
+ }
2066
+ print_token();
2067
+ flags.in_case = true;
2068
+ flags.in_case_statement = true;
2069
+ return;
2070
+ }
2071
+
2072
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
2073
+ if (in_array(flags.last_text, ['}', ';']) || (output.just_added_newline() && ! in_array(flags.last_text, ['[', '{', ':', '=', ',']))) {
2074
+ // make sure there is a nice clean space of at least one blank line
2075
+ // before a new function definition
2076
+ if ( !output.just_added_blankline() && !current_token.comments_before.length) {
2077
+ print_newline();
2078
+ print_newline(true);
2079
+ }
2080
+ }
2081
+ if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
2082
+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
2083
+ output.space_before_token = true;
2084
+ } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
2085
+ output.space_before_token = true;
2086
+ } else {
2087
+ print_newline();
2088
+ }
2089
+ } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
2090
+ // foo = function
2091
+ output.space_before_token = true;
2092
+ } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
2093
+ // (function
2094
+ } else {
2095
+ print_newline();
2096
+ }
2097
+ }
2098
+
2099
+ if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
2100
+ if (!start_of_object_property()) {
2101
+ allow_wrap_or_preserved_newline();
2102
+ }
2103
+ }
2104
+
2105
+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['function', 'get', 'set'])) {
2106
+ print_token();
2107
+ flags.last_word = current_token.text;
2108
+ return;
2109
+ }
2110
+
2111
+ prefix = 'NONE';
2112
+
2113
+ if (last_type === 'TK_END_BLOCK') {
2114
+ if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally']))) {
2115
+ prefix = 'NEWLINE';
2116
+ } else {
2117
+ if (opt.brace_style === "expand" ||
2118
+ opt.brace_style === "end-expand" ||
2119
+ (opt.brace_style === "none" && current_token.wanted_newline)) {
2120
+ prefix = 'NEWLINE';
2121
+ } else {
2122
+ prefix = 'SPACE';
2123
+ output.space_before_token = true;
2124
+ }
2125
+ }
2126
+ } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
2127
+ // TODO: Should this be for STATEMENT as well?
2128
+ prefix = 'NEWLINE';
2129
+ } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
2130
+ prefix = 'SPACE';
2131
+ } else if (last_type === 'TK_STRING') {
2132
+ prefix = 'NEWLINE';
2133
+ } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
2134
+ (flags.last_text === '*' && last_last_text === 'function')) {
2135
+ prefix = 'SPACE';
2136
+ } else if (last_type === 'TK_START_BLOCK') {
2137
+ prefix = 'NEWLINE';
2138
+ } else if (last_type === 'TK_END_EXPR') {
2139
+ output.space_before_token = true;
2140
+ prefix = 'NEWLINE';
2141
+ }
2142
+
2143
+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
2144
+ if (flags.last_text === 'else' || flags.last_text === 'export') {
2145
+ prefix = 'SPACE';
2146
+ } else {
2147
+ prefix = 'NEWLINE';
2148
+ }
2149
+
2150
+ }
2151
+
2152
+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
2153
+ if (last_type !== 'TK_END_BLOCK' ||
2154
+ opt.brace_style === "expand" ||
2155
+ opt.brace_style === "end-expand" ||
2156
+ (opt.brace_style === "none" && current_token.wanted_newline)) {
2157
+ print_newline();
2158
+ } else {
2159
+ output.trim(true);
2160
+ var line = output.current_line;
2161
+ // If we trimmed and there's something other than a close block before us
2162
+ // put a newline back in. Handles '} // comment' scenario.
2163
+ if (line.last() !== '}') {
2164
+ print_newline();
2165
+ }
2166
+ output.space_before_token = true;
2167
+ }
2168
+ } else if (prefix === 'NEWLINE') {
2169
+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
2170
+ // no newline between 'return nnn'
2171
+ output.space_before_token = true;
2172
+ } else if (last_type !== 'TK_END_EXPR') {
2173
+ if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
2174
+ // no need to force newline on 'var': for (var x = 0...)
2175
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
2176
+ // no newline for } else if {
2177
+ output.space_before_token = true;
2178
+ } else {
2179
+ print_newline();
2180
+ }
2181
+ }
2182
+ } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
2183
+ print_newline();
2184
+ }
2185
+ } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
2186
+ print_newline(); // }, in lists get a newline treatment
2187
+ } else if (prefix === 'SPACE') {
2188
+ output.space_before_token = true;
2189
+ }
2190
+ print_token();
2191
+ flags.last_word = current_token.text;
2192
+
2193
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'do') {
2194
+ flags.do_block = true;
2195
+ }
2196
+
2197
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'if') {
2198
+ flags.if_block = true;
2199
+ }
2200
+ }
2201
+
2202
+ function handle_semicolon() {
2203
+ if (start_of_statement()) {
2204
+ // The conditional starts the statement if appropriate.
2205
+ // Semicolon can be the start (and end) of a statement
2206
+ output.space_before_token = false;
2207
+ }
2208
+ while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
2209
+ restore_mode();
2210
+ }
2211
+ print_token();
2212
+ }
2213
+
2214
+ function handle_string() {
2215
+ if (start_of_statement()) {
2216
+ // The conditional starts the statement if appropriate.
2217
+ // One difference - strings want at least a space before
2218
+ output.space_before_token = true;
2219
+ } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
2220
+ output.space_before_token = true;
2221
+ } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
2222
+ if (!start_of_object_property()) {
2223
+ allow_wrap_or_preserved_newline();
2224
+ }
2225
+ } else {
2226
+ print_newline();
2227
+ }
2228
+ print_token();
2229
+ }
2230
+
2231
+ function handle_equals() {
2232
+ if (start_of_statement()) {
2233
+ // The conditional starts the statement if appropriate.
2234
+ }
2235
+
2236
+ if (flags.declaration_statement) {
2237
+ // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
2238
+ flags.declaration_assignment = true;
2239
+ }
2240
+ output.space_before_token = true;
2241
+ print_token();
2242
+ output.space_before_token = true;
2243
+ }
2244
+
2245
+ function handle_comma() {
2246
+ if (flags.declaration_statement) {
2247
+ if (is_expression(flags.parent.mode)) {
2248
+ // do not break on comma, for(var a = 1, b = 2)
2249
+ flags.declaration_assignment = false;
2250
+ }
2251
+
2252
+ print_token();
2253
+
2254
+ if (flags.declaration_assignment) {
2255
+ flags.declaration_assignment = false;
2256
+ print_newline(false, true);
2257
+ } else {
2258
+ output.space_before_token = true;
2259
+ // for comma-first, we want to allow a newline before the comma
2260
+ // to turn into a newline after the comma, which we will fixup later
2261
+ if (opt.comma_first) {
2262
+ allow_wrap_or_preserved_newline();
2263
+ }
2264
+ }
2265
+ return;
2266
+ }
2267
+
2268
+ print_token();
2269
+ if (flags.mode === MODE.ObjectLiteral ||
2270
+ (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
2271
+ if (flags.mode === MODE.Statement) {
2272
+ restore_mode();
2273
+ }
2274
+ print_newline();
2275
+ } else {
2276
+ // EXPR or DO_BLOCK
2277
+ output.space_before_token = true;
2278
+ // for comma-first, we want to allow a newline before the comma
2279
+ // to turn into a newline after the comma, which we will fixup later
2280
+ if (opt.comma_first) {
2281
+ allow_wrap_or_preserved_newline();
2282
+ }
2283
+ }
2284
+
2285
+ }
2286
+
2287
+ function handle_operator() {
2288
+ if (start_of_statement()) {
2289
+ // The conditional starts the statement if appropriate.
2290
+ }
2291
+
2292
+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
2293
+ // "return" had a special handling in TK_WORD. Now we need to return the favor
2294
+ output.space_before_token = true;
2295
+ print_token();
2296
+ return;
2297
+ }
2298
+
2299
+ // hack for actionscript's import .*;
2300
+ if (current_token.text === '*' && last_type === 'TK_DOT') {
2301
+ print_token();
2302
+ return;
2303
+ }
2304
+
2305
+ if (current_token.text === ':' && flags.in_case) {
2306
+ flags.case_body = true;
2307
+ indent();
2308
+ print_token();
2309
+ print_newline();
2310
+ flags.in_case = false;
2311
+ return;
2312
+ }
2313
+
2314
+ if (current_token.text === '::') {
2315
+ // no spaces around exotic namespacing syntax operator
2316
+ print_token();
2317
+ return;
2318
+ }
2319
+
2320
+ // Allow line wrapping between operators
2321
+ if (last_type === 'TK_OPERATOR') {
2322
+ allow_wrap_or_preserved_newline();
2323
+ }
2324
+
2325
+ var space_before = true;
2326
+ var space_after = true;
2327
+
2328
+ if (in_array(current_token.text, ['--', '++', '!', '~']) || (in_array(current_token.text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === ','))) {
2329
+ // unary operators (and binary +/- pretending to be unary) special cases
2330
+
2331
+ space_before = false;
2332
+ space_after = false;
2333
+
2334
+ // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
2335
+ // if there is a newline between -- or ++ and anything else we should preserve it.
2336
+ if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
2337
+ print_newline(false, true);
2338
+ }
2339
+
2340
+ if (flags.last_text === ';' && is_expression(flags.mode)) {
2341
+ // for (;; ++i)
2342
+ // ^^^
2343
+ space_before = true;
2344
+ }
2345
+
2346
+ if (last_type === 'TK_RESERVED') {
2347
+ space_before = true;
2348
+ } else if (last_type === 'TK_END_EXPR') {
2349
+ space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
2350
+ } else if (last_type === 'TK_OPERATOR') {
2351
+ // a++ + ++b;
2352
+ // a - -b
2353
+ space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
2354
+ // + and - are not unary when preceeded by -- or ++ operator
2355
+ // a-- + b
2356
+ // a * +b
2357
+ // a - -b
2358
+ if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
2359
+ space_after = true;
2360
+ }
2361
+ }
2362
+
2363
+ if ((flags.mode === MODE.BlockStatement || flags.mode === MODE.Statement) && (flags.last_text === '{' || flags.last_text === ';')) {
2364
+ // { foo; --i }
2365
+ // foo(); --bar;
2366
+ print_newline();
2367
+ }
2368
+ } else if (current_token.text === ':') {
2369
+ if (flags.ternary_depth === 0) {
2370
+ // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
2371
+ space_before = false;
2372
+ } else {
2373
+ flags.ternary_depth -= 1;
2374
+ }
2375
+ } else if (current_token.text === '?') {
2376
+ flags.ternary_depth += 1;
2377
+ } else if (current_token.text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function') {
2378
+ space_before = false;
2379
+ space_after = false;
2380
+ }
2381
+ output.space_before_token = output.space_before_token || space_before;
2382
+ print_token();
2383
+ output.space_before_token = space_after;
2384
+ }
2385
+
2386
+ function handle_block_comment() {
2387
+ if (output.raw) {
2388
+ output.add_raw_token(current_token)
2389
+ if (current_token.directives && current_token.directives['preserve'] === 'end') {
2390
+ // If we're testing the raw output behavior, do not allow a directive to turn it off.
2391
+ if (!opt.test_output_raw) {
2392
+ output.raw = false;
2393
+ }
2394
+ }
2395
+ return;
2396
+ }
2397
+
2398
+ if (current_token.directives) {
2399
+ print_newline(false, true);
2400
+ print_token();
2401
+ if (current_token.directives['preserve'] === 'start') {
2402
+ output.raw = true;
2403
+ }
2404
+ print_newline(false, true);
2405
+ return;
2406
+ }
2407
+
2408
+ // inline block
2409
+ if (!acorn.newline.test(current_token.text) && !current_token.wanted_newline) {
2410
+ output.space_before_token = true;
2411
+ print_token();
2412
+ output.space_before_token = true;
2413
+ return;
2414
+ }
2415
+
2416
+ var lines = split_newlines(current_token.text);
2417
+ var j; // iterator for this case
2418
+ var javadoc = false;
2419
+ var starless = false;
2420
+ var lastIndent = current_token.whitespace_before;
2421
+ var lastIndentLength = lastIndent.length;
2422
+
2423
+ // block comment starts with a new line
2424
+ print_newline(false, true);
2425
+ if (lines.length > 1) {
2426
+ if (all_lines_start_with(lines.slice(1), '*')) {
2427
+ javadoc = true;
2428
+ }
2429
+ else if (each_line_matches_indent(lines.slice(1), lastIndent)) {
2430
+ starless = true;
2431
+ }
2432
+ }
2433
+
2434
+ // first line always indented
2435
+ print_token(lines[0]);
2436
+ for (j = 1; j < lines.length; j++) {
2437
+ print_newline(false, true);
2438
+ if (javadoc) {
2439
+ // javadoc: reformat and re-indent
2440
+ print_token(' ' + ltrim(lines[j]));
2441
+ } else if (starless && lines[j].length > lastIndentLength) {
2442
+ // starless: re-indent non-empty content, avoiding trim
2443
+ print_token(lines[j].substring(lastIndentLength));
2444
+ } else {
2445
+ // normal comments output raw
2446
+ output.add_token(lines[j]);
2447
+ }
2448
+ }
2449
+
2450
+ // for comments of more than one line, make sure there's a new line after
2451
+ print_newline(false, true);
2452
+ }
2453
+
2454
+ function handle_comment() {
2455
+ if (current_token.wanted_newline) {
2456
+ print_newline(false, true);
2457
+ } else {
2458
+ output.trim(true);
2459
+ }
2460
+
2461
+ output.space_before_token = true;
2462
+ print_token();
2463
+ print_newline(false, true);
2464
+ }
2465
+
2466
+ function handle_dot() {
2467
+ if (start_of_statement()) {
2468
+ // The conditional starts the statement if appropriate.
2469
+ }
2470
+
2471
+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
2472
+ output.space_before_token = true;
2473
+ } else {
2474
+ // allow preserved newlines before dots in general
2475
+ // force newlines on dots after close paren when break_chained - for bar().baz()
2476
+ allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
2477
+ }
2478
+
2479
+ print_token();
2480
+ }
2481
+
2482
+ function handle_unknown() {
2483
+ print_token();
2484
+
2485
+ if (current_token.text[current_token.text.length - 1] === '\n') {
2486
+ print_newline();
2487
+ }
2488
+ }
2489
+
2490
+ function handle_eof() {
2491
+ // Unwind any open statements
2492
+ while (flags.mode === MODE.Statement) {
2493
+ restore_mode();
2494
+ }
2495
+ }
2496
+ }
2497
+
2498
+
2499
+ function OutputLine(parent) {
2500
+ var _character_count = 0;
2501
+ // use indent_count as a marker for lines that have preserved indentation
2502
+ var _indent_count = -1;
2503
+
2504
+ var _items = [];
2505
+ var _empty = true;
2506
+
2507
+ this.set_indent = function(level) {
2508
+ _character_count = parent.baseIndentLength + level * parent.indent_length
2509
+ _indent_count = level;
2510
+ }
2511
+
2512
+ this.get_character_count = function() {
2513
+ return _character_count;
2514
+ }
2515
+
2516
+ this.is_empty = function() {
2517
+ return _empty;
2518
+ }
2519
+
2520
+ this.last = function() {
2521
+ if (!this._empty) {
2522
+ return _items[_items.length - 1];
2523
+ } else {
2524
+ return null;
2525
+ }
2526
+ }
2527
+
2528
+ this.push = function(input) {
2529
+ _items.push(input);
2530
+ _character_count += input.length;
2531
+ _empty = false;
2532
+ }
2533
+
2534
+ this.pop = function() {
2535
+ var item = null;
2536
+ if (!_empty) {
2537
+ item = _items.pop();
2538
+ _character_count -= item.length;
2539
+ _empty = _items.length === 0;
2540
+ }
2541
+ return item;
2542
+ }
2543
+
2544
+ this.remove_indent = function() {
2545
+ if (_indent_count > 0) {
2546
+ _indent_count -= 1;
2547
+ _character_count -= parent.indent_length
2548
+ }
2549
+ }
2550
+
2551
+ this.trim = function() {
2552
+ while (this.last() === ' ') {
2553
+ var item = _items.pop();
2554
+ _character_count -= 1;
2555
+ }
2556
+ _empty = _items.length === 0;
2557
+ }
2558
+
2559
+ this.toString = function() {
2560
+ var result = '';
2561
+ if (!this._empty) {
2562
+ if (_indent_count >= 0) {
2563
+ result = parent.indent_cache[_indent_count];
2564
+ }
2565
+ result += _items.join('')
2566
+ }
2567
+ return result;
2568
+ }
2569
+ }
2570
+
2571
+ function Output(indent_string, baseIndentString) {
2572
+ baseIndentString = baseIndentString || '';
2573
+ this.indent_cache = [ baseIndentString ];
2574
+ this.baseIndentLength = baseIndentString.length;
2575
+ this.indent_length = indent_string.length;
2576
+ this.raw = false;
2577
+
2578
+ var lines =[];
2579
+ this.baseIndentString = baseIndentString;
2580
+ this.indent_string = indent_string;
2581
+ this.previous_line = null;
2582
+ this.current_line = null;
2583
+ this.space_before_token = false;
2584
+
2585
+ this.add_outputline = function() {
2586
+ this.previous_line = this.current_line;
2587
+ this.current_line = new OutputLine(this);
2588
+ lines.push(this.current_line);
2589
+ }
2590
+
2591
+ // initialize
2592
+ this.add_outputline();
2593
+
2594
+
2595
+ this.get_line_number = function() {
2596
+ return lines.length;
2597
+ }
2598
+
2599
+ // Using object instead of string to allow for later expansion of info about each line
2600
+ this.add_new_line = function(force_newline) {
2601
+ if (this.get_line_number() === 1 && this.just_added_newline()) {
2602
+ return false; // no newline on start of file
2603
+ }
2604
+
2605
+ if (force_newline || !this.just_added_newline()) {
2606
+ if (!this.raw) {
2607
+ this.add_outputline();
2608
+ }
2609
+ return true;
2610
+ }
2611
+
2612
+ return false;
2613
+ }
2614
+
2615
+ this.get_code = function() {
2616
+ var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
2617
+ return sweet_code;
2618
+ }
2619
+
2620
+ this.set_indent = function(level) {
2621
+ // Never indent your first output indent at the start of the file
2622
+ if (lines.length > 1) {
2623
+ while(level >= this.indent_cache.length) {
2624
+ this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
2625
+ }
2626
+
2627
+ this.current_line.set_indent(level);
2628
+ return true;
2629
+ }
2630
+ this.current_line.set_indent(0);
2631
+ return false;
2632
+ }
2633
+
2634
+ this.add_raw_token = function(token) {
2635
+ for (var x = 0; x < token.newlines; x++) {
2636
+ this.add_outputline();
2637
+ }
2638
+ this.current_line.push(token.whitespace_before);
2639
+ this.current_line.push(token.text);
2640
+ this.space_before_token = false;
2641
+ }
2642
+
2643
+ this.add_token = function(printable_token) {
2644
+ this.add_space_before_token();
2645
+ this.current_line.push(printable_token);
2646
+ }
2647
+
2648
+ this.add_space_before_token = function() {
2649
+ if (this.space_before_token && !this.just_added_newline()) {
2650
+ this.current_line.push(' ');
2651
+ }
2652
+ this.space_before_token = false;
2653
+ }
2654
+
2655
+ this.remove_redundant_indentation = function (frame) {
2656
+ // This implementation is effective but has some issues:
2657
+ // - can cause line wrap to happen too soon due to indent removal
2658
+ // after wrap points are calculated
2659
+ // These issues are minor compared to ugly indentation.
2660
+
2661
+ if (frame.multiline_frame ||
2662
+ frame.mode === MODE.ForInitializer ||
2663
+ frame.mode === MODE.Conditional) {
2664
+ return;
2665
+ }
2666
+
2667
+ // remove one indent from each line inside this section
2668
+ var index = frame.start_line_index;
2669
+ var line;
2670
+
2671
+ var output_length = lines.length;
2672
+ while (index < output_length) {
2673
+ lines[index].remove_indent();
2674
+ index++;
2675
+ }
2676
+ }
2677
+
2678
+ this.trim = function(eat_newlines) {
2679
+ eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
2680
+
2681
+ this.current_line.trim(indent_string, baseIndentString);
2682
+
2683
+ while (eat_newlines && lines.length > 1 &&
2684
+ this.current_line.is_empty()) {
2685
+ lines.pop();
2686
+ this.current_line = lines[lines.length - 1]
2687
+ this.current_line.trim();
2688
+ }
2689
+
2690
+ this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
2691
+ }
2692
+
2693
+ this.just_added_newline = function() {
2694
+ return this.current_line.is_empty();
2695
+ }
2696
+
2697
+ this.just_added_blankline = function() {
2698
+ if (this.just_added_newline()) {
2699
+ if (lines.length === 1) {
2700
+ return true; // start of the file and newline = blank
2701
+ }
2702
+
2703
+ var line = lines[lines.length - 2];
2704
+ return line.is_empty();
2705
+ }
2706
+ return false;
2707
+ }
2708
+ }
2709
+
2710
+
2711
+ var Token = function(type, text, newlines, whitespace_before, mode, parent) {
2712
+ this.type = type;
2713
+ this.text = text;
2714
+ this.comments_before = [];
2715
+ this.newlines = newlines || 0;
2716
+ this.wanted_newline = newlines > 0;
2717
+ this.whitespace_before = whitespace_before || '';
2718
+ this.parent = null;
2719
+ this.directives = null;
2720
+ }
2721
+
2722
+ function tokenizer(input, opts, indent_string) {
2723
+
2724
+ var whitespace = "\n\r\t ".split('');
2725
+ var digit = /[0-9]/;
2726
+ var digit_oct = /[01234567]/;
2727
+ var digit_hex = /[0123456789abcdefABCDEF]/;
2728
+
2729
+ var punct = ('+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! ~ , : ? ^ ^= |= :: =>').split(' ');
2730
+ // words which should always start on new line.
2731
+ this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
2732
+ var reserved_words = this.line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await']);
2733
+
2734
+ // /* ... */ comment ends with nearest */ or end of file
2735
+ var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;
2736
+
2737
+ // comment ends just before nearest linefeed or end of file
2738
+ var comment_pattern = /([^\n\r\u2028\u2029]*)/g;
2739
+
2740
+ var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
2741
+ var directive_pattern = / (\w+)[:](\w+)/g;
2742
+ var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;
2743
+
2744
+ var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g
2745
+
2746
+ var n_newlines, whitespace_before_token, in_html_comment, tokens, parser_pos;
2747
+ var input_length;
2748
+
2749
+ this.tokenize = function() {
2750
+ // cache the source's length.
2751
+ input_length = input.length
2752
+ parser_pos = 0;
2753
+ in_html_comment = false
2754
+ tokens = [];
2755
+
2756
+ var next, last;
2757
+ var token_values;
2758
+ var open = null;
2759
+ var open_stack = [];
2760
+ var comments = [];
2761
+
2762
+ while (!(last && last.type === 'TK_EOF')) {
2763
+ token_values = tokenize_next();
2764
+ next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
2765
+ while(next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
2766
+ if (next.type === 'TK_BLOCK_COMMENT') {
2767
+ next.directives = token_values[2];
2768
+ }
2769
+ comments.push(next);
2770
+ token_values = tokenize_next();
2771
+ next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
2772
+ }
2773
+
2774
+ if (comments.length) {
2775
+ next.comments_before = comments;
2776
+ comments = [];
2777
+ }
2778
+
2779
+ if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
2780
+ next.parent = last;
2781
+ open_stack.push(open);
2782
+ open = next;
2783
+ } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
2784
+ (open && (
2785
+ (next.text === ']' && open.text === '[') ||
2786
+ (next.text === ')' && open.text === '(') ||
2787
+ (next.text === '}' && open.text === '{')))) {
2788
+ next.parent = open.parent;
2789
+ open = open_stack.pop();
2790
+ }
2791
+
2792
+ tokens.push(next);
2793
+ last = next;
2794
+ }
2795
+
2796
+ return tokens;
2797
+ }
2798
+
2799
+ function get_directives (text) {
2800
+ if (!text.match(directives_block_pattern)) {
2801
+ return null;
2802
+ }
2803
+
2804
+ var directives = {};
2805
+ directive_pattern.lastIndex = 0;
2806
+ var directive_match = directive_pattern.exec(text);
2807
+
2808
+ while (directive_match) {
2809
+ directives[directive_match[1]] = directive_match[2];
2810
+ directive_match = directive_pattern.exec(text);
2811
+ }
2812
+
2813
+ return directives;
2814
+ }
2815
+
2816
+ function tokenize_next() {
2817
+ var i, resulting_string;
2818
+ var whitespace_on_this_line = [];
2819
+
2820
+ n_newlines = 0;
2821
+ whitespace_before_token = '';
2822
+
2823
+ if (parser_pos >= input_length) {
2824
+ return ['', 'TK_EOF'];
2825
+ }
2826
+
2827
+ var last_token;
2828
+ if (tokens.length) {
2829
+ last_token = tokens[tokens.length-1];
2830
+ } else {
2831
+ // For the sake of tokenizing we can pretend that there was on open brace to start
2832
+ last_token = new Token('TK_START_BLOCK', '{');
2833
+ }
2834
+
2835
+
2836
+ var c = input.charAt(parser_pos);
2837
+ parser_pos += 1;
2838
+
2839
+ while (in_array(c, whitespace)) {
2840
+
2841
+ if (acorn.newline.test(c)) {
2842
+ if (!(c === '\n' && input.charAt(parser_pos-2) === '\r')) {
2843
+ n_newlines += 1;
2844
+ whitespace_on_this_line = [];
2845
+ }
2846
+ } else {
2847
+ whitespace_on_this_line.push(c);
2848
+ }
2849
+
2850
+ if (parser_pos >= input_length) {
2851
+ return ['', 'TK_EOF'];
2852
+ }
2853
+
2854
+ c = input.charAt(parser_pos);
2855
+ parser_pos += 1;
2856
+ }
2857
+
2858
+ if(whitespace_on_this_line.length) {
2859
+ whitespace_before_token = whitespace_on_this_line.join('');
2860
+ }
2861
+
2862
+ if (digit.test(c)) {
2863
+ var allow_decimal = true;
2864
+ var allow_e = true;
2865
+ var local_digit = digit;
2866
+
2867
+ if (c === '0' && parser_pos < input_length && /[Xxo]/.test(input.charAt(parser_pos))) {
2868
+ // switch to hex/oct number, no decimal or e, just hex/oct digits
2869
+ allow_decimal = false;
2870
+ allow_e = false;
2871
+ c += input.charAt(parser_pos);
2872
+ parser_pos += 1;
2873
+ local_digit = /[o]/.test(input.charAt(parser_pos)) ? digit_oct : digit_hex;
2874
+ } else {
2875
+ // we know this first loop will run. It keeps the logic simpler.
2876
+ c = '';
2877
+ parser_pos -= 1;
2878
+ }
2879
+
2880
+ // Add the digits
2881
+ while (parser_pos < input_length && local_digit.test(input.charAt(parser_pos))) {
2882
+ c += input.charAt(parser_pos);
2883
+ parser_pos += 1;
2884
+
2885
+ if (allow_decimal && parser_pos < input_length && input.charAt(parser_pos) === '.') {
2886
+ c += input.charAt(parser_pos);
2887
+ parser_pos += 1;
2888
+ allow_decimal = false;
2889
+ }
2890
+
2891
+ if (allow_e && parser_pos < input_length && /[Ee]/.test(input.charAt(parser_pos))) {
2892
+ c += input.charAt(parser_pos);
2893
+ parser_pos += 1;
2894
+
2895
+ if (parser_pos < input_length && /[+-]/.test(input.charAt(parser_pos))) {
2896
+ c += input.charAt(parser_pos);
2897
+ parser_pos += 1;
2898
+ }
2899
+
2900
+ allow_e = false;
2901
+ allow_decimal = false;
2902
+ }
2903
+ }
2904
+
2905
+ return [c, 'TK_WORD'];
2906
+ }
2907
+
2908
+ if (acorn.isIdentifierStart(input.charCodeAt(parser_pos-1))) {
2909
+ if (parser_pos < input_length) {
2910
+ while (acorn.isIdentifierChar(input.charCodeAt(parser_pos))) {
2911
+ c += input.charAt(parser_pos);
2912
+ parser_pos += 1;
2913
+ if (parser_pos === input_length) {
2914
+ break;
2915
+ }
2916
+ }
2917
+ }
2918
+
2919
+ if (!(last_token.type === 'TK_DOT' ||
2920
+ (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get'])))
2921
+ && in_array(c, reserved_words)) {
2922
+ if (c === 'in') { // hack for 'in' operator
2923
+ return [c, 'TK_OPERATOR'];
2924
+ }
2925
+ return [c, 'TK_RESERVED'];
2926
+ }
2927
+
2928
+ return [c, 'TK_WORD'];
2929
+ }
2930
+
2931
+ if (c === '(' || c === '[') {
2932
+ return [c, 'TK_START_EXPR'];
2933
+ }
2934
+
2935
+ if (c === ')' || c === ']') {
2936
+ return [c, 'TK_END_EXPR'];
2937
+ }
2938
+
2939
+ if (c === '{') {
2940
+ return [c, 'TK_START_BLOCK'];
2941
+ }
2942
+
2943
+ if (c === '}') {
2944
+ return [c, 'TK_END_BLOCK'];
2945
+ }
2946
+
2947
+ if (c === ';') {
2948
+ return [c, 'TK_SEMICOLON'];
2949
+ }
2950
+
2951
+ if (c === '/') {
2952
+ var comment = '';
2953
+ // peek for comment /* ... */
2954
+ if (input.charAt(parser_pos) === '*') {
2955
+ parser_pos += 1;
2956
+ block_comment_pattern.lastIndex = parser_pos;
2957
+ var comment_match = block_comment_pattern.exec(input);
2958
+ comment = '/*' + comment_match[0];
2959
+ parser_pos += comment_match[0].length;
2960
+ var directives = get_directives(comment);
2961
+ if (directives && directives['ignore'] === 'start') {
2962
+ directives_end_ignore_pattern.lastIndex = parser_pos;
2963
+ comment_match = directives_end_ignore_pattern.exec(input)
2964
+ comment += comment_match[0];
2965
+ parser_pos += comment_match[0].length;
2966
+ }
2967
+ comment = comment.replace(acorn.lineBreak, '\n');
2968
+ return [comment, 'TK_BLOCK_COMMENT', directives];
2969
+ }
2970
+ // peek for comment // ...
2971
+ if (input.charAt(parser_pos) === '/') {
2972
+ parser_pos += 1;
2973
+ comment_pattern.lastIndex = parser_pos;
2974
+ var comment_match = comment_pattern.exec(input);
2975
+ comment = '//' + comment_match[0];
2976
+ parser_pos += comment_match[0].length;
2977
+ return [comment, 'TK_COMMENT'];
2978
+ }
2979
+
2980
+ }
2981
+
2982
+ if (c === '`' || c === "'" || c === '"' || // string
2983
+ (
2984
+ (c === '/') || // regexp
2985
+ (opts.e4x && c === "<" && input.slice(parser_pos - 1).match(/^<([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])(\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.*?}))*\s*(\/?)\s*>/)) // xml
2986
+ ) && ( // regex and xml can only appear in specific locations during parsing
2987
+ (last_token.type === 'TK_RESERVED' && in_array(last_token.text , ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
2988
+ (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
2989
+ last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
2990
+ (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
2991
+ 'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
2992
+ ]))
2993
+ )) {
2994
+
2995
+ var sep = c,
2996
+ esc = false,
2997
+ has_char_escapes = false;
2998
+
2999
+ resulting_string = c;
3000
+
3001
+ if (sep === '/') {
3002
+ //
3003
+ // handle regexp
3004
+ //
3005
+ var in_char_class = false;
3006
+ while (parser_pos < input_length &&
3007
+ ((esc || in_char_class || input.charAt(parser_pos) !== sep) &&
3008
+ !acorn.newline.test(input.charAt(parser_pos)))) {
3009
+ resulting_string += input.charAt(parser_pos);
3010
+ if (!esc) {
3011
+ esc = input.charAt(parser_pos) === '\\';
3012
+ if (input.charAt(parser_pos) === '[') {
3013
+ in_char_class = true;
3014
+ } else if (input.charAt(parser_pos) === ']') {
3015
+ in_char_class = false;
3016
+ }
3017
+ } else {
3018
+ esc = false;
3019
+ }
3020
+ parser_pos += 1;
3021
+ }
3022
+ } else if (opts.e4x && sep === '<') {
3023
+ //
3024
+ // handle e4x xml literals
3025
+ //
3026
+ var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])(\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.*?}))*\s*(\/?)\s*>/g;
3027
+ var xmlStr = input.slice(parser_pos - 1);
3028
+ var match = xmlRegExp.exec(xmlStr);
3029
+ if (match && match.index === 0) {
3030
+ var rootTag = match[2];
3031
+ var depth = 0;
3032
+ while (match) {
3033
+ var isEndTag = !! match[1];
3034
+ var tagName = match[2];
3035
+ var isSingletonTag = ( !! match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
3036
+ if (tagName === rootTag && !isSingletonTag) {
3037
+ if (isEndTag) {
3038
+ --depth;
3039
+ } else {
3040
+ ++depth;
3041
+ }
3042
+ }
3043
+ if (depth <= 0) {
3044
+ break;
3045
+ }
3046
+ match = xmlRegExp.exec(xmlStr);
3047
+ }
3048
+ var xmlLength = match ? match.index + match[0].length : xmlStr.length;
3049
+ xmlStr = xmlStr.slice(0, xmlLength);
3050
+ parser_pos += xmlLength - 1;
3051
+ xmlStr = xmlStr.replace(acorn.lineBreak, '\n');
3052
+ return [xmlStr, "TK_STRING"];
3053
+ }
3054
+ } else {
3055
+ //
3056
+ // handle string
3057
+ //
3058
+ // Template strings can travers lines without escape characters.
3059
+ // Other strings cannot
3060
+ while (parser_pos < input_length &&
3061
+ (esc || (input.charAt(parser_pos) !== sep &&
3062
+ (sep === '`' || !acorn.newline.test(input.charAt(parser_pos)))))) {
3063
+ // Handle \r\n linebreaks after escapes or in template strings
3064
+ if ((esc || sep === '`') && acorn.newline.test(input.charAt(parser_pos))) {
3065
+ if (input.charAt(parser_pos) === '\r' && input.charAt(parser_pos + 1) === '\n') {
3066
+ parser_pos += 1;
3067
+ }
3068
+ resulting_string += '\n';
3069
+ } else {
3070
+ resulting_string += input.charAt(parser_pos);
3071
+ }
3072
+ if (esc) {
3073
+ if (input.charAt(parser_pos) === 'x' || input.charAt(parser_pos) === 'u') {
3074
+ has_char_escapes = true;
3075
+ }
3076
+ esc = false;
3077
+ } else {
3078
+ esc = input.charAt(parser_pos) === '\\';
3079
+ }
3080
+ parser_pos += 1;
3081
+ }
3082
+
3083
+ }
3084
+
3085
+ if (has_char_escapes && opts.unescape_strings) {
3086
+ resulting_string = unescape_string(resulting_string);
3087
+ }
3088
+
3089
+ if (parser_pos < input_length && input.charAt(parser_pos) === sep) {
3090
+ resulting_string += sep;
3091
+ parser_pos += 1;
3092
+
3093
+ if (sep === '/') {
3094
+ // regexps may have modifiers /regexp/MOD , so fetch those, too
3095
+ // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
3096
+ while (parser_pos < input_length && acorn.isIdentifierStart(input.charCodeAt(parser_pos))) {
3097
+ resulting_string += input.charAt(parser_pos);
3098
+ parser_pos += 1;
3099
+ }
3100
+ }
3101
+ }
3102
+ return [resulting_string, 'TK_STRING'];
3103
+ }
3104
+
3105
+ if (c === '#') {
3106
+
3107
+ if (tokens.length === 0 && input.charAt(parser_pos) === '!') {
3108
+ // shebang
3109
+ resulting_string = c;
3110
+ while (parser_pos < input_length && c !== '\n') {
3111
+ c = input.charAt(parser_pos);
3112
+ resulting_string += c;
3113
+ parser_pos += 1;
3114
+ }
3115
+ return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
3116
+ }
3117
+
3118
+
3119
+
3120
+ // Spidermonkey-specific sharp variables for circular references
3121
+ // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
3122
+ // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
3123
+ var sharp = '#';
3124
+ if (parser_pos < input_length && digit.test(input.charAt(parser_pos))) {
3125
+ do {
3126
+ c = input.charAt(parser_pos);
3127
+ sharp += c;
3128
+ parser_pos += 1;
3129
+ } while (parser_pos < input_length && c !== '#' && c !== '=');
3130
+ if (c === '#') {
3131
+ //
3132
+ } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
3133
+ sharp += '[]';
3134
+ parser_pos += 2;
3135
+ } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
3136
+ sharp += '{}';
3137
+ parser_pos += 2;
3138
+ }
3139
+ return [sharp, 'TK_WORD'];
3140
+ }
3141
+ }
3142
+
3143
+ if (c === '<' && (input.charAt(parser_pos) === '?' || input.charAt(parser_pos) === '%')) {
3144
+ template_pattern.lastIndex = parser_pos - 1;
3145
+ var template_match = template_pattern.exec(input);
3146
+ if(template_match) {
3147
+ c = template_match[0];
3148
+ parser_pos += c.length - 1;
3149
+ c = c.replace(acorn.lineBreak, '\n');
3150
+ return [c, 'TK_STRING'];
3151
+ }
3152
+ }
3153
+
3154
+ if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
3155
+ parser_pos += 3;
3156
+ c = '<!--';
3157
+ while (!acorn.newline.test(input.charAt(parser_pos)) && parser_pos < input_length) {
3158
+ c += input.charAt(parser_pos);
3159
+ parser_pos++;
3160
+ }
3161
+ in_html_comment = true;
3162
+ return [c, 'TK_COMMENT'];
3163
+ }
3164
+
3165
+ if (c === '-' && in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
3166
+ in_html_comment = false;
3167
+ parser_pos += 2;
3168
+ return ['-->', 'TK_COMMENT'];
3169
+ }
3170
+
3171
+ if (c === '.') {
3172
+ return [c, 'TK_DOT'];
3173
+ }
3174
+
3175
+ if (in_array(c, punct)) {
3176
+ while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
3177
+ c += input.charAt(parser_pos);
3178
+ parser_pos += 1;
3179
+ if (parser_pos >= input_length) {
3180
+ break;
3181
+ }
3182
+ }
3183
+
3184
+ if (c === ',') {
3185
+ return [c, 'TK_COMMA'];
3186
+ } else if (c === '=') {
3187
+ return [c, 'TK_EQUALS'];
3188
+ } else {
3189
+ return [c, 'TK_OPERATOR'];
3190
+ }
3191
+ }
3192
+
3193
+ return [c, 'TK_UNKNOWN'];
3194
+ }
3195
+
3196
+
3197
+ function unescape_string(s) {
3198
+ var esc = false,
3199
+ out = '',
3200
+ pos = 0,
3201
+ s_hex = '',
3202
+ escaped = 0,
3203
+ c;
3204
+
3205
+ while (esc || pos < s.length) {
3206
+
3207
+ c = s.charAt(pos);
3208
+ pos++;
3209
+
3210
+ if (esc) {
3211
+ esc = false;
3212
+ if (c === 'x') {
3213
+ // simple hex-escape \x24
3214
+ s_hex = s.substr(pos, 2);
3215
+ pos += 2;
3216
+ } else if (c === 'u') {
3217
+ // unicode-escape, \u2134
3218
+ s_hex = s.substr(pos, 4);
3219
+ pos += 4;
3220
+ } else {
3221
+ // some common escape, e.g \n
3222
+ out += '\\' + c;
3223
+ continue;
3224
+ }
3225
+ if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) {
3226
+ // some weird escaping, bail out,
3227
+ // leaving whole string intact
3228
+ return s;
3229
+ }
3230
+
3231
+ escaped = parseInt(s_hex, 16);
3232
+
3233
+ if (escaped >= 0x00 && escaped < 0x20) {
3234
+ // leave 0x00...0x1f escaped
3235
+ if (c === 'x') {
3236
+ out += '\\x' + s_hex;
3237
+ } else {
3238
+ out += '\\u' + s_hex;
3239
+ }
3240
+ continue;
3241
+ } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
3242
+ // single-quote, apostrophe, backslash - escape these
3243
+ out += '\\' + String.fromCharCode(escaped);
3244
+ } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) {
3245
+ // we bail out on \x7f..\xff,
3246
+ // leaving whole string escaped,
3247
+ // as it's probably completely binary
3248
+ return s;
3249
+ } else {
3250
+ out += String.fromCharCode(escaped);
3251
+ }
3252
+ } else if (c === '\\') {
3253
+ esc = true;
3254
+ } else {
3255
+ out += c;
3256
+ }
3257
+ }
3258
+ return out;
3259
+ }
3260
+
3261
+ }
3262
+
3263
+ /* jshint ignore:end */
3264
+ /* jscs:enable */
3265
+
3266
+ return {
3267
+ run: run
3268
+ }
3269
+ };
3270
+
3271
+ }));