rapi_doc 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -20,16 +20,22 @@ Then invoke the generation by calling:
20
20
 
21
21
  `rake rapi_doc:generate`
22
22
 
23
- It will analyze `rake routes` output and find the controller files in app/controllers directory that may have the annotation.
24
- It will confirm from the user whether he/she wants the plugin to look for annotations in each of these files.
25
- For files confirmed by the user, it will parse the annotations and generate the HTML output using the template files and styles
23
+ It will analyze the `routes.rb` files and find the controller files in app/controllers directory that may have the annotation.
24
+ Only controllers actions with annotation will be parsed and generated the HTML output using the template files and styles
26
25
  in the `config/rapi_doc` folder.
26
+ The final result is placed in `public/apidoc/`.
27
+
28
+ `rake rapi_doc:generate confirmation=true`
29
+
30
+ This rake task is the same as `rake rapi_doc:generate` but it will acquire your confirmation to generate API doc for each controller.
27
31
 
28
32
  `rake rapi_doc:clean`
29
- will remove the documentation thus generated.
33
+
34
+ This rake task will remove the documentation thus generated.
30
35
 
31
36
  `rake rapi_doc:distclean`
32
- will remove the documentation and also the Haml template files and styles created by the `rake rapi_doc:setup`
37
+
38
+ This rake task will remove the documentation and also the Haml template files and styles created by the `rake rapi_doc:setup`
33
39
 
34
40
 
35
41
  Markup Reference
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.5.0
@@ -21,9 +21,21 @@ module RapiDoc
21
21
  # Reads 'rake routes' output and gets the controller info
22
22
  def get_controller_info!
23
23
  controller_info = {}
24
- routes = Dir.chdir(::Rails.root.to_s) { `rake routes` }
25
- routes.split("\n").each do |entry|
26
- method, url, controller_action = entry.split.slice(-3, 3)
24
+
25
+ # Use Railties to get routes information.
26
+ # Manually set routes config file, because we're not actually in Rails.
27
+ Rails.application.routes_reloader.instance_variable_set(:@paths, [File.join(Rails.root, "config/routes.rb")])
28
+ Rails.application.reload_routes!
29
+ all_routes = Rails.application.routes.routes
30
+ require 'rails/application/route_inspector'
31
+ inspector = Rails::Application::RouteInspector.new
32
+ # Get Rails routes information.
33
+ routes = inspector.collect_routes(all_routes)
34
+
35
+ routes.each do |entry|
36
+ method = entry[:verb].blank? ? "GET" : entry[:verb]
37
+ url = entry[:path]
38
+ controller_action = entry[:reqs]
27
39
  controller, action = controller_action.split('#')
28
40
  puts "For \"#{controller}\", found action \"#{action}\" with #{method} at \"#{url}\""
29
41
  controller_info[controller] ||= []
@@ -37,12 +49,9 @@ module RapiDoc
37
49
  #yml.collect { |key, val| ResourceDoc.new(key, val["location"], controller_dir(val["controller_name"])) }
38
50
  controller_info = get_controller_info!
39
51
  resources = []
40
- controller_info.each do |controller, action_entries|
41
- #controller_class = controller.capitalize + 'Controller'
52
+ controller_info.each do |controller, controller_base_routes|
42
53
  controller_location = controller_dir(controller + '_controller.rb')
43
- controller_base_routes = action_entries.select do |action, method, url|
44
- url.index('/', 1).nil?
45
- end
54
+ next if !File.exist?(controller_location) # In case of some external controller in gems like DeviseController
46
55
  # base urls differ only by the method [GET or POST]. So, any one will do.
47
56
  controller_url = controller_base_routes[0][2].gsub(/\(.*\)/, '') # omit the trailing format
48
57
  #controller_methods = controller_base_routes.map { |action, method, url| method }
@@ -83,11 +92,18 @@ module RapiDoc
83
92
 
84
93
  # Creates views for the resources
85
94
  def generate_resource_templates!(resource_docs)
86
- class_template = IO.read(template_dir('_resource_header.html.haml'))
87
- method_template = IO.read(template_dir('_resource_method.html.haml'))
88
- resource_docs.each { |resource| resource.parse_apidoc!(class_template, method_template) }
95
+ class_template = IO.read(config_dir('_resource_header.html.haml'))
96
+ method_template = IO.read(config_dir('_resource_method.html.haml'))
97
+ final_resource_docs = []
98
+ resource_docs.each { |resource|
99
+ output = resource.parse_apidoc!(class_template, method_template)
100
+ if !output.nil? && !output.empty? # Keep only files that have API documentation.
101
+ final_resource_docs << resource
102
+ end
103
+ }
104
+
89
105
  template = IO.read(config_dir('index.html.haml'))
90
- parsed = Haml::Engine.new(template).render(Object.new, :resource_docs => resource_docs)
106
+ parsed = Haml::Engine.new(template).render(Object.new, :resource_docs => final_resource_docs)
91
107
  File.open(temp_dir("index.html"), 'w') { |file| file.write parsed }
92
108
  end
93
109
 
@@ -1,20 +1,26 @@
1
1
  module RapiDoc
2
2
  # This class holds methods about a doc.
3
3
  class MethodDoc
4
- attr_accessor :scope, :method_order, :content, :request, :response, :outputs, :params
4
+ attr_accessor :scope, :method_order, :content, :request_header, :request, :response, :outputs, :params,
5
+ :name, :url, :method
5
6
 
6
7
  def initialize(resource_name, type, order)
7
8
  @resource_name = resource_name
8
9
  @scope = type
9
10
  @method_order = order
10
11
  @content = ""
12
+ @request_header = ""
11
13
  @request = ""
12
14
  @response = ""
13
15
  @outputs = {}
14
16
  @params = []
17
+ @name = ""
18
+ @url = ""
19
+ @method = ""
15
20
  end
16
21
 
17
22
  def process_line(line, current_scope)
23
+ line.strip!
18
24
  #puts "In scope #{current_scope} processing: #{line}"
19
25
  new_scope = current_scope
20
26
  case current_scope
@@ -24,6 +30,12 @@ module RapiDoc
24
30
  else
25
31
  @response << line
26
32
  end
33
+ when :request_header
34
+ if line =~ /::request_header-end::/
35
+ new_scope = :function
36
+ else
37
+ @request_header << line
38
+ end
27
39
  when :request
28
40
  if line =~ /::request-end::/
29
41
  new_scope = :function
@@ -38,17 +50,20 @@ module RapiDoc
38
50
  @outputs[last_output_key] << ERB::Util.html_escape(line)
39
51
  end
40
52
  when :class, :function
41
- result = line.scan(/(\w+)\:\:\s*(.+)/)
53
+ result = line.scan(/(\w+)\:\:\s*(.*)/)
42
54
  if not result.empty?
43
55
  key, value = result[0]
56
+ value.strip!
44
57
  case key
45
- when "response", "request"
58
+ when "response", "request", "request_header"
46
59
  new_scope = key.to_sym
47
60
  when "output"
48
61
  new_scope = key.to_sym
49
62
  @outputs[value] = '' # add the new output format as a key
50
63
  when "param"
51
64
  @params << value
65
+ when "url", "name", "method"
66
+ self.send("#{key}=", value)
52
67
  else # user wants this new shiny variable whose name is the key with value = value
53
68
  instance_variable_set("@#{key}".to_sym, value)
54
69
  define_singleton_method(key.to_sym) { value } # define accessor for the templates to read it
@@ -67,5 +82,10 @@ module RapiDoc
67
82
  binding
68
83
  end
69
84
 
85
+ # Return url if the @name is not set.
86
+ def name
87
+ return @url if @name.empty?
88
+ @name
89
+ end
70
90
  end
71
- end
91
+ end
@@ -74,7 +74,7 @@ module RapiDoc
74
74
  when /class/ # keep track of whether a resource or an api is being annotated
75
75
  in_class = true
76
76
  else
77
- if current_api_block # process ines only if they are apidoc comments
77
+ if current_api_block # process lines only if they are apidoc comments
78
78
  current_scope = current_api_block.process_line(line, current_scope)
79
79
  end
80
80
  end
@@ -3,18 +3,23 @@ include RapiDoc::RAPIDoc
3
3
  namespace :rapi_doc do
4
4
 
5
5
  desc "Generate the config files"
6
- task :setup do
6
+ task :setup => :environment do
7
7
  create_structure!
8
8
  #puts "Now specify controllers in config/rapi_doc/config.yml for which api documentation is to be generated and then run rapi_doc::generate"
9
9
  end
10
10
 
11
11
  desc "Generate the api documentation"
12
- task :generate do
13
- resources = get_resources! do |controller, controller_url, controller_location|
14
- print "Generate documentation for resource \"#{controller}\" mapped at \"#{controller_url}\" (\"#{File.basename(controller_location)}\")? (Y/n):"
15
- response = STDIN.gets.chomp
16
- ['y', 'Y'].include? response
12
+ task :generate => :environment do
13
+ if ENV['confirmation'] # Need confirmation?
14
+ resources = get_resources! do |controller, controller_url, controller_location|
15
+ print "Generate documentation for resource \"#{controller}\" mapped at \"#{controller_url}\" (\"#{File.basename(controller_location)}\")? (Y/n):"
16
+ response = STDIN.gets.chomp
17
+ ['y', 'Y'].include? response
18
+ end
19
+ else # Silent mode
20
+ resources = get_resources!
17
21
  end
22
+
18
23
  if resources.empty?
19
24
  puts "Nothing to generate"
20
25
  #puts "Please specify controllers in config/rapi_doc/config.yml for which api documentation is to be generated and then run rapi_doc::generate again"
@@ -0,0 +1,94 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "rapi_doc"
8
+ s.version = "0.4.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Husein Choroomi", "Adinda Praditya", "Salil Wadnerkar"]
12
+ s.date = "2012-12-08"
13
+ s.description = "Rails API Doc Generator. Parses the apidoc annotations to generate HTML pages."
14
+ s.email = "hchoroomi@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".project",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "autoscan.log",
28
+ "configure.scan",
29
+ "init.rb",
30
+ "install.rb",
31
+ "lib/rapi_doc.rb",
32
+ "lib/rapi_doc/method_doc.rb",
33
+ "lib/rapi_doc/railtie.rb",
34
+ "lib/rapi_doc/rapi_config.rb",
35
+ "lib/rapi_doc/resource_doc.rb",
36
+ "lib/rapi_doc/tasks/rapi_doc_tasks.rake",
37
+ "rapi_doc.gemspec",
38
+ "spec/doc_parser_spec.rb",
39
+ "spec/method_doc_spec.rb",
40
+ "spec/spec_helper.rb",
41
+ "templates/_resource_header.html.haml",
42
+ "templates/_resource_method.html.haml",
43
+ "templates/beautify.js",
44
+ "templates/index.html.haml",
45
+ "templates/prettify.css",
46
+ "templates/prettify.js",
47
+ "templates/scripts.js",
48
+ "templates/search.png",
49
+ "templates/styles.css",
50
+ "uninstall.rb"
51
+ ]
52
+ s.homepage = "http://github.com/elc/rapi_doc"
53
+ s.licenses = ["MIT"]
54
+ s.require_paths = ["lib"]
55
+ s.rubygems_version = "1.8.24"
56
+ s.summary = "Rails API Doc Generator"
57
+
58
+ if s.respond_to? :specification_version then
59
+ s.specification_version = 3
60
+
61
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
62
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.1"])
63
+ s.add_runtime_dependency(%q<haml>, [">= 0"])
64
+ s.add_runtime_dependency(%q<rdoc>, [">= 0"])
65
+ s.add_development_dependency(%q<rspec>, [">= 2.7.0"])
66
+ s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
67
+ s.add_development_dependency(%q<jeweler>, [">= 1.6.4"])
68
+ s.add_development_dependency(%q<rcov>, [">= 0"])
69
+ s.add_runtime_dependency(%q<haml>, [">= 0"])
70
+ s.add_runtime_dependency(%q<rdoc>, [">= 0"])
71
+ else
72
+ s.add_dependency(%q<activesupport>, [">= 2.1"])
73
+ s.add_dependency(%q<haml>, [">= 0"])
74
+ s.add_dependency(%q<rdoc>, [">= 0"])
75
+ s.add_dependency(%q<rspec>, [">= 2.7.0"])
76
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
77
+ s.add_dependency(%q<jeweler>, [">= 1.6.4"])
78
+ s.add_dependency(%q<rcov>, [">= 0"])
79
+ s.add_dependency(%q<haml>, [">= 0"])
80
+ s.add_dependency(%q<rdoc>, [">= 0"])
81
+ end
82
+ else
83
+ s.add_dependency(%q<activesupport>, [">= 2.1"])
84
+ s.add_dependency(%q<haml>, [">= 0"])
85
+ s.add_dependency(%q<rdoc>, [">= 0"])
86
+ s.add_dependency(%q<rspec>, [">= 2.7.0"])
87
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
88
+ s.add_dependency(%q<jeweler>, [">= 1.6.4"])
89
+ s.add_dependency(%q<rcov>, [">= 0"])
90
+ s.add_dependency(%q<haml>, [">= 0"])
91
+ s.add_dependency(%q<rdoc>, [">= 0"])
92
+ end
93
+ end
94
+
@@ -1,9 +1,10 @@
1
1
  .resource_methods
2
2
  %a{:id => "#{@resource_name}_method_#{@method_order}"}
3
- %p
3
+ %h2.api-name= @name
4
+ %h2.api-url.blue= "#{@method} #{@url}"
5
+ %p.description
4
6
  %strong Description:
5
- = @content
6
- %h2= @url
7
+ = @content
7
8
  - if @access
8
9
  %strong Access:
9
10
  = @access
@@ -17,10 +18,20 @@
17
18
  %ul
18
19
  - @params.each do |v|
19
20
  %li= v
21
+ - unless @request_header.blank?
22
+ %strong Request Headers:
23
+ %pre= @request_header
24
+ - unless @request.blank?
25
+ %strong Request:
26
+ %pre= @request
27
+ - unless @response.blank?
28
+ %strong Response:
29
+ %pre= @response
20
30
  - unless @outputs.empty?
31
+ %strong Response Output:
21
32
  %ul.output_format
22
33
  - @outputs.each do |output_format, output|
23
- %li{:class => output_format}
34
+ %li.output-format{:class => output_format}
24
35
  %a{:href => "##{"#{@resource_name}_#{output_format}_#{@method_order}"}"}= output_format
25
36
  .output
26
37
  - @outputs.each do |output_format, output|
@@ -28,11 +39,3 @@
28
39
  %a{:id => "#{@resource_name}_#{output_format}_#{@method_order}"}
29
40
  %pre.prettyprint= output
30
41
  %br/
31
- - unless @request.blank?
32
- %strong Request:
33
- %pre.code
34
- %code= @request
35
- - unless @response.blank?
36
- %strong Response:
37
- %pre.code
38
- %code= @response
@@ -0,0 +1,1255 @@
1
+ /*jslint onevar: false, plusplus: false */
2
+ /*
3
+
4
+ JS Beautifier
5
+ ---------------
6
+
7
+
8
+ Written by Einar Lielmanis, <einar@jsbeautifier.org>
9
+ http://jsbeautifier.org/
10
+
11
+ Originally converted to javascript by Vital, <vital76@gmail.com>
12
+ "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
13
+
14
+ You are free to use this in any way you want, in case you find this useful or working for you.
15
+
16
+ Usage:
17
+ js_beautify(js_source_text);
18
+ js_beautify(js_source_text, options);
19
+
20
+ The options are:
21
+ indent_size (default 4) — indentation size,
22
+ indent_char (default space) — character to indent with,
23
+ preserve_newlines (default true) — whether existing line breaks should be preserved,
24
+ preserve_max_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
25
+
26
+ jslint_happy (default false) — if true, then jslint-stricter mode is enforced.
27
+
28
+ jslint_happy !jslint_happy
29
+ ---------------------------------
30
+ function () function()
31
+
32
+ brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "expand-strict"
33
+ put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
34
+
35
+ expand-strict: put brace on own line even in such cases:
36
+
37
+ var a =
38
+ {
39
+ a: 5,
40
+ b: 6
41
+ }
42
+ This mode may break your scripts - e.g "return { a: 1 }" will be broken into two lines, so beware.
43
+
44
+ space_before_conditional: should the space before conditional statement be added, "if(true)" vs "if (true)"
45
+
46
+ e.g
47
+
48
+ js_beautify(js_source_text, {
49
+ 'indent_size': 1,
50
+ 'indent_char': '\t'
51
+ });
52
+
53
+
54
+ */
55
+
56
+
57
+
58
+ function js_beautify(js_source_text, options) {
59
+
60
+ var input, output, token_text, last_type, last_text, last_last_text, last_word, flags, flag_store, indent_string;
61
+ var whitespace, wordchar, punct, parser_pos, line_starters, digits;
62
+ var prefix, token_type, do_block_just_closed;
63
+ var wanted_newline, just_added_newline, n_newlines;
64
+ var preindent_string = '';
65
+
66
+
67
+ // Some interpreters have unexpected results with foo = baz || bar;
68
+ options = options ? options : {};
69
+
70
+ var opt_brace_style;
71
+
72
+ // compatibility
73
+ if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) {
74
+ options.jslint_happy = options.space_after_anon_function;
75
+ }
76
+ if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
77
+ opt_brace_style = options.braces_on_own_line ? "expand" : "collapse";
78
+ }
79
+ opt_brace_style = options.brace_style ? options.brace_style : (opt_brace_style ? opt_brace_style : "collapse");
80
+
81
+
82
+ var opt_indent_size = options.indent_size ? options.indent_size : 4;
83
+ var opt_indent_char = options.indent_char ? options.indent_char : ' ';
84
+ var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines;
85
+ var opt_max_preserve_newlines = typeof options.max_preserve_newlines === 'undefined' ? false : options.max_preserve_newlines;
86
+ var opt_jslint_happy = options.jslint_happy === 'undefined' ? false : options.jslint_happy;
87
+ var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? false : options.keep_array_indentation;
88
+ var opt_space_before_conditional = typeof options.space_before_conditional === 'undefined' ? true : options.space_before_conditional;
89
+ var opt_indent_case = typeof options.indent_case === 'undefined' ? false : options.indent_case;
90
+
91
+ just_added_newline = false;
92
+
93
+ // cache the source's length.
94
+ var input_length = js_source_text.length;
95
+
96
+ function trim_output(eat_newlines) {
97
+ eat_newlines = typeof eat_newlines === 'undefined' ? false : eat_newlines;
98
+ while (output.length && (output[output.length - 1] === ' '
99
+ || output[output.length - 1] === indent_string
100
+ || output[output.length - 1] === preindent_string
101
+ || (eat_newlines && (output[output.length - 1] === '\n' || output[output.length - 1] === '\r')))) {
102
+ output.pop();
103
+ }
104
+ }
105
+
106
+ function trim(s) {
107
+ return s.replace(/^\s\s*|\s\s*$/, '');
108
+ }
109
+
110
+ function force_newline()
111
+ {
112
+ var old_keep_array_indentation = opt_keep_array_indentation;
113
+ opt_keep_array_indentation = false;
114
+ print_newline()
115
+ opt_keep_array_indentation = old_keep_array_indentation;
116
+ }
117
+
118
+ function print_newline(ignore_repeated) {
119
+
120
+ flags.eat_next_space = false;
121
+ if (opt_keep_array_indentation && is_array(flags.mode)) {
122
+ return;
123
+ }
124
+
125
+ ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated;
126
+
127
+ flags.if_line = false;
128
+ trim_output();
129
+
130
+ if (!output.length) {
131
+ return; // no newline on start of file
132
+ }
133
+
134
+ if (output[output.length - 1] !== "\n" || !ignore_repeated) {
135
+ just_added_newline = true;
136
+ output.push("\n");
137
+ }
138
+ if (preindent_string) {
139
+ output.push(preindent_string);
140
+ }
141
+ for (var i = 0; i < flags.indentation_level; i += 1) {
142
+ output.push(indent_string);
143
+ }
144
+ if (flags.var_line && flags.var_line_reindented) {
145
+ output.push(indent_string); // skip space-stuffing, if indenting with a tab
146
+ }
147
+ if (flags.case_body) {
148
+ output.push(indent_string);
149
+ }
150
+ }
151
+
152
+
153
+
154
+ function print_single_space() {
155
+
156
+ if (last_type === 'TK_COMMENT') {
157
+ // no you will not print just a space after a comment
158
+ return print_newline(true);
159
+ }
160
+
161
+ if (flags.eat_next_space) {
162
+ flags.eat_next_space = false;
163
+ return;
164
+ }
165
+ var last_output = ' ';
166
+ if (output.length) {
167
+ last_output = output[output.length - 1];
168
+ }
169
+ if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space
170
+ output.push(' ');
171
+ }
172
+ }
173
+
174
+
175
+ function print_token() {
176
+ just_added_newline = false;
177
+ flags.eat_next_space = false;
178
+ output.push(token_text);
179
+ }
180
+
181
+ function indent() {
182
+ flags.indentation_level += 1;
183
+ }
184
+
185
+
186
+ function remove_indent() {
187
+ if (output.length && output[output.length - 1] === indent_string) {
188
+ output.pop();
189
+ }
190
+ }
191
+
192
+ function set_mode(mode) {
193
+ if (flags) {
194
+ flag_store.push(flags);
195
+ }
196
+ flags = {
197
+ previous_mode: flags ? flags.mode : 'BLOCK',
198
+ mode: mode,
199
+ var_line: false,
200
+ var_line_tainted: false,
201
+ var_line_reindented: false,
202
+ in_html_comment: false,
203
+ if_line: false,
204
+ in_case: false,
205
+ case_body: false,
206
+ eat_next_space: false,
207
+ indentation_baseline: -1,
208
+ indentation_level: (flags ? flags.indentation_level + (flags.case_body?1:0) + ((flags.var_line && flags.var_line_reindented) ? 1 : 0) : 0),
209
+ ternary_depth: 0
210
+ };
211
+ }
212
+
213
+ function is_array(mode) {
214
+ return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]';
215
+ }
216
+
217
+ function is_expression(mode) {
218
+ return in_array(mode, ['[EXPRESSION]', '(EXPRESSION)', '(FOR-EXPRESSION)', '(COND-EXPRESSION)']);
219
+ }
220
+
221
+ function restore_mode() {
222
+ do_block_just_closed = flags.mode === 'DO_BLOCK';
223
+ if (flag_store.length > 0) {
224
+ var mode = flags.mode;
225
+ flags = flag_store.pop();
226
+ flags.previous_mode = mode;
227
+ }
228
+ }
229
+
230
+ function all_lines_start_with(lines, c) {
231
+ for (var i = 0; i < lines.length; i++) {
232
+ if (trim(lines[i])[0] != c) {
233
+ return false;
234
+ }
235
+ }
236
+ return true;
237
+ }
238
+
239
+ function is_special_word(word)
240
+ {
241
+ return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
242
+ }
243
+
244
+ function in_array(what, arr) {
245
+ for (var i = 0; i < arr.length; i += 1) {
246
+ if (arr[i] === what) {
247
+ return true;
248
+ }
249
+ }
250
+ return false;
251
+ }
252
+
253
+ function look_up(exclude) {
254
+ var local_pos = parser_pos;
255
+ var c = input.charAt(local_pos);
256
+ while (in_array(c, whitespace) && c != exclude) {
257
+ local_pos++;
258
+ if (local_pos >= input_length) return 0;
259
+ c = input.charAt(local_pos);
260
+ }
261
+ return c;
262
+ }
263
+
264
+ function get_next_token() {
265
+ n_newlines = 0;
266
+
267
+ if (parser_pos >= input_length) {
268
+ return ['', 'TK_EOF'];
269
+ }
270
+
271
+ wanted_newline = false;
272
+
273
+ var c = input.charAt(parser_pos);
274
+ parser_pos += 1;
275
+
276
+
277
+ var keep_whitespace = opt_keep_array_indentation && is_array(flags.mode);
278
+
279
+ if (keep_whitespace) {
280
+
281
+ //
282
+ // slight mess to allow nice preservation of array indentation and reindent that correctly
283
+ // first time when we get to the arrays:
284
+ // var a = [
285
+ // ....'something'
286
+ // we make note of whitespace_count = 4 into flags.indentation_baseline
287
+ // so we know that 4 whitespaces in original source match indent_level of reindented source
288
+ //
289
+ // and afterwards, when we get to
290
+ // 'something,
291
+ // .......'something else'
292
+ // we know that this should be indented to indent_level + (7 - indentation_baseline) spaces
293
+ //
294
+ var whitespace_count = 0;
295
+
296
+ while (in_array(c, whitespace)) {
297
+
298
+ if (c === "\n") {
299
+ trim_output();
300
+ output.push("\n");
301
+ just_added_newline = true;
302
+ whitespace_count = 0;
303
+ } else {
304
+ if (c === '\t') {
305
+ whitespace_count += 4;
306
+ } else if (c === '\r') {
307
+ // nothing
308
+ } else {
309
+ whitespace_count += 1;
310
+ }
311
+ }
312
+
313
+ if (parser_pos >= input_length) {
314
+ return ['', 'TK_EOF'];
315
+ }
316
+
317
+ c = input.charAt(parser_pos);
318
+ parser_pos += 1;
319
+
320
+ }
321
+ if (flags.indentation_baseline === -1) {
322
+ flags.indentation_baseline = whitespace_count;
323
+ }
324
+
325
+ if (just_added_newline) {
326
+ var i;
327
+ for (i = 0; i < flags.indentation_level + 1; i += 1) {
328
+ output.push(indent_string);
329
+ }
330
+ if (flags.indentation_baseline !== -1) {
331
+ for (i = 0; i < whitespace_count - flags.indentation_baseline; i++) {
332
+ output.push(' ');
333
+ }
334
+ }
335
+ }
336
+
337
+ } else {
338
+ while (in_array(c, whitespace)) {
339
+
340
+ if (c === "\n") {
341
+ n_newlines += ( (opt_max_preserve_newlines) ? (n_newlines <= opt_max_preserve_newlines) ? 1: 0: 1 );
342
+ }
343
+
344
+
345
+ if (parser_pos >= input_length) {
346
+ return ['', 'TK_EOF'];
347
+ }
348
+
349
+ c = input.charAt(parser_pos);
350
+ parser_pos += 1;
351
+
352
+ }
353
+
354
+ if (opt_preserve_newlines) {
355
+ if (n_newlines > 1) {
356
+ for (i = 0; i < n_newlines; i += 1) {
357
+ print_newline(i === 0);
358
+ just_added_newline = true;
359
+ }
360
+ }
361
+ }
362
+ wanted_newline = n_newlines > 0;
363
+ }
364
+
365
+
366
+ if (in_array(c, wordchar)) {
367
+ if (parser_pos < input_length) {
368
+ while (in_array(input.charAt(parser_pos), wordchar)) {
369
+ c += input.charAt(parser_pos);
370
+ parser_pos += 1;
371
+ if (parser_pos === input_length) {
372
+ break;
373
+ }
374
+ }
375
+ }
376
+
377
+ // small and surprisingly unugly hack for 1E-10 representation
378
+ if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) {
379
+
380
+ var sign = input.charAt(parser_pos);
381
+ parser_pos += 1;
382
+
383
+ var t = get_next_token(parser_pos);
384
+ c += sign + t[0];
385
+ return [c, 'TK_WORD'];
386
+ }
387
+
388
+ if (c === 'in') { // hack for 'in' operator
389
+ return [c, 'TK_OPERATOR'];
390
+ }
391
+ if (wanted_newline && last_type !== 'TK_OPERATOR'
392
+ && last_type !== 'TK_EQUALS'
393
+ && !flags.if_line && (opt_preserve_newlines || last_text !== 'var')) {
394
+ print_newline();
395
+ }
396
+ return [c, 'TK_WORD'];
397
+ }
398
+
399
+ if (c === '(' || c === '[') {
400
+ return [c, 'TK_START_EXPR'];
401
+ }
402
+
403
+ if (c === ')' || c === ']') {
404
+ return [c, 'TK_END_EXPR'];
405
+ }
406
+
407
+ if (c === '{') {
408
+ return [c, 'TK_START_BLOCK'];
409
+ }
410
+
411
+ if (c === '}') {
412
+ return [c, 'TK_END_BLOCK'];
413
+ }
414
+
415
+ if (c === ';') {
416
+ return [c, 'TK_SEMICOLON'];
417
+ }
418
+
419
+ if (c === '/') {
420
+ var comment = '';
421
+ // peek for comment /* ... */
422
+ var inline_comment = true;
423
+ if (input.charAt(parser_pos) === '*') {
424
+ parser_pos += 1;
425
+ if (parser_pos < input_length) {
426
+ while (! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/') && parser_pos < input_length) {
427
+ c = input.charAt(parser_pos);
428
+ comment += c;
429
+ if (c === '\x0d' || c === '\x0a') {
430
+ inline_comment = false;
431
+ }
432
+ parser_pos += 1;
433
+ if (parser_pos >= input_length) {
434
+ break;
435
+ }
436
+ }
437
+ }
438
+ parser_pos += 2;
439
+ if (inline_comment && n_newlines == 0) {
440
+ return ['/*' + comment + '*/', 'TK_INLINE_COMMENT'];
441
+ } else {
442
+ return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT'];
443
+ }
444
+ }
445
+ // peek for comment // ...
446
+ if (input.charAt(parser_pos) === '/') {
447
+ comment = c;
448
+ while (input.charAt(parser_pos) !== '\r' && input.charAt(parser_pos) !== '\n') {
449
+ comment += input.charAt(parser_pos);
450
+ parser_pos += 1;
451
+ if (parser_pos >= input_length) {
452
+ break;
453
+ }
454
+ }
455
+ parser_pos += 1;
456
+ if (wanted_newline) {
457
+ print_newline();
458
+ }
459
+ return [comment, 'TK_COMMENT'];
460
+ }
461
+
462
+ }
463
+
464
+ if (c === "'" || // string
465
+ c === '"' || // string
466
+ (c === '/' &&
467
+ ((last_type === 'TK_WORD' && is_special_word(last_text)) ||
468
+ (last_text === ')' && in_array(flags.previous_mode, ['(COND-EXPRESSION)', '(FOR-EXPRESSION)'])) ||
469
+ (last_type === 'TK_COMMENT' || last_type === 'TK_START_EXPR' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EQUALS' || last_type === 'TK_EOF' || last_type === 'TK_SEMICOLON')))) { // regexp
470
+ var sep = c;
471
+ var esc = false;
472
+ var resulting_string = c;
473
+
474
+ if (parser_pos < input_length) {
475
+ if (sep === '/') {
476
+ //
477
+ // handle regexp separately...
478
+ //
479
+ var in_char_class = false;
480
+ while (esc || in_char_class || input.charAt(parser_pos) !== sep) {
481
+ resulting_string += input.charAt(parser_pos);
482
+ if (!esc) {
483
+ esc = input.charAt(parser_pos) === '\\';
484
+ if (input.charAt(parser_pos) === '[') {
485
+ in_char_class = true;
486
+ } else if (input.charAt(parser_pos) === ']') {
487
+ in_char_class = false;
488
+ }
489
+ } else {
490
+ esc = false;
491
+ }
492
+ parser_pos += 1;
493
+ if (parser_pos >= input_length) {
494
+ // incomplete string/rexp when end-of-file reached.
495
+ // bail out with what had been received so far.
496
+ return [resulting_string, 'TK_STRING'];
497
+ }
498
+ }
499
+
500
+ } else {
501
+ //
502
+ // and handle string also separately
503
+ //
504
+ while (esc || input.charAt(parser_pos) !== sep) {
505
+ resulting_string += input.charAt(parser_pos);
506
+ if (!esc) {
507
+ esc = input.charAt(parser_pos) === '\\';
508
+ } else {
509
+ esc = false;
510
+ }
511
+ parser_pos += 1;
512
+ if (parser_pos >= input_length) {
513
+ // incomplete string/rexp when end-of-file reached.
514
+ // bail out with what had been received so far.
515
+ return [resulting_string, 'TK_STRING'];
516
+ }
517
+ }
518
+ }
519
+
520
+
521
+
522
+ }
523
+
524
+ parser_pos += 1;
525
+
526
+ resulting_string += sep;
527
+
528
+ if (sep === '/') {
529
+ // regexps may have modifiers /regexp/MOD , so fetch those, too
530
+ while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) {
531
+ resulting_string += input.charAt(parser_pos);
532
+ parser_pos += 1;
533
+ }
534
+ }
535
+ return [resulting_string, 'TK_STRING'];
536
+ }
537
+
538
+ if (c === '#') {
539
+
540
+
541
+ if (output.length === 0 && input.charAt(parser_pos) === '!') {
542
+ // shebang
543
+ resulting_string = c;
544
+ while (parser_pos < input_length && c != '\n') {
545
+ c = input.charAt(parser_pos);
546
+ resulting_string += c;
547
+ parser_pos += 1;
548
+ }
549
+ output.push(trim(resulting_string) + '\n');
550
+ print_newline();
551
+ return get_next_token();
552
+ }
553
+
554
+
555
+
556
+ // Spidermonkey-specific sharp variables for circular references
557
+ // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
558
+ // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
559
+ var sharp = '#';
560
+ if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) {
561
+ do {
562
+ c = input.charAt(parser_pos);
563
+ sharp += c;
564
+ parser_pos += 1;
565
+ } while (parser_pos < input_length && c !== '#' && c !== '=');
566
+ if (c === '#') {
567
+ //
568
+ } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
569
+ sharp += '[]';
570
+ parser_pos += 2;
571
+ } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
572
+ sharp += '{}';
573
+ parser_pos += 2;
574
+ }
575
+ return [sharp, 'TK_WORD'];
576
+ }
577
+ }
578
+
579
+ if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
580
+ parser_pos += 3;
581
+ c = '<!--';
582
+ while (input[parser_pos] != '\n' && parser_pos < input_length) {
583
+ c += input[parser_pos];
584
+ parser_pos++;
585
+ }
586
+ flags.in_html_comment = true;
587
+ return [c, 'TK_COMMENT'];
588
+ }
589
+
590
+ if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
591
+ flags.in_html_comment = false;
592
+ parser_pos += 2;
593
+ if (wanted_newline) {
594
+ print_newline();
595
+ }
596
+ return ['-->', 'TK_COMMENT'];
597
+ }
598
+
599
+ if (in_array(c, punct)) {
600
+ while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
601
+ c += input.charAt(parser_pos);
602
+ parser_pos += 1;
603
+ if (parser_pos >= input_length) {
604
+ break;
605
+ }
606
+ }
607
+
608
+ if (c === '=') {
609
+ return [c, 'TK_EQUALS'];
610
+ } else {
611
+ return [c, 'TK_OPERATOR'];
612
+ }
613
+ }
614
+
615
+ return [c, 'TK_UNKNOWN'];
616
+ }
617
+
618
+ //----------------------------------
619
+ indent_string = '';
620
+ while (opt_indent_size > 0) {
621
+ indent_string += opt_indent_char;
622
+ opt_indent_size -= 1;
623
+ }
624
+
625
+ while (js_source_text && (js_source_text[0] === ' ' || js_source_text[0] === '\t')) {
626
+ preindent_string += js_source_text[0];
627
+ js_source_text = js_source_text.substring(1);
628
+ }
629
+ input = js_source_text;
630
+
631
+ last_word = ''; // last 'TK_WORD' passed
632
+ last_type = 'TK_START_EXPR'; // last token type
633
+ last_text = ''; // last token text
634
+ last_last_text = ''; // pre-last token text
635
+ output = [];
636
+
637
+ do_block_just_closed = false;
638
+
639
+ whitespace = "\n\r\t ".split('');
640
+ wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
641
+ digits = '0123456789'.split('');
642
+
643
+ punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::';
644
+ punct += ' <%= <% %> <?= <? ?>'; // try to be a good boy and try not to break the markup language identifiers
645
+ punct = punct.split(' ');
646
+
647
+ // words which should always start on new line.
648
+ line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',');
649
+
650
+ // states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'.
651
+ // some formatting depends on that.
652
+ flag_store = [];
653
+ set_mode('BLOCK');
654
+
655
+ parser_pos = 0;
656
+ while (true) {
657
+ var t = get_next_token(parser_pos);
658
+ token_text = t[0];
659
+ token_type = t[1];
660
+ if (token_type === 'TK_EOF') {
661
+ break;
662
+ }
663
+
664
+ switch (token_type) {
665
+
666
+ case 'TK_START_EXPR':
667
+
668
+ if (token_text === '[') {
669
+
670
+ if (last_type === 'TK_WORD' || last_text === ')') {
671
+ // this is array index specifier, break immediately
672
+ // a[x], fn()[x]
673
+ if (in_array(last_text, line_starters)) {
674
+ print_single_space();
675
+ }
676
+ set_mode('(EXPRESSION)');
677
+ print_token();
678
+ break;
679
+ }
680
+
681
+ if (flags.mode === '[EXPRESSION]' || flags.mode === '[INDENTED-EXPRESSION]') {
682
+ if (last_last_text === ']' && last_text === ',') {
683
+ // ], [ goes to new line
684
+ if (flags.mode === '[EXPRESSION]') {
685
+ flags.mode = '[INDENTED-EXPRESSION]';
686
+ if (!opt_keep_array_indentation) {
687
+ indent();
688
+ }
689
+ }
690
+ set_mode('[EXPRESSION]');
691
+ if (!opt_keep_array_indentation) {
692
+ print_newline();
693
+ }
694
+ } else if (last_text === '[') {
695
+ if (flags.mode === '[EXPRESSION]') {
696
+ flags.mode = '[INDENTED-EXPRESSION]';
697
+ if (!opt_keep_array_indentation) {
698
+ indent();
699
+ }
700
+ }
701
+ set_mode('[EXPRESSION]');
702
+
703
+ if (!opt_keep_array_indentation) {
704
+ print_newline();
705
+ }
706
+ } else {
707
+ set_mode('[EXPRESSION]');
708
+ }
709
+ } else {
710
+ set_mode('[EXPRESSION]');
711
+ }
712
+
713
+
714
+
715
+ } else {
716
+ if (last_word === 'for') {
717
+ set_mode('(FOR-EXPRESSION)');
718
+ } else if (in_array(last_word, ['if', 'while'])) {
719
+ set_mode('(COND-EXPRESSION)');
720
+ } else {
721
+ set_mode('(EXPRESSION)');
722
+ }
723
+ }
724
+
725
+ if (last_text === ';' || last_type === 'TK_START_BLOCK') {
726
+ print_newline();
727
+ } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || last_text === '.') {
728
+ if (wanted_newline) {
729
+ print_newline();
730
+ }
731
+ // do nothing on (( and )( and ][ and ]( and .(
732
+ } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
733
+ print_single_space();
734
+ } else if (last_word === 'function' || last_word === 'typeof') {
735
+ // function() vs function ()
736
+ if (opt_jslint_happy) {
737
+ print_single_space();
738
+ }
739
+ } else if (in_array(last_text, line_starters) || last_text === 'catch') {
740
+ if (opt_space_before_conditional) {
741
+ print_single_space();
742
+ }
743
+ }
744
+ print_token();
745
+
746
+ break;
747
+
748
+ case 'TK_END_EXPR':
749
+ if (token_text === ']') {
750
+ if (opt_keep_array_indentation) {
751
+ if (last_text === '}') {
752
+ // trim_output();
753
+ // print_newline(true);
754
+ remove_indent();
755
+ print_token();
756
+ restore_mode();
757
+ break;
758
+ }
759
+ } else {
760
+ if (flags.mode === '[INDENTED-EXPRESSION]') {
761
+ if (last_text === ']') {
762
+ restore_mode();
763
+ print_newline();
764
+ print_token();
765
+ break;
766
+ }
767
+ }
768
+ }
769
+ }
770
+ restore_mode();
771
+ print_token();
772
+ break;
773
+
774
+ case 'TK_START_BLOCK':
775
+
776
+ if (last_word === 'do') {
777
+ set_mode('DO_BLOCK');
778
+ } else {
779
+ set_mode('BLOCK');
780
+ }
781
+ if (opt_brace_style=="expand" || opt_brace_style=="expand-strict") {
782
+ var empty_braces = false;
783
+ if (opt_brace_style == "expand-strict")
784
+ {
785
+ empty_braces = (look_up() == '}');
786
+ if (!empty_braces) {
787
+ print_newline(true);
788
+ }
789
+ } else {
790
+ if (last_type !== 'TK_OPERATOR') {
791
+ if (last_text === '=' || (is_special_word(last_text) && last_text !== 'else')) {
792
+ print_single_space();
793
+ } else {
794
+ print_newline(true);
795
+ }
796
+ }
797
+ }
798
+ print_token();
799
+ if (!empty_braces) indent();
800
+ } else {
801
+ if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
802
+ if (last_type === 'TK_START_BLOCK') {
803
+ print_newline();
804
+ } else {
805
+ print_single_space();
806
+ }
807
+ } else {
808
+ // if TK_OPERATOR or TK_START_EXPR
809
+ if (is_array(flags.previous_mode) && last_text === ',') {
810
+ if (last_last_text === '}') {
811
+ // }, { in array context
812
+ print_single_space();
813
+ } else {
814
+ print_newline(); // [a, b, c, {
815
+ }
816
+ }
817
+ }
818
+ indent();
819
+ print_token();
820
+ }
821
+
822
+ break;
823
+
824
+ case 'TK_END_BLOCK':
825
+ restore_mode();
826
+ if (opt_brace_style=="expand" || opt_brace_style == "expand-strict") {
827
+ if (last_text !== '{') {
828
+ print_newline();
829
+ }
830
+ print_token();
831
+ } else {
832
+ if (last_type === 'TK_START_BLOCK') {
833
+ // nothing
834
+ if (just_added_newline) {
835
+ remove_indent();
836
+ } else {
837
+ // {}
838
+ trim_output();
839
+ }
840
+ } else {
841
+ if (is_array(flags.mode) && opt_keep_array_indentation) {
842
+ // we REALLY need a newline here, but newliner would skip that
843
+ opt_keep_array_indentation = false;
844
+ print_newline();
845
+ opt_keep_array_indentation = true;
846
+
847
+ } else {
848
+ print_newline();
849
+ }
850
+ }
851
+ print_token();
852
+ }
853
+ break;
854
+
855
+ case 'TK_WORD':
856
+
857
+ // no, it's not you. even I have problems understanding how this works
858
+ // and what does what.
859
+ if (do_block_just_closed) {
860
+ // do {} ## while ()
861
+ print_single_space();
862
+ print_token();
863
+ print_single_space();
864
+ do_block_just_closed = false;
865
+ break;
866
+ }
867
+
868
+ if (token_text === 'function') {
869
+ if (flags.var_line) {
870
+ flags.var_line_reindented = true;
871
+ }
872
+ if ((just_added_newline || last_text === ';') && last_text !== '{'
873
+ && last_type != 'TK_BLOCK_COMMENT' && last_type != 'TK_COMMENT') {
874
+ // make sure there is a nice clean space of at least one blank line
875
+ // before a new function definition
876
+ n_newlines = just_added_newline ? n_newlines : 0;
877
+ if ( ! opt_preserve_newlines) {
878
+ n_newlines = 1;
879
+ }
880
+
881
+ for (var i = 0; i < 2 - n_newlines; i++) {
882
+ print_newline(false);
883
+ }
884
+ }
885
+ }
886
+
887
+ if (token_text === 'case' || token_text === 'default') {
888
+ if (last_text === ':' || flags.case_body) {
889
+ // switch cases following one another
890
+ remove_indent();
891
+ } else {
892
+ // case statement starts in the same line where switch
893
+ if (!opt_indent_case)
894
+ flags.indentation_level--;
895
+ print_newline();
896
+ if (!opt_indent_case)
897
+ flags.indentation_level++;
898
+ }
899
+ print_token();
900
+ flags.in_case = true;
901
+ flags.case_body = false;
902
+ break;
903
+ }
904
+
905
+ prefix = 'NONE';
906
+
907
+ if (last_type === 'TK_END_BLOCK') {
908
+
909
+ if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
910
+ prefix = 'NEWLINE';
911
+ } else {
912
+ if (opt_brace_style=="expand" || opt_brace_style=="end-expand" || opt_brace_style == "expand-strict") {
913
+ prefix = 'NEWLINE';
914
+ } else {
915
+ prefix = 'SPACE';
916
+ print_single_space();
917
+ }
918
+ }
919
+ } else if (last_type === 'TK_SEMICOLON' && (flags.mode === 'BLOCK' || flags.mode === 'DO_BLOCK')) {
920
+ prefix = 'NEWLINE';
921
+ } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
922
+ prefix = 'SPACE';
923
+ } else if (last_type === 'TK_STRING') {
924
+ prefix = 'NEWLINE';
925
+ } else if (last_type === 'TK_WORD') {
926
+ if (last_text === 'else') {
927
+ // eat newlines between ...else *** some_op...
928
+ // won't preserve extra newlines in this place (if any), but don't care that much
929
+ trim_output(true);
930
+ }
931
+ prefix = 'SPACE';
932
+ } else if (last_type === 'TK_START_BLOCK') {
933
+ prefix = 'NEWLINE';
934
+ } else if (last_type === 'TK_END_EXPR') {
935
+ print_single_space();
936
+ prefix = 'NEWLINE';
937
+ }
938
+
939
+ if (in_array(token_text, line_starters) && last_text !== ')') {
940
+ if (last_text == 'else') {
941
+ prefix = 'SPACE';
942
+ } else {
943
+ prefix = 'NEWLINE';
944
+ }
945
+
946
+ if (token_text === 'function' && (last_text === 'get' || last_text === 'set')) {
947
+ prefix = 'SPACE';
948
+ }
949
+ }
950
+
951
+ if (flags.if_line && last_type === 'TK_END_EXPR') {
952
+ flags.if_line = false;
953
+ }
954
+ if (in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
955
+ if (last_type !== 'TK_END_BLOCK' || opt_brace_style=="expand" || opt_brace_style=="end-expand" || opt_brace_style == "expand-strict") {
956
+ print_newline();
957
+ } else {
958
+ trim_output(true);
959
+ print_single_space();
960
+ }
961
+ } else if (prefix === 'NEWLINE') {
962
+ if ((last_type === 'TK_START_EXPR' || last_text === '=' || last_text === ',') && token_text === 'function') {
963
+ // no need to force newline on 'function': (function
964
+ // DONOTHING
965
+ } else if (token_text === 'function' && last_text == 'new') {
966
+ print_single_space();
967
+ } else if (is_special_word(last_text)) {
968
+ // no newline between 'return nnn'
969
+ print_single_space();
970
+ } else if (last_type !== 'TK_END_EXPR') {
971
+ if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') {
972
+ // no need to force newline on 'var': for (var x = 0...)
973
+ if (token_text === 'if' && last_word === 'else' && last_text !== '{') {
974
+ // no newline for } else if {
975
+ print_single_space();
976
+ } else {
977
+ flags.var_line = false;
978
+ flags.var_line_reindented = false;
979
+ print_newline();
980
+ }
981
+ }
982
+ } else if (in_array(token_text, line_starters) && last_text != ')') {
983
+ flags.var_line = false;
984
+ flags.var_line_reindented = false;
985
+ print_newline();
986
+ }
987
+ } else if (is_array(flags.mode) && last_text === ',' && last_last_text === '}') {
988
+ print_newline(); // }, in lists get a newline treatment
989
+ } else if (prefix === 'SPACE') {
990
+ print_single_space();
991
+ }
992
+ print_token();
993
+ last_word = token_text;
994
+
995
+ if (token_text === 'var') {
996
+ flags.var_line = true;
997
+ flags.var_line_reindented = false;
998
+ flags.var_line_tainted = false;
999
+ }
1000
+
1001
+ if (token_text === 'if') {
1002
+ flags.if_line = true;
1003
+ }
1004
+ if (token_text === 'else') {
1005
+ flags.if_line = false;
1006
+ }
1007
+
1008
+ break;
1009
+
1010
+ case 'TK_SEMICOLON':
1011
+
1012
+ print_token();
1013
+ flags.var_line = false;
1014
+ flags.var_line_reindented = false;
1015
+ if (flags.mode == 'OBJECT') {
1016
+ // OBJECT mode is weird and doesn't get reset too well.
1017
+ flags.mode = 'BLOCK';
1018
+ }
1019
+ break;
1020
+
1021
+ case 'TK_STRING':
1022
+
1023
+ if (last_type === 'TK_END_EXPR' && in_array(flags.previous_mode, ['(COND-EXPRESSION)', '(FOR-EXPRESSION)'])) {
1024
+ print_single_space();
1025
+ } else if (last_type == 'TK_STRING' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_SEMICOLON') {
1026
+ print_newline();
1027
+ } else if (last_type === 'TK_WORD') {
1028
+ print_single_space();
1029
+ }
1030
+ print_token();
1031
+ break;
1032
+
1033
+ case 'TK_EQUALS':
1034
+ if (flags.var_line) {
1035
+ // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
1036
+ flags.var_line_tainted = true;
1037
+ }
1038
+ print_single_space();
1039
+ print_token();
1040
+ print_single_space();
1041
+ break;
1042
+
1043
+ case 'TK_OPERATOR':
1044
+
1045
+ var space_before = true;
1046
+ var space_after = true;
1047
+
1048
+ if (flags.var_line && token_text === ',' && (is_expression(flags.mode))) {
1049
+ // do not break on comma, for(var a = 1, b = 2)
1050
+ flags.var_line_tainted = false;
1051
+ }
1052
+
1053
+ if (flags.var_line) {
1054
+ if (token_text === ',') {
1055
+ if (flags.var_line_tainted) {
1056
+ print_token();
1057
+ flags.var_line_reindented = true;
1058
+ flags.var_line_tainted = false;
1059
+ print_newline();
1060
+ break;
1061
+ } else {
1062
+ flags.var_line_tainted = false;
1063
+ }
1064
+ // } else if (token_text === ':') {
1065
+ // hmm, when does this happen? tests don't catch this
1066
+ // flags.var_line = false;
1067
+ }
1068
+ }
1069
+
1070
+ if (is_special_word(last_text)) {
1071
+ // "return" had a special handling in TK_WORD. Now we need to return the favor
1072
+ print_single_space();
1073
+ print_token();
1074
+ break;
1075
+ }
1076
+
1077
+ if (token_text === ':' && flags.in_case) {
1078
+ if (opt_indent_case)
1079
+ flags.case_body = true;
1080
+ print_token(); // colon really asks for separate treatment
1081
+ print_newline();
1082
+ flags.in_case = false;
1083
+ break;
1084
+ }
1085
+
1086
+ if (token_text === '::') {
1087
+ // no spaces around exotic namespacing syntax operator
1088
+ print_token();
1089
+ break;
1090
+ }
1091
+
1092
+ if (token_text === ',') {
1093
+ if (flags.var_line) {
1094
+ if (flags.var_line_tainted) {
1095
+ print_token();
1096
+ print_newline();
1097
+ flags.var_line_tainted = false;
1098
+ } else {
1099
+ print_token();
1100
+ print_single_space();
1101
+ }
1102
+ } else if (last_type === 'TK_END_BLOCK' && flags.mode !== "(EXPRESSION)") {
1103
+ print_token();
1104
+ if (flags.mode === 'OBJECT' && last_text === '}') {
1105
+ print_newline();
1106
+ } else {
1107
+ print_single_space();
1108
+ }
1109
+ } else {
1110
+ if (flags.mode === 'OBJECT') {
1111
+ print_token();
1112
+ print_newline();
1113
+ } else {
1114
+ // EXPR or DO_BLOCK
1115
+ print_token();
1116
+ print_single_space();
1117
+ }
1118
+ }
1119
+ break;
1120
+ // } else if (in_array(token_text, ['--', '++', '!']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS']) || in_array(last_text, line_starters) || in_array(last_text, ['==', '!=', '+=', '-=', '*=', '/=', '+', '-'])))) {
1121
+ } else if (in_array(token_text, ['--', '++', '!']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(last_text, line_starters)))) {
1122
+ // unary operators (and binary +/- pretending to be unary) special cases
1123
+
1124
+ space_before = false;
1125
+ space_after = false;
1126
+
1127
+ if (last_text === ';' && is_expression(flags.mode)) {
1128
+ // for (;; ++i)
1129
+ // ^^^
1130
+ space_before = true;
1131
+ }
1132
+ if (last_type === 'TK_WORD' && in_array(last_text, line_starters)) {
1133
+ space_before = true;
1134
+ }
1135
+
1136
+ if (flags.mode === 'BLOCK' && (last_text === '{' || last_text === ';')) {
1137
+ // { foo; --i }
1138
+ // foo(); --bar;
1139
+ print_newline();
1140
+ }
1141
+ } else if (token_text === '.') {
1142
+ // decimal digits or object.property
1143
+ space_before = false;
1144
+
1145
+ } else if (token_text === ':') {
1146
+ if (flags.ternary_depth == 0) {
1147
+ flags.mode = 'OBJECT';
1148
+ space_before = false;
1149
+ } else {
1150
+ flags.ternary_depth -= 1;
1151
+ }
1152
+ } else if (token_text === '?') {
1153
+ flags.ternary_depth += 1;
1154
+ }
1155
+ if (space_before) {
1156
+ print_single_space();
1157
+ }
1158
+
1159
+ print_token();
1160
+
1161
+ if (space_after) {
1162
+ print_single_space();
1163
+ }
1164
+
1165
+ if (token_text === '!') {
1166
+ // flags.eat_next_space = true;
1167
+ }
1168
+
1169
+ break;
1170
+
1171
+ case 'TK_BLOCK_COMMENT':
1172
+
1173
+ var lines = token_text.split(/\x0a|\x0d\x0a/);
1174
+
1175
+ if (all_lines_start_with(lines.slice(1), '*')) {
1176
+ // javadoc: reformat and reindent
1177
+ print_newline();
1178
+ output.push(lines[0]);
1179
+ for (i = 1; i < lines.length; i++) {
1180
+ print_newline();
1181
+ output.push(' ');
1182
+ output.push(trim(lines[i]));
1183
+ }
1184
+
1185
+ } else {
1186
+
1187
+ // simple block comment: leave intact
1188
+ if (lines.length > 1) {
1189
+ // multiline comment block starts with a new line
1190
+ print_newline();
1191
+ } else {
1192
+ // single-line /* comment */ stays where it is
1193
+ if (last_type === 'TK_END_BLOCK') {
1194
+ print_newline();
1195
+ } else {
1196
+ print_single_space();
1197
+ }
1198
+
1199
+ }
1200
+
1201
+ for (i = 0; i < lines.length; i++) {
1202
+ output.push(lines[i]);
1203
+ output.push('\n');
1204
+ }
1205
+
1206
+ }
1207
+ if(look_up('\n') != '\n')
1208
+ print_newline();
1209
+ break;
1210
+
1211
+ case 'TK_INLINE_COMMENT':
1212
+ print_single_space();
1213
+ print_token();
1214
+ if (is_expression(flags.mode)) {
1215
+ print_single_space();
1216
+ } else {
1217
+ force_newline();
1218
+ }
1219
+ break;
1220
+
1221
+ case 'TK_COMMENT':
1222
+
1223
+ // print_newline();
1224
+ if (wanted_newline) {
1225
+ print_newline();
1226
+ } else {
1227
+ print_single_space();
1228
+ }
1229
+ print_token();
1230
+ if(look_up('\n') != '\n')
1231
+ force_newline();
1232
+ break;
1233
+
1234
+ case 'TK_UNKNOWN':
1235
+ if (is_special_word(last_text)) {
1236
+ print_single_space();
1237
+ }
1238
+ print_token();
1239
+ break;
1240
+ }
1241
+
1242
+ last_last_text = last_text;
1243
+ last_type = token_type;
1244
+ last_text = token_text;
1245
+ }
1246
+
1247
+ var sweet_code = preindent_string + output.join('').replace(/[\n ]+$/, '');
1248
+ return sweet_code;
1249
+
1250
+ }
1251
+
1252
+ // Add support for CommonJS. Just put this file somewhere on your require.paths
1253
+ // and you will be able to `var js_beautify = require("beautify").js_beautify`.
1254
+ if (typeof exports !== "undefined")
1255
+ exports.js_beautify = js_beautify;