xmt_froala 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (203) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +2 -0
  5. data/app/assets/javascripts/xmt_froala.js +31 -0
  6. data/app/assets/stylesheets/xmt_froala.css +17 -0
  7. data/app/controllers/xmt_froala/application_controller.rb +5 -0
  8. data/app/controllers/xmt_froala/assets_controller.rb +94 -0
  9. data/app/models/xmt_froala/asset_uploader.rb +107 -0
  10. data/app/models/xmt_froala/file_uploader.rb +10 -0
  11. data/app/models/xmt_froala/flash_uploader.rb +10 -0
  12. data/app/models/xmt_froala/image_uploader.rb +10 -0
  13. data/app/models/xmt_froala/media_uploader.rb +10 -0
  14. data/config/routes.rb +6 -0
  15. data/lib/generators/xmt_froala/install/USAGE +10 -0
  16. data/lib/generators/xmt_froala/install/install_generator.rb +23 -0
  17. data/lib/generators/xmt_froala/install/templates/application.js +17 -0
  18. data/lib/generators/xmt_froala/install/templates/xmt_froala.rb +29 -0
  19. data/lib/generators/xmt_froala/migration/USAGE +14 -0
  20. data/lib/generators/xmt_froala/migration/migration_generator.rb +36 -0
  21. data/lib/generators/xmt_froala/migration/templates/migration/migration.rb +18 -0
  22. data/lib/generators/xmt_froala/migration/templates/models/active_record/xmt_froala/asset.rb +14 -0
  23. data/lib/generators/xmt_froala/migration/templates/models/active_record/xmt_froala/file.rb +3 -0
  24. data/lib/generators/xmt_froala/migration/templates/models/active_record/xmt_froala/flash.rb +3 -0
  25. data/lib/generators/xmt_froala/migration/templates/models/active_record/xmt_froala/image.rb +3 -0
  26. data/lib/generators/xmt_froala/migration/templates/models/active_record/xmt_froala/media.rb +3 -0
  27. data/lib/generators/xmt_froala/migration/templates/models/mongoid/xmt_froala/asset.rb +27 -0
  28. data/lib/generators/xmt_froala/migration/templates/models/mongoid/xmt_froala/file.rb +3 -0
  29. data/lib/generators/xmt_froala/migration/templates/models/mongoid/xmt_froala/flash.rb +3 -0
  30. data/lib/generators/xmt_froala/migration/templates/models/mongoid/xmt_froala/image.rb +3 -0
  31. data/lib/generators/xmt_froala/migration/templates/models/mongoid/xmt_froala/media.rb +3 -0
  32. data/lib/tasks/xmt_froala_tasks.rake +9 -0
  33. data/lib/xmt_froala.rb +65 -0
  34. data/lib/xmt_froala/active_record.rb +14 -0
  35. data/lib/xmt_froala/engine.rb +32 -0
  36. data/lib/xmt_froala/formtastic.rb +12 -0
  37. data/lib/xmt_froala/helper.rb +105 -0
  38. data/lib/xmt_froala/simple_form.rb +11 -0
  39. data/lib/xmt_froala/version.rb +3 -0
  40. data/vendor/assets/javascripts/xmt_froala/froala_editor.js +0 -0
  41. data/vendor/assets/javascripts/xmt_froala/languages/ar.js +318 -0
  42. data/vendor/assets/javascripts/xmt_froala/languages/bs.js +318 -0
  43. data/vendor/assets/javascripts/xmt_froala/languages/cs.js +318 -0
  44. data/vendor/assets/javascripts/xmt_froala/languages/da.js +318 -0
  45. data/vendor/assets/javascripts/xmt_froala/languages/de.js +318 -0
  46. data/vendor/assets/javascripts/xmt_froala/languages/en_ca.js +262 -0
  47. data/vendor/assets/javascripts/xmt_froala/languages/en_gb.js +262 -0
  48. data/vendor/assets/javascripts/xmt_froala/languages/es.js +318 -0
  49. data/vendor/assets/javascripts/xmt_froala/languages/et.js +318 -0
  50. data/vendor/assets/javascripts/xmt_froala/languages/fa.js +318 -0
  51. data/vendor/assets/javascripts/xmt_froala/languages/fi.js +318 -0
  52. data/vendor/assets/javascripts/xmt_froala/languages/fr.js +318 -0
  53. data/vendor/assets/javascripts/xmt_froala/languages/he.js +318 -0
  54. data/vendor/assets/javascripts/xmt_froala/languages/hr.js +318 -0
  55. data/vendor/assets/javascripts/xmt_froala/languages/hu.js +318 -0
  56. data/vendor/assets/javascripts/xmt_froala/languages/id.js +319 -0
  57. data/vendor/assets/javascripts/xmt_froala/languages/it.js +318 -0
  58. data/vendor/assets/javascripts/xmt_froala/languages/ja.js +318 -0
  59. data/vendor/assets/javascripts/xmt_froala/languages/ko.js +318 -0
  60. data/vendor/assets/javascripts/xmt_froala/languages/me.js +318 -0
  61. data/vendor/assets/javascripts/xmt_froala/languages/nb.js +318 -0
  62. data/vendor/assets/javascripts/xmt_froala/languages/nl.js +318 -0
  63. data/vendor/assets/javascripts/xmt_froala/languages/pl.js +318 -0
  64. data/vendor/assets/javascripts/xmt_froala/languages/pt_br.js +318 -0
  65. data/vendor/assets/javascripts/xmt_froala/languages/pt_pt.js +318 -0
  66. data/vendor/assets/javascripts/xmt_froala/languages/ro.js +319 -0
  67. data/vendor/assets/javascripts/xmt_froala/languages/ru.js +318 -0
  68. data/vendor/assets/javascripts/xmt_froala/languages/sk.js +318 -0
  69. data/vendor/assets/javascripts/xmt_froala/languages/sr.js +318 -0
  70. data/vendor/assets/javascripts/xmt_froala/languages/sv.js +318 -0
  71. data/vendor/assets/javascripts/xmt_froala/languages/th.js +318 -0
  72. data/vendor/assets/javascripts/xmt_froala/languages/tr.js +318 -0
  73. data/vendor/assets/javascripts/xmt_froala/languages/uk.js +318 -0
  74. data/vendor/assets/javascripts/xmt_froala/languages/vi.js +258 -0
  75. data/vendor/assets/javascripts/xmt_froala/languages/zh_cn.js +320 -0
  76. data/vendor/assets/javascripts/xmt_froala/languages/zh_tw.js +318 -0
  77. data/vendor/assets/javascripts/xmt_froala/plugins/align.js +139 -0
  78. data/vendor/assets/javascripts/xmt_froala/plugins/align.min.js +7 -0
  79. data/vendor/assets/javascripts/xmt_froala/plugins/char_counter.js +154 -0
  80. data/vendor/assets/javascripts/xmt_froala/plugins/char_counter.min.js +7 -0
  81. data/vendor/assets/javascripts/xmt_froala/plugins/code_beautifier.js +3270 -0
  82. data/vendor/assets/javascripts/xmt_froala/plugins/code_beautifier.min.js +7 -0
  83. data/vendor/assets/javascripts/xmt_froala/plugins/code_view.js +393 -0
  84. data/vendor/assets/javascripts/xmt_froala/plugins/code_view.min.js +7 -0
  85. data/vendor/assets/javascripts/xmt_froala/plugins/colors.js +492 -0
  86. data/vendor/assets/javascripts/xmt_froala/plugins/colors.min.js +7 -0
  87. data/vendor/assets/javascripts/xmt_froala/plugins/draggable.js +459 -0
  88. data/vendor/assets/javascripts/xmt_froala/plugins/draggable.min.js +7 -0
  89. data/vendor/assets/javascripts/xmt_froala/plugins/emoticons.js +509 -0
  90. data/vendor/assets/javascripts/xmt_froala/plugins/emoticons.min.js +7 -0
  91. data/vendor/assets/javascripts/xmt_froala/plugins/entities.js +121 -0
  92. data/vendor/assets/javascripts/xmt_froala/plugins/entities.min.js +7 -0
  93. data/vendor/assets/javascripts/xmt_froala/plugins/file.js +736 -0
  94. data/vendor/assets/javascripts/xmt_froala/plugins/file.min.js +239 -0
  95. data/vendor/assets/javascripts/xmt_froala/plugins/font_family.js +182 -0
  96. data/vendor/assets/javascripts/xmt_froala/plugins/font_family.min.js +7 -0
  97. data/vendor/assets/javascripts/xmt_froala/plugins/font_size.js +118 -0
  98. data/vendor/assets/javascripts/xmt_froala/plugins/font_size.min.js +7 -0
  99. data/vendor/assets/javascripts/xmt_froala/plugins/forms.js +430 -0
  100. data/vendor/assets/javascripts/xmt_froala/plugins/forms.min.js +7 -0
  101. data/vendor/assets/javascripts/xmt_froala/plugins/fullscreen.js +274 -0
  102. data/vendor/assets/javascripts/xmt_froala/plugins/fullscreen.min.js +7 -0
  103. data/vendor/assets/javascripts/xmt_froala/plugins/help.js +216 -0
  104. data/vendor/assets/javascripts/xmt_froala/plugins/help.min.js +7 -0
  105. data/vendor/assets/javascripts/xmt_froala/plugins/image.js +3323 -0
  106. data/vendor/assets/javascripts/xmt_froala/plugins/image.min.js +7 -0
  107. data/vendor/assets/javascripts/xmt_froala/plugins/image_manager.js +1056 -0
  108. data/vendor/assets/javascripts/xmt_froala/plugins/image_manager.min.js +7 -0
  109. data/vendor/assets/javascripts/xmt_froala/plugins/inline_style.js +94 -0
  110. data/vendor/assets/javascripts/xmt_froala/plugins/inline_style.min.js +7 -0
  111. data/vendor/assets/javascripts/xmt_froala/plugins/line_breaker.js +537 -0
  112. data/vendor/assets/javascripts/xmt_froala/plugins/line_breaker.min.js +7 -0
  113. data/vendor/assets/javascripts/xmt_froala/plugins/link.js +1157 -0
  114. data/vendor/assets/javascripts/xmt_froala/plugins/link.min.js +7 -0
  115. data/vendor/assets/javascripts/xmt_froala/plugins/lists.js +462 -0
  116. data/vendor/assets/javascripts/xmt_froala/plugins/lists.min.js +7 -0
  117. data/vendor/assets/javascripts/xmt_froala/plugins/paragraph_format.js +290 -0
  118. data/vendor/assets/javascripts/xmt_froala/plugins/paragraph_format.min.js +7 -0
  119. data/vendor/assets/javascripts/xmt_froala/plugins/paragraph_style.js +144 -0
  120. data/vendor/assets/javascripts/xmt_froala/plugins/paragraph_style.min.js +7 -0
  121. data/vendor/assets/javascripts/xmt_froala/plugins/plain_paste.js +96 -0
  122. data/vendor/assets/javascripts/xmt_froala/plugins/print.js +137 -0
  123. data/vendor/assets/javascripts/xmt_froala/plugins/print.min.js +7 -0
  124. data/vendor/assets/javascripts/xmt_froala/plugins/quick_format.js +89 -0
  125. data/vendor/assets/javascripts/xmt_froala/plugins/quick_insert.js +478 -0
  126. data/vendor/assets/javascripts/xmt_froala/plugins/quick_insert.min.js +7 -0
  127. data/vendor/assets/javascripts/xmt_froala/plugins/quote.js +141 -0
  128. data/vendor/assets/javascripts/xmt_froala/plugins/quote.min.js +7 -0
  129. data/vendor/assets/javascripts/xmt_froala/plugins/save.js +189 -0
  130. data/vendor/assets/javascripts/xmt_froala/plugins/save.min.js +7 -0
  131. data/vendor/assets/javascripts/xmt_froala/plugins/special_characters.js +781 -0
  132. data/vendor/assets/javascripts/xmt_froala/plugins/special_characters.min.js +7 -0
  133. data/vendor/assets/javascripts/xmt_froala/plugins/table.js +4194 -0
  134. data/vendor/assets/javascripts/xmt_froala/plugins/table.min.js +7 -0
  135. data/vendor/assets/javascripts/xmt_froala/plugins/url.js +194 -0
  136. data/vendor/assets/javascripts/xmt_froala/plugins/url.min.js +7 -0
  137. data/vendor/assets/javascripts/xmt_froala/plugins/video.js +2342 -0
  138. data/vendor/assets/javascripts/xmt_froala/plugins/video.min.js +7 -0
  139. data/vendor/assets/javascripts/xmt_froala/plugins/word_paste.js +1403 -0
  140. data/vendor/assets/javascripts/xmt_froala/plugins/word_paste.min.js +7 -0
  141. data/vendor/assets/javascripts/xmt_froala/third_party/embedly.js +543 -0
  142. data/vendor/assets/javascripts/xmt_froala/third_party/embedly.min.js +7 -0
  143. data/vendor/assets/javascripts/xmt_froala/third_party/image_aviary.js +163 -0
  144. data/vendor/assets/javascripts/xmt_froala/third_party/image_aviary.min.js +7 -0
  145. data/vendor/assets/javascripts/xmt_froala/third_party/spell_checker.js +222 -0
  146. data/vendor/assets/javascripts/xmt_froala/third_party/spell_checker.min.js +7 -0
  147. data/vendor/assets/javascripts/xmt_froala/xmt_froala.js +15172 -0
  148. data/vendor/assets/stylesheets/xmt_froala/css/font-awesome.css +2546 -0
  149. data/vendor/assets/stylesheets/xmt_froala/css/froala_editor.min.css +7 -0
  150. data/vendor/assets/stylesheets/xmt_froala/css/froala_editor.pkgd.css +2902 -0
  151. data/vendor/assets/stylesheets/xmt_froala/css/froala_editor.pkgd.min.css +7 -0
  152. data/vendor/assets/stylesheets/xmt_froala/css/froala_style.css +413 -0
  153. data/vendor/assets/stylesheets/xmt_froala/css/froala_style.min.css +7 -0
  154. data/vendor/assets/stylesheets/xmt_froala/css/plugins/char_counter.css +57 -0
  155. data/vendor/assets/stylesheets/xmt_froala/css/plugins/char_counter.min.css +7 -0
  156. data/vendor/assets/stylesheets/xmt_froala/css/plugins/code_view.css +112 -0
  157. data/vendor/assets/stylesheets/xmt_froala/css/plugins/code_view.min.css +7 -0
  158. data/vendor/assets/stylesheets/xmt_froala/css/plugins/colors.css +155 -0
  159. data/vendor/assets/stylesheets/xmt_froala/css/plugins/colors.min.css +7 -0
  160. data/vendor/assets/stylesheets/xmt_froala/css/plugins/draggable.css +43 -0
  161. data/vendor/assets/stylesheets/xmt_froala/css/plugins/draggable.min.css +7 -0
  162. data/vendor/assets/stylesheets/xmt_froala/css/plugins/emoticons.css +42 -0
  163. data/vendor/assets/stylesheets/xmt_froala/css/plugins/emoticons.min.css +7 -0
  164. data/vendor/assets/stylesheets/xmt_froala/css/plugins/file.css +146 -0
  165. data/vendor/assets/stylesheets/xmt_froala/css/plugins/file.min.css +7 -0
  166. data/vendor/assets/stylesheets/xmt_froala/css/plugins/fullscreen.css +28 -0
  167. data/vendor/assets/stylesheets/xmt_froala/css/plugins/fullscreen.min.css +7 -0
  168. data/vendor/assets/stylesheets/xmt_froala/css/plugins/help.css +52 -0
  169. data/vendor/assets/stylesheets/xmt_froala/css/plugins/help.min.css +7 -0
  170. data/vendor/assets/stylesheets/xmt_froala/css/plugins/image.css +244 -0
  171. data/vendor/assets/stylesheets/xmt_froala/css/plugins/image.min.css +7 -0
  172. data/vendor/assets/stylesheets/xmt_froala/css/plugins/image_manager.css +266 -0
  173. data/vendor/assets/stylesheets/xmt_froala/css/plugins/image_manager.min.css +7 -0
  174. data/vendor/assets/stylesheets/xmt_froala/css/plugins/line_breaker.css +37 -0
  175. data/vendor/assets/stylesheets/xmt_froala/css/plugins/line_breaker.min.css +7 -0
  176. data/vendor/assets/stylesheets/xmt_froala/css/plugins/quick_insert.css +70 -0
  177. data/vendor/assets/stylesheets/xmt_froala/css/plugins/quick_insert.min.css +7 -0
  178. data/vendor/assets/stylesheets/xmt_froala/css/plugins/special_characters.css +51 -0
  179. data/vendor/assets/stylesheets/xmt_froala/css/plugins/special_characters.min.css +7 -0
  180. data/vendor/assets/stylesheets/xmt_froala/css/plugins/table.css +181 -0
  181. data/vendor/assets/stylesheets/xmt_froala/css/plugins/table.min.css +7 -0
  182. data/vendor/assets/stylesheets/xmt_froala/css/plugins/video.css +231 -0
  183. data/vendor/assets/stylesheets/xmt_froala/css/plugins/video.min.css +7 -0
  184. data/vendor/assets/stylesheets/xmt_froala/css/themes/dark.css +1281 -0
  185. data/vendor/assets/stylesheets/xmt_froala/css/themes/dark.min.css +7 -0
  186. data/vendor/assets/stylesheets/xmt_froala/css/themes/gray.css +1281 -0
  187. data/vendor/assets/stylesheets/xmt_froala/css/themes/gray.min.css +7 -0
  188. data/vendor/assets/stylesheets/xmt_froala/css/themes/red.css +1281 -0
  189. data/vendor/assets/stylesheets/xmt_froala/css/themes/red.min.css +7 -0
  190. data/vendor/assets/stylesheets/xmt_froala/css/themes/royal.css +1281 -0
  191. data/vendor/assets/stylesheets/xmt_froala/css/themes/royal.min.css +7 -0
  192. data/vendor/assets/stylesheets/xmt_froala/css/third_party/embedly.css +64 -0
  193. data/vendor/assets/stylesheets/xmt_froala/css/third_party/embedly.min.css +7 -0
  194. data/vendor/assets/stylesheets/xmt_froala/css/third_party/spell_checker.css +72 -0
  195. data/vendor/assets/stylesheets/xmt_froala/css/third_party/spell_checker.min.css +7 -0
  196. data/vendor/assets/stylesheets/xmt_froala/css/xmt_froala.css +1423 -0
  197. data/vendor/assets/stylesheets/xmt_froala/fonts/FontAwesome.otf +0 -0
  198. data/vendor/assets/stylesheets/xmt_froala/fonts/fontawesome-webfont.eot +0 -0
  199. data/vendor/assets/stylesheets/xmt_froala/fonts/fontawesome-webfont.svg +2671 -0
  200. data/vendor/assets/stylesheets/xmt_froala/fonts/fontawesome-webfont.ttf +0 -0
  201. data/vendor/assets/stylesheets/xmt_froala/fonts/fontawesome-webfont.woff +0 -0
  202. data/vendor/assets/stylesheets/xmt_froala/fonts/fontawesome-webfont.woff2 +0 -0
  203. metadata +273 -0
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * froala_editor v2.8.1 (https://www.froala.com/wysiwyg-editor)
3
+ * License https://froala.com/wysiwyg-editor/terms/
4
+ * Copyright 2014-2018 Froala Labs
5
+ */
6
+
7
+ !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&module.exports?module.exports=function(e,n){return n===undefined&&(n="undefined"!=typeof window?require("jquery"):require("jquery")(e)),t(n)}:t(window.jQuery)}(function(l){l.FE.PLUGINS.align=function(r){return{apply:function(e){var n=r.selection.element();if(l(n).parents(".fr-img-caption").length)l(n).css("text-align",e);else{r.selection.save(),r.html.wrap(!0,!0,!0,!0),r.selection.restore();for(var t=r.selection.blocks(),i=0;i<t.length;i++)r.helpers.getAlignment(l(t[i].parentNode))==e?l(t[i]).css("text-align","").removeClass("fr-temp-div"):l(t[i]).css("text-align",e).removeClass("fr-temp-div"),""===l(t[i]).attr("class")&&l(t[i]).removeAttr("class"),""===l(t[i]).attr("style")&&l(t[i]).removeAttr("style");r.selection.save(),r.html.unwrap(),r.selection.restore()}},refresh:function(e){var n=r.selection.blocks();if(n.length){var t=r.helpers.getAlignment(l(n[0]));e.find("> *:first").replaceWith(r.icon.create("align-"+t))}},refreshOnShow:function(e,n){var t=r.selection.blocks();if(t.length){var i=r.helpers.getAlignment(l(t[0]));n.find('a.fr-command[data-param1="'+i+'"]').addClass("fr-active").attr("aria-selected",!0)}}}},l.FE.DefineIcon("align",{NAME:"align-left"}),l.FE.DefineIcon("align-left",{NAME:"align-left"}),l.FE.DefineIcon("align-right",{NAME:"align-right"}),l.FE.DefineIcon("align-center",{NAME:"align-center"}),l.FE.DefineIcon("align-justify",{NAME:"align-justify"}),l.FE.RegisterCommand("align",{type:"dropdown",title:"Align",options:{left:"Align Left",center:"Align Center",right:"Align Right",justify:"Align Justify"},html:function(){var e='<ul class="fr-dropdown-list" role="presentation">',n=l.FE.COMMANDS.align.options;for(var t in n)n.hasOwnProperty(t)&&(e+='<li role="presentation"><a class="fr-command fr-title" tabIndex="-1" role="option" data-cmd="align" data-param1="'+t+'" title="'+this.language.translate(n[t])+'">'+this.icon.create("align-"+t)+'<span class="fr-sr-only">'+this.language.translate(n[t])+"</span></a></li>");return e+="</ul>"},callback:function(e,n){this.align.apply(n)},refresh:function(e){this.align.refresh(e)},refreshOnShow:function(e,n){this.align.refreshOnShow(e,n)},plugin:"align"})});
@@ -0,0 +1,154 @@
1
+ /*!
2
+ * froala_editor v2.8.1 (https://www.froala.com/wysiwyg-editor)
3
+ * License https://froala.com/wysiwyg-editor/terms/
4
+ * Copyright 2014-2018 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
+ return factory(jQuery);
27
+ };
28
+ } else {
29
+ // Browser globals
30
+ factory(window.jQuery);
31
+ }
32
+ }(function ($) {
33
+
34
+
35
+
36
+ // Extend defaults.
37
+ $.extend($.FE.DEFAULTS, {
38
+ charCounterMax: -1,
39
+ charCounterCount: true
40
+ });
41
+
42
+
43
+ $.FE.PLUGINS.charCounter = function (editor) {
44
+ var $counter;
45
+
46
+ /**
47
+ * Get the char number.
48
+ */
49
+ function count () {
50
+ return (editor.el.textContent || '').replace(/\u200B/g, '').length;
51
+ }
52
+
53
+ /**
54
+ * Check chars on typing.
55
+ */
56
+ function _checkCharNumber (e) {
57
+
58
+ // Continue if infinite characters;
59
+ if (editor.opts.charCounterMax < 0) return true;
60
+
61
+ // Continue if enough characters.
62
+ if (count() < editor.opts.charCounterMax) return true;
63
+
64
+ // Stop if the key will produce a new char.
65
+ var keyCode = e.which;
66
+
67
+ if ((!editor.keys.ctrlKey(e) && editor.keys.isCharacter(keyCode)) || (keyCode === $.FE.KEYCODE.IME)) {
68
+ e.preventDefault();
69
+ e.stopPropagation();
70
+ editor.events.trigger('charCounter.exceeded');
71
+
72
+ return false;
73
+ }
74
+
75
+ return true;
76
+ }
77
+
78
+ /**
79
+ * Check chars on paste.
80
+ */
81
+ function _checkCharNumberOnPaste (html) {
82
+ if (editor.opts.charCounterMax < 0) return html;
83
+
84
+ var len = $('<div>').html(html).text().length;
85
+
86
+ if (len + count() <= editor.opts.charCounterMax) return html;
87
+
88
+ editor.events.trigger('charCounter.exceeded');
89
+
90
+ return '';
91
+ }
92
+
93
+ /**
94
+ * Update the char counter.
95
+ */
96
+ function _updateCharNumber () {
97
+ if (editor.opts.charCounterCount) {
98
+ var chars = count() + (editor.opts.charCounterMax > 0 ? '/' + editor.opts.charCounterMax : '');
99
+
100
+ $counter.text(chars);
101
+
102
+ if (editor.opts.toolbarBottom) {
103
+ $counter.css('margin-bottom', editor.$tb.outerHeight(true))
104
+ }
105
+
106
+ // Scroll size correction.
107
+ var scroll_size = editor.$wp.get(0).offsetWidth - editor.$wp.get(0).clientWidth;
108
+
109
+ if (scroll_size >= 0) {
110
+ if (editor.opts.direction == 'rtl') {
111
+ $counter.css('margin-left', scroll_size);
112
+ }
113
+ else {
114
+ $counter.css('margin-right', scroll_size);
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ /*
121
+ * Initialize.
122
+ */
123
+ function _init () {
124
+ if (!editor.$wp) return false;
125
+
126
+ if (!editor.opts.charCounterCount) return false;
127
+
128
+ $counter = $('<span class="fr-counter"></span>');
129
+ $counter.css('bottom', editor.$wp.css('border-bottom-width'));
130
+ editor.$box.append($counter);
131
+
132
+ editor.events.on('keydown', _checkCharNumber, true);
133
+ editor.events.on('paste.afterCleanup', _checkCharNumberOnPaste);
134
+ editor.events.on('keyup contentChanged input', function () {
135
+ editor.events.trigger('charCounter.update');
136
+ });
137
+
138
+ editor.events.on('charCounter.update', _updateCharNumber);
139
+ editor.events.trigger('charCounter.update');
140
+
141
+ editor.events.on('destroy', function () {
142
+ $(editor.o_win).off('resize.char' + editor.id);
143
+ $counter.removeData().remove();
144
+ $counter = null;
145
+ });
146
+ }
147
+
148
+ return {
149
+ _init: _init,
150
+ count: count
151
+ }
152
+ }
153
+
154
+ }));
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * froala_editor v2.8.1 (https://www.froala.com/wysiwyg-editor)
3
+ * License https://froala.com/wysiwyg-editor/terms/
4
+ * Copyright 2014-2018 Froala Labs
5
+ */
6
+
7
+ !function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t)}:n(window.jQuery)}(function(a){a.extend(a.FE.DEFAULTS,{charCounterMax:-1,charCounterCount:!0}),a.FE.PLUGINS.charCounter=function(n){var r;function o(){return(n.el.textContent||"").replace(/\u200B/g,"").length}function e(e){if(n.opts.charCounterMax<0)return!0;if(o()<n.opts.charCounterMax)return!0;var t=e.which;return!(!n.keys.ctrlKey(e)&&n.keys.isCharacter(t)||t===a.FE.KEYCODE.IME)||(e.preventDefault(),e.stopPropagation(),n.events.trigger("charCounter.exceeded"),!1)}function t(e){return n.opts.charCounterMax<0?e:a("<div>").html(e).text().length+o()<=n.opts.charCounterMax?e:(n.events.trigger("charCounter.exceeded"),"")}function u(){if(n.opts.charCounterCount){var e=o()+(0<n.opts.charCounterMax?"/"+n.opts.charCounterMax:"");r.text(e),n.opts.toolbarBottom&&r.css("margin-bottom",n.$tb.outerHeight(!0));var t=n.$wp.get(0).offsetWidth-n.$wp.get(0).clientWidth;0<=t&&("rtl"==n.opts.direction?r.css("margin-left",t):r.css("margin-right",t))}}return{_init:function(){return!!n.$wp&&!!n.opts.charCounterCount&&((r=a('<span class="fr-counter"></span>')).css("bottom",n.$wp.css("border-bottom-width")),n.$box.append(r),n.events.on("keydown",e,!0),n.events.on("paste.afterCleanup",t),n.events.on("keyup contentChanged input",function(){n.events.trigger("charCounter.update")}),n.events.on("charCounter.update",u),n.events.trigger("charCounter.update"),void n.events.on("destroy",function(){a(n.o_win).off("resize.char"+n.id),r.removeData().remove(),r=null}))},count:o}}});
@@ -0,0 +1,3270 @@
1
+ /*!
2
+ * froala_editor v2.8.1 (https://www.froala.com/wysiwyg-editor)
3
+ * License https://froala.com/wysiwyg-editor/terms/
4
+ * Copyright 2014-2018 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
+ return factory(jQuery);
27
+ };
28
+ } else {
29
+ // Browser globals
30
+ factory(window.jQuery);
31
+ }
32
+ }(function ($) {
33
+
34
+
35
+
36
+ $.FE.PLUGINS.codeBeautifier = function () {
37
+ /**
38
+ * HTML BEAUTIFIER
39
+ *
40
+ * LICENSE: The MIT License (MIT)
41
+ *
42
+ * Written by Nochum Sossonko, (nsossonko@hotmail.com)
43
+ *
44
+ * Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
45
+ * http://jsbeautifier.org/
46
+ *
47
+ */
48
+ /* jshint ignore:start */
49
+ /* jscs:disable */
50
+
51
+
52
+ var acorn = {};
53
+ (function(exports) {
54
+ /* jshint curly: false */
55
+ // This section of code is taken from acorn.
56
+ //
57
+ // Acorn was written by Marijn Haverbeke and released under an MIT
58
+ // license. The Unicode regexps (for identifiers and whitespace) were
59
+ // taken from [Esprima](http://esprima.org) by Ariya Hidayat.
60
+ //
61
+ // Git repositories for Acorn are available at
62
+ //
63
+ // http://marijnhaverbeke.nl/git/acorn
64
+ // https://github.com/marijnh/acorn.git
65
+
66
+ // ## Character categories
67
+
68
+ // Big ugly regular expressions that match characters in the
69
+ // whitespace, identifier, and identifier-start categories. These
70
+ // are only applied when a character is found to actually have a
71
+ // code point above 128.
72
+
73
+ var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
74
+ 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";
75
+ 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";
76
+ var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
77
+ var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
78
+
79
+ // Whether a single character denotes a newline.
80
+
81
+ exports.newline = /[\n\r\u2028\u2029]/;
82
+
83
+ // Matches a whole line break (where CRLF is considered a single
84
+ // line break). Used to count lines.
85
+
86
+ // in javascript, these two differ
87
+ // in python they are the same, different methods are called on them
88
+ exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
89
+ exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
90
+
91
+
92
+ // Test whether a given character code starts an identifier.
93
+
94
+ exports.isIdentifierStart = function(code) {
95
+ // permit $ (36) and @ (64). @ is used in ES7 decorators.
96
+ if (code < 65) return code === 36 || code === 64;
97
+ // 65 through 91 are uppercase letters.
98
+ if (code < 91) return true;
99
+ // permit _ (95).
100
+ if (code < 97) return code === 95;
101
+ // 97 through 123 are lowercase letters.
102
+ if (code < 123) return true;
103
+ return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
104
+ };
105
+
106
+ // Test whether a given character is part of an identifier.
107
+
108
+ exports.isIdentifierChar = function(code) {
109
+ if (code < 48) return code === 36;
110
+ if (code < 58) return true;
111
+ if (code < 65) return false;
112
+ if (code < 91) return true;
113
+ if (code < 97) return code === 95;
114
+ if (code < 123) return true;
115
+ return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
116
+ };
117
+ })(acorn);
118
+
119
+ function run (html_source, options) {
120
+ function ltrim(s) {
121
+ return s.replace(/^\s+/g, '');
122
+ }
123
+
124
+ function rtrim(s) {
125
+ return s.replace(/\s+$/g, '');
126
+ }
127
+
128
+ var multi_parser;
129
+ var indent_inner_html;
130
+ var indent_size;
131
+ var indent_character;
132
+ var wrap_line_length;
133
+ var brace_style;
134
+ var unformatted;
135
+ var preserve_newlines;
136
+ var max_preserve_newlines;
137
+ var indent_handlebars;
138
+ var wrap_attributes;
139
+ var wrap_attributes_indent_size;
140
+ var end_with_newline;
141
+ var extra_liners;
142
+
143
+ options = options || {};
144
+
145
+ // backwards compatibility to 1.3.4
146
+ if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
147
+ (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
148
+ options.wrap_line_length = options.max_char;
149
+ }
150
+
151
+ indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
152
+ indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
153
+ indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
154
+ brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
155
+ wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
156
+ unformatted = options.unformatted || ['a', 'span', 'img', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd',
157
+ 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike',
158
+ 'font', 'ins', 'del', 'address', 'pre'
159
+ ];
160
+ preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
161
+ max_preserve_newlines = preserve_newlines ?
162
+ (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10)) : 0;
163
+ indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
164
+ wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes;
165
+ wrap_attributes_indent_size = (options.wrap_attributes_indent_size === undefined) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10) || indent_size;
166
+ end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
167
+ extra_liners = Array.isArray(options.extra_liners) ?
168
+ options.extra_liners.concat() : (typeof options.extra_liners == 'string') ?
169
+ options.extra_liners.split(',') : 'head,body,/html'.split(',');
170
+
171
+ if (options.indent_with_tabs) {
172
+ indent_character = '\t';
173
+ indent_size = 1;
174
+ }
175
+
176
+ function Parser() {
177
+
178
+ this.pos = 0; //Parser position
179
+ this.token = '';
180
+ this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
181
+ this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
182
+ parent: 'parent1',
183
+ parentcount: 1,
184
+ parent1: ''
185
+ };
186
+ this.tag_type = '';
187
+ this.token_text = this.last_token = this.last_text = this.token_type = '';
188
+ this.newlines = 0;
189
+ this.indent_content = indent_inner_html;
190
+
191
+ this.Utils = { //Uilities made available to the various functions
192
+ whitespace: "\n\r\t ".split(''),
193
+ single_token: 'br,input,link,meta,source,!doctype,basefont,base,area,hr,wbr,param,img,isindex,embed'.split(','), //all the single tags for HTML
194
+ extra_liners: extra_liners, //for tags that need a line of whitespace before them
195
+ in_array: function(what, arr) {
196
+ for (var i = 0; i < arr.length; i++) {
197
+ if (what == arr[i]) {
198
+ return true;
199
+ }
200
+ }
201
+ return false;
202
+ }
203
+ };
204
+
205
+ // Return true if the given text is composed entirely of whitespace.
206
+ this.is_whitespace = function(text) {
207
+ for (var n = 0; n < text.length; text++) {
208
+ if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) {
209
+ return false;
210
+ }
211
+ }
212
+ return true;
213
+ };
214
+
215
+ this.traverse_whitespace = function() {
216
+ var input_char = '';
217
+
218
+ input_char = this.input.charAt(this.pos);
219
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
220
+ this.newlines = 0;
221
+ while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
222
+ if (preserve_newlines && input_char == '\n' && this.newlines <= max_preserve_newlines) {
223
+ this.newlines += 1;
224
+ }
225
+
226
+ this.pos++;
227
+ input_char = this.input.charAt(this.pos);
228
+ }
229
+ return true;
230
+ }
231
+ return false;
232
+ };
233
+
234
+ // Append a space to the given content (string array) or, if we are
235
+ // at the wrap_line_length, append a newline/indentation.
236
+ this.space_or_wrap = function(content) {
237
+ if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
238
+ this.print_newline(false, content);
239
+ this.print_indentation(content);
240
+ } else {
241
+ this.line_char_count++;
242
+ content.push(' ');
243
+ }
244
+ };
245
+
246
+ this.get_content = function() { //function to capture regular content between tags
247
+ var input_char = '',
248
+ content = [];
249
+
250
+ while (this.input.charAt(this.pos) != '<') {
251
+ if (this.pos >= this.input.length) {
252
+ return content.length ? content.join('') : ['', 'TK_EOF'];
253
+ }
254
+
255
+ if (this.traverse_whitespace()) {
256
+ this.space_or_wrap(content);
257
+ continue;
258
+ }
259
+
260
+ if (indent_handlebars) {
261
+ // Handlebars parsing is complicated.
262
+ // {{#foo}} and {{/foo}} are formatted tags.
263
+ // {{something}} should get treated as content, except:
264
+ // {{else}} specifically behaves like {{#if}} and {{/if}}
265
+ var peek3 = this.input.substr(this.pos, 3);
266
+ if (peek3 == '{{#' || peek3 == '{{/') {
267
+ // These are tags and not content.
268
+ break;
269
+ } else if (peek3 == '{{!') {
270
+ return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT'];
271
+ } else if (this.input.substr(this.pos, 2) == '{{') {
272
+ if (this.get_tag(true) == '{{else}}') {
273
+ break;
274
+ }
275
+ }
276
+ }
277
+
278
+ input_char = this.input.charAt(this.pos);
279
+ this.pos++;
280
+ this.line_char_count++;
281
+ content.push(input_char); //letter at-a-time (or string) inserted to an array
282
+ }
283
+ return content.length ? content.join('') : '';
284
+ };
285
+
286
+ this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
287
+ if (this.pos == this.input.length) {
288
+ return ['', 'TK_EOF'];
289
+ }
290
+ var content = '';
291
+ var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
292
+ reg_match.lastIndex = this.pos;
293
+ var reg_array = reg_match.exec(this.input);
294
+ var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
295
+ if (this.pos < end_script) { //get everything in between the script tags
296
+ content = this.input.substring(this.pos, end_script);
297
+ this.pos = end_script;
298
+ }
299
+ return content;
300
+ };
301
+
302
+ this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
303
+ if (this.tags[tag + 'count']) { //check for the existence of this tag type
304
+ this.tags[tag + 'count']++;
305
+ this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
306
+ } else { //otherwise initialize this tag type
307
+ this.tags[tag + 'count'] = 1;
308
+ this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
309
+ }
310
+ 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)
311
+ this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
312
+ };
313
+
314
+ this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
315
+ if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
316
+ var temp_parent = this.tags.parent; //check to see if it's a closable tag.
317
+ while (temp_parent) { //till we reach '' (the initial value);
318
+ if (tag + this.tags[tag + 'count'] == temp_parent) { //if this is it use it
319
+ break;
320
+ }
321
+ temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
322
+ }
323
+ if (temp_parent) { //if we caught something
324
+ this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
325
+ this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
326
+ }
327
+ delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
328
+ delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
329
+ if (this.tags[tag + 'count'] == 1) {
330
+ delete this.tags[tag + 'count'];
331
+ } else {
332
+ this.tags[tag + 'count']--;
333
+ }
334
+ }
335
+ };
336
+
337
+ this.indent_to_tag = function(tag) {
338
+ // Match the indentation level to the last use of this tag, but don't remove it.
339
+ if (!this.tags[tag + 'count']) {
340
+ return;
341
+ }
342
+ var temp_parent = this.tags.parent;
343
+ while (temp_parent) {
344
+ if (tag + this.tags[tag + 'count'] == temp_parent) {
345
+ break;
346
+ }
347
+ temp_parent = this.tags[temp_parent + 'parent'];
348
+ }
349
+ if (temp_parent) {
350
+ this.indent_level = this.tags[tag + this.tags[tag + 'count']];
351
+ }
352
+ };
353
+
354
+ this.get_tag = function(peek) { //function to get a full tag and parse its type
355
+ var input_char = '',
356
+ content = [],
357
+ comment = '',
358
+ space = false,
359
+ first_attr = true,
360
+ tag_start, tag_end,
361
+ tag_start_char,
362
+ orig_pos = this.pos,
363
+ orig_line_char_count = this.line_char_count;
364
+
365
+ peek = peek !== undefined ? peek : false;
366
+
367
+ do {
368
+ if (this.pos >= this.input.length) {
369
+ if (peek) {
370
+ this.pos = orig_pos;
371
+ this.line_char_count = orig_line_char_count;
372
+ }
373
+ return content.length ? content.join('') : ['', 'TK_EOF'];
374
+ }
375
+
376
+ input_char = this.input.charAt(this.pos);
377
+ this.pos++;
378
+
379
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
380
+ space = true;
381
+ continue;
382
+ }
383
+
384
+ if (input_char == "'" || input_char == '"') {
385
+ input_char += this.get_unformatted(input_char);
386
+ space = true;
387
+
388
+ }
389
+
390
+ if (input_char == '=') { //no space before =
391
+ space = false;
392
+ }
393
+
394
+ if (content.length && content[content.length - 1] != '=' && input_char != '>' && space) {
395
+ //no space after = or before >
396
+ this.space_or_wrap(content);
397
+ space = false;
398
+ if (!first_attr && wrap_attributes == 'force' && input_char != '/') {
399
+ this.print_newline(true, content);
400
+ this.print_indentation(content);
401
+ for (var count = 0; count < wrap_attributes_indent_size; count++) {
402
+ content.push(indent_character);
403
+ }
404
+ }
405
+ for (var i = 0; i < content.length; i++) {
406
+ if (content[i] == ' ') {
407
+ first_attr = false;
408
+ break;
409
+ }
410
+ }
411
+ }
412
+
413
+ if (indent_handlebars && tag_start_char == '<') {
414
+ // When inside an angle-bracket tag, put spaces around
415
+ // handlebars not inside of strings.
416
+ if ((input_char + this.input.charAt(this.pos)) == '{{') {
417
+ input_char += this.get_unformatted('}}');
418
+ if (content.length && content[content.length - 1] != ' ' && content[content.length - 1] != '<') {
419
+ input_char = ' ' + input_char;
420
+ }
421
+ space = true;
422
+ }
423
+ }
424
+
425
+ if (input_char == '<' && !tag_start_char) {
426
+ tag_start = this.pos - 1;
427
+ tag_start_char = '<';
428
+ }
429
+
430
+ if (indent_handlebars && !tag_start_char) {
431
+ if (content.length >= 2 && content[content.length - 1] == '{' && content[content.length - 2] == '{') {
432
+ if (input_char == '#' || input_char == '/' || input_char == '!') {
433
+ tag_start = this.pos - 3;
434
+ } else {
435
+ tag_start = this.pos - 2;
436
+ }
437
+ tag_start_char = '{';
438
+ }
439
+ }
440
+
441
+ this.line_char_count++;
442
+ content.push(input_char); //inserts character at-a-time (or string)
443
+
444
+ if (content[1] && (content[1] == '!' || content[1] == '?' || content[1] == '%')) { //if we're in a comment, do something special
445
+ // We treat all comments as literals, even more than preformatted tags
446
+ // we just look for the appropriate close tag
447
+ content = [this.get_comment(tag_start)];
448
+ break;
449
+ }
450
+
451
+ if (indent_handlebars && content[1] && content[1] == '{' && content[2] && content[2] == '!') { //if we're in a comment, do something special
452
+ // We treat all comments as literals, even more than preformatted tags
453
+ // we just look for the appropriate close tag
454
+ content = [this.get_comment(tag_start)];
455
+ break;
456
+ }
457
+
458
+ if (indent_handlebars && tag_start_char == '{' && content.length > 2 && content[content.length - 2] == '}' && content[content.length - 1] == '}') {
459
+ break;
460
+ }
461
+ } while (input_char != '>');
462
+
463
+ var tag_complete = content.join('');
464
+ var tag_index;
465
+ var tag_offset;
466
+
467
+ if (tag_complete.indexOf(' ') != -1) { //if there's whitespace, thats where the tag name ends
468
+ tag_index = tag_complete.indexOf(' ');
469
+ } else if (tag_complete[0] == '{') {
470
+ tag_index = tag_complete.indexOf('}');
471
+ } else { //otherwise go with the tag ending
472
+ tag_index = tag_complete.indexOf('>');
473
+ }
474
+ if (tag_complete[0] == '<' || !indent_handlebars) {
475
+ tag_offset = 1;
476
+ } else {
477
+ tag_offset = tag_complete[2] == '#' ? 3 : 2;
478
+ }
479
+ var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
480
+ if (tag_complete.charAt(tag_complete.length - 2) == '/' ||
481
+ 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 /)
482
+ if (!peek) {
483
+ this.tag_type = 'SINGLE';
484
+ }
485
+ } else if (indent_handlebars && tag_complete[0] == '{' && tag_check == 'else') {
486
+ if (!peek) {
487
+ this.indent_to_tag('if');
488
+ this.tag_type = 'HANDLEBARS_ELSE';
489
+ this.indent_content = true;
490
+ this.traverse_whitespace();
491
+ }
492
+ } else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
493
+ comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
494
+ content.push(comment);
495
+ tag_end = this.pos - 1;
496
+ this.tag_type = 'SINGLE';
497
+ } else if (tag_check == 'script' &&
498
+ (tag_complete.search('type') == -1 ||
499
+ (tag_complete.search('type') > -1 &&
500
+ tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript)/) > -1))) {
501
+ if (!peek) {
502
+ this.record_tag(tag_check);
503
+ this.tag_type = 'SCRIPT';
504
+ }
505
+ } else if (tag_check == 'style' &&
506
+ (tag_complete.search('type') == -1 ||
507
+ (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
508
+ if (!peek) {
509
+ this.record_tag(tag_check);
510
+ this.tag_type = 'STYLE';
511
+ }
512
+ } else if (tag_check.charAt(0) == '!') { //peek for <! comment
513
+ // for comments content is already correct.
514
+ if (!peek) {
515
+ this.tag_type = 'SINGLE';
516
+ this.traverse_whitespace();
517
+ }
518
+ } else if (!peek) {
519
+ if (tag_check.charAt(0) == '/') { //this tag is a double tag so check for tag-ending
520
+ this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
521
+ this.tag_type = 'END';
522
+ } else { //otherwise it's a start-tag
523
+ this.record_tag(tag_check); //push it on the tag stack
524
+ if (tag_check.toLowerCase() != 'html') {
525
+ this.indent_content = true;
526
+ }
527
+ this.tag_type = 'START';
528
+ }
529
+
530
+ // Allow preserving of newlines after a start or end tag
531
+ if (this.traverse_whitespace()) {
532
+ this.space_or_wrap(content);
533
+ }
534
+
535
+ if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
536
+ this.print_newline(false, this.output);
537
+ if (this.output.length && this.output[this.output.length - 2] != '\n') {
538
+ this.print_newline(true, this.output);
539
+ }
540
+ }
541
+ }
542
+
543
+ if (peek) {
544
+ this.pos = orig_pos;
545
+ this.line_char_count = orig_line_char_count;
546
+ }
547
+
548
+ return content.join(''); //returns fully formatted tag
549
+ };
550
+
551
+ this.get_comment = function(start_pos) { //function to return comment content in its entirety
552
+ // this is will have very poor perf, but will work for now.
553
+ var comment = '',
554
+ delimiter = '>',
555
+ matched = false;
556
+
557
+ this.pos = start_pos;
558
+ var input_char = this.input.charAt(this.pos);
559
+ this.pos++;
560
+
561
+ while (this.pos <= this.input.length) {
562
+ comment += input_char;
563
+
564
+ // only need to check for the delimiter if the last chars match
565
+ if (comment[comment.length - 1] == delimiter[delimiter.length - 1] &&
566
+ comment.indexOf(delimiter) != -1) {
567
+ break;
568
+ }
569
+
570
+ // only need to search for custom delimiter for the first few characters
571
+ if (!matched && comment.length < 10) {
572
+ if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
573
+ delimiter = '<![endif]>';
574
+ matched = true;
575
+ } else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
576
+ delimiter = ']]>';
577
+ matched = true;
578
+ } else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
579
+ delimiter = ']>';
580
+ matched = true;
581
+ } else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
582
+ delimiter = '-->';
583
+ matched = true;
584
+ } else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment
585
+ delimiter = '}}';
586
+ matched = true;
587
+ } else if (comment.indexOf('<?') === 0) { // {{! handlebars comment
588
+ delimiter = '?>';
589
+ matched = true;
590
+ } else if (comment.indexOf('<%') === 0) { // {{! handlebars comment
591
+ delimiter = '%>';
592
+ matched = true;
593
+ }
594
+ }
595
+
596
+ input_char = this.input.charAt(this.pos);
597
+ this.pos++;
598
+ }
599
+
600
+ return comment;
601
+ };
602
+
603
+ this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety
604
+
605
+ if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) != -1) {
606
+ return '';
607
+ }
608
+ var input_char = '';
609
+ var content = '';
610
+ var min_index = 0;
611
+ var space = true;
612
+ do {
613
+
614
+ if (this.pos >= this.input.length) {
615
+ return content;
616
+ }
617
+
618
+ input_char = this.input.charAt(this.pos);
619
+ this.pos++;
620
+
621
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
622
+ if (!space) {
623
+ this.line_char_count--;
624
+ continue;
625
+ }
626
+ if (input_char == '\n' || input_char == '\r') {
627
+ content += '\n';
628
+ /* 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'
629
+ for (var i=0; i<this.indent_level; i++) {
630
+ content += this.indent_string;
631
+ }
632
+ space = false; //...and make sure other indentation is erased
633
+ */
634
+ this.line_char_count = 0;
635
+ continue;
636
+ }
637
+ }
638
+ content += input_char;
639
+ this.line_char_count++;
640
+ space = true;
641
+
642
+ if (indent_handlebars && input_char == '{' && content.length && content[content.length - 2] == '{') {
643
+ // Handlebars expressions in strings should also be unformatted.
644
+ content += this.get_unformatted('}}');
645
+ // These expressions are opaque. Ignore delimiters found in them.
646
+ min_index = content.length;
647
+ }
648
+ } while (content.toLowerCase().indexOf(delimiter, min_index) == -1);
649
+ return content;
650
+ };
651
+
652
+ this.get_token = function() { //initial handler for token-retrieval
653
+ var token;
654
+
655
+ if (this.last_token == 'TK_TAG_SCRIPT' || this.last_token == 'TK_TAG_STYLE') { //check if we need to format javascript
656
+ var type = this.last_token.substr(7);
657
+ token = this.get_contents_to(type);
658
+ if (typeof token != 'string') {
659
+ return token;
660
+ }
661
+ return [token, 'TK_' + type];
662
+ }
663
+ if (this.current_mode == 'CONTENT') {
664
+ token = this.get_content();
665
+ if (typeof token != 'string') {
666
+ return token;
667
+ } else {
668
+ return [token, 'TK_CONTENT'];
669
+ }
670
+ }
671
+
672
+ if (this.current_mode == 'TAG') {
673
+ token = this.get_tag();
674
+ if (typeof token != 'string') {
675
+ return token;
676
+ } else {
677
+ var tag_name_type = 'TK_TAG_' + this.tag_type;
678
+ return [token, tag_name_type];
679
+ }
680
+ }
681
+ };
682
+
683
+ this.get_full_indent = function(level) {
684
+ level = this.indent_level + level || 0;
685
+ if (level < 1) {
686
+ return '';
687
+ }
688
+
689
+ return (new Array(level + 1)).join(this.indent_string);
690
+ };
691
+
692
+ this.is_unformatted = function(tag_check, unformatted) {
693
+ //is this an HTML5 block-level link?
694
+ if (!this.Utils.in_array(tag_check, unformatted)) {
695
+ return false;
696
+ }
697
+
698
+ if (tag_check.toLowerCase() != 'a' || !this.Utils.in_array('a', unformatted)) {
699
+ return true;
700
+ }
701
+
702
+ //at this point we have an tag; is its first child something we want to remain
703
+ //unformatted?
704
+ var next_tag = this.get_tag(true /* peek. */ );
705
+
706
+ // test next_tag to see if it is just html tag (no external content)
707
+ var tag = (next_tag || '').match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);
708
+
709
+ // if next_tag comes back but is not an isolated tag, then
710
+ // let's treat the 'a' tag as having content
711
+ // and respect the unformatted option
712
+ if (!tag || this.Utils.in_array(tag, unformatted)) {
713
+ return true;
714
+ } else {
715
+ return false;
716
+ }
717
+ };
718
+
719
+ this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
720
+
721
+ this.input = js_source || ''; //gets the input for the Parser
722
+ this.output = [];
723
+ this.indent_character = indent_character;
724
+ this.indent_string = '';
725
+ this.indent_size = indent_size;
726
+ this.brace_style = brace_style;
727
+ this.indent_level = 0;
728
+ this.wrap_line_length = wrap_line_length;
729
+ this.line_char_count = 0; //count to see if wrap_line_length was exceeded
730
+
731
+ for (var i = 0; i < this.indent_size; i++) {
732
+ this.indent_string += this.indent_character;
733
+ }
734
+
735
+ this.print_newline = function(force, arr) {
736
+ this.line_char_count = 0;
737
+ if (!arr || !arr.length) {
738
+ return;
739
+ }
740
+ if (force || (arr[arr.length - 1] != '\n')) { //we might want the extra line
741
+ if ((arr[arr.length - 1] != '\n')) {
742
+ arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
743
+ }
744
+ arr.push('\n');
745
+ }
746
+ };
747
+
748
+ this.print_indentation = function(arr) {
749
+ for (var i = 0; i < this.indent_level; i++) {
750
+ arr.push(this.indent_string);
751
+ this.line_char_count += this.indent_string.length;
752
+ }
753
+ };
754
+
755
+ this.print_token = function(text) {
756
+ // Avoid printing initial whitespace.
757
+ if (this.is_whitespace(text) && !this.output.length) {
758
+ return;
759
+ }
760
+ if (text || text !== '') {
761
+ if (this.output.length && this.output[this.output.length - 1] == '\n') {
762
+ this.print_indentation(this.output);
763
+ text = ltrim(text);
764
+ }
765
+ }
766
+ this.print_token_raw(text);
767
+ };
768
+
769
+ this.print_token_raw = function(text) {
770
+ // If we are going to print newlines, truncate trailing
771
+ // whitespace, as the newlines will represent the space.
772
+ if (this.newlines > 0) {
773
+ text = rtrim(text);
774
+ }
775
+
776
+ if (text && text !== '') {
777
+ if (text.length > 1 && text[text.length - 1] == '\n') {
778
+ // unformatted tags can grab newlines as their last character
779
+ this.output.push(text.slice(0, -1));
780
+ this.print_newline(false, this.output);
781
+ } else {
782
+ this.output.push(text);
783
+ }
784
+ }
785
+
786
+ for (var n = 0; n < this.newlines; n++) {
787
+ this.print_newline(n > 0, this.output);
788
+ }
789
+ this.newlines = 0;
790
+ };
791
+
792
+ this.indent = function() {
793
+ this.indent_level++;
794
+ };
795
+
796
+ this.unindent = function() {
797
+ if (this.indent_level > 0) {
798
+ this.indent_level--;
799
+ }
800
+ };
801
+ };
802
+ return this;
803
+ }
804
+
805
+ /*_____________________--------------------_____________________*/
806
+
807
+ multi_parser = new Parser(); //wrapping functions Parser
808
+ multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
809
+
810
+ while (true) {
811
+ var t = multi_parser.get_token();
812
+ multi_parser.token_text = t[0];
813
+ multi_parser.token_type = t[1];
814
+
815
+ if (multi_parser.token_type == 'TK_EOF') {
816
+ break;
817
+ }
818
+
819
+ switch (multi_parser.token_type) {
820
+ case 'TK_TAG_START':
821
+ multi_parser.print_newline(false, multi_parser.output);
822
+ multi_parser.print_token(multi_parser.token_text);
823
+ if (multi_parser.indent_content) {
824
+ multi_parser.indent();
825
+ multi_parser.indent_content = false;
826
+ }
827
+ multi_parser.current_mode = 'CONTENT';
828
+ break;
829
+ case 'TK_TAG_STYLE':
830
+ case 'TK_TAG_SCRIPT':
831
+ multi_parser.print_newline(false, multi_parser.output);
832
+ multi_parser.print_token(multi_parser.token_text);
833
+ multi_parser.current_mode = 'CONTENT';
834
+ break;
835
+ case 'TK_TAG_END':
836
+ // Print new line only if the tag has no content and has child
837
+ if (multi_parser.last_token == 'TK_CONTENT' && multi_parser.last_text === '') {
838
+ var tag_name = multi_parser.token_text.match(/\w+/)[0];
839
+ var tag_extracted_from_last_output = null;
840
+ if (multi_parser.output.length) {
841
+ tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
842
+ }
843
+ if (tag_extracted_from_last_output == null ||
844
+ (tag_extracted_from_last_output[1] != tag_name && !multi_parser.Utils.in_array(tag_extracted_from_last_output[1], unformatted))) {
845
+ multi_parser.print_newline(false, multi_parser.output);
846
+ }
847
+ }
848
+ multi_parser.print_token(multi_parser.token_text);
849
+ multi_parser.current_mode = 'CONTENT';
850
+ break;
851
+ case 'TK_TAG_SINGLE':
852
+ // Don't add a newline before elements that should remain unformatted.
853
+ var tag_check = multi_parser.token_text.match(/^\s*<([a-z-]+)/i);
854
+ if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
855
+ multi_parser.print_newline(false, multi_parser.output);
856
+ }
857
+ multi_parser.print_token(multi_parser.token_text);
858
+ multi_parser.current_mode = 'CONTENT';
859
+ break;
860
+ case 'TK_TAG_HANDLEBARS_ELSE':
861
+ multi_parser.print_token(multi_parser.token_text);
862
+ if (multi_parser.indent_content) {
863
+ multi_parser.indent();
864
+ multi_parser.indent_content = false;
865
+ }
866
+ multi_parser.current_mode = 'CONTENT';
867
+ break;
868
+ case 'TK_TAG_HANDLEBARS_COMMENT':
869
+ multi_parser.print_token(multi_parser.token_text);
870
+ multi_parser.current_mode = 'TAG';
871
+ break;
872
+ case 'TK_CONTENT':
873
+ multi_parser.print_token(multi_parser.token_text);
874
+ multi_parser.current_mode = 'TAG';
875
+ break;
876
+ case 'TK_STYLE':
877
+ case 'TK_SCRIPT':
878
+ if (multi_parser.token_text !== '') {
879
+ multi_parser.print_newline(false, multi_parser.output);
880
+ var text = multi_parser.token_text;
881
+ var _beautifier;
882
+ var script_indent_level = 1;
883
+
884
+ if (multi_parser.token_type == 'TK_SCRIPT') {
885
+ _beautifier = typeof js_beautify == 'function' && js_beautify;
886
+ } else if (multi_parser.token_type == 'TK_STYLE') {
887
+ _beautifier = typeof css_beautify == 'function' && css_beautify;
888
+ }
889
+
890
+ if (options.indent_scripts == 'keep') {
891
+ script_indent_level = 0;
892
+ } else if (options.indent_scripts == 'separate') {
893
+ script_indent_level = -multi_parser.indent_level;
894
+ }
895
+
896
+ var indentation = multi_parser.get_full_indent(script_indent_level);
897
+ if (_beautifier) {
898
+ // call the Beautifier if avaliable
899
+ text = _beautifier(text.replace(/^\s*/, indentation), options);
900
+ } else {
901
+ // simply indent the string otherwise
902
+ var white = text.match(/^\s*/)[0];
903
+ var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
904
+ var reindent = multi_parser.get_full_indent(script_indent_level - _level);
905
+
906
+ text = text.replace(/^\s*/, indentation)
907
+ .replace(/\r\n|\r|\n/g, '\n' + reindent)
908
+ .replace(/\s+$/, '');
909
+ }
910
+ if (text) {
911
+ multi_parser.print_token_raw(text);
912
+ multi_parser.print_newline(true, multi_parser.output);
913
+ }
914
+ }
915
+ multi_parser.current_mode = 'TAG';
916
+ break;
917
+ default:
918
+ // We should not be getting here but we don't want to drop input on the floor
919
+ // Just output the text and move on
920
+ if (multi_parser.token_text !== '') {
921
+ multi_parser.print_token(multi_parser.token_text);
922
+ }
923
+ break;
924
+ }
925
+ multi_parser.last_token = multi_parser.token_type;
926
+ multi_parser.last_text = multi_parser.token_text;
927
+ }
928
+ var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');
929
+ if (end_with_newline) {
930
+ sweet_code += '\n';
931
+ }
932
+ return sweet_code;
933
+ }
934
+
935
+ function css_beautify (source_text, options) {
936
+ var NESTED_AT_RULE = {
937
+ "@page": true,
938
+ "@font-face": true,
939
+ "@keyframes": true,
940
+ // also in CONDITIONAL_GROUP_RULE below
941
+ "@media": true,
942
+ "@supports": true,
943
+ "@document": true
944
+ };
945
+ var CONDITIONAL_GROUP_RULE = {
946
+ "@media": true,
947
+ "@supports": true,
948
+ "@document": true
949
+ };
950
+
951
+ options = options || {};
952
+ source_text = source_text || '';
953
+ // HACK: newline parsing inconsistent. This brute force normalizes the input.
954
+ source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n')
955
+
956
+ var indentSize = options.indent_size || 4;
957
+ var indentCharacter = options.indent_char || ' ';
958
+ var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
959
+ var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
960
+ var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
961
+ var eol = options.eol ? options.eol : '\n';
962
+
963
+ // compatibility
964
+ if (typeof indentSize === "string") {
965
+ indentSize = parseInt(indentSize, 10);
966
+ }
967
+
968
+ if(options.indent_with_tabs){
969
+ indentCharacter = '\t';
970
+ indentSize = 1;
971
+ }
972
+
973
+ eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
974
+
975
+
976
+ // tokenizer
977
+ var whiteRe = /^\s+$/;
978
+ var wordRe = /[\w$\-_]/;
979
+
980
+ var pos = -1,
981
+ ch;
982
+ var parenLevel = 0;
983
+
984
+ function next() {
985
+ ch = source_text.charAt(++pos);
986
+ return ch || '';
987
+ }
988
+
989
+ function peek(skipWhitespace) {
990
+ var result = '';
991
+ var prev_pos = pos;
992
+ if (skipWhitespace) {
993
+ eatWhitespace();
994
+ }
995
+ result = source_text.charAt(pos + 1) || '';
996
+ pos = prev_pos - 1;
997
+ next();
998
+ return result;
999
+ }
1000
+
1001
+ function eatString(endChars) {
1002
+ var start = pos;
1003
+ while (next()) {
1004
+ if (ch === "\\") {
1005
+ next();
1006
+ } else if (endChars.indexOf(ch) !== -1) {
1007
+ break;
1008
+ } else if (ch === "\n") {
1009
+ break;
1010
+ }
1011
+ }
1012
+ return source_text.substring(start, pos + 1);
1013
+ }
1014
+
1015
+ function peekString(endChar) {
1016
+ var prev_pos = pos;
1017
+ var str = eatString(endChar);
1018
+ pos = prev_pos - 1;
1019
+ next();
1020
+ return str;
1021
+ }
1022
+
1023
+ function eatWhitespace() {
1024
+ var result = '';
1025
+ while (whiteRe.test(peek())) {
1026
+ next();
1027
+ result += ch;
1028
+ }
1029
+ return result;
1030
+ }
1031
+
1032
+ function skipWhitespace() {
1033
+ var result = '';
1034
+ if (ch && whiteRe.test(ch)) {
1035
+ result = ch;
1036
+ }
1037
+ while (whiteRe.test(next())) {
1038
+ result += ch;
1039
+ }
1040
+ return result;
1041
+ }
1042
+
1043
+ function eatComment(singleLine) {
1044
+ var start = pos;
1045
+ singleLine = peek() === "/";
1046
+ next();
1047
+ while (next()) {
1048
+ if (!singleLine && ch === "*" && peek() === "/") {
1049
+ next();
1050
+ break;
1051
+ } else if (singleLine && ch === "\n") {
1052
+ return source_text.substring(start, pos);
1053
+ }
1054
+ }
1055
+
1056
+ return source_text.substring(start, pos) + ch;
1057
+ }
1058
+
1059
+
1060
+ function lookBack(str) {
1061
+ return source_text.substring(pos - str.length, pos).toLowerCase() ===
1062
+ str;
1063
+ }
1064
+
1065
+ // Nested pseudo-class if we are insideRule
1066
+ // and the next special character found opens
1067
+ // a new block
1068
+ function foundNestedPseudoClass() {
1069
+ var openParen = 0;
1070
+ for (var i = pos + 1; i < source_text.length; i++) {
1071
+ var ch = source_text.charAt(i);
1072
+ if (ch === "{") {
1073
+ return true;
1074
+ } else if (ch === '(') {
1075
+ // pseudoclasses can contain ()
1076
+ openParen += 1;
1077
+ } else if (ch === ')') {
1078
+ if (openParen == 0) {
1079
+ return false;
1080
+ }
1081
+ openParen -= 1;
1082
+ } else if (ch === ";" || ch === "}") {
1083
+ return false;
1084
+ }
1085
+ }
1086
+ return false;
1087
+ }
1088
+
1089
+ // printer
1090
+ var basebaseIndentString = source_text.match(/^[\t ]*/)[0];
1091
+ var singleIndent = new Array(indentSize + 1).join(indentCharacter);
1092
+ var indentLevel = 0;
1093
+ var nestedLevel = 0;
1094
+
1095
+ function indent() {
1096
+ indentLevel++;
1097
+ basebaseIndentString += singleIndent;
1098
+ }
1099
+
1100
+ function outdent() {
1101
+ indentLevel--;
1102
+ basebaseIndentString = basebaseIndentString.slice(0, -indentSize);
1103
+ }
1104
+
1105
+ var print = {};
1106
+ print["{"] = function(ch) {
1107
+ print.singleSpace();
1108
+ output.push(ch);
1109
+ print.newLine();
1110
+ };
1111
+ print["}"] = function(ch) {
1112
+ print.newLine();
1113
+ output.push(ch);
1114
+ print.newLine();
1115
+ };
1116
+
1117
+ print._lastCharWhitespace = function() {
1118
+ return whiteRe.test(output[output.length - 1]);
1119
+ };
1120
+
1121
+ print.newLine = function(keepWhitespace) {
1122
+ if (output.length) {
1123
+ if (!keepWhitespace && output[output.length - 1] !== '\n') {
1124
+ print.trim();
1125
+ }
1126
+
1127
+ output.push('\n');
1128
+
1129
+ if (basebaseIndentString) {
1130
+ output.push(basebaseIndentString);
1131
+ }
1132
+ }
1133
+ };
1134
+ print.singleSpace = function() {
1135
+ if (output.length && !print._lastCharWhitespace()) {
1136
+ output.push(' ');
1137
+ }
1138
+ };
1139
+
1140
+ print.preserveSingleSpace = function() {
1141
+ if (isAfterSpace) {
1142
+ print.singleSpace();
1143
+ }
1144
+ };
1145
+
1146
+ print.trim = function() {
1147
+ while (print._lastCharWhitespace()) {
1148
+ output.pop();
1149
+ }
1150
+ };
1151
+
1152
+
1153
+ var output = [];
1154
+ /*_____________________--------------------_____________________*/
1155
+
1156
+ var insideRule = false;
1157
+ var insidePropertyValue = false;
1158
+ var enteringConditionalGroup = false;
1159
+ var top_ch = '';
1160
+ var last_top_ch = '';
1161
+
1162
+ while (true) {
1163
+ var whitespace = skipWhitespace();
1164
+ var isAfterSpace = whitespace !== '';
1165
+ var isAfterNewline = whitespace.indexOf('\n') !== -1;
1166
+ last_top_ch = top_ch;
1167
+ top_ch = ch;
1168
+
1169
+ if (!ch) {
1170
+ break;
1171
+ } else if (ch === '/' && peek() === '*') { /* css comment */
1172
+ var header = indentLevel === 0;
1173
+
1174
+ if (isAfterNewline || header) {
1175
+ print.newLine();
1176
+ }
1177
+
1178
+ output.push(eatComment());
1179
+ print.newLine();
1180
+ if (header) {
1181
+ print.newLine(true);
1182
+ }
1183
+ } else if (ch === '/' && peek() === '/') { // single line comment
1184
+ if (!isAfterNewline && last_top_ch !== '{' ) {
1185
+ print.trim();
1186
+ }
1187
+ print.singleSpace();
1188
+ output.push(eatComment());
1189
+ print.newLine();
1190
+ } else if (ch === '@') {
1191
+ print.preserveSingleSpace();
1192
+ output.push(ch);
1193
+
1194
+ // strip trailing space, if present, for hash property checks
1195
+ var variableOrRule = peekString(": ,;{}()[]/='\"");
1196
+
1197
+ if (variableOrRule.match(/[ :]$/)) {
1198
+ // we have a variable or pseudo-class, add it and insert one space before continuing
1199
+ next();
1200
+ variableOrRule = eatString(": ").replace(/\s$/, '');
1201
+ output.push(variableOrRule);
1202
+ print.singleSpace();
1203
+ }
1204
+
1205
+ variableOrRule = variableOrRule.replace(/\s$/, '')
1206
+
1207
+ // might be a nesting at-rule
1208
+ if (variableOrRule in NESTED_AT_RULE) {
1209
+ nestedLevel += 1;
1210
+ if (variableOrRule in CONDITIONAL_GROUP_RULE) {
1211
+ enteringConditionalGroup = true;
1212
+ }
1213
+ }
1214
+ } else if (ch === '#' && peek() === '{') {
1215
+ print.preserveSingleSpace();
1216
+ output.push(eatString('}'));
1217
+ } else if (ch === '{') {
1218
+ if (peek(true) === '}') {
1219
+ eatWhitespace();
1220
+ next();
1221
+ print.singleSpace();
1222
+ output.push("{}");
1223
+ print.newLine();
1224
+ if (newline_between_rules && indentLevel === 0) {
1225
+ print.newLine(true);
1226
+ }
1227
+ } else {
1228
+ indent();
1229
+ print["{"](ch);
1230
+ // when entering conditional groups, only rulesets are allowed
1231
+ if (enteringConditionalGroup) {
1232
+ enteringConditionalGroup = false;
1233
+ insideRule = (indentLevel > nestedLevel);
1234
+ } else {
1235
+ // otherwise, declarations are also allowed
1236
+ insideRule = (indentLevel >= nestedLevel);
1237
+ }
1238
+ }
1239
+ } else if (ch === '}') {
1240
+ outdent();
1241
+ print["}"](ch);
1242
+ insideRule = false;
1243
+ insidePropertyValue = false;
1244
+ if (nestedLevel) {
1245
+ nestedLevel--;
1246
+ }
1247
+ if (newline_between_rules && indentLevel === 0) {
1248
+ print.newLine(true);
1249
+ }
1250
+ } else if (ch === ":") {
1251
+ eatWhitespace();
1252
+ if ((insideRule || enteringConditionalGroup) &&
1253
+ !(lookBack("&") || foundNestedPseudoClass())) {
1254
+ // 'property: value' delimiter
1255
+ // which could be in a conditional group query
1256
+ insidePropertyValue = true;
1257
+ output.push(':');
1258
+ print.singleSpace();
1259
+ } else {
1260
+ // sass/less parent reference don't use a space
1261
+ // sass nested pseudo-class don't use a space
1262
+ if (peek() === ":") {
1263
+ // pseudo-element
1264
+ next();
1265
+ output.push("::");
1266
+ } else {
1267
+ // pseudo-class
1268
+ output.push(':');
1269
+ }
1270
+ }
1271
+ } else if (ch === '"' || ch === '\'') {
1272
+ print.preserveSingleSpace();
1273
+ output.push(eatString(ch));
1274
+ } else if (ch === ';') {
1275
+ insidePropertyValue = false;
1276
+ output.push(ch);
1277
+ print.newLine();
1278
+ } else if (ch === '(') { // may be a url
1279
+ if (lookBack("url")) {
1280
+ output.push(ch);
1281
+ eatWhitespace();
1282
+ if (next()) {
1283
+ if (ch !== ')' && ch !== '"' && ch !== '\'') {
1284
+ output.push(eatString(')'));
1285
+ } else {
1286
+ pos--;
1287
+ }
1288
+ }
1289
+ } else {
1290
+ parenLevel++;
1291
+ print.preserveSingleSpace();
1292
+ output.push(ch);
1293
+ eatWhitespace();
1294
+ }
1295
+ } else if (ch === ')') {
1296
+ output.push(ch);
1297
+ parenLevel--;
1298
+ } else if (ch === ',') {
1299
+ output.push(ch);
1300
+ eatWhitespace();
1301
+ if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
1302
+ print.newLine();
1303
+ } else {
1304
+ print.singleSpace();
1305
+ }
1306
+ } else if (ch === ']') {
1307
+ output.push(ch);
1308
+ } else if (ch === '[') {
1309
+ print.preserveSingleSpace();
1310
+ output.push(ch);
1311
+ } else if (ch === '=') { // no whitespace before or after
1312
+ eatWhitespace()
1313
+ ch = '=';
1314
+ output.push(ch);
1315
+ } else {
1316
+ print.preserveSingleSpace();
1317
+ output.push(ch);
1318
+ }
1319
+ }
1320
+
1321
+
1322
+ var sweetCode = '';
1323
+ if (basebaseIndentString) {
1324
+ sweetCode += basebaseIndentString;
1325
+ }
1326
+
1327
+ sweetCode += output.join('').replace(/[\r\n\t ]+$/, '');
1328
+
1329
+ // establish end_with_newline
1330
+ if (end_with_newline) {
1331
+ sweetCode += '\n';
1332
+ }
1333
+
1334
+ if (eol != '\n') {
1335
+ sweetCode = sweetCode.replace(/[\n]/g, eol);
1336
+ }
1337
+
1338
+ return sweetCode;
1339
+ }
1340
+
1341
+ function in_array(what, arr) {
1342
+ for (var i = 0; i < arr.length; i += 1) {
1343
+ if (arr[i] === what) {
1344
+ return true;
1345
+ }
1346
+ }
1347
+ return false;
1348
+ }
1349
+
1350
+ function trim(s) {
1351
+ return s.replace(/^\s+|\s+$/g, '');
1352
+ }
1353
+
1354
+ function ltrim(s) {
1355
+ return s.replace(/^\s+/g, '');
1356
+ }
1357
+
1358
+ function rtrim(s) {
1359
+ return s.replace(/\s+$/g, '');
1360
+ }
1361
+
1362
+ function js_beautify(js_source_text, options) {
1363
+
1364
+ var beautifier = new Beautifier(js_source_text, options);
1365
+ return beautifier.beautify();
1366
+ }
1367
+
1368
+ var MODE = {
1369
+ BlockStatement: 'BlockStatement', // 'BLOCK'
1370
+ Statement: 'Statement', // 'STATEMENT'
1371
+ ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
1372
+ ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
1373
+ ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
1374
+ Conditional: 'Conditional', //'(COND-EXPRESSION)',
1375
+ Expression: 'Expression' //'(EXPRESSION)'
1376
+ };
1377
+
1378
+ function Beautifier(js_source_text, options) {
1379
+
1380
+ var output
1381
+ var tokens = [], token_pos;
1382
+ var Tokenizer;
1383
+ var current_token;
1384
+ var last_type, last_last_text, indent_string;
1385
+ var flags, previous_flags, flag_store;
1386
+ var prefix;
1387
+
1388
+ var handlers, opt;
1389
+ var baseIndentString = '';
1390
+
1391
+ handlers = {
1392
+ 'TK_START_EXPR': handle_start_expr,
1393
+ 'TK_END_EXPR': handle_end_expr,
1394
+ 'TK_START_BLOCK': handle_start_block,
1395
+ 'TK_END_BLOCK': handle_end_block,
1396
+ 'TK_WORD': handle_word,
1397
+ 'TK_RESERVED': handle_word,
1398
+ 'TK_SEMICOLON': handle_semicolon,
1399
+ 'TK_STRING': handle_string,
1400
+ 'TK_EQUALS': handle_equals,
1401
+ 'TK_OPERATOR': handle_operator,
1402
+ 'TK_COMMA': handle_comma,
1403
+ 'TK_BLOCK_COMMENT': handle_block_comment,
1404
+ 'TK_COMMENT': handle_comment,
1405
+ 'TK_DOT': handle_dot,
1406
+ 'TK_UNKNOWN': handle_unknown,
1407
+ 'TK_EOF': handle_eof
1408
+ };
1409
+
1410
+ function create_flags(flags_base, mode) {
1411
+ var next_indent_level = 0;
1412
+ if (flags_base) {
1413
+ next_indent_level = flags_base.indentation_level;
1414
+ if (!output.just_added_newline() &&
1415
+ flags_base.line_indent_level > next_indent_level) {
1416
+ next_indent_level = flags_base.line_indent_level;
1417
+ }
1418
+ }
1419
+
1420
+ var next_flags = {
1421
+ mode: mode,
1422
+ parent: flags_base,
1423
+ last_text: flags_base ? flags_base.last_text : '', // last token text
1424
+ last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
1425
+ declaration_statement: false,
1426
+ declaration_assignment: false,
1427
+ multiline_frame: false,
1428
+ if_block: false,
1429
+ else_block: false,
1430
+ do_block: false,
1431
+ do_while: false,
1432
+ in_case_statement: false, // switch(..){ INSIDE HERE }
1433
+ in_case: false, // we're on the exact line with "case 0:"
1434
+ case_body: false, // the indented case-action block
1435
+ indentation_level: next_indent_level,
1436
+ line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
1437
+ start_line_index: output.get_line_number(),
1438
+ ternary_depth: 0
1439
+ };
1440
+ return next_flags;
1441
+ }
1442
+
1443
+ // Some interpreters have unexpected results with foo = baz || bar;
1444
+ options = options ? options : {};
1445
+ opt = {};
1446
+
1447
+ // compatibility
1448
+ if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
1449
+ opt.brace_style = options.braces_on_own_line ? "expand" : "collapse";
1450
+ }
1451
+ opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse");
1452
+
1453
+ // graceful handling of deprecated option
1454
+ if (opt.brace_style === "expand-strict") {
1455
+ opt.brace_style = "expand";
1456
+ }
1457
+
1458
+
1459
+ opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
1460
+ opt.indent_char = options.indent_char ? options.indent_char : ' ';
1461
+ opt.eol = options.eol ? options.eol : '\n';
1462
+ opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
1463
+ opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
1464
+ opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
1465
+ opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
1466
+ opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
1467
+ opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
1468
+ opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
1469
+ opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
1470
+ opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
1471
+ opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
1472
+ opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
1473
+ opt.e4x = (options.e4x === undefined) ? false : options.e4x;
1474
+ opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
1475
+ opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
1476
+
1477
+ // For testing of beautify ignore:start directive
1478
+ opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;
1479
+
1480
+ // force opt.space_after_anon_function to true if opt.jslint_happy
1481
+ if(opt.jslint_happy) {
1482
+ opt.space_after_anon_function = true;
1483
+ }
1484
+
1485
+ if(options.indent_with_tabs){
1486
+ opt.indent_char = '\t';
1487
+ opt.indent_size = 1;
1488
+ }
1489
+
1490
+ opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
1491
+
1492
+ //----------------------------------
1493
+ indent_string = '';
1494
+ while (opt.indent_size > 0) {
1495
+ indent_string += opt.indent_char;
1496
+ opt.indent_size -= 1;
1497
+ }
1498
+
1499
+ var preindent_index = 0;
1500
+ if(js_source_text && js_source_text.length) {
1501
+ while ( (js_source_text.charAt(preindent_index) === ' ' ||
1502
+ js_source_text.charAt(preindent_index) === '\t')) {
1503
+ baseIndentString += js_source_text.charAt(preindent_index);
1504
+ preindent_index += 1;
1505
+ }
1506
+ js_source_text = js_source_text.substring(preindent_index);
1507
+ }
1508
+
1509
+ last_type = 'TK_START_BLOCK'; // last token type
1510
+ last_last_text = ''; // pre-last token text
1511
+ output = new Output(indent_string, baseIndentString);
1512
+
1513
+ // If testing the ignore directive, start with output disable set to true
1514
+ output.raw = opt.test_output_raw;
1515
+
1516
+
1517
+ // Stack of parsing/formatting states, including MODE.
1518
+ // We tokenize, parse, and output in an almost purely a forward-only stream of token input
1519
+ // and formatted output. This makes the beautifier less accurate than full parsers
1520
+ // but also far more tolerant of syntax errors.
1521
+ //
1522
+ // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
1523
+ // MODE.BlockStatement on the the stack, even though it could be object literal. If we later
1524
+ // encounter a ":", we'll switch to to MODE.ObjectLiteral. If we then see a ";",
1525
+ // most full parsers would die, but the beautifier gracefully falls back to
1526
+ // MODE.BlockStatement and continues on.
1527
+ flag_store = [];
1528
+ set_mode(MODE.BlockStatement);
1529
+
1530
+ this.beautify = function() {
1531
+
1532
+ /*jshint onevar:true */
1533
+ var local_token, sweet_code;
1534
+ Tokenizer = new tokenizer(js_source_text, opt, indent_string);
1535
+ tokens = Tokenizer.tokenize();
1536
+ token_pos = 0;
1537
+
1538
+ while (local_token = get_token()) {
1539
+ for(var i = 0; i < local_token.comments_before.length; i++) {
1540
+ // The cleanest handling of inline comments is to treat them as though they aren't there.
1541
+ // Just continue formatting and the behavior should be logical.
1542
+ // Also ignore unknown tokens. Again, this should result in better behavior.
1543
+ handle_token(local_token.comments_before[i]);
1544
+ }
1545
+ handle_token(local_token);
1546
+
1547
+ last_last_text = flags.last_text;
1548
+ last_type = local_token.type;
1549
+ flags.last_text = local_token.text;
1550
+
1551
+ token_pos += 1;
1552
+ }
1553
+
1554
+ sweet_code = output.get_code();
1555
+ if (opt.end_with_newline) {
1556
+ sweet_code += '\n';
1557
+ }
1558
+
1559
+ if (opt.eol != '\n') {
1560
+ sweet_code = sweet_code.replace(/[\n]/g, opt.eol);
1561
+ }
1562
+
1563
+ return sweet_code;
1564
+ };
1565
+
1566
+ function handle_token(local_token) {
1567
+ var newlines = local_token.newlines;
1568
+ var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
1569
+
1570
+ if (keep_whitespace) {
1571
+ for (i = 0; i < newlines; i += 1) {
1572
+ print_newline(i > 0);
1573
+ }
1574
+ } else {
1575
+ if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
1576
+ newlines = opt.max_preserve_newlines;
1577
+ }
1578
+
1579
+ if (opt.preserve_newlines) {
1580
+ if (local_token.newlines > 1) {
1581
+ print_newline();
1582
+ for (var i = 1; i < newlines; i += 1) {
1583
+ print_newline(true);
1584
+ }
1585
+ }
1586
+ }
1587
+ }
1588
+
1589
+ current_token = local_token;
1590
+ handlers[current_token.type]();
1591
+ }
1592
+
1593
+ // we could use just string.split, but
1594
+ // IE doesn't like returning empty strings
1595
+ function split_newlines(s) {
1596
+ //return s.split(/\x0d\x0a|\x0a/);
1597
+
1598
+ s = s.replace(/\x0d/g, '');
1599
+ var out = [],
1600
+ idx = s.indexOf("\n");
1601
+ while (idx !== -1) {
1602
+ out.push(s.substring(0, idx));
1603
+ s = s.substring(idx + 1);
1604
+ idx = s.indexOf("\n");
1605
+ }
1606
+ if (s.length) {
1607
+ out.push(s);
1608
+ }
1609
+ return out;
1610
+ }
1611
+
1612
+ function allow_wrap_or_preserved_newline(force_linewrap) {
1613
+ force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
1614
+
1615
+ // Never wrap the first token on a line
1616
+ if (output.just_added_newline()) {
1617
+ return
1618
+ }
1619
+
1620
+ if ((opt.preserve_newlines && current_token.wanted_newline) || force_linewrap) {
1621
+ print_newline(false, true);
1622
+ } else if (opt.wrap_line_length) {
1623
+ var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
1624
+ (output.space_before_token ? 1 : 0);
1625
+ if (proposed_line_length >= opt.wrap_line_length) {
1626
+ print_newline(false, true);
1627
+ }
1628
+ }
1629
+ }
1630
+
1631
+ function print_newline(force_newline, preserve_statement_flags) {
1632
+ if (!preserve_statement_flags) {
1633
+ if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
1634
+ while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
1635
+ restore_mode();
1636
+ }
1637
+ }
1638
+ }
1639
+
1640
+ if (output.add_new_line(force_newline)) {
1641
+ flags.multiline_frame = true;
1642
+ }
1643
+ }
1644
+
1645
+ function print_token_line_indentation() {
1646
+ if (output.just_added_newline()) {
1647
+ if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
1648
+ output.current_line.push(current_token.whitespace_before);
1649
+ output.space_before_token = false;
1650
+ } else if (output.set_indent(flags.indentation_level)) {
1651
+ flags.line_indent_level = flags.indentation_level;
1652
+ }
1653
+ }
1654
+ }
1655
+
1656
+ function print_token(printable_token) {
1657
+ if (output.raw) {
1658
+ output.add_raw_token(current_token)
1659
+ return;
1660
+ }
1661
+
1662
+ if (opt.comma_first && last_type === 'TK_COMMA'
1663
+ && output.just_added_newline()) {
1664
+ if(output.previous_line.last() === ',') {
1665
+ output.previous_line.pop();
1666
+ print_token_line_indentation();
1667
+ output.add_token(',');
1668
+ output.space_before_token = true;
1669
+ }
1670
+ }
1671
+
1672
+ printable_token = printable_token || current_token.text;
1673
+ print_token_line_indentation();
1674
+ output.add_token(printable_token);
1675
+ }
1676
+
1677
+ function indent() {
1678
+ flags.indentation_level += 1;
1679
+ }
1680
+
1681
+ function deindent() {
1682
+ if (flags.indentation_level > 0 &&
1683
+ ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level))
1684
+ flags.indentation_level -= 1;
1685
+ }
1686
+
1687
+ function set_mode(mode) {
1688
+ if (flags) {
1689
+ flag_store.push(flags);
1690
+ previous_flags = flags;
1691
+ } else {
1692
+ previous_flags = create_flags(null, mode);
1693
+ }
1694
+
1695
+ flags = create_flags(previous_flags, mode);
1696
+ }
1697
+
1698
+ function is_array(mode) {
1699
+ return mode === MODE.ArrayLiteral;
1700
+ }
1701
+
1702
+ function is_expression(mode) {
1703
+ return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
1704
+ }
1705
+
1706
+ function restore_mode() {
1707
+ if (flag_store.length > 0) {
1708
+ previous_flags = flags;
1709
+ flags = flag_store.pop();
1710
+ if (previous_flags.mode === MODE.Statement) {
1711
+ output.remove_redundant_indentation(previous_flags);
1712
+ }
1713
+ }
1714
+ }
1715
+
1716
+ function start_of_object_property() {
1717
+ return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
1718
+ (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
1719
+ }
1720
+
1721
+ function start_of_statement() {
1722
+ if (
1723
+ (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
1724
+ (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
1725
+ (last_type === 'TK_RESERVED' && flags.last_text === 'return' && !current_token.wanted_newline) ||
1726
+ (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(current_token.type === 'TK_RESERVED' && current_token.text === 'if')) ||
1727
+ (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
1728
+ (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement
1729
+ && !flags.in_case
1730
+ && !(current_token.text === '--' || current_token.text === '++')
1731
+ && last_last_text !== 'function'
1732
+ && current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
1733
+ (flags.mode === MODE.ObjectLiteral && (
1734
+ (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
1735
+ ) {
1736
+
1737
+ set_mode(MODE.Statement);
1738
+ indent();
1739
+
1740
+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
1741
+ flags.declaration_statement = true;
1742
+ }
1743
+
1744
+ // Issue #276:
1745
+ // If starting a new statement with [if, for, while, do], push to a new line.
1746
+ // if (a) if (b) if(c) d(); else e(); else f();
1747
+ if (!start_of_object_property()) {
1748
+ allow_wrap_or_preserved_newline(
1749
+ current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
1750
+ }
1751
+
1752
+ return true;
1753
+ }
1754
+ return false;
1755
+ }
1756
+
1757
+ function all_lines_start_with(lines, c) {
1758
+ for (var i = 0; i < lines.length; i++) {
1759
+ var line = trim(lines[i]);
1760
+ if (line.charAt(0) !== c) {
1761
+ return false;
1762
+ }
1763
+ }
1764
+ return true;
1765
+ }
1766
+
1767
+ function each_line_matches_indent(lines, indent) {
1768
+ var i = 0,
1769
+ len = lines.length,
1770
+ line;
1771
+ for (; i < len; i++) {
1772
+ line = lines[i];
1773
+ // allow empty lines to pass through
1774
+ if (line && line.indexOf(indent) !== 0) {
1775
+ return false;
1776
+ }
1777
+ }
1778
+ return true;
1779
+ }
1780
+
1781
+ function is_special_word(word) {
1782
+ return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
1783
+ }
1784
+
1785
+ function get_token(offset) {
1786
+ var index = token_pos + (offset || 0);
1787
+ return (index < 0 || index >= tokens.length) ? null : tokens[index];
1788
+ }
1789
+
1790
+ function handle_start_expr() {
1791
+ if (start_of_statement()) {
1792
+ // The conditional starts the statement if appropriate.
1793
+ }
1794
+
1795
+ var next_mode = MODE.Expression;
1796
+ if (current_token.text === '[') {
1797
+
1798
+ if (last_type === 'TK_WORD' || flags.last_text === ')') {
1799
+ // this is array index specifier, break immediately
1800
+ // a[x], fn()[x]
1801
+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) {
1802
+ output.space_before_token = true;
1803
+ }
1804
+ set_mode(next_mode);
1805
+ print_token();
1806
+ indent();
1807
+ if (opt.space_in_paren) {
1808
+ output.space_before_token = true;
1809
+ }
1810
+ return;
1811
+ }
1812
+
1813
+ next_mode = MODE.ArrayLiteral;
1814
+ if (is_array(flags.mode)) {
1815
+ if (flags.last_text === '[' ||
1816
+ (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
1817
+ // ], [ goes to new line
1818
+ // }, [ goes to new line
1819
+ if (!opt.keep_array_indentation) {
1820
+ print_newline();
1821
+ }
1822
+ }
1823
+ }
1824
+
1825
+ } else {
1826
+ if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
1827
+ next_mode = MODE.ForInitializer;
1828
+ } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
1829
+ next_mode = MODE.Conditional;
1830
+ } else {
1831
+ // next_mode = MODE.Expression;
1832
+ }
1833
+ }
1834
+
1835
+ if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
1836
+ print_newline();
1837
+ } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
1838
+ // TODO: Consider whether forcing this is required. Review failing tests when removed.
1839
+ allow_wrap_or_preserved_newline(current_token.wanted_newline);
1840
+ // do nothing on (( and )( and ][ and ]( and .(
1841
+ } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
1842
+ output.space_before_token = true;
1843
+ } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
1844
+ (flags.last_text === '*' && last_last_text === 'function')) {
1845
+ // function() vs function ()
1846
+ if (opt.space_after_anon_function) {
1847
+ output.space_before_token = true;
1848
+ }
1849
+ } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) {
1850
+ if (opt.space_before_conditional) {
1851
+ output.space_before_token = true;
1852
+ }
1853
+ }
1854
+
1855
+ // Should be a space between await and an IIFE
1856
+ if(current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await'){
1857
+ output.space_before_token = true;
1858
+ }
1859
+
1860
+ // Support of this kind of newline preservation.
1861
+ // a = (b &&
1862
+ // (c || d));
1863
+ if (current_token.text === '(') {
1864
+ if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
1865
+ if (!start_of_object_property()) {
1866
+ allow_wrap_or_preserved_newline();
1867
+ }
1868
+ }
1869
+ }
1870
+
1871
+ set_mode(next_mode);
1872
+ print_token();
1873
+ if (opt.space_in_paren) {
1874
+ output.space_before_token = true;
1875
+ }
1876
+
1877
+ // In all cases, if we newline while inside an expression it should be indented.
1878
+ indent();
1879
+ }
1880
+
1881
+ function handle_end_expr() {
1882
+ // statements inside expressions are not valid syntax, but...
1883
+ // statements must all be closed when their container closes
1884
+ while (flags.mode === MODE.Statement) {
1885
+ restore_mode();
1886
+ }
1887
+
1888
+ if (flags.multiline_frame) {
1889
+ allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
1890
+ }
1891
+
1892
+ if (opt.space_in_paren) {
1893
+ if (last_type === 'TK_START_EXPR' && ! opt.space_in_empty_paren) {
1894
+ // () [] no inner space in empty parens like these, ever, ref #320
1895
+ output.trim();
1896
+ output.space_before_token = false;
1897
+ } else {
1898
+ output.space_before_token = true;
1899
+ }
1900
+ }
1901
+ if (current_token.text === ']' && opt.keep_array_indentation) {
1902
+ print_token();
1903
+ restore_mode();
1904
+ } else {
1905
+ restore_mode();
1906
+ print_token();
1907
+ }
1908
+ output.remove_redundant_indentation(previous_flags);
1909
+
1910
+ // do {} while () // no statement required after
1911
+ if (flags.do_while && previous_flags.mode === MODE.Conditional) {
1912
+ previous_flags.mode = MODE.Expression;
1913
+ flags.do_block = false;
1914
+ flags.do_while = false;
1915
+
1916
+ }
1917
+ }
1918
+
1919
+ function handle_start_block() {
1920
+ // Check if this is should be treated as a ObjectLiteral
1921
+ var next_token = get_token(1)
1922
+ var second_token = get_token(2)
1923
+ if (second_token && (
1924
+ (second_token.text === ':' && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED']))
1925
+ || (in_array(next_token.text, ['get', 'set']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
1926
+ )) {
1927
+ // We don't support TypeScript,but we didn't break it for a very long time.
1928
+ // We'll try to keep not breaking it.
1929
+ if (!in_array(last_last_text, ['class','interface'])) {
1930
+ set_mode(MODE.ObjectLiteral);
1931
+ } else {
1932
+ set_mode(MODE.BlockStatement);
1933
+ }
1934
+ } else {
1935
+ set_mode(MODE.BlockStatement);
1936
+ }
1937
+
1938
+ var empty_braces = !next_token.comments_before.length && next_token.text === '}';
1939
+ var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
1940
+ last_type === 'TK_END_EXPR';
1941
+
1942
+ if (opt.brace_style === "expand" ||
1943
+ (opt.brace_style === "none" && current_token.wanted_newline)) {
1944
+ if (last_type !== 'TK_OPERATOR' &&
1945
+ (empty_anonymous_function ||
1946
+ last_type === 'TK_EQUALS' ||
1947
+ (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
1948
+ output.space_before_token = true;
1949
+ } else {
1950
+ print_newline(false, true);
1951
+ }
1952
+ } else { // collapse
1953
+ if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
1954
+ if (last_type === 'TK_START_BLOCK') {
1955
+ print_newline();
1956
+ } else {
1957
+ output.space_before_token = true;
1958
+ }
1959
+ } else {
1960
+ // if TK_OPERATOR or TK_START_EXPR
1961
+ if (is_array(previous_flags.mode) && flags.last_text === ',') {
1962
+ if (last_last_text === '}') {
1963
+ // }, { in array context
1964
+ output.space_before_token = true;
1965
+ } else {
1966
+ print_newline(); // [a, b, c, {
1967
+ }
1968
+ }
1969
+ }
1970
+ }
1971
+ print_token();
1972
+ indent();
1973
+ }
1974
+
1975
+ function handle_end_block() {
1976
+ // statements must all be closed when their container closes
1977
+ while (flags.mode === MODE.Statement) {
1978
+ restore_mode();
1979
+ }
1980
+ var empty_braces = last_type === 'TK_START_BLOCK';
1981
+
1982
+ if (opt.brace_style === "expand") {
1983
+ if (!empty_braces) {
1984
+ print_newline();
1985
+ }
1986
+ } else {
1987
+ // skip {}
1988
+ if (!empty_braces) {
1989
+ if (is_array(flags.mode) && opt.keep_array_indentation) {
1990
+ // we REALLY need a newline here, but newliner would skip that
1991
+ opt.keep_array_indentation = false;
1992
+ print_newline();
1993
+ opt.keep_array_indentation = true;
1994
+
1995
+ } else {
1996
+ print_newline();
1997
+ }
1998
+ }
1999
+ }
2000
+ restore_mode();
2001
+ print_token();
2002
+ }
2003
+
2004
+ function handle_word() {
2005
+ if (current_token.type === 'TK_RESERVED' && flags.mode !== MODE.ObjectLiteral &&
2006
+ in_array(current_token.text, ['set', 'get'])) {
2007
+ current_token.type = 'TK_WORD';
2008
+ }
2009
+
2010
+ if (current_token.type === 'TK_RESERVED' && flags.mode === MODE.ObjectLiteral) {
2011
+ var next_token = get_token(1);
2012
+ if (next_token.text == ':') {
2013
+ current_token.type = 'TK_WORD';
2014
+ }
2015
+ }
2016
+
2017
+ if (start_of_statement()) {
2018
+ // The conditional starts the statement if appropriate.
2019
+ } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
2020
+ (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
2021
+ last_type !== 'TK_EQUALS' &&
2022
+ (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {
2023
+
2024
+ print_newline();
2025
+ }
2026
+
2027
+ if (flags.do_block && !flags.do_while) {
2028
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
2029
+ // do {} ## while ()
2030
+ output.space_before_token = true;
2031
+ print_token();
2032
+ output.space_before_token = true;
2033
+ flags.do_while = true;
2034
+ return;
2035
+ } else {
2036
+ // do {} should always have while as the next word.
2037
+ // if we don't see the expected while, recover
2038
+ print_newline();
2039
+ flags.do_block = false;
2040
+ }
2041
+ }
2042
+
2043
+ // if may be followed by else, or not
2044
+ // Bare/inline ifs are tricky
2045
+ // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
2046
+ if (flags.if_block) {
2047
+ if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
2048
+ flags.else_block = true;
2049
+ } else {
2050
+ while (flags.mode === MODE.Statement) {
2051
+ restore_mode();
2052
+ }
2053
+ flags.if_block = false;
2054
+ flags.else_block = false;
2055
+ }
2056
+ }
2057
+
2058
+ if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
2059
+ print_newline();
2060
+ if (flags.case_body || opt.jslint_happy) {
2061
+ // switch cases following one another
2062
+ deindent();
2063
+ flags.case_body = false;
2064
+ }
2065
+ print_token();
2066
+ flags.in_case = true;
2067
+ flags.in_case_statement = true;
2068
+ return;
2069
+ }
2070
+
2071
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
2072
+ if (in_array(flags.last_text, ['}', ';']) || (output.just_added_newline() && ! in_array(flags.last_text, ['[', '{', ':', '=', ',']))) {
2073
+ // make sure there is a nice clean space of at least one blank line
2074
+ // before a new function definition
2075
+ if ( !output.just_added_blankline() && !current_token.comments_before.length) {
2076
+ print_newline();
2077
+ print_newline(true);
2078
+ }
2079
+ }
2080
+ if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
2081
+ if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
2082
+ output.space_before_token = true;
2083
+ } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
2084
+ output.space_before_token = true;
2085
+ } else {
2086
+ print_newline();
2087
+ }
2088
+ } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
2089
+ // foo = function
2090
+ output.space_before_token = true;
2091
+ } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
2092
+ // (function
2093
+ } else {
2094
+ print_newline();
2095
+ }
2096
+ }
2097
+
2098
+ if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
2099
+ if (!start_of_object_property()) {
2100
+ allow_wrap_or_preserved_newline();
2101
+ }
2102
+ }
2103
+
2104
+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['function', 'get', 'set'])) {
2105
+ print_token();
2106
+ flags.last_word = current_token.text;
2107
+ return;
2108
+ }
2109
+
2110
+ prefix = 'NONE';
2111
+
2112
+ if (last_type === 'TK_END_BLOCK') {
2113
+ if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally']))) {
2114
+ prefix = 'NEWLINE';
2115
+ } else {
2116
+ if (opt.brace_style === "expand" ||
2117
+ opt.brace_style === "end-expand" ||
2118
+ (opt.brace_style === "none" && current_token.wanted_newline)) {
2119
+ prefix = 'NEWLINE';
2120
+ } else {
2121
+ prefix = 'SPACE';
2122
+ output.space_before_token = true;
2123
+ }
2124
+ }
2125
+ } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
2126
+ // TODO: Should this be for STATEMENT as well?
2127
+ prefix = 'NEWLINE';
2128
+ } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
2129
+ prefix = 'SPACE';
2130
+ } else if (last_type === 'TK_STRING') {
2131
+ prefix = 'NEWLINE';
2132
+ } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
2133
+ (flags.last_text === '*' && last_last_text === 'function')) {
2134
+ prefix = 'SPACE';
2135
+ } else if (last_type === 'TK_START_BLOCK') {
2136
+ prefix = 'NEWLINE';
2137
+ } else if (last_type === 'TK_END_EXPR') {
2138
+ output.space_before_token = true;
2139
+ prefix = 'NEWLINE';
2140
+ }
2141
+
2142
+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
2143
+ if (flags.last_text === 'else' || flags.last_text === 'export') {
2144
+ prefix = 'SPACE';
2145
+ } else {
2146
+ prefix = 'NEWLINE';
2147
+ }
2148
+
2149
+ }
2150
+
2151
+ if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
2152
+ if (last_type !== 'TK_END_BLOCK' ||
2153
+ opt.brace_style === "expand" ||
2154
+ opt.brace_style === "end-expand" ||
2155
+ (opt.brace_style === "none" && current_token.wanted_newline)) {
2156
+ print_newline();
2157
+ } else {
2158
+ output.trim(true);
2159
+ var line = output.current_line;
2160
+ // If we trimmed and there's something other than a close block before us
2161
+ // put a newline back in. Handles '} // comment' scenario.
2162
+ if (line.last() !== '}') {
2163
+ print_newline();
2164
+ }
2165
+ output.space_before_token = true;
2166
+ }
2167
+ } else if (prefix === 'NEWLINE') {
2168
+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
2169
+ // no newline between 'return nnn'
2170
+ output.space_before_token = true;
2171
+ } else if (last_type !== 'TK_END_EXPR') {
2172
+ if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
2173
+ // no need to force newline on 'var': for (var x = 0...)
2174
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
2175
+ // no newline for } else if {
2176
+ output.space_before_token = true;
2177
+ } else {
2178
+ print_newline();
2179
+ }
2180
+ }
2181
+ } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
2182
+ print_newline();
2183
+ }
2184
+ } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
2185
+ print_newline(); // }, in lists get a newline treatment
2186
+ } else if (prefix === 'SPACE') {
2187
+ output.space_before_token = true;
2188
+ }
2189
+ print_token();
2190
+ flags.last_word = current_token.text;
2191
+
2192
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'do') {
2193
+ flags.do_block = true;
2194
+ }
2195
+
2196
+ if (current_token.type === 'TK_RESERVED' && current_token.text === 'if') {
2197
+ flags.if_block = true;
2198
+ }
2199
+ }
2200
+
2201
+ function handle_semicolon() {
2202
+ if (start_of_statement()) {
2203
+ // The conditional starts the statement if appropriate.
2204
+ // Semicolon can be the start (and end) of a statement
2205
+ output.space_before_token = false;
2206
+ }
2207
+ while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
2208
+ restore_mode();
2209
+ }
2210
+ print_token();
2211
+ }
2212
+
2213
+ function handle_string() {
2214
+ if (start_of_statement()) {
2215
+ // The conditional starts the statement if appropriate.
2216
+ // One difference - strings want at least a space before
2217
+ output.space_before_token = true;
2218
+ } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
2219
+ output.space_before_token = true;
2220
+ } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
2221
+ if (!start_of_object_property()) {
2222
+ allow_wrap_or_preserved_newline();
2223
+ }
2224
+ } else {
2225
+ print_newline();
2226
+ }
2227
+ print_token();
2228
+ }
2229
+
2230
+ function handle_equals() {
2231
+ if (start_of_statement()) {
2232
+ // The conditional starts the statement if appropriate.
2233
+ }
2234
+
2235
+ if (flags.declaration_statement) {
2236
+ // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
2237
+ flags.declaration_assignment = true;
2238
+ }
2239
+ output.space_before_token = true;
2240
+ print_token();
2241
+ output.space_before_token = true;
2242
+ }
2243
+
2244
+ function handle_comma() {
2245
+ if (flags.declaration_statement) {
2246
+ if (is_expression(flags.parent.mode)) {
2247
+ // do not break on comma, for(var a = 1, b = 2)
2248
+ flags.declaration_assignment = false;
2249
+ }
2250
+
2251
+ print_token();
2252
+
2253
+ if (flags.declaration_assignment) {
2254
+ flags.declaration_assignment = false;
2255
+ print_newline(false, true);
2256
+ } else {
2257
+ output.space_before_token = true;
2258
+ // for comma-first, we want to allow a newline before the comma
2259
+ // to turn into a newline after the comma, which we will fixup later
2260
+ if (opt.comma_first) {
2261
+ allow_wrap_or_preserved_newline();
2262
+ }
2263
+ }
2264
+ return;
2265
+ }
2266
+
2267
+ print_token();
2268
+ if (flags.mode === MODE.ObjectLiteral ||
2269
+ (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
2270
+ if (flags.mode === MODE.Statement) {
2271
+ restore_mode();
2272
+ }
2273
+ print_newline();
2274
+ } else {
2275
+ // EXPR or DO_BLOCK
2276
+ output.space_before_token = true;
2277
+ // for comma-first, we want to allow a newline before the comma
2278
+ // to turn into a newline after the comma, which we will fixup later
2279
+ if (opt.comma_first) {
2280
+ allow_wrap_or_preserved_newline();
2281
+ }
2282
+ }
2283
+
2284
+ }
2285
+
2286
+ function handle_operator() {
2287
+ if (start_of_statement()) {
2288
+ // The conditional starts the statement if appropriate.
2289
+ }
2290
+
2291
+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
2292
+ // "return" had a special handling in TK_WORD. Now we need to return the favor
2293
+ output.space_before_token = true;
2294
+ print_token();
2295
+ return;
2296
+ }
2297
+
2298
+ // hack for actionscript's import .*;
2299
+ if (current_token.text === '*' && last_type === 'TK_DOT') {
2300
+ print_token();
2301
+ return;
2302
+ }
2303
+
2304
+ if (current_token.text === ':' && flags.in_case) {
2305
+ flags.case_body = true;
2306
+ indent();
2307
+ print_token();
2308
+ print_newline();
2309
+ flags.in_case = false;
2310
+ return;
2311
+ }
2312
+
2313
+ if (current_token.text === '::') {
2314
+ // no spaces around exotic namespacing syntax operator
2315
+ print_token();
2316
+ return;
2317
+ }
2318
+
2319
+ // Allow line wrapping between operators
2320
+ if (last_type === 'TK_OPERATOR') {
2321
+ allow_wrap_or_preserved_newline();
2322
+ }
2323
+
2324
+ var space_before = true;
2325
+ var space_after = true;
2326
+
2327
+ 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 === ','))) {
2328
+ // unary operators (and binary +/- pretending to be unary) special cases
2329
+
2330
+ space_before = false;
2331
+ space_after = false;
2332
+
2333
+ // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
2334
+ // if there is a newline between -- or ++ and anything else we should preserve it.
2335
+ if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
2336
+ print_newline(false, true);
2337
+ }
2338
+
2339
+ if (flags.last_text === ';' && is_expression(flags.mode)) {
2340
+ // for (;; ++i)
2341
+ // ^^^
2342
+ space_before = true;
2343
+ }
2344
+
2345
+ if (last_type === 'TK_RESERVED') {
2346
+ space_before = true;
2347
+ } else if (last_type === 'TK_END_EXPR') {
2348
+ space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
2349
+ } else if (last_type === 'TK_OPERATOR') {
2350
+ // a++ + ++b;
2351
+ // a - -b
2352
+ space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
2353
+ // + and - are not unary when preceeded by -- or ++ operator
2354
+ // a-- + b
2355
+ // a * +b
2356
+ // a - -b
2357
+ if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
2358
+ space_after = true;
2359
+ }
2360
+ }
2361
+
2362
+ if ((flags.mode === MODE.BlockStatement || flags.mode === MODE.Statement) && (flags.last_text === '{' || flags.last_text === ';')) {
2363
+ // { foo; --i }
2364
+ // foo(); --bar;
2365
+ print_newline();
2366
+ }
2367
+ } else if (current_token.text === ':') {
2368
+ if (flags.ternary_depth === 0) {
2369
+ // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
2370
+ space_before = false;
2371
+ } else {
2372
+ flags.ternary_depth -= 1;
2373
+ }
2374
+ } else if (current_token.text === '?') {
2375
+ flags.ternary_depth += 1;
2376
+ } else if (current_token.text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function') {
2377
+ space_before = false;
2378
+ space_after = false;
2379
+ }
2380
+ output.space_before_token = output.space_before_token || space_before;
2381
+ print_token();
2382
+ output.space_before_token = space_after;
2383
+ }
2384
+
2385
+ function handle_block_comment() {
2386
+ if (output.raw) {
2387
+ output.add_raw_token(current_token)
2388
+ if (current_token.directives && current_token.directives['preserve'] === 'end') {
2389
+ // If we're testing the raw output behavior, do not allow a directive to turn it off.
2390
+ if (!opt.test_output_raw) {
2391
+ output.raw = false;
2392
+ }
2393
+ }
2394
+ return;
2395
+ }
2396
+
2397
+ if (current_token.directives) {
2398
+ print_newline(false, true);
2399
+ print_token();
2400
+ if (current_token.directives['preserve'] === 'start') {
2401
+ output.raw = true;
2402
+ }
2403
+ print_newline(false, true);
2404
+ return;
2405
+ }
2406
+
2407
+ // inline block
2408
+ if (!acorn.newline.test(current_token.text) && !current_token.wanted_newline) {
2409
+ output.space_before_token = true;
2410
+ print_token();
2411
+ output.space_before_token = true;
2412
+ return;
2413
+ }
2414
+
2415
+ var lines = split_newlines(current_token.text);
2416
+ var j; // iterator for this case
2417
+ var javadoc = false;
2418
+ var starless = false;
2419
+ var lastIndent = current_token.whitespace_before;
2420
+ var lastIndentLength = lastIndent.length;
2421
+
2422
+ // block comment starts with a new line
2423
+ print_newline(false, true);
2424
+ if (lines.length > 1) {
2425
+ if (all_lines_start_with(lines.slice(1), '*')) {
2426
+ javadoc = true;
2427
+ }
2428
+ else if (each_line_matches_indent(lines.slice(1), lastIndent)) {
2429
+ starless = true;
2430
+ }
2431
+ }
2432
+
2433
+ // first line always indented
2434
+ print_token(lines[0]);
2435
+ for (j = 1; j < lines.length; j++) {
2436
+ print_newline(false, true);
2437
+ if (javadoc) {
2438
+ // javadoc: reformat and re-indent
2439
+ print_token(' ' + ltrim(lines[j]));
2440
+ } else if (starless && lines[j].length > lastIndentLength) {
2441
+ // starless: re-indent non-empty content, avoiding trim
2442
+ print_token(lines[j].substring(lastIndentLength));
2443
+ } else {
2444
+ // normal comments output raw
2445
+ output.add_token(lines[j]);
2446
+ }
2447
+ }
2448
+
2449
+ // for comments of more than one line, make sure there's a new line after
2450
+ print_newline(false, true);
2451
+ }
2452
+
2453
+ function handle_comment() {
2454
+ if (current_token.wanted_newline) {
2455
+ print_newline(false, true);
2456
+ } else {
2457
+ output.trim(true);
2458
+ }
2459
+
2460
+ output.space_before_token = true;
2461
+ print_token();
2462
+ print_newline(false, true);
2463
+ }
2464
+
2465
+ function handle_dot() {
2466
+ if (start_of_statement()) {
2467
+ // The conditional starts the statement if appropriate.
2468
+ }
2469
+
2470
+ if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
2471
+ output.space_before_token = true;
2472
+ } else {
2473
+ // allow preserved newlines before dots in general
2474
+ // force newlines on dots after close paren when break_chained - for bar().baz()
2475
+ allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
2476
+ }
2477
+
2478
+ print_token();
2479
+ }
2480
+
2481
+ function handle_unknown() {
2482
+ print_token();
2483
+
2484
+ if (current_token.text[current_token.text.length - 1] === '\n') {
2485
+ print_newline();
2486
+ }
2487
+ }
2488
+
2489
+ function handle_eof() {
2490
+ // Unwind any open statements
2491
+ while (flags.mode === MODE.Statement) {
2492
+ restore_mode();
2493
+ }
2494
+ }
2495
+ }
2496
+
2497
+
2498
+ function OutputLine(parent) {
2499
+ var _character_count = 0;
2500
+ // use indent_count as a marker for lines that have preserved indentation
2501
+ var _indent_count = -1;
2502
+
2503
+ var _items = [];
2504
+ var _empty = true;
2505
+
2506
+ this.set_indent = function(level) {
2507
+ _character_count = parent.baseIndentLength + level * parent.indent_length
2508
+ _indent_count = level;
2509
+ }
2510
+
2511
+ this.get_character_count = function() {
2512
+ return _character_count;
2513
+ }
2514
+
2515
+ this.is_empty = function() {
2516
+ return _empty;
2517
+ }
2518
+
2519
+ this.last = function() {
2520
+ if (!this._empty) {
2521
+ return _items[_items.length - 1];
2522
+ } else {
2523
+ return null;
2524
+ }
2525
+ }
2526
+
2527
+ this.push = function(input) {
2528
+ _items.push(input);
2529
+ _character_count += input.length;
2530
+ _empty = false;
2531
+ }
2532
+
2533
+ this.pop = function() {
2534
+ var item = null;
2535
+ if (!_empty) {
2536
+ item = _items.pop();
2537
+ _character_count -= item.length;
2538
+ _empty = _items.length === 0;
2539
+ }
2540
+ return item;
2541
+ }
2542
+
2543
+ this.remove_indent = function() {
2544
+ if (_indent_count > 0) {
2545
+ _indent_count -= 1;
2546
+ _character_count -= parent.indent_length
2547
+ }
2548
+ }
2549
+
2550
+ this.trim = function() {
2551
+ while (this.last() === ' ') {
2552
+ var item = _items.pop();
2553
+ _character_count -= 1;
2554
+ }
2555
+ _empty = _items.length === 0;
2556
+ }
2557
+
2558
+ this.toString = function() {
2559
+ var result = '';
2560
+ if (!this._empty) {
2561
+ if (_indent_count >= 0) {
2562
+ result = parent.indent_cache[_indent_count];
2563
+ }
2564
+ result += _items.join('')
2565
+ }
2566
+ return result;
2567
+ }
2568
+ }
2569
+
2570
+ function Output(indent_string, baseIndentString) {
2571
+ baseIndentString = baseIndentString || '';
2572
+ this.indent_cache = [ baseIndentString ];
2573
+ this.baseIndentLength = baseIndentString.length;
2574
+ this.indent_length = indent_string.length;
2575
+ this.raw = false;
2576
+
2577
+ var lines =[];
2578
+ this.baseIndentString = baseIndentString;
2579
+ this.indent_string = indent_string;
2580
+ this.previous_line = null;
2581
+ this.current_line = null;
2582
+ this.space_before_token = false;
2583
+
2584
+ this.add_outputline = function() {
2585
+ this.previous_line = this.current_line;
2586
+ this.current_line = new OutputLine(this);
2587
+ lines.push(this.current_line);
2588
+ }
2589
+
2590
+ // initialize
2591
+ this.add_outputline();
2592
+
2593
+
2594
+ this.get_line_number = function() {
2595
+ return lines.length;
2596
+ }
2597
+
2598
+ // Using object instead of string to allow for later expansion of info about each line
2599
+ this.add_new_line = function(force_newline) {
2600
+ if (this.get_line_number() === 1 && this.just_added_newline()) {
2601
+ return false; // no newline on start of file
2602
+ }
2603
+
2604
+ if (force_newline || !this.just_added_newline()) {
2605
+ if (!this.raw) {
2606
+ this.add_outputline();
2607
+ }
2608
+ return true;
2609
+ }
2610
+
2611
+ return false;
2612
+ }
2613
+
2614
+ this.get_code = function() {
2615
+ var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
2616
+ return sweet_code;
2617
+ }
2618
+
2619
+ this.set_indent = function(level) {
2620
+ // Never indent your first output indent at the start of the file
2621
+ if (lines.length > 1) {
2622
+ while(level >= this.indent_cache.length) {
2623
+ this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
2624
+ }
2625
+
2626
+ this.current_line.set_indent(level);
2627
+ return true;
2628
+ }
2629
+ this.current_line.set_indent(0);
2630
+ return false;
2631
+ }
2632
+
2633
+ this.add_raw_token = function(token) {
2634
+ for (var x = 0; x < token.newlines; x++) {
2635
+ this.add_outputline();
2636
+ }
2637
+ this.current_line.push(token.whitespace_before);
2638
+ this.current_line.push(token.text);
2639
+ this.space_before_token = false;
2640
+ }
2641
+
2642
+ this.add_token = function(printable_token) {
2643
+ this.add_space_before_token();
2644
+ this.current_line.push(printable_token);
2645
+ }
2646
+
2647
+ this.add_space_before_token = function() {
2648
+ if (this.space_before_token && !this.just_added_newline()) {
2649
+ this.current_line.push(' ');
2650
+ }
2651
+ this.space_before_token = false;
2652
+ }
2653
+
2654
+ this.remove_redundant_indentation = function (frame) {
2655
+ // This implementation is effective but has some issues:
2656
+ // - can cause line wrap to happen too soon due to indent removal
2657
+ // after wrap points are calculated
2658
+ // These issues are minor compared to ugly indentation.
2659
+
2660
+ if (frame.multiline_frame ||
2661
+ frame.mode === MODE.ForInitializer ||
2662
+ frame.mode === MODE.Conditional) {
2663
+ return;
2664
+ }
2665
+
2666
+ // remove one indent from each line inside this section
2667
+ var index = frame.start_line_index;
2668
+ var line;
2669
+
2670
+ var output_length = lines.length;
2671
+ while (index < output_length) {
2672
+ lines[index].remove_indent();
2673
+ index++;
2674
+ }
2675
+ }
2676
+
2677
+ this.trim = function(eat_newlines) {
2678
+ eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
2679
+
2680
+ this.current_line.trim(indent_string, baseIndentString);
2681
+
2682
+ while (eat_newlines && lines.length > 1 &&
2683
+ this.current_line.is_empty()) {
2684
+ lines.pop();
2685
+ this.current_line = lines[lines.length - 1]
2686
+ this.current_line.trim();
2687
+ }
2688
+
2689
+ this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
2690
+ }
2691
+
2692
+ this.just_added_newline = function() {
2693
+ return this.current_line.is_empty();
2694
+ }
2695
+
2696
+ this.just_added_blankline = function() {
2697
+ if (this.just_added_newline()) {
2698
+ if (lines.length === 1) {
2699
+ return true; // start of the file and newline = blank
2700
+ }
2701
+
2702
+ var line = lines[lines.length - 2];
2703
+ return line.is_empty();
2704
+ }
2705
+ return false;
2706
+ }
2707
+ }
2708
+
2709
+
2710
+ var Token = function(type, text, newlines, whitespace_before, mode, parent) {
2711
+ this.type = type;
2712
+ this.text = text;
2713
+ this.comments_before = [];
2714
+ this.newlines = newlines || 0;
2715
+ this.wanted_newline = newlines > 0;
2716
+ this.whitespace_before = whitespace_before || '';
2717
+ this.parent = null;
2718
+ this.directives = null;
2719
+ }
2720
+
2721
+ function tokenizer(input, opts, indent_string) {
2722
+
2723
+ var whitespace = "\n\r\t ".split('');
2724
+ var digit = /[0-9]/;
2725
+ var digit_oct = /[01234567]/;
2726
+ var digit_hex = /[0123456789abcdefABCDEF]/;
2727
+
2728
+ var punct = ('+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! ~ , : ? ^ ^= |= :: =>').split(' ');
2729
+ // words which should always start on new line.
2730
+ this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
2731
+ var reserved_words = this.line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await']);
2732
+
2733
+ // /* ... */ comment ends with nearest */ or end of file
2734
+ var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;
2735
+
2736
+ // comment ends just before nearest linefeed or end of file
2737
+ var comment_pattern = /([^\n\r\u2028\u2029]*)/g;
2738
+
2739
+ var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
2740
+ var directive_pattern = / (\w+)[:](\w+)/g;
2741
+ var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;
2742
+
2743
+ var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g
2744
+
2745
+ var n_newlines, whitespace_before_token, in_html_comment, tokens, parser_pos;
2746
+ var input_length;
2747
+
2748
+ this.tokenize = function() {
2749
+ // cache the source's length.
2750
+ input_length = input.length
2751
+ parser_pos = 0;
2752
+ in_html_comment = false
2753
+ tokens = [];
2754
+
2755
+ var next, last;
2756
+ var token_values;
2757
+ var open = null;
2758
+ var open_stack = [];
2759
+ var comments = [];
2760
+
2761
+ while (!(last && last.type === 'TK_EOF')) {
2762
+ token_values = tokenize_next();
2763
+ next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
2764
+ while(next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
2765
+ if (next.type === 'TK_BLOCK_COMMENT') {
2766
+ next.directives = token_values[2];
2767
+ }
2768
+ comments.push(next);
2769
+ token_values = tokenize_next();
2770
+ next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
2771
+ }
2772
+
2773
+ if (comments.length) {
2774
+ next.comments_before = comments;
2775
+ comments = [];
2776
+ }
2777
+
2778
+ if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
2779
+ next.parent = last;
2780
+ open_stack.push(open);
2781
+ open = next;
2782
+ } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
2783
+ (open && (
2784
+ (next.text === ']' && open.text === '[') ||
2785
+ (next.text === ')' && open.text === '(') ||
2786
+ (next.text === '}' && open.text === '{')))) {
2787
+ next.parent = open.parent;
2788
+ open = open_stack.pop();
2789
+ }
2790
+
2791
+ tokens.push(next);
2792
+ last = next;
2793
+ }
2794
+
2795
+ return tokens;
2796
+ }
2797
+
2798
+ function get_directives (text) {
2799
+ if (!text.match(directives_block_pattern)) {
2800
+ return null;
2801
+ }
2802
+
2803
+ var directives = {};
2804
+ directive_pattern.lastIndex = 0;
2805
+ var directive_match = directive_pattern.exec(text);
2806
+
2807
+ while (directive_match) {
2808
+ directives[directive_match[1]] = directive_match[2];
2809
+ directive_match = directive_pattern.exec(text);
2810
+ }
2811
+
2812
+ return directives;
2813
+ }
2814
+
2815
+ function tokenize_next() {
2816
+ var i, resulting_string;
2817
+ var whitespace_on_this_line = [];
2818
+
2819
+ n_newlines = 0;
2820
+ whitespace_before_token = '';
2821
+
2822
+ if (parser_pos >= input_length) {
2823
+ return ['', 'TK_EOF'];
2824
+ }
2825
+
2826
+ var last_token;
2827
+ if (tokens.length) {
2828
+ last_token = tokens[tokens.length-1];
2829
+ } else {
2830
+ // For the sake of tokenizing we can pretend that there was on open brace to start
2831
+ last_token = new Token('TK_START_BLOCK', '{');
2832
+ }
2833
+
2834
+
2835
+ var c = input.charAt(parser_pos);
2836
+ parser_pos += 1;
2837
+
2838
+ while (in_array(c, whitespace)) {
2839
+
2840
+ if (acorn.newline.test(c)) {
2841
+ if (!(c === '\n' && input.charAt(parser_pos-2) === '\r')) {
2842
+ n_newlines += 1;
2843
+ whitespace_on_this_line = [];
2844
+ }
2845
+ } else {
2846
+ whitespace_on_this_line.push(c);
2847
+ }
2848
+
2849
+ if (parser_pos >= input_length) {
2850
+ return ['', 'TK_EOF'];
2851
+ }
2852
+
2853
+ c = input.charAt(parser_pos);
2854
+ parser_pos += 1;
2855
+ }
2856
+
2857
+ if(whitespace_on_this_line.length) {
2858
+ whitespace_before_token = whitespace_on_this_line.join('');
2859
+ }
2860
+
2861
+ if (digit.test(c)) {
2862
+ var allow_decimal = true;
2863
+ var allow_e = true;
2864
+ var local_digit = digit;
2865
+
2866
+ if (c === '0' && parser_pos < input_length && /[Xxo]/.test(input.charAt(parser_pos))) {
2867
+ // switch to hex/oct number, no decimal or e, just hex/oct digits
2868
+ allow_decimal = false;
2869
+ allow_e = false;
2870
+ c += input.charAt(parser_pos);
2871
+ parser_pos += 1;
2872
+ local_digit = /[o]/.test(input.charAt(parser_pos)) ? digit_oct : digit_hex;
2873
+ } else {
2874
+ // we know this first loop will run. It keeps the logic simpler.
2875
+ c = '';
2876
+ parser_pos -= 1;
2877
+ }
2878
+
2879
+ // Add the digits
2880
+ while (parser_pos < input_length && local_digit.test(input.charAt(parser_pos))) {
2881
+ c += input.charAt(parser_pos);
2882
+ parser_pos += 1;
2883
+
2884
+ if (allow_decimal && parser_pos < input_length && input.charAt(parser_pos) === '.') {
2885
+ c += input.charAt(parser_pos);
2886
+ parser_pos += 1;
2887
+ allow_decimal = false;
2888
+ }
2889
+
2890
+ if (allow_e && parser_pos < input_length && /[Ee]/.test(input.charAt(parser_pos))) {
2891
+ c += input.charAt(parser_pos);
2892
+ parser_pos += 1;
2893
+
2894
+ if (parser_pos < input_length && /[+-]/.test(input.charAt(parser_pos))) {
2895
+ c += input.charAt(parser_pos);
2896
+ parser_pos += 1;
2897
+ }
2898
+
2899
+ allow_e = false;
2900
+ allow_decimal = false;
2901
+ }
2902
+ }
2903
+
2904
+ return [c, 'TK_WORD'];
2905
+ }
2906
+
2907
+ if (acorn.isIdentifierStart(input.charCodeAt(parser_pos-1))) {
2908
+ if (parser_pos < input_length) {
2909
+ while (acorn.isIdentifierChar(input.charCodeAt(parser_pos))) {
2910
+ c += input.charAt(parser_pos);
2911
+ parser_pos += 1;
2912
+ if (parser_pos === input_length) {
2913
+ break;
2914
+ }
2915
+ }
2916
+ }
2917
+
2918
+ if (!(last_token.type === 'TK_DOT' ||
2919
+ (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get'])))
2920
+ && in_array(c, reserved_words)) {
2921
+ if (c === 'in') { // hack for 'in' operator
2922
+ return [c, 'TK_OPERATOR'];
2923
+ }
2924
+ return [c, 'TK_RESERVED'];
2925
+ }
2926
+
2927
+ return [c, 'TK_WORD'];
2928
+ }
2929
+
2930
+ if (c === '(' || c === '[') {
2931
+ return [c, 'TK_START_EXPR'];
2932
+ }
2933
+
2934
+ if (c === ')' || c === ']') {
2935
+ return [c, 'TK_END_EXPR'];
2936
+ }
2937
+
2938
+ if (c === '{') {
2939
+ return [c, 'TK_START_BLOCK'];
2940
+ }
2941
+
2942
+ if (c === '}') {
2943
+ return [c, 'TK_END_BLOCK'];
2944
+ }
2945
+
2946
+ if (c === ';') {
2947
+ return [c, 'TK_SEMICOLON'];
2948
+ }
2949
+
2950
+ if (c === '/') {
2951
+ var comment = '';
2952
+ // peek for comment /* ... */
2953
+ if (input.charAt(parser_pos) === '*') {
2954
+ parser_pos += 1;
2955
+ block_comment_pattern.lastIndex = parser_pos;
2956
+ var comment_match = block_comment_pattern.exec(input);
2957
+ comment = '/*' + comment_match[0];
2958
+ parser_pos += comment_match[0].length;
2959
+ var directives = get_directives(comment);
2960
+ if (directives && directives['ignore'] === 'start') {
2961
+ directives_end_ignore_pattern.lastIndex = parser_pos;
2962
+ comment_match = directives_end_ignore_pattern.exec(input)
2963
+ comment += comment_match[0];
2964
+ parser_pos += comment_match[0].length;
2965
+ }
2966
+ comment = comment.replace(acorn.lineBreak, '\n');
2967
+ return [comment, 'TK_BLOCK_COMMENT', directives];
2968
+ }
2969
+ // peek for comment // ...
2970
+ if (input.charAt(parser_pos) === '/') {
2971
+ parser_pos += 1;
2972
+ comment_pattern.lastIndex = parser_pos;
2973
+ var comment_match = comment_pattern.exec(input);
2974
+ comment = '//' + comment_match[0];
2975
+ parser_pos += comment_match[0].length;
2976
+ return [comment, 'TK_COMMENT'];
2977
+ }
2978
+
2979
+ }
2980
+
2981
+ if (c === '`' || c === "'" || c === '"' || // string
2982
+ (
2983
+ (c === '/') || // regexp
2984
+ (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
2985
+ ) && ( // regex and xml can only appear in specific locations during parsing
2986
+ (last_token.type === 'TK_RESERVED' && in_array(last_token.text , ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
2987
+ (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
2988
+ last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
2989
+ (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
2990
+ 'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
2991
+ ]))
2992
+ )) {
2993
+
2994
+ var sep = c,
2995
+ esc = false,
2996
+ has_char_escapes = false;
2997
+
2998
+ resulting_string = c;
2999
+
3000
+ if (sep === '/') {
3001
+ //
3002
+ // handle regexp
3003
+ //
3004
+ var in_char_class = false;
3005
+ while (parser_pos < input_length &&
3006
+ ((esc || in_char_class || input.charAt(parser_pos) !== sep) &&
3007
+ !acorn.newline.test(input.charAt(parser_pos)))) {
3008
+ resulting_string += input.charAt(parser_pos);
3009
+ if (!esc) {
3010
+ esc = input.charAt(parser_pos) === '\\';
3011
+ if (input.charAt(parser_pos) === '[') {
3012
+ in_char_class = true;
3013
+ } else if (input.charAt(parser_pos) === ']') {
3014
+ in_char_class = false;
3015
+ }
3016
+ } else {
3017
+ esc = false;
3018
+ }
3019
+ parser_pos += 1;
3020
+ }
3021
+ } else if (opts.e4x && sep === '<') {
3022
+ //
3023
+ // handle e4x xml literals
3024
+ //
3025
+ var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])(\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.*?}))*\s*(\/?)\s*>/g;
3026
+ var xmlStr = input.slice(parser_pos - 1);
3027
+ var match = xmlRegExp.exec(xmlStr);
3028
+ if (match && match.index === 0) {
3029
+ var rootTag = match[2];
3030
+ var depth = 0;
3031
+ while (match) {
3032
+ var isEndTag = !! match[1];
3033
+ var tagName = match[2];
3034
+ var isSingletonTag = ( !! match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
3035
+ if (tagName === rootTag && !isSingletonTag) {
3036
+ if (isEndTag) {
3037
+ --depth;
3038
+ } else {
3039
+ ++depth;
3040
+ }
3041
+ }
3042
+ if (depth <= 0) {
3043
+ break;
3044
+ }
3045
+ match = xmlRegExp.exec(xmlStr);
3046
+ }
3047
+ var xmlLength = match ? match.index + match[0].length : xmlStr.length;
3048
+ xmlStr = xmlStr.slice(0, xmlLength);
3049
+ parser_pos += xmlLength - 1;
3050
+ xmlStr = xmlStr.replace(acorn.lineBreak, '\n');
3051
+ return [xmlStr, "TK_STRING"];
3052
+ }
3053
+ } else {
3054
+ //
3055
+ // handle string
3056
+ //
3057
+ // Template strings can travers lines without escape characters.
3058
+ // Other strings cannot
3059
+ while (parser_pos < input_length &&
3060
+ (esc || (input.charAt(parser_pos) !== sep &&
3061
+ (sep === '`' || !acorn.newline.test(input.charAt(parser_pos)))))) {
3062
+ // Handle \r\n linebreaks after escapes or in template strings
3063
+ if ((esc || sep === '`') && acorn.newline.test(input.charAt(parser_pos))) {
3064
+ if (input.charAt(parser_pos) === '\r' && input.charAt(parser_pos + 1) === '\n') {
3065
+ parser_pos += 1;
3066
+ }
3067
+ resulting_string += '\n';
3068
+ } else {
3069
+ resulting_string += input.charAt(parser_pos);
3070
+ }
3071
+ if (esc) {
3072
+ if (input.charAt(parser_pos) === 'x' || input.charAt(parser_pos) === 'u') {
3073
+ has_char_escapes = true;
3074
+ }
3075
+ esc = false;
3076
+ } else {
3077
+ esc = input.charAt(parser_pos) === '\\';
3078
+ }
3079
+ parser_pos += 1;
3080
+ }
3081
+
3082
+ }
3083
+
3084
+ if (has_char_escapes && opts.unescape_strings) {
3085
+ resulting_string = unescape_string(resulting_string);
3086
+ }
3087
+
3088
+ if (parser_pos < input_length && input.charAt(parser_pos) === sep) {
3089
+ resulting_string += sep;
3090
+ parser_pos += 1;
3091
+
3092
+ if (sep === '/') {
3093
+ // regexps may have modifiers /regexp/MOD , so fetch those, too
3094
+ // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
3095
+ while (parser_pos < input_length && acorn.isIdentifierStart(input.charCodeAt(parser_pos))) {
3096
+ resulting_string += input.charAt(parser_pos);
3097
+ parser_pos += 1;
3098
+ }
3099
+ }
3100
+ }
3101
+ return [resulting_string, 'TK_STRING'];
3102
+ }
3103
+
3104
+ if (c === '#') {
3105
+
3106
+ if (tokens.length === 0 && input.charAt(parser_pos) === '!') {
3107
+ // shebang
3108
+ resulting_string = c;
3109
+ while (parser_pos < input_length && c !== '\n') {
3110
+ c = input.charAt(parser_pos);
3111
+ resulting_string += c;
3112
+ parser_pos += 1;
3113
+ }
3114
+ return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
3115
+ }
3116
+
3117
+
3118
+
3119
+ // Spidermonkey-specific sharp variables for circular references
3120
+ // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
3121
+ // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
3122
+ var sharp = '#';
3123
+ if (parser_pos < input_length && digit.test(input.charAt(parser_pos))) {
3124
+ do {
3125
+ c = input.charAt(parser_pos);
3126
+ sharp += c;
3127
+ parser_pos += 1;
3128
+ } while (parser_pos < input_length && c !== '#' && c !== '=');
3129
+ if (c === '#') {
3130
+ //
3131
+ } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
3132
+ sharp += '[]';
3133
+ parser_pos += 2;
3134
+ } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
3135
+ sharp += '{}';
3136
+ parser_pos += 2;
3137
+ }
3138
+ return [sharp, 'TK_WORD'];
3139
+ }
3140
+ }
3141
+
3142
+ if (c === '<' && (input.charAt(parser_pos) === '?' || input.charAt(parser_pos) === '%')) {
3143
+ template_pattern.lastIndex = parser_pos - 1;
3144
+ var template_match = template_pattern.exec(input);
3145
+ if(template_match) {
3146
+ c = template_match[0];
3147
+ parser_pos += c.length - 1;
3148
+ c = c.replace(acorn.lineBreak, '\n');
3149
+ return [c, 'TK_STRING'];
3150
+ }
3151
+ }
3152
+
3153
+ if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
3154
+ parser_pos += 3;
3155
+ c = '<!--';
3156
+ while (!acorn.newline.test(input.charAt(parser_pos)) && parser_pos < input_length) {
3157
+ c += input.charAt(parser_pos);
3158
+ parser_pos++;
3159
+ }
3160
+ in_html_comment = true;
3161
+ return [c, 'TK_COMMENT'];
3162
+ }
3163
+
3164
+ if (c === '-' && in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
3165
+ in_html_comment = false;
3166
+ parser_pos += 2;
3167
+ return ['-->', 'TK_COMMENT'];
3168
+ }
3169
+
3170
+ if (c === '.') {
3171
+ return [c, 'TK_DOT'];
3172
+ }
3173
+
3174
+ if (in_array(c, punct)) {
3175
+ while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
3176
+ c += input.charAt(parser_pos);
3177
+ parser_pos += 1;
3178
+ if (parser_pos >= input_length) {
3179
+ break;
3180
+ }
3181
+ }
3182
+
3183
+ if (c === ',') {
3184
+ return [c, 'TK_COMMA'];
3185
+ } else if (c === '=') {
3186
+ return [c, 'TK_EQUALS'];
3187
+ } else {
3188
+ return [c, 'TK_OPERATOR'];
3189
+ }
3190
+ }
3191
+
3192
+ return [c, 'TK_UNKNOWN'];
3193
+ }
3194
+
3195
+
3196
+ function unescape_string(s) {
3197
+ var esc = false,
3198
+ out = '',
3199
+ pos = 0,
3200
+ s_hex = '',
3201
+ escaped = 0,
3202
+ c;
3203
+
3204
+ while (esc || pos < s.length) {
3205
+
3206
+ c = s.charAt(pos);
3207
+ pos++;
3208
+
3209
+ if (esc) {
3210
+ esc = false;
3211
+ if (c === 'x') {
3212
+ // simple hex-escape \x24
3213
+ s_hex = s.substr(pos, 2);
3214
+ pos += 2;
3215
+ } else if (c === 'u') {
3216
+ // unicode-escape, \u2134
3217
+ s_hex = s.substr(pos, 4);
3218
+ pos += 4;
3219
+ } else {
3220
+ // some common escape, e.g \n
3221
+ out += '\\' + c;
3222
+ continue;
3223
+ }
3224
+ if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) {
3225
+ // some weird escaping, bail out,
3226
+ // leaving whole string intact
3227
+ return s;
3228
+ }
3229
+
3230
+ escaped = parseInt(s_hex, 16);
3231
+
3232
+ if (escaped >= 0x00 && escaped < 0x20) {
3233
+ // leave 0x00...0x1f escaped
3234
+ if (c === 'x') {
3235
+ out += '\\x' + s_hex;
3236
+ } else {
3237
+ out += '\\u' + s_hex;
3238
+ }
3239
+ continue;
3240
+ } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
3241
+ // single-quote, apostrophe, backslash - escape these
3242
+ out += '\\' + String.fromCharCode(escaped);
3243
+ } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) {
3244
+ // we bail out on \x7f..\xff,
3245
+ // leaving whole string escaped,
3246
+ // as it's probably completely binary
3247
+ return s;
3248
+ } else {
3249
+ out += String.fromCharCode(escaped);
3250
+ }
3251
+ } else if (c === '\\') {
3252
+ esc = true;
3253
+ } else {
3254
+ out += c;
3255
+ }
3256
+ }
3257
+ return out;
3258
+ }
3259
+
3260
+ }
3261
+
3262
+ return {
3263
+ run: run
3264
+ }
3265
+
3266
+ /* jshint ignore:end */
3267
+ /* jscs:enable */
3268
+ };
3269
+
3270
+ }));