Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 496437 | Differences between
and this patch

Collapse All | Expand All

(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/javascriptFormatter.js (+195 lines)
Added Link Here
1
 /*******************************************************************************
2
 * @license
3
 * Copyright (c) 2016 IBM Corporation and others.
4
 * All rights reserved. This program and the accompanying materials are made 
5
 * available under the terms of the Eclipse Public License v1.0 
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
7
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
8
 *
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
/*eslint-env amd, browser*/
13
define([
14
'orion/objects',
15
'orion/Deferred'
16
], function(Objects, Deferred) {
17
	var config = {
18
		// 0:off, 1:warning, 2:error
19
		defaults: {
20
			"indent_size": 4,
21
			"indent_char": " ",
22
			"eol": "\n",
23
			"indent_level": 0,
24
			"indent_with_tabs": false,
25
			"preserve_newlines": true,
26
			"max_preserve_newlines": 10,
27
			"jslint_happy": false,
28
			"space_after_anon_function": false,
29
			"brace_style": "collapse",
30
			"keep_array_indentation": false,
31
			"keep_function_indentation": false,
32
			"space_before_conditional": true,
33
			"break_chained_methods": false,
34
			"eval_code": false,
35
			"unescape_strings": false,
36
			"wrap_line_length": 0,
37
			"wrap_attributes": "auto",
38
			"wrap_attributes_indent_size": 4,
39
			"end_with_newline": false
40
		},
41
		
42
		setOption: function(ruleId, value, key) {
43
			if(Array.isArray(this.rules[ruleId])) {
44
				var ruleConfig = this.rules[ruleId];
45
				if (key) {
46
					ruleConfig[1] = ruleConfig[1] || {};
47
					ruleConfig[1][key] = value;
48
				} else {
49
					ruleConfig[0] = value;
50
				}
51
			}
52
			else {
53
				this.rules[ruleId] = value;
54
			}
55
		},
56
		
57
		/**
58
		 * @description Resets the rules to their default values
59
		 * @function
60
		 */
61
		setDefaults: function setDefaults() {
62
			this.rules = Object.create(null);
63
			var keys = Object.keys(this.defaults);
64
			for(var i = 0; i < keys.length; i++) {
65
				var key = keys[i];
66
				this.rules[key] = this.defaults[key];
67
			}
68
		}
69
	};
70
71
	/**
72
	 * @name javascript.JavaScriptFormatter
73
	 * @description creates a new instance of the javascript formatter
74
	 * @constructor
75
	 * @public
76
	 * @param {Worker} ternWorker
77
	 */
78
	function JavaScriptFormatter(ternWorker, jsProject) {
79
		this.ternworker = ternWorker;
80
		this.project = jsProject;
81
		config.setDefaults();
82
	}
83
	
84
	Objects.mixin(JavaScriptFormatter.prototype, /** @lends javascript.JavaScriptFormatter.prototype*/ {
85
		
86
		/**
87
		 * @description Callback from the editor to format the source code
88
		 * @function
89
		 * @public 
90
		 * @memberof javascript.JavaScriptFormatter.prototype
91
		 * @param {orion.edit.EditorContext} editorContext The current editor context
92
		 * @param {Object} context The current selection context
93
		 */
94
		format: function(editorContext, context) {
95
			var deferred = new Deferred();
96
			if(this.project) {
97
				//TODO make sure we can get the options as set in the formatting preference page Right now only the indent character is customizable
98
				// We should expose all existing options - see defaults above
99
				this.project.getFormattingOptions().then(function(cfg) {
100
					this._format(editorContext, deferred, cfg ? cfg : config.rules);
101
				}.bind(this));
102
			} else {
103
				this._format(editorContext, deferred, config.rules);
104
			}
105
			return deferred;
106
		},
107
	
108
		/**
109
		 * @description Format the given editor
110
		 * @function
111
		 * @private
112
		 * @param {orion.edit.EditorContext} editorContext The given editor context
113
		 * @param {Deferred} deferred the given deferred object
114
		 * @param {Object} configuration the given configuration
115
		 * @since 6.0
116
		 */
117
		_format: function(editorContext, deferred, configuration) {
118
			return editorContext.getFileMetadata().then(function(meta) {
119
				return editorContext.getSelection().then(function(selection) {
120
					var start = selection.start;
121
					var end = selection.end;
122
					var files, request;
123
					if (end !== start) {
124
						return editorContext.getText(start, end).then(function(text) {
125
							files = [{type: 'full', name: meta.location, text: text}]; //$NON-NLS-1$
126
							request = {request: 'beautify', args: {meta: {location: meta.location}, files: files, config: configuration, start: start, end: end, contentType: meta.contentType.id}}; //$NON-NLS-1$
127
							this.ternworker.postMessage(
128
								request, 
129
								function(formatted, err) {
130
									if(err) {
131
										deferred.reject();
132
									}
133
									if(formatted && formatted.text) {
134
										deferred.resolve(editorContext.setText(formatted.text, start, end));
135
									} else {
136
										deferred.reject();
137
									}
138
								});
139
							return deferred;
140
						}.bind(this));
141
					}
142
					return editorContext.getText().then(function(text) {
143
						files = [{type: 'full', name: meta.location, text: text}]; //$NON-NLS-1$
144
						request = {request: 'beautify', args: {meta: {location: meta.location}, files: files, config: configuration, contentType: meta.contentType.id}}; //$NON-NLS-1$
145
						this.ternworker.postMessage(
146
							request, 
147
							function(formatted, err) {
148
								if(err) {
149
									deferred.reject();
150
								}
151
								if(formatted && formatted.text) {
152
									deferred.resolve(editorContext.setText(formatted.text));
153
								} else {
154
									deferred.reject();
155
								}
156
							});
157
						return deferred;
158
					}.bind(this));
159
				}.bind(this));
160
			}.bind(this));
161
		},
162
		
163
				/**
164
		 * @description Callback from orion.cm.managedservice
165
		 * @function
166
		 * @public
167
		 * @param {Object} properties The properties that have been changed
168
		 */
169
		updated: function(properties) {
170
			if (!properties) {
171
				return;
172
			}
173
			var oldconfig = properties.pid === 'jsbeautify.config.js';
174
			var keys = Object.keys(properties);
175
			for(var i = 0; i < keys.length; i++) {
176
				var key = keys[i];
177
				var originalKey = key;
178
				/*
179
				 * Do the key mapping for common options between css, js and html formatters
180
				 */
181
				key = key.replace(/^js_/, "");
182
				var ruleId = key;
183
				if(oldconfig && config.rules[key] !== config.defaults[key]) {
184
					//don't overwrite a new setting with an old one
185
					continue;
186
				}
187
				config.setOption(ruleId, properties[originalKey]);
188
			}
189
		},
190
	});
191
	
192
	return {
193
		JavaScriptFormatter: JavaScriptFormatter
194
	};
195
});
(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternWorkerCore.js (-1 / +34 lines)
Lines 513-519 function(Tern, defaultOptions, Deferred, Objects, Serialize, Messages, i18nUtil) Link Here
513
			} else {
513
			} else {
514
		       callback(null, {message: Messages['failedToComputeOccurrencesNoServer']});
514
		       callback(null, {message: Messages['failedToComputeOccurrencesNoServer']});
515
		   }
515
		   }
516
		}
516
		},
517
		/* lint message handler */
518
		'beautify': function(args, callback) {
519
			if(ternserver) {
520
				var query =
521
					{
522
						type: "beautify",  //$NON-NLS-1$
523
						file: args.meta.location,
524
						args: {
525
							config: args.config,
526
							start: args.start,
527
							end: args.end,
528
							contentType: args.contentType
529
						}
530
					};
531
				ternserver.request(
532
					{
533
						query: query,
534
						files: args.files
535
					},
536
					function(error, text) {
537
						if(error) {
538
							callback({request: 'beautify', error: error.message, message: Messages['failedToFormat']}); //$NON-NLS-1$
539
						} else if(text) {
540
							callback({request: 'beautify', text: text}); //$NON-NLS-1$
541
						} else {
542
							callback({request: 'beautify', text: ""}); //$NON-NLS-1$
543
						}
544
					}
545
				);
546
			} else {
547
				callback(null, {message: Messages['failedToFormatNoServer']});
548
			}
549
		},
517
	};
550
	};
518
551
519
	var ternID = 0;
552
	var ternID = 0;
(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/ternPlugins/beautifier.js (+52 lines)
Added Link Here
1
/*******************************************************************************
2
 * @license
3
 * Copyright (c) 2016 IBM Corporation and others.
4
 * All rights reserved. This program and the accompanying materials are made 
5
 * available under the terms of the Eclipse Public License v1.0 
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
7
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
8
 *
9
 * Contributors:
10
 *	 IBM Corporation - Allow original requirejs plugin to find files in Orion workspace
11
 *******************************************************************************/
12
/* eslint-disable missing-nls */
13
/*eslint-env node, amd*/
14
/*globals tern tern */
15
define([
16
	"tern/lib/tern",
17
	"beautifier/beautify-js"
18
], function(tern, Beautifier) {
19
20
	tern.registerPlugin("beautifier", /* @callback */ function(server, options) {
21
		return {
22
			//don't need any passes yet
23
		};
24
	});
25
26
	tern.defineQueryType("beautify", {
27
		takesFile: true,
28
		/**
29
		 * @callback
30
		 */
31
		run: function(server, query, file) {
32
			return format(query, file);
33
		}
34
	});
35
36
	/**
37
	 * @description Format the code using the right beautifier
38
	 * @param {Object} query The original Tern query object
39
	 * @param {Object} file The file object from Tern 
40
	 */
41
	function format(query, file) {
42
		var args = query.args;
43
		var text = file.text;
44
		if (args) {
45
			if (args.start && args.end) {
46
				text = file.text.substring(args.start, args.end);
47
			}
48
			return Beautifier.js_beautify(text, args.config);
49
		}
50
		return text;
51
	}
52
});
(-)a/bundles/org.eclipse.orion.client.ui/web/orion/edit/nls/root/messages.js (-1 / +3 lines)
Lines 105-109 define({ Link Here
105
	"showTooltipTooltip": "Shows the tooltip immediately based on the caret position",
105
	"showTooltipTooltip": "Shows the tooltip immediately based on the caret position",
106
	"emptyDeploymentInfoMessage": "Use the Launch Configurations dropdown to deploy this project",
106
	"emptyDeploymentInfoMessage": "Use the Launch Configurations dropdown to deploy this project",
107
	"Orion": "Orion",
107
	"Orion": "Orion",
108
	"OK": "Ok"
108
	"OK": "Ok",
109
	"Format" : "Format",
110
	"FormatTooltip":"Format editor contents"	
109
}); 
111
}); 
(-)a/bundles/org.eclipse.orion.client.ui/web/orion/formatter.js (+94 lines)
Added Link Here
1
/*******************************************************************************
2
 * @license
3
 * Copyright (c) 2016 IBM Corporation and others.
4
 * All rights reserved. This program and the accompanying materials are made 
5
 * available under the terms of the Eclipse Public License v1.0 
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
7
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
8
 *
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
/*eslint-env browser, amd*/
13
14
define ([
15
], function() {
16
	
17
	function Formatter(serviceRegistry, inputManager, editor) {
18
		this.serviceRegistry = serviceRegistry;
19
		this.inputManager = inputManager;
20
		this.editor = editor;
21
	}
22
	
23
	Formatter.prototype = {
24
		getFormatter: function() {
25
			var inputManagerContentType = this.inputManager.getContentType();
26
			var formatters = this.serviceRegistry.getServiceReferences("orion.edit.format"); //$NON-NLS-0$
27
			for (var i=0; i < formatters.length; i++) {
28
				var serviceReference = formatters[i];
29
				var contentTypes = serviceReference.getProperty("contentType"); //$NON-NLS-0$
30
				if (inputManagerContentType && inputManagerContentType.id) {
31
					var inputContentType = inputManagerContentType.id;
32
					if (Array.isArray(contentTypes)) {
33
						for (var j = 0, max = contentTypes.length; j < max; j++) {
34
							if (contentTypes[j] === inputContentType) {
35
								return this.serviceRegistry.getService(serviceReference);
36
							}
37
						}
38
					} else if (inputContentType === contentTypes) {
39
						return this.serviceRegistry.getService(serviceReference);
40
					}
41
				}
42
			}
43
			return null;
44
		},
45
		/*
46
		getServiceRefs : function(registry, contentType) {
47
			var contentTypeService = registry.getService("orion.core.contentTypeRegistry"); //$NON-NLS-0$
48
			function getFilteredServiceRef(registry, sReference, contentType) {
49
				var contentTypeIds = sReference.getProperty("contentType"); //$NON-NLS-0$
50
				return contentTypeService.isSomeExtensionOf(contentType, contentTypeIds).then(function(result) {
51
					return result ? sReference : null;
52
				});
53
			}
54
			var serviceRefs = registry.getServiceReferences("orion.edit.format"); //$NON-NLS-0$
55
			var filteredServiceRefs = [];
56
			for (var i=0; i < serviceRefs.length; i++) {
57
				var serviceRef = serviceRefs[i];
58
				if (serviceRef.getProperty("contentType")) { //$NON-NLS-0$
59
					filteredServiceRefs.push(getFilteredServiceRef(registry, serviceRef, contentType));
60
				}
61
			}
62
			
63
			// Return a promise that gives the service references that aren't null
64
			return Deferred.all(filteredServiceRefs, function(error) {return {_error: error}; }).then(
65
				function(serviceRefs) {
66
					var capableServiceRefs = [];
67
					for (var i=0; i < serviceRefs.length; i++) {
68
						var currentServiceRef = serviceRefs[i];
69
						if (currentServiceRef && !currentServiceRef._error) {
70
							capableServiceRefs.push(currentServiceRef);
71
						}
72
					}
73
					return capableServiceRefs;
74
				});
75
		},
76
*/
77
		isVisible: function() {
78
			return !!this.getFormatter();
79
		},
80
		
81
		doFormat: function() {
82
			var service = this.getFormatter();
83
			if (service) {
84
				var inputManager = this.inputManager;
85
				var selection = this.editor.getSelection();
86
				var context = {metadata: inputManager.getFileMetadata(), start: selection.start, end: selection.end};
87
				return service.format(this.editor.getEditorContext(), context);
88
			}
89
		}
90
	};
91
	return {Formatter: Formatter}; 
92
});
93
94
(-)a/bundles/org.eclipse.orion.client.ui/web/orion/nls/root/messages.js (+1 lines)
Lines 91-96 define({//Default message bundle Link Here
91
	"Generate a sample plugin for integrating with Orion.": "Generate a sample plug-in for integrating with Orion.",
91
	"Generate a sample plugin for integrating with Orion.": "Generate a sample plug-in for integrating with Orion.",
92
	"Browser": "Web Browser",
92
	"Browser": "Web Browser",
93
	"OutlineProgress": "Getting outline for ${0} from ${1}",
93
	"OutlineProgress": "Getting outline for ${0} from ${1}",
94
	"FormatProgress" : "Formatting ${0} from ${1}",
94
	"outlineTimeout": "Outline service timed out. Try reloading the page and opening the outline again.",
95
	"outlineTimeout": "Outline service timed out. Try reloading the page and opening the outline again.",
95
	"UnknownError": "An unknown error occurred.",
96
	"UnknownError": "An unknown error occurred.",
96
	"Filter": "Filter (* = any string, ? = any character)",
97
	"Filter": "Filter (* = any string, ? = any character)",
(-)a/bundles/org.eclipse.orion.client.webtools/web/beautifier/beautifier.js (+20 lines)
Added Link Here
1
/*eslint-env amd, node*/
2
define([
3
	"./lib/beautify-css",
4
	"./lib/beautify-html"
5
], function(css_beautify, html_beautify) {
6
7
	function get_beautify(css_beautify, html_beautify) {
8
		var beautify = Object.create(null);
9
		// short aliases
10
		beautify.css = css_beautify.css_beautify;
11
		beautify.html = html_beautify.html_beautify;
12
	
13
		// legacy aliases
14
		beautify.css_beautify = css_beautify.css_beautify;
15
		beautify.html_beautify = html_beautify.html_beautify;
16
	
17
		return beautify;
18
	}
19
	return get_beautify(css_beautify, html_beautify);
20
});
(-)a/bundles/org.eclipse.orion.client.webtools/web/beautifier/lib/beautify-css.js (+490 lines)
Added Link Here
1
/*
2
3
  The MIT License (MIT)
4
5
  Copyright (c) 2007-2013 Einar Lielmanis and contributors.
6
7
  Permission is hereby granted, free of charge, to any person
8
  obtaining a copy of this software and associated documentation files
9
  (the "Software"), to deal in the Software without restriction,
10
  including without limitation the rights to use, copy, modify, merge,
11
  publish, distribute, sublicense, and/or sell copies of the Software,
12
  and to permit persons to whom the Software is furnished to do so,
13
  subject to the following conditions:
14
15
  The above copyright notice and this permission notice shall be
16
  included in all copies or substantial portions of the Software.
17
18
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
  SOFTWARE.
26
27
28
 CSS Beautifier
29
---------------
30
31
    Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)
32
33
    Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
34
        http://jsbeautifier.org/
35
36
    Usage:
37
        css_beautify(source_text);
38
        css_beautify(source_text, options);
39
40
    The options are (default in brackets):
41
        indent_size (4)                         - indentation size,
42
        indent_char (space)                     - character to indent with,
43
        selector_separator_newline (true)       - separate selectors with newline or
44
                                                  not (e.g. "a,\nbr" or "a, br")
45
        end_with_newline (false)                - end with a newline
46
        newline_between_rules (true)            - add a new line after every css rule
47
        space_around_selector_separator (false) - ensure space around selector separators:
48
                                                  '>', '+', '~' (e.g. "a>b" -> "a > b")
49
    e.g
50
51
    css_beautify(css_source_text, {
52
      'indent_size': 1,
53
      'indent_char': '\t',
54
      'selector_separator': ' ',
55
      'end_with_newline': false,
56
      'newline_between_rules': true,
57
      'space_around_selector_separator': true
58
    });
59
*/
60
61
// http://www.w3.org/TR/CSS21/syndata.html#tokenization
62
// http://www.w3.org/TR/css3-syntax/
63
/*eslint-env amd */
64
define([], function() {
65
	
66
    function css_beautify(source_text, options) {
67
        options = options || {};
68
        source_text = source_text || '';
69
        // HACK: newline parsing inconsistent. This brute force normalizes the input.
70
        source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n');
71
72
        var indentSize = options.indent_size || 4;
73
        var indentCharacter = options.indent_char || ' ';
74
        var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
75
        var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
76
        var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
77
        var spaceAroundSelectorSeparator = (options.space_around_selector_separator === undefined) ? false : options.space_around_selector_separator;
78
        var eol = options.eol ? options.eol : '\n';
79
80
        // compatibility
81
        if (typeof indentSize === "string") {
82
            indentSize = parseInt(indentSize, 10);
83
        }
84
85
        if (options.indent_with_tabs) {
86
            indentCharacter = '\t';
87
            indentSize = 1;
88
        }
89
90
        eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
91
92
93
        // tokenizer
94
        var whiteRe = /^\s+$/;
95
96
        var pos = -1,
97
            ch;
98
        var parenLevel = 0;
99
100
        function next() {
101
            ch = source_text.charAt(++pos);
102
            return ch || '';
103
        }
104
105
        function peek(skipWhitespace) {
106
            var result = '';
107
            var prev_pos = pos;
108
            if (skipWhitespace) {
109
                eatWhitespace();
110
            }
111
            result = source_text.charAt(pos + 1) || '';
112
            pos = prev_pos - 1;
113
            next();
114
            return result;
115
        }
116
117
        function eatString(endChars) {
118
            var start = pos;
119
            while (next()) {
120
                if (ch === "\\") {
121
                    next();
122
                } else if (endChars.indexOf(ch) !== -1) {
123
                    break;
124
                } else if (ch === "\n") {
125
                    break;
126
                }
127
            }
128
            return source_text.substring(start, pos + 1);
129
        }
130
131
        function peekString(endChar) {
132
            var prev_pos = pos;
133
            var str = eatString(endChar);
134
            pos = prev_pos - 1;
135
            next();
136
            return str;
137
        }
138
139
        function eatWhitespace() {
140
            var result = '';
141
            while (whiteRe.test(peek())) {
142
                next();
143
                result += ch;
144
            }
145
            return result;
146
        }
147
148
        function skipWhitespace() {
149
            var result = '';
150
            if (ch && whiteRe.test(ch)) {
151
                result = ch;
152
            }
153
            while (whiteRe.test(next())) {
154
                result += ch;
155
            }
156
            return result;
157
        }
158
159
        function eatComment(singleLine) {
160
            var start = pos;
161
            singleLine = peek() === "/";
162
            next();
163
            while (next()) {
164
                if (!singleLine && ch === "*" && peek() === "/") {
165
                    next();
166
                    break;
167
                } else if (singleLine && ch === "\n") {
168
                    return source_text.substring(start, pos);
169
                }
170
            }
171
172
            return source_text.substring(start, pos) + ch;
173
        }
174
175
176
        function lookBack(str) {
177
            return source_text.substring(pos - str.length, pos).toLowerCase() ===
178
                str;
179
        }
180
181
        // Nested pseudo-class if we are insideRule
182
        // and the next special character found opens
183
        // a new block
184
        function foundNestedPseudoClass() {
185
            var openParen = 0;
186
            for (var i = pos + 1; i < source_text.length; i++) {
187
                var ch = source_text.charAt(i);
188
                if (ch === "{") {
189
                    return true;
190
                } else if (ch === '(') {
191
                    // pseudoclasses can contain ()
192
                    openParen += 1;
193
                } else if (ch === ')') {
194
                    if (openParen === 0) {
195
                        return false;
196
                    }
197
                    openParen -= 1;
198
                } else if (ch === ";" || ch === "}") {
199
                    return false;
200
                }
201
            }
202
            return false;
203
        }
204
205
        // printer
206
        var basebaseIndentString = source_text.match(/^[\t ]*/)[0];
207
        var singleIndent = new Array(indentSize + 1).join(indentCharacter);
208
        var indentLevel = 0;
209
        var nestedLevel = 0;
210
211
        function indent() {
212
            indentLevel++;
213
            basebaseIndentString += singleIndent;
214
        }
215
216
        function outdent() {
217
            indentLevel--;
218
            basebaseIndentString = basebaseIndentString.slice(0, -indentSize);
219
        }
220
221
        var print = {};
222
        print["{"] = function(ch) {
223
            print.singleSpace();
224
            output.push(ch);
225
            print.newLine();
226
        };
227
        print["}"] = function(ch) {
228
            print.newLine();
229
            output.push(ch);
230
            print.newLine();
231
        };
232
233
        print._lastCharWhitespace = function() {
234
            return whiteRe.test(output[output.length - 1]);
235
        };
236
237
        print.newLine = function(keepWhitespace) {
238
            if (output.length) {
239
                if (!keepWhitespace && output[output.length - 1] !== '\n') {
240
                    print.trim();
241
                }
242
243
                output.push('\n');
244
245
                if (basebaseIndentString) {
246
                    output.push(basebaseIndentString);
247
                }
248
            }
249
        };
250
        print.singleSpace = function() {
251
            if (output.length && !print._lastCharWhitespace()) {
252
                output.push(' ');
253
            }
254
        };
255
256
        print.preserveSingleSpace = function() {
257
            if (isAfterSpace) {
258
                print.singleSpace();
259
            }
260
        };
261
262
        print.trim = function() {
263
            while (print._lastCharWhitespace()) {
264
                output.pop();
265
            }
266
        };
267
268
269
        var output = [];
270
        /*_____________________--------------------_____________________*/
271
272
        var insideRule = false;
273
        var insidePropertyValue = false;
274
        var enteringConditionalGroup = false;
275
        var top_ch = '';
276
        var last_top_ch = '';
277
278
        while (true) {
279
            var whitespace = skipWhitespace();
280
            var isAfterSpace = whitespace !== '';
281
            var isAfterNewline = whitespace.indexOf('\n') !== -1;
282
            last_top_ch = top_ch;
283
            top_ch = ch;
284
285
            if (!ch) {
286
                break;
287
            } else if (ch === '/' && peek() === '*') { /* css comment */
288
                var header = indentLevel === 0;
289
290
                if (isAfterNewline || header) {
291
                    print.newLine();
292
                }
293
294
                output.push(eatComment());
295
                print.newLine();
296
                if (header) {
297
                    print.newLine(true);
298
                }
299
            } else if (ch === '/' && peek() === '/') { // single line comment
300
                if (!isAfterNewline && last_top_ch !== '{') {
301
                    print.trim();
302
                }
303
                print.singleSpace();
304
                output.push(eatComment());
305
                print.newLine();
306
            } else if (ch === '@') {
307
                print.preserveSingleSpace();
308
309
                // deal with less propery mixins @{...}
310
                if (peek() === '{') {
311
                    output.push(eatString('}'));
312
                } else {
313
                    output.push(ch);
314
315
                    // strip trailing space, if present, for hash property checks
316
                    var variableOrRule = peekString(": ,;{}()[]/='\"");
317
318
                    if (variableOrRule.match(/[ :]$/)) {
319
                        // we have a variable or pseudo-class, add it and insert one space before continuing
320
                        next();
321
                        variableOrRule = eatString(": ").replace(/\s$/, '');
322
                        output.push(variableOrRule);
323
                        print.singleSpace();
324
                    }
325
326
                    variableOrRule = variableOrRule.replace(/\s$/, '');
327
328
                    // might be a nesting at-rule
329
                    if (variableOrRule in css_beautify.NESTED_AT_RULE) {
330
                        nestedLevel += 1;
331
                        if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) {
332
                            enteringConditionalGroup = true;
333
                        }
334
                    }
335
                }
336
            } else if (ch === '#' && peek() === '{') {
337
                print.preserveSingleSpace();
338
                output.push(eatString('}'));
339
            } else if (ch === '{') {
340
                if (peek(true) === '}') {
341
                    eatWhitespace();
342
                    next();
343
                    print.singleSpace();
344
                    output.push("{}");
345
                    print.newLine();
346
                    if (newline_between_rules && indentLevel === 0) {
347
                        print.newLine(true);
348
                    }
349
                } else {
350
                    indent();
351
                    print["{"](ch);
352
                    // when entering conditional groups, only rulesets are allowed
353
                    if (enteringConditionalGroup) {
354
                        enteringConditionalGroup = false;
355
                        insideRule = (indentLevel > nestedLevel);
356
                    } else {
357
                        // otherwise, declarations are also allowed
358
                        insideRule = (indentLevel >= nestedLevel);
359
                    }
360
                }
361
            } else if (ch === '}') {
362
                outdent();
363
                print["}"](ch);
364
                insideRule = false;
365
                insidePropertyValue = false;
366
                if (nestedLevel) {
367
                    nestedLevel--;
368
                }
369
                if (newline_between_rules && indentLevel === 0) {
370
                    print.newLine(true);
371
                }
372
            } else if (ch === ":") {
373
                eatWhitespace();
374
                if ((insideRule || enteringConditionalGroup) &&
375
                    !(lookBack("&") || foundNestedPseudoClass())) {
376
                    // 'property: value' delimiter
377
                    // which could be in a conditional group query
378
                    insidePropertyValue = true;
379
                    output.push(':');
380
                    print.singleSpace();
381
                } else {
382
                    // sass/less parent reference don't use a space
383
                    // sass nested pseudo-class don't use a space
384
                    if (peek() === ":") {
385
                        // pseudo-element
386
                        next();
387
                        output.push("::");
388
                    } else {
389
                        // pseudo-class
390
                        output.push(':');
391
                    }
392
                }
393
            } else if (ch === '"' || ch === '\'') {
394
                print.preserveSingleSpace();
395
                output.push(eatString(ch));
396
            } else if (ch === ';') {
397
                insidePropertyValue = false;
398
                output.push(ch);
399
                print.newLine();
400
            } else if (ch === '(') { // may be a url
401
                if (lookBack("url")) {
402
                    output.push(ch);
403
                    eatWhitespace();
404
                    if (next()) {
405
                        if (ch !== ')' && ch !== '"' && ch !== '\'') {
406
                            output.push(eatString(')'));
407
                        } else {
408
                            pos--;
409
                        }
410
                    }
411
                } else {
412
                    parenLevel++;
413
                    print.preserveSingleSpace();
414
                    output.push(ch);
415
                    eatWhitespace();
416
                }
417
            } else if (ch === ')') {
418
                output.push(ch);
419
                parenLevel--;
420
            } else if (ch === ',') {
421
                output.push(ch);
422
                eatWhitespace();
423
                if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
424
                    print.newLine();
425
                } else {
426
                    print.singleSpace();
427
                }
428
            } else if (ch === '>' || ch === '+' || ch === '~') {
429
                //handl selector separator spacing
430
                if (spaceAroundSelectorSeparator && !insidePropertyValue && parenLevel < 1) {
431
                    print.singleSpace();
432
                    output.push(ch);
433
                    print.singleSpace();
434
                } else {
435
                    output.push(ch);
436
                }
437
            } else if (ch === ']') {
438
                output.push(ch);
439
            } else if (ch === '[') {
440
                print.preserveSingleSpace();
441
                output.push(ch);
442
            } else if (ch === '=') { // no whitespace before or after
443
                eatWhitespace();
444
                ch = '=';
445
                output.push(ch);
446
            } else {
447
                print.preserveSingleSpace();
448
                output.push(ch);
449
            }
450
        }
451
452
453
        var sweetCode = '';
454
        if (basebaseIndentString) {
455
            sweetCode += basebaseIndentString;
456
        }
457
458
        sweetCode += output.join('').replace(/[\r\n\t ]+$/, '');
459
460
        // establish end_with_newline
461
        if (end_with_newline) {
462
            sweetCode += '\n';
463
        }
464
465
        if (eol !== '\n') {
466
            sweetCode = sweetCode.replace(/[\n]/g, eol);
467
        }
468
469
        return sweetCode;
470
    }
471
472
    // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
473
    css_beautify.NESTED_AT_RULE = {
474
        "@page": true,
475
        "@font-face": true,
476
        "@keyframes": true,
477
        // also in CONDITIONAL_GROUP_RULE below
478
        "@media": true,
479
        "@supports": true,
480
        "@document": true
481
    };
482
    css_beautify.CONDITIONAL_GROUP_RULE = {
483
        "@media": true,
484
        "@supports": true,
485
        "@document": true
486
    };
487
    return {
488
        css_beautify: css_beautify
489
    };
490
});
(-)a/bundles/org.eclipse.orion.client.webtools/web/webtools/cssFormatter.js (+165 lines)
Added Link Here
1
/*******************************************************************************
2
 * @license
3
 * Copyright (c) 2016 IBM Corporation and others.
4
 * All rights reserved. This program and the accompanying materials are made 
5
 * available under the terms of the Eclipse Public License v1.0 
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
7
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
8
 *
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
/*eslint-env amd */
13
define("webtools/cssFormatter", [
14
'orion/objects',
15
'orion/Deferred',
16
'beautifier/beautifier'
17
], function(Objects, Deferred, Beautifier) {
18
	var config = {
19
		// 0:off, 1:warning, 2:error
20
		/*
21
		 *     The options are (default in brackets):
22
        indent_size (4)                         - indentation size,
23
        indent_char (space)                     - character to indent with,
24
        selector_separator_newline (true)       - separate selectors with newline or
25
                                                  not (e.g. "a,\nbr" or "a, br")
26
        end_with_newline (false)                - end with a newline
27
        newline_between_rules (true)            - add a new line after every css rule
28
        space_around_selector_separator (false) - ensure space around selector separators:
29
                                                  '>', '+', '~' (e.g. "a>b" -> "a > b")
30
		 * 
31
		 */
32
		defaults: {
33
			"indent_size": 4,
34
			"indent_char": " ",
35
			"eol": "\n",
36
			"end_with_newline": false,
37
			"newline_between_rules" : true,
38
			"space_around_selector_separator" : false
39
		},
40
		
41
		setOption: function(ruleId, value, key) {
42
			if(Array.isArray(this.rules[ruleId])) {
43
				var ruleConfig = this.rules[ruleId];
44
				if (key) {
45
					ruleConfig[1] = ruleConfig[1] || {};
46
					ruleConfig[1][key] = value;
47
				} else {
48
					ruleConfig[0] = value;
49
				}
50
			}
51
			else {
52
				this.rules[ruleId] = value;
53
			}
54
		},
55
		
56
		/**
57
		 * @description Resets the rules to their default values
58
		 * @function
59
		 */
60
		setDefaults: function setDefaults() {
61
			this.rules = Object.create(null);
62
			var keys = Object.keys(this.defaults);
63
			for(var i = 0; i < keys.length; i++) {
64
				var key = keys[i];
65
				this.rules[key] = this.defaults[key];
66
			}
67
		}
68
	};
69
70
	/**
71
	 * @name javascript.CssFormatter
72
	 * @description creates a new instance of the javascript formatter
73
	 * @constructor
74
	 * @public
75
	 * @param {Worker} ternWorker
76
	 */
77
	function CssFormatter() {
78
		config.setDefaults();
79
	}
80
	
81
	Objects.mixin(CssFormatter.prototype, /** @lends javascript.CssFormatter.prototype*/ {
82
		
83
		/**
84
		 * @description Callback from the editor to format the source code
85
		 * @function
86
		 * @public 
87
		 * @memberof javascript.CssFormatter.prototype
88
		 * @param {orion.edit.EditorContext} editorContext The current editor context
89
		 * @param {Object} context The current selection context
90
		 */
91
		format: function(editorContext, context) {
92
			var deferred = new Deferred();
93
			this._format(editorContext, deferred, config.rules);
94
			return deferred;
95
		},
96
	
97
		/**
98
		 * @description Format the given editor
99
		 * @function
100
		 * @private
101
		 * @param {orion.edit.EditorContext} editorContext The given editor context
102
		 * @param {Deferred} deferred the given deferred object
103
		 * @param {Object} configuration the given configuration
104
		 * @since 6.0
105
		 */
106
		_format: function(editorContext, deferred, configuration) {
107
			return editorContext.getSelection().then(function(selection) {
108
				var start = selection.start;
109
				var end = selection.end;
110
				if (end !== start) {
111
					return editorContext.getText(start, end).then(function(text) {
112
						var formatted = Beautifier.css_beautify(text, configuration);
113
						if (formatted) {
114
							deferred.resolve(editorContext.setText(formatted.text, start, end));
115
						} else {
116
							deferred.reject();
117
						}
118
						return deferred;
119
					}.bind(this));
120
				}
121
				return editorContext.getText().then(function(text) {
122
					var formatted = Beautifier.css_beautify(text, configuration);
123
					if (formatted) {
124
						deferred.resolve(editorContext.setText(formatted));
125
					} else {
126
						deferred.reject();
127
					}
128
					return deferred;
129
				}.bind(this));
130
			}.bind(this));
131
		},
132
		
133
		/**
134
		 * @description Callback from orion.cm.managedservice
135
		 * @function
136
		 * @public
137
		 * @param {Object} properties The properties that have been changed
138
		 */
139
		updated: function(properties) {
140
			if (!properties) {
141
				return;
142
			}
143
			var oldconfig = properties.pid === 'jsbeautify.config.css';
144
			var keys = Object.keys(properties);
145
			for(var i = 0; i < keys.length; i++) {
146
				var key = keys[i];
147
				var originalKey = key;
148
				/*
149
				 * Do the key mapping for common options between css, js and html formatters
150
				 */
151
				key = key.replace(/^css_/, "");
152
				var ruleId = key;
153
				if(oldconfig && config.rules[key] !== config.defaults[key]) {
154
					//don't overwrite a new setting with an old one
155
					continue;
156
				}
157
				config.setOption(ruleId, properties[originalKey]);
158
			}
159
		},
160
	});
161
	
162
	return {
163
		CssFormatter: CssFormatter
164
	};
165
});
(-)a/bundles/org.eclipse.orion.client.webtools/web/webtools/nls/root/messages.js (-4 / +53 lines)
Lines 19-30 define({//Default message bundle Link Here
19
	'csslintValidator' : 'CSS Validator',   
19
	'csslintValidator' : 'CSS Validator',   
20
	'pluginName': 'Orion Web Tools Support',   
20
	'pluginName': 'Orion Web Tools Support',   
21
	'pluginDescription': 'This plug-in provides web language tools support for Orion, including HTML and CSS.',   
21
	'pluginDescription': 'This plug-in provides web language tools support for Orion, including HTML and CSS.',   
22
	'fontHoverExampleText': 'Lorem ipsum dolor...',   
22
	'fontHoverExampleText': 'Lorem ipsum dolor...',
23
	'cssFormatter' : 'CSS Formatter',
24
	'htmlFormatter' : 'HTML Formatter',
25
26
	// CSS and HTML formatting options names
27
	'indentation_space' : 'space',
28
	'indentation_tab' : 'tab',
29
	'indentation_unix' : 'Unix',
30
	'indentation_mac' : 'Mac',
31
	'indentation_windows' : 'Windows',
32
	'collapse_preserve_inline' : 'Collapse Preserve inline',
33
	'collapse' : 'Collapse',
34
	'expand' : 'Expand',
35
	'end_expand' : 'End expand',
36
	'none' : 'None',
37
	'normal' : 'Normal',
38
	'keep' : 'Keep',
39
	'separate' : 'Separate',
40
	'auto' : 'Auto',
41
	'force' : 'Force',
42
	
43
	// CSS Formatter Settings
44
	'cssFormattingSettings' : 'Formatting Settings for CSS',
45
	'cssFormatting' : 'CSS Formatting',
46
	'css_indent_size' : 'Indentation size:' ,
47
	'css_indent_char' : 'Indentation character:',
48
	'css_eol' : 'Charater(s) to use as line terminators:',
49
	'css_end_with_newline' : 'End ouput with newline:',
50
	'selector_separator_newline' : 'Add a newline between multiple selectors:',
51
	'newline_between_rules' : 'Add a newline between CSS rules:',
52
	'space_around_selector_separator' : 'Ensure space around selector separators:',
53
54
	// HTML Formatter Settings
55
	'htmlFormattingOptions' : 'Formatting Settings for HTML',
56
	'htmlFormatting' : 'HTML Formatting',
57
	'html_indent_size' : 'Indentation size:',
58
	'html_indent_char' : 'Indentation character:',
59
	'html_eol' : 'Charater(s) to use as line terminators:',
60
	'html_end_with_newline' : 'End ouput with newline:',
61
	'html_preserve_new_lines' : 'Preserve line-breaks:',
62
	'html_max_preserve_new_lines' : 'Number of line-breaks to be preserved in one chunk:',
63
	'html_brace_style' : 'Brace Style:',
64
	'html_wrap_line_length' : 'Wrap lines at next opportunity after N characters: (0 for unlimited)',
65
	'indent_inner_html' : 'Indent <head> and <body> sections:',
66
	'indent_handlebars' : 'Format and indent {{#foo}} and {{/foo}}:',
67
	'wrap_attributes' : 'Wrap attributes to new lines:',
68
	'wrap_attributes_indent_size' : 'Indent wrapped attributes to after N characters:',
69
	'extra_liners' : 'List of tags that should have an extra newline before them (separate with commas):',
70
	'indent_scripts' : 'Indent scripts:',
71
23
	
72
	
24
	// Validator Severities
73
	// Validator Severities
25
	'ignore' : 'Ignore',   
74
	'ignore' : 'Ignore',
26
	'warning' : 'Warning',   
75
	'warning' : 'Warning',
27
	'error' : 'Error',   
76
	'error' : 'Error',
28
	
77
	
29
	// CSS Validator Settings
78
	// CSS Validator Settings
30
	'adjoining-classes': 'Disallow adjoining classes:',   
79
	'adjoining-classes': 'Disallow adjoining classes:',   
(-)a/bundles/org.eclipse.orion.client.webtools/web/webtools/htmlFormatter.js (+224 lines)
Added Link Here
1
/*******************************************************************************
2
 * @license
3
 * Copyright (c) 2016 IBM Corporation and others.
4
 * All rights reserved. This program and the accompanying materials are made 
5
 * available under the terms of the Eclipse Public License v1.0 
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
7
 * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
8
 *
9
 * Contributors:
10
 *     IBM Corporation - initial API and implementation
11
 *******************************************************************************/
12
/*eslint-env amd */
13
define("webtools/htmlFormatter", [
14
'orion/objects',
15
'orion/Deferred',
16
'beautifier/beautifier'
17
], function(Objects, Deferred, Beautifier) {
18
	var config = {
19
		// 0:off, 1:warning, 2:error
20
		/*
21
		 *     The options are (default in brackets):
22
    indent_inner_html (default false)  - indent <head> and <body> sections,
23
    indent_size (default 4)            - indentation size,
24
    indent_char (default space)        - character to indent with,
25
    wrap_line_length (default 250)            -  maximum amount of characters per line (0 = disable)
26
    brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none"
27
            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, or attempt to keep them where they are.
28
    unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
29
    indent_scripts (default normal)  - "keep"|"separate"|"normal"
30
    preserve_newlines (default true) - whether existing line breaks before elements should be preserved
31
                                        Only works before elements, not inside tags or for text.
32
    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk
33
    indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}}
34
    end_with_newline (false)          - end with a newline
35
    extra_liners (default [head,body,/html]) -List of tags that should have an extra newline before them.
36
    eol
37
		 * 
38
		 */
39
		defaults: {
40
			"indent_size": 4,
41
			"indent_char": " ",
42
			"indent_inner_html" : false,
43
			"eol": "\n",
44
			"end_with_newline": false,
45
			"wrap_line_length" : 250,
46
			"brace_style" : "collapse",
47
			"indent_scripts" : "normal",
48
			"preserve_newlines" : true,
49
			"max_preserve_newlines" : 10,
50
			"indent_handlebars" : false,
51
			"extra_liners" : "head,body,html",
52
			"newline_between_rules" : true,
53
			"space_around_selector_separator" : false,
54
			"indent_level": 0,
55
			"space_after_anon_function": false,
56
			"keep_array_indentation": false,
57
			"keep_function_indentation": false,
58
			"space_before_conditional": true,
59
			"break_chained_methods": false,
60
			"eval_code": false,
61
			"unescape_strings": false,
62
			"wrap_attributes": "auto",
63
			"wrap_attributes_indent_size": 4,
64
		},
65
		
66
		setOption: function(ruleId, value, key) {
67
			if(Array.isArray(this.rules[ruleId])) {
68
				var ruleConfig = this.rules[ruleId];
69
				if (key) {
70
					ruleConfig[1] = ruleConfig[1] || {};
71
					ruleConfig[1][key] = value;
72
				} else {
73
					ruleConfig[0] = value;
74
				}
75
			}
76
			else {
77
				this.rules[ruleId] = value;
78
			}
79
		},
80
		
81
		/**
82
		 * @description Resets the rules to their default values
83
		 * @function
84
		 */
85
		setDefaults: function setDefaults() {
86
			this.rules = Object.create(null);
87
			var keys = Object.keys(this.defaults);
88
			for(var i = 0; i < keys.length; i++) {
89
				var key = keys[i];
90
				this.rules[key] = this.defaults[key];
91
			}
92
		}
93
	};
94
95
	/**
96
	 * @name javascript.HtmlFormatter
97
	 * @description creates a new instance of the html formatter
98
	 * @constructor
99
	 * @public
100
	 */
101
	function HtmlFormatter() {
102
		config.setDefaults();
103
	}
104
	
105
	Objects.mixin(HtmlFormatter.prototype, /** @lends javascript.HtmlFormatter.prototype*/ {
106
		
107
		/**
108
		 * @description Callback from the editor to format the source code
109
		 * @function
110
		 * @public 
111
		 * @memberof javascript.HtmlFormatter.prototype
112
		 * @param {orion.edit.EditorContext} editorContext The current editor context
113
		 * @param {Object} context The current selection context
114
		 */
115
		format: function(editorContext, context) {
116
			var deferred = new Deferred();
117
			this._format(editorContext, deferred, config.rules);
118
			return deferred;
119
		},
120
	
121
		/**
122
		 * @description Format the given editor
123
		 * @function
124
		 * @private
125
		 * @param {orion.edit.EditorContext} editorContext The given editor context
126
		 * @param {Deferred} deferred the given deferred object
127
		 * @param {Object} configuration the given configuration
128
		 * @since 6.0
129
		 */
130
		_format: function(editorContext, deferred, configuration) {
131
			return editorContext.getSelection().then(function(selection) {
132
				var start = selection.start;
133
				var end = selection.end;
134
				if (end !== start) {
135
					return editorContext.getText(start, end).then(function(text) {
136
						var formatted = Beautifier.html_beautify(text, configuration);
137
						if (formatted) {
138
							deferred.resolve(editorContext.setText(formatted.text, start, end));
139
						} else {
140
							deferred.reject();
141
						}
142
						return deferred;
143
					}.bind(this));
144
				}
145
				return editorContext.getText().then(function(text) {
146
					var formatted = Beautifier.html_beautify(text, configuration);
147
					if (formatted) {
148
						deferred.resolve(editorContext.setText(formatted));
149
					} else {
150
						deferred.reject();
151
					}
152
					return deferred;
153
				}.bind(this));
154
			}.bind(this));
155
		},
156
		
157
		/**
158
		 * @description Callback from orion.cm.managedservice
159
		 * @function
160
		 * @public
161
		 * @param {Object} properties The properties that have been changed
162
		 */
163
		updated: function(properties) {
164
			if (!properties) {
165
				return;
166
			}
167
			var oldconfig = properties.pid === 'jsbeautify.config.css';
168
			var keys = Object.keys(properties);
169
			var i, key, ruleId, originalKey;
170
			for(i = 0; i < keys.length; i++) {
171
				key = keys[i];
172
				originalKey = key;
173
				/*
174
				 * Do the key mapping for common options between css, js and html formatters
175
				 */
176
				key = key.replace(/^css_/, "");
177
				ruleId = key;
178
				if(oldconfig && config.rules[key] !== config.defaults[key]) {
179
					//don't overwrite a new setting with an old one
180
					continue;
181
				}
182
				config.setOption(ruleId, properties[originalKey]);
183
			}
184
			
185
			oldconfig = properties.pid === 'jsbeautify.config.js';
186
			keys = Object.keys(properties);
187
			for(i = 0; i < keys.length; i++) {
188
				key = keys[i];
189
				originalKey = key;
190
				/*
191
				 * Do the key mapping for common options between css, js and html formatters
192
				 */
193
				key = key.replace(/^js_/, "");
194
				ruleId = key;
195
				if(oldconfig && config.rules[key] !== config.defaults[key]) {
196
					//don't overwrite a new setting with an old one
197
					continue;
198
				}
199
				config.setOption(ruleId, properties[originalKey]);
200
			}
201
202
			oldconfig = properties.pid === 'jsbeautify.config.html';
203
			keys = Object.keys(properties);
204
			for(i = 0; i < keys.length; i++) {
205
				key = keys[i];
206
				originalKey = key;
207
				/*
208
				 * Do the key mapping for common options between css, js and html formatters
209
				 */
210
				key = key.replace(/^html_/, "");
211
				ruleId = key;
212
				if(oldconfig && config.rules[key] !== config.defaults[key]) {
213
					//don't overwrite a new setting with an old one
214
					continue;
215
				}
216
				config.setOption(ruleId, properties[originalKey]);
217
			}
218
		},
219
	});
220
	
221
	return {
222
		HtmlFormatter: HtmlFormatter
223
	};
224
});
(-)a/bundles/org.eclipse.orion.client.webtools/web/beautifier/lib/beautify-html.js (+995 lines)
Added Link Here
1
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
2
/*
3
4
  The MIT License (MIT)
5
6
  Copyright (c) 2007-2013 Einar Lielmanis and contributors.
7
8
  Permission is hereby granted, free of charge, to any person
9
  obtaining a copy of this software and associated documentation files
10
  (the "Software"), to deal in the Software without restriction,
11
  including without limitation the rights to use, copy, modify, merge,
12
  publish, distribute, sublicense, and/or sell copies of the Software,
13
  and to permit persons to whom the Software is furnished to do so,
14
  subject to the following conditions:
15
16
  The above copyright notice and this permission notice shall be
17
  included in all copies or substantial portions of the Software.
18
19
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
  SOFTWARE.
27
28
29
 Style HTML
30
---------------
31
32
  Written by Nochum Sossonko, (nsossonko@hotmail.com)
33
34
  Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
35
    http://jsbeautifier.org/
36
37
  Usage:
38
    style_html(html_source);
39
40
    style_html(html_source, options);
41
42
  The options are:
43
    indent_inner_html (default false)  - indent <head> and <body> sections,
44
    indent_size (default 4)            - indentation size,
45
    indent_char (default space)        - character to indent with,
46
    wrap_line_length (default 250)            -  maximum amount of characters per line (0 = disable)
47
    brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none"
48
            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, or attempt to keep them where they are.
49
    unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
50
    indent_scripts (default normal)  - "keep"|"separate"|"normal"
51
    preserve_newlines (default true) - whether existing line breaks before elements should be preserved
52
                                        Only works before elements, not inside tags or for text.
53
    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk
54
    indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}}
55
    end_with_newline (false)          - end with a newline
56
    extra_liners (default [head,body,/html]) -List of tags that should have an extra newline before them.
57
58
    e.g.
59
60
    style_html(html_source, {
61
      'indent_inner_html': false,
62
      'indent_size': 2,
63
      'indent_char': ' ',
64
      'wrap_line_length': 78,
65
      'brace_style': 'expand',
66
      'preserve_newlines': true,
67
      'max_preserve_newlines': 5,
68
      'indent_handlebars': false,
69
      'extra_liners': ['/html']
70
    });
71
*/
72
/*eslint-env amd */
73
define([
74
	"beautifier/beautify-js", // reference the javascript beautifier inside javascript tooling
75
	"./beautify-css"
76
], function(js_beautify, css_beautify) {
77
78
    // function trim(s) {
79
    //     return s.replace(/^\s+|\s+$/g, '');
80
    // }
81
82
    function ltrim(s) {
83
        return s.replace(/^\s+/g, '');
84
    }
85
86
    function rtrim(s) {
87
        return s.replace(/\s+$/g, '');
88
    }
89
90
    function style_html(html_source, options, js_beautify, css_beautify) {
91
        //Wrapper function to invoke all the necessary constructors and deal with the output.
92
93
        var multi_parser,
94
            indent_inner_html,
95
            indent_size,
96
            indent_character,
97
            wrap_line_length,
98
            brace_style,
99
            unformatted,
100
            preserve_newlines,
101
            max_preserve_newlines,
102
            indent_handlebars,
103
            wrap_attributes,
104
            wrap_attributes_indent_size,
105
            end_with_newline,
106
            extra_liners,
107
            eol;
108
109
        options = options || {};
110
111
        // backwards compatibility to 1.3.4
112
        if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
113
            (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
114
            options.wrap_line_length = options.max_char;
115
        }
116
117
        indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
118
        indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
119
        indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
120
        brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
121
        wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
122
        unformatted = options.unformatted || [
123
            // https://www.w3.org/TR/html5/dom.html#phrasing-content
124
            'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite',
125
            'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img',
126
            'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark', 'math', 'meter', 'noscript',
127
            'object', 'output', 'progress', 'q', 'ruby', 's', 'samp', /* 'script', */ 'select', 'small',
128
            'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var',
129
            'video', 'wbr', 'text',
130
            // prexisting - not sure of full effect of removing, leaving in
131
            'acronym', 'address', 'big', 'dt', 'ins', 'small', 'strike', 'tt',
132
            'pre',
133
            'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
134
        ];
135
        preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
136
        max_preserve_newlines = preserve_newlines ?
137
            (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10)) :
138
            0;
139
        indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
140
        wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes;
141
        wrap_attributes_indent_size = (isNaN(parseInt(options.wrap_attributes_indent_size, 10))) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10);
142
        end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
143
        extra_liners = (typeof options.extra_liners === 'object') && options.extra_liners ?
144
            options.extra_liners.concat() : (typeof options.extra_liners === 'string') ?
145
            options.extra_liners.split(',') : 'head,body,/html'.split(',');
146
        eol = options.eol ? options.eol : '\n';
147
148
        if (options.indent_with_tabs) {
149
            indent_character = '\t';
150
            indent_size = 1;
151
        }
152
153
        eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
154
155
        function Parser() {
156
157
            this.pos = 0; //Parser position
158
            this.token = '';
159
            this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
160
            this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
161
                parent: 'parent1',
162
                parentcount: 1,
163
                parent1: ''
164
            };
165
            this.tag_type = '';
166
            this.token_text = this.last_token = this.last_text = this.token_type = '';
167
            this.newlines = 0;
168
            this.indent_content = indent_inner_html;
169
170
            this.Utils = { //Uilities made available to the various functions
171
                whitespace: "\n\r\t ".split(''),
172
173
                single_token: [
174
                    // HTLM void elements - aka self-closing tags - aka singletons
175
                    // https://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
176
                    'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
177
                    'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr',
178
                    // NOTE: Optional tags - are not understood.
179
                    // https://www.w3.org/TR/html5/syntax.html#optional-tags
180
                    // The rules for optional tags are too complex for a simple list
181
                    // Also, the content of these tags should still be indented in many cases.
182
                    // 'li' is a good exmple.
183
184
                    // Doctype and xml elements
185
                    '!doctype', '?xml',
186
                    // ?php tag
187
                    '?php',
188
                    // other tags that were in this list, keeping just in case
189
                    'basefont', 'isindex'
190
                ],
191
                extra_liners: extra_liners, //for tags that need a line of whitespace before them
192
                in_array: function(what, arr) {
193
                    for (var i = 0; i < arr.length; i++) {
194
                        if (what === arr[i]) {
195
                            return true;
196
                        }
197
                    }
198
                    return false;
199
                }
200
            };
201
202
            // Return true if the given text is composed entirely of whitespace.
203
            this.is_whitespace = function(text) {
204
                for (var n = 0; n < text.length; n++) {
205
                    if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) {
206
                        return false;
207
                    }
208
                }
209
                return true;
210
            };
211
212
            this.traverse_whitespace = function() {
213
                var input_char = '';
214
215
                input_char = this.input.charAt(this.pos);
216
                if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
217
                    this.newlines = 0;
218
                    while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
219
                        if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) {
220
                            this.newlines += 1;
221
                        }
222
223
                        this.pos++;
224
                        input_char = this.input.charAt(this.pos);
225
                    }
226
                    return true;
227
                }
228
                return false;
229
            };
230
231
            // Append a space to the given content (string array) or, if we are
232
            // at the wrap_line_length, append a newline/indentation.
233
            // return true if a newline was added, false if a space was added
234
            this.space_or_wrap = function(content) {
235
                if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
236
                    this.print_newline(false, content);
237
                    this.print_indentation(content);
238
                    return true;
239
                } else {
240
                    this.line_char_count++;
241
                    content.push(' ');
242
                    return false;
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
                        var wrapped = this.space_or_wrap(content);
397
                        var indentAttrs = wrapped && input_char !== '/' && wrap_attributes !== 'force';
398
                        space = false;
399
                        if (!first_attr && wrap_attributes === 'force' && input_char !== '/') {
400
                            this.print_newline(false, content);
401
                            this.print_indentation(content);
402
                            indentAttrs = true;
403
                        }
404
                        if (indentAttrs) {
405
                            //indent attributes an auto or forced line-wrap
406
                            for (var count = 0; count < wrap_attributes_indent_size; count++) {
407
                                content.push(indent_character);
408
                            }
409
                        }
410
                        for (var i = 0; i < content.length; i++) {
411
                            if (content[i] === ' ') {
412
                                first_attr = false;
413
                                break;
414
                            }
415
                        }
416
                    }
417
418
                    if (indent_handlebars && tag_start_char === '<') {
419
                        // When inside an angle-bracket tag, put spaces around
420
                        // handlebars not inside of strings.
421
                        if ((input_char + this.input.charAt(this.pos)) === '{{') {
422
                            input_char += this.get_unformatted('}}');
423
                            if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
424
                                input_char = ' ' + input_char;
425
                            }
426
                            space = true;
427
                        }
428
                    }
429
430
                    if (input_char === '<' && !tag_start_char) {
431
                        tag_start = this.pos - 1;
432
                        tag_start_char = '<';
433
                    }
434
435
                    if (indent_handlebars && !tag_start_char) {
436
                        if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] === '{') {
437
                            if (input_char === '#' || input_char === '/' || input_char === '!') {
438
                                tag_start = this.pos - 3;
439
                            } else {
440
                                tag_start = this.pos - 2;
441
                            }
442
                            tag_start_char = '{';
443
                        }
444
                    }
445
446
                    this.line_char_count++;
447
                    content.push(input_char); //inserts character at-a-time (or string)
448
449
                    if (content[1] && (content[1] === '!' || content[1] === '?' || content[1] === '%')) { //if we're in a comment, do something special
450
                        // We treat all comments as literals, even more than preformatted tags
451
                        // we just look for the appropriate close tag
452
                        content = [this.get_comment(tag_start)];
453
                        break;
454
                    }
455
456
                    if (indent_handlebars && content[1] && content[1] === '{' && content[2] && content[2] === '!') { //if we're in a comment, do something special
457
                        // We treat all comments as literals, even more than preformatted tags
458
                        // we just look for the appropriate close tag
459
                        content = [this.get_comment(tag_start)];
460
                        break;
461
                    }
462
463
                    if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') {
464
                        break;
465
                    }
466
                } while (input_char !== '>');
467
468
                var tag_complete = content.join('');
469
                var tag_index;
470
                var tag_offset;
471
472
                if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends
473
                    tag_index = tag_complete.indexOf(' ');
474
                } else if (tag_complete.charAt(0) === '{') {
475
                    tag_index = tag_complete.indexOf('}');
476
                } else { //otherwise go with the tag ending
477
                    tag_index = tag_complete.indexOf('>');
478
                }
479
                if (tag_complete.charAt(0) === '<' || !indent_handlebars) {
480
                    tag_offset = 1;
481
                } else {
482
                    tag_offset = tag_complete.charAt(2) === '#' ? 3 : 2;
483
                }
484
                var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
485
                if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
486
                    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 /)
487
                    if (!peek) {
488
                        this.tag_type = 'SINGLE';
489
                    }
490
                } else if (indent_handlebars && tag_complete.charAt(0) === '{' && tag_check === 'else') {
491
                    if (!peek) {
492
                        this.indent_to_tag('if');
493
                        this.tag_type = 'HANDLEBARS_ELSE';
494
                        this.indent_content = true;
495
                        this.traverse_whitespace();
496
                    }
497
                } else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
498
                    comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
499
                    content.push(comment);
500
                    tag_end = this.pos - 1;
501
                    this.tag_type = 'SINGLE';
502
                } else if (tag_check === 'script' &&
503
                    (tag_complete.search('type') === -1 ||
504
                        (tag_complete.search('type') > -1 &&
505
                            tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json)/) > -1))) {
506
                    if (!peek) {
507
                        this.record_tag(tag_check);
508
                        this.tag_type = 'SCRIPT';
509
                    }
510
                } else if (tag_check === 'style' &&
511
                    (tag_complete.search('type') === -1 ||
512
                        (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
513
                    if (!peek) {
514
                        this.record_tag(tag_check);
515
                        this.tag_type = 'STYLE';
516
                    }
517
                } else if (tag_check.charAt(0) === '!') { //peek for <! comment
518
                    // for comments content is already correct.
519
                    if (!peek) {
520
                        this.tag_type = 'SINGLE';
521
                        this.traverse_whitespace();
522
                    }
523
                } else if (!peek) {
524
                    if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
525
                        this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
526
                        this.tag_type = 'END';
527
                    } else { //otherwise it's a start-tag
528
                        this.record_tag(tag_check); //push it on the tag stack
529
                        if (tag_check.toLowerCase() !== 'html') {
530
                            this.indent_content = true;
531
                        }
532
                        this.tag_type = 'START';
533
                    }
534
535
                    // Allow preserving of newlines after a start or end tag
536
                    if (this.traverse_whitespace()) {
537
                        this.space_or_wrap(content);
538
                    }
539
540
                    if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
541
                        this.print_newline(false, this.output);
542
                        if (this.output.length && this.output[this.output.length - 2] !== '\n') {
543
                            this.print_newline(true, this.output);
544
                        }
545
                    }
546
                }
547
548
                if (peek) {
549
                    this.pos = orig_pos;
550
                    this.line_char_count = orig_line_char_count;
551
                }
552
553
                return content.join(''); //returns fully formatted tag
554
            };
555
556
            this.get_comment = function(start_pos) { //function to return comment content in its entirety
557
                // this is will have very poor perf, but will work for now.
558
                var comment = '',
559
                    delimiter = '>',
560
                    matched = false;
561
562
                this.pos = start_pos;
563
                var input_char = this.input.charAt(this.pos);
564
                this.pos++;
565
566
                while (this.pos <= this.input.length) {
567
                    comment += input_char;
568
569
                    // only need to check for the delimiter if the last chars match
570
                    if (comment.charAt(comment.length - 1) === delimiter.charAt(delimiter.length - 1) &&
571
                        comment.indexOf(delimiter) !== -1) {
572
                        break;
573
                    }
574
575
                    // only need to search for custom delimiter for the first few characters
576
                    if (!matched && comment.length < 10) {
577
                        if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
578
                            delimiter = '<![endif]>';
579
                            matched = true;
580
                        } else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
581
                            delimiter = ']]>';
582
                            matched = true;
583
                        } else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
584
                            delimiter = ']>';
585
                            matched = true;
586
                        } else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
587
                            delimiter = '-->';
588
                            matched = true;
589
                        } else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment
590
                            delimiter = '}}';
591
                            matched = true;
592
                        } else if (comment.indexOf('<?') === 0) { // {{! handlebars comment
593
                            delimiter = '?>';
594
                            matched = true;
595
                        } else if (comment.indexOf('<%') === 0) { // {{! handlebars comment
596
                            delimiter = '%>';
597
                            matched = true;
598
                        }
599
                    }
600
601
                    input_char = this.input.charAt(this.pos);
602
                    this.pos++;
603
                }
604
605
                return comment;
606
            };
607
608
            function tokenMatcher(delimiter) {
609
                var token = '';
610
611
                var add = function(str) {
612
                    var newToken = token + str.toLowerCase();
613
                    token = newToken.length <= delimiter.length ? newToken : newToken.substr(newToken.length - delimiter.length, delimiter.length);
614
                };
615
616
                var doesNotMatch = function() {
617
                    return token.indexOf(delimiter) === -1;
618
                };
619
620
                return {
621
                    add: add,
622
                    doesNotMatch: doesNotMatch
623
                };
624
            }
625
626
            this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety
627
                if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) {
628
                    return '';
629
                }
630
                var input_char = '';
631
                var content = '';
632
                var space = true;
633
634
                var delimiterMatcher = tokenMatcher(delimiter);
635
636
                do {
637
638
                    if (this.pos >= this.input.length) {
639
                        return content;
640
                    }
641
642
                    input_char = this.input.charAt(this.pos);
643
                    this.pos++;
644
645
                    if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
646
                        if (!space) {
647
                            this.line_char_count--;
648
                            continue;
649
                        }
650
                        if (input_char === '\n' || input_char === '\r') {
651
                            content += '\n';
652
                            /*  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'
653
                for (var i=0; i<this.indent_level; i++) {
654
                  content += this.indent_string;
655
                }
656
                space = false; //...and make sure other indentation is erased
657
                */
658
                            this.line_char_count = 0;
659
                            continue;
660
                        }
661
                    }
662
                    content += input_char;
663
                    delimiterMatcher.add(input_char);
664
                    this.line_char_count++;
665
                    space = true;
666
667
                    if (indent_handlebars && input_char === '{' && content.length && content.charAt(content.length - 2) === '{') {
668
                        // Handlebars expressions in strings should also be unformatted.
669
                        content += this.get_unformatted('}}');
670
                        // Don't consider when stopping for delimiters.
671
                    }
672
                } while (delimiterMatcher.doesNotMatch());
673
674
                return content;
675
            };
676
677
            this.get_token = function() { //initial handler for token-retrieval
678
                var token;
679
680
                if (this.last_token === 'TK_TAG_SCRIPT' || this.last_token === 'TK_TAG_STYLE') { //check if we need to format javascript
681
                    var type = this.last_token.substr(7);
682
                    token = this.get_contents_to(type);
683
                    if (typeof token !== 'string') {
684
                        return token;
685
                    }
686
                    return [token, 'TK_' + type];
687
                }
688
                if (this.current_mode === 'CONTENT') {
689
                    token = this.get_content();
690
                    if (typeof token !== 'string') {
691
                        return token;
692
                    } else {
693
                        return [token, 'TK_CONTENT'];
694
                    }
695
                }
696
697
                if (this.current_mode === 'TAG') {
698
                    token = this.get_tag();
699
                    if (typeof token !== 'string') {
700
                        return token;
701
                    } else {
702
                        var tag_name_type = 'TK_TAG_' + this.tag_type;
703
                        return [token, tag_name_type];
704
                    }
705
                }
706
            };
707
708
            this.get_full_indent = function(level) {
709
                level = this.indent_level + level || 0;
710
                if (level < 1) {
711
                    return '';
712
                }
713
714
                return Array(level + 1).join(this.indent_string);
715
            };
716
717
            this.is_unformatted = function(tag_check, unformatted) {
718
                //is this an HTML5 block-level link?
719
                if (!this.Utils.in_array(tag_check, unformatted)) {
720
                    return false;
721
                }
722
723
                if (tag_check.toLowerCase() !== 'a' || !this.Utils.in_array('a', unformatted)) {
724
                    return true;
725
                }
726
727
                //at this point we have an  tag; is its first child something we want to remain
728
                //unformatted?
729
                var next_tag = this.get_tag(true /* peek. */ );
730
731
                // test next_tag to see if it is just html tag (no external content)
732
                var tag = (next_tag || "").match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);
733
734
                // if next_tag comes back but is not an isolated tag, then
735
                // let's treat the 'a' tag as having content
736
                // and respect the unformatted option
737
                if (!tag || this.Utils.in_array(tag, unformatted)) {
738
                    return true;
739
                } else {
740
                    return false;
741
                }
742
            };
743
744
            this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
745
746
                this.input = js_source || ''; //gets the input for the Parser
747
748
                // HACK: newline parsing inconsistent. This brute force normalizes the input.
749
                this.input = this.input.replace(/\r\n|[\r\u2028\u2029]/g, '\n');
750
751
                this.output = [];
752
                this.indent_character = indent_character;
753
                this.indent_string = '';
754
                this.indent_size = indent_size;
755
                this.brace_style = brace_style;
756
                this.indent_level = 0;
757
                this.wrap_line_length = wrap_line_length;
758
                this.line_char_count = 0; //count to see if wrap_line_length was exceeded
759
760
                for (var i = 0; i < this.indent_size; i++) {
761
                    this.indent_string += this.indent_character;
762
                }
763
764
                this.print_newline = function(force, arr) {
765
                    this.line_char_count = 0;
766
                    if (!arr || !arr.length) {
767
                        return;
768
                    }
769
                    if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
770
                        if ((arr[arr.length - 1] !== '\n')) {
771
                            arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
772
                        }
773
                        arr.push('\n');
774
                    }
775
                };
776
777
                this.print_indentation = function(arr) {
778
                    for (var i = 0; i < this.indent_level; i++) {
779
                        arr.push(this.indent_string);
780
                        this.line_char_count += this.indent_string.length;
781
                    }
782
                };
783
784
                this.print_token = function(text) {
785
                    // Avoid printing initial whitespace.
786
                    if (this.is_whitespace(text) && !this.output.length) {
787
                        return;
788
                    }
789
                    if (text || text !== '') {
790
                        if (this.output.length && this.output[this.output.length - 1] === '\n') {
791
                            this.print_indentation(this.output);
792
                            text = ltrim(text);
793
                        }
794
                    }
795
                    this.print_token_raw(text);
796
                };
797
798
                this.print_token_raw = function(text) {
799
                    // If we are going to print newlines, truncate trailing
800
                    // whitespace, as the newlines will represent the space.
801
                    if (this.newlines > 0) {
802
                        text = rtrim(text);
803
                    }
804
805
                    if (text && text !== '') {
806
                        if (text.length > 1 && text.charAt(text.length - 1) === '\n') {
807
                            // unformatted tags can grab newlines as their last character
808
                            this.output.push(text.slice(0, -1));
809
                            this.print_newline(false, this.output);
810
                        } else {
811
                            this.output.push(text);
812
                        }
813
                    }
814
815
                    for (var n = 0; n < this.newlines; n++) {
816
                        this.print_newline(n > 0, this.output);
817
                    }
818
                    this.newlines = 0;
819
                };
820
821
                this.indent = function() {
822
                    this.indent_level++;
823
                };
824
825
                this.unindent = function() {
826
                    if (this.indent_level > 0) {
827
                        this.indent_level--;
828
                    }
829
                };
830
            };
831
            return this;
832
        }
833
834
        /*_____________________--------------------_____________________*/
835
836
        multi_parser = new Parser(); //wrapping functions Parser
837
        multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
838
839
        while (true) {
840
            var t = multi_parser.get_token();
841
            multi_parser.token_text = t[0];
842
            multi_parser.token_type = t[1];
843
844
            if (multi_parser.token_type === 'TK_EOF') {
845
                break;
846
            }
847
848
            switch (multi_parser.token_type) {
849
                case 'TK_TAG_START':
850
                    multi_parser.print_newline(false, multi_parser.output);
851
                    multi_parser.print_token(multi_parser.token_text);
852
                    if (multi_parser.indent_content) {
853
                        multi_parser.indent();
854
                        multi_parser.indent_content = false;
855
                    }
856
                    multi_parser.current_mode = 'CONTENT';
857
                    break;
858
                case 'TK_TAG_STYLE':
859
                case 'TK_TAG_SCRIPT':
860
                    multi_parser.print_newline(false, multi_parser.output);
861
                    multi_parser.print_token(multi_parser.token_text);
862
                    multi_parser.current_mode = 'CONTENT';
863
                    break;
864
                case 'TK_TAG_END':
865
                    //Print new line only if the tag has no content and has child
866
                    if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
867
                        var tag_name = multi_parser.token_text.match(/\w+/)[0];
868
                        var tag_extracted_from_last_output = null;
869
                        if (multi_parser.output.length) {
870
                            tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
871
                        }
872
                        if (tag_extracted_from_last_output === null ||
873
                            (tag_extracted_from_last_output[1] !== tag_name && !multi_parser.Utils.in_array(tag_extracted_from_last_output[1], unformatted))) {
874
                            multi_parser.print_newline(false, multi_parser.output);
875
                        }
876
                    }
877
                    multi_parser.print_token(multi_parser.token_text);
878
                    multi_parser.current_mode = 'CONTENT';
879
                    break;
880
                case 'TK_TAG_SINGLE':
881
                    // Don't add a newline before elements that should remain unformatted.
882
                    var tag_check = multi_parser.token_text.match(/^\s*<([a-z-]+)/i);
883
                    if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
884
                        multi_parser.print_newline(false, multi_parser.output);
885
                    }
886
                    multi_parser.print_token(multi_parser.token_text);
887
                    multi_parser.current_mode = 'CONTENT';
888
                    break;
889
                case 'TK_TAG_HANDLEBARS_ELSE':
890
                    // Don't add a newline if opening {{#if}} tag is on the current line
891
                    var foundIfOnCurrentLine = false;
892
                    for (var lastCheckedOutput = multi_parser.output.length - 1; lastCheckedOutput >= 0; lastCheckedOutput--) {
893
                        if (multi_parser.output[lastCheckedOutput] === '\n') {
894
                            break;
895
                        } else {
896
                            if (multi_parser.output[lastCheckedOutput].match(/{{#if/)) {
897
                                foundIfOnCurrentLine = true;
898
                                break;
899
                            }
900
                        }
901
                    }
902
                    if (!foundIfOnCurrentLine) {
903
                        multi_parser.print_newline(false, multi_parser.output);
904
                    }
905
                    multi_parser.print_token(multi_parser.token_text);
906
                    if (multi_parser.indent_content) {
907
                        multi_parser.indent();
908
                        multi_parser.indent_content = false;
909
                    }
910
                    multi_parser.current_mode = 'CONTENT';
911
                    break;
912
                case 'TK_TAG_HANDLEBARS_COMMENT':
913
                    multi_parser.print_token(multi_parser.token_text);
914
                    multi_parser.current_mode = 'TAG';
915
                    break;
916
                case 'TK_CONTENT':
917
                    multi_parser.print_token(multi_parser.token_text);
918
                    multi_parser.current_mode = 'TAG';
919
                    break;
920
                case 'TK_STYLE':
921
                case 'TK_SCRIPT':
922
                    if (multi_parser.token_text !== '') {
923
                        multi_parser.print_newline(false, multi_parser.output);
924
                        var text = multi_parser.token_text,
925
                            _beautifier,
926
                            script_indent_level = 1;
927
                        if (multi_parser.token_type === 'TK_SCRIPT') {
928
                            _beautifier = typeof js_beautify === 'function' && js_beautify;
929
                        } else if (multi_parser.token_type === 'TK_STYLE') {
930
                            _beautifier = typeof css_beautify === 'function' && css_beautify;
931
                        }
932
933
                        if (options.indent_scripts === "keep") {
934
                            script_indent_level = 0;
935
                        } else if (options.indent_scripts === "separate") {
936
                            script_indent_level = -multi_parser.indent_level;
937
                        }
938
939
                        var indentation = multi_parser.get_full_indent(script_indent_level);
940
                        if (_beautifier) {
941
942
                            // call the Beautifier if avaliable
943
                            var Child_options = function() {
944
                                this.eol = '\n';
945
                            };
946
                            Child_options.prototype = options;
947
                            var child_options = new Child_options();
948
                            text = _beautifier(text.replace(/^\s*/, indentation), child_options);
949
                        } else {
950
                            // simply indent the string otherwise
951
                            var white = text.match(/^\s*/)[0];
952
                            var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
953
                            var reindent = multi_parser.get_full_indent(script_indent_level - _level);
954
                            text = text.replace(/^\s*/, indentation)
955
                                .replace(/\r\n|\r|\n/g, '\n' + reindent)
956
                                .replace(/\s+$/, '');
957
                        }
958
                        if (text) {
959
                            multi_parser.print_token_raw(text);
960
                            multi_parser.print_newline(true, multi_parser.output);
961
                        }
962
                    }
963
                    multi_parser.current_mode = 'TAG';
964
                    break;
965
                default:
966
                    // We should not be getting here but we don't want to drop input on the floor
967
                    // Just output the text and move on
968
                    if (multi_parser.token_text !== '') {
969
                        multi_parser.print_token(multi_parser.token_text);
970
                    }
971
                    break;
972
            }
973
            multi_parser.last_token = multi_parser.token_type;
974
            multi_parser.last_text = multi_parser.token_text;
975
        }
976
        var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');
977
978
        // establish end_with_newline
979
        if (end_with_newline) {
980
            sweet_code += '\n';
981
        }
982
983
        if (eol !== '\n') {
984
            sweet_code = sweet_code.replace(/[\n]/g, eol);
985
        }
986
987
        return sweet_code;
988
    }
989
990
	return {
991
		html_beautify: function(html_source, options) {
992
			return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
993
		}
994
	};
995
});
(-)a/bundles/org.eclipse.orion.client.javascript/web/beautifier/beautify-js.js (+2253 lines)
Added Link Here
1
/*eslint-env amd, node*/
2
/*
3
4
  The MIT License (MIT)
5
6
  Copyright (c) 2007-2013 Einar Lielmanis and contributors.
7
8
  Permission is hereby granted, free of charge, to any person
9
  obtaining a copy of this software and associated documentation files
10
  (the "Software"), to deal in the Software without restriction,
11
  including without limitation the rights to use, copy, modify, merge,
12
  publish, distribute, sublicense, and/or sell copies of the Software,
13
  and to permit persons to whom the Software is furnished to do so,
14
  subject to the following conditions:
15
16
  The above copyright notice and this permission notice shall be
17
  included in all copies or substantial portions of the Software.
18
19
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
  SOFTWARE.
27
28
 JS Beautifier
29
---------------
30
31
32
  Written by Einar Lielmanis, <einar@jsbeautifier.org>
33
      http://jsbeautifier.org/
34
35
  Originally converted to javascript by Vital, <vital76@gmail.com>
36
  "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
37
  Parsing improvements for brace-less statements by Liam Newman <bitwiseman@gmail.com>
38
39
40
  Usage:
41
    js_beautify(js_source_text);
42
    js_beautify(js_source_text, options);
43
44
  The options are:
45
    indent_size (default 4)          - indentation size,
46
    indent_char (default space)      - character to indent with,
47
    preserve_newlines (default true) - whether existing line breaks should be preserved,
48
    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
49
50
    jslint_happy (default false) - if true, then jslint-stricter mode is enforced.
51
52
            jslint_happy        !jslint_happy
53
            ---------------------------------
54
            function ()         function()
55
56
            switch () {         switch() {
57
            case 1:               case 1:
58
              break;                break;
59
            }                   }
60
61
    space_after_anon_function (default false) - should the space before an anonymous function's parens be added, "function()" vs "function ()",
62
          NOTE: This option is overriden by jslint_happy (i.e. if jslint_happy is true, space_after_anon_function is true by design)
63
64
    brace_style (default "collapse") - "collapse-preserve-inline" | "collapse" | "expand" | "end-expand" | "none"
65
            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, or attempt to keep them where they are.
66
67
    space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",
68
69
    unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"
70
71
    wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters.
72
          NOTE: This is not a hard limit. Lines will continue until a point where a newline would
73
                be preserved if it were present.
74
75
    end_with_newline (default false)  - end output with a newline
76
77
78
    e.g
79
80
    js_beautify(js_source_text, {
81
      'indent_size': 1,
82
      'indent_char': '\t'
83
    });
84
85
*/
86
define([
87
	"acorn/dist/acorn"
88
], function(acorn) {
89
90
91
	// Object.values polyfill found here:
92
	// http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html
93
	if (!Object.values) {
94
	    Object.values = function(o) {
95
	        if (o !== Object(o)) {
96
	            throw new TypeError('Object.values called on a non-object');
97
	        }
98
	        var k = [],
99
	            p;
100
	        for (p in o) {
101
	            if (Object.prototype.hasOwnProperty.call(o, p)) {
102
	                k.push(o[p]);
103
	            }
104
	        }
105
	        return k;
106
	    };
107
	}
108
109
	function js_beautify(js_source_text, options) {
110
111
        function in_array(what, arr) {
112
            for (var i = 0; i < arr.length; i += 1) {
113
                if (arr[i] === what) {
114
                    return true;
115
                }
116
            }
117
            return false;
118
        }
119
120
        function trim(s) {
121
            return s.replace(/^\s+|\s+$/g, '');
122
        }
123
124
        function ltrim(s) {
125
            return s.replace(/^\s+/g, '');
126
        }
127
128
        // function rtrim(s) {
129
        //     return s.replace(/\s+$/g, '');
130
        // }
131
132
        function sanitizeOperatorPosition(opPosition) {
133
            opPosition = opPosition || OPERATOR_POSITION.before_newline;
134
135
            var validPositionValues = Object.values(OPERATOR_POSITION);
136
137
            if (!in_array(opPosition, validPositionValues)) {
138
                throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
139
                    validPositionValues +
140
                    "\nYou passed in: '" + opPosition + "'");
141
            }
142
143
            return opPosition;
144
        }
145
146
        var OPERATOR_POSITION = {
147
            before_newline: 'before-newline',
148
            after_newline: 'after-newline',
149
            preserve_newline: 'preserve-newline',
150
        };
151
152
        var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline];
153
154
        var MODE = {
155
            BlockStatement: 'BlockStatement', // 'BLOCK'
156
            Statement: 'Statement', // 'STATEMENT'
157
            ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
158
            ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
159
            ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
160
            Conditional: 'Conditional', //'(COND-EXPRESSION)',
161
            Expression: 'Expression' //'(EXPRESSION)'
162
        };
163
164
        function Beautifier(js_source_text, options) {
165
            "use strict";
166
            var output;
167
            var tokens = [],
168
                token_pos;
169
            var Tokenizer;
170
            var current_token;
171
            var last_type, last_last_text, indent_string;
172
            var flags, previous_flags, flag_store;
173
            var prefix;
174
175
            var handlers, opt;
176
            var baseIndentString = '';
177
178
            handlers = {
179
                'TK_START_EXPR': handle_start_expr,
180
                'TK_END_EXPR': handle_end_expr,
181
                'TK_START_BLOCK': handle_start_block,
182
                'TK_END_BLOCK': handle_end_block,
183
                'TK_WORD': handle_word,
184
                'TK_RESERVED': handle_word,
185
                'TK_SEMICOLON': handle_semicolon,
186
                'TK_STRING': handle_string,
187
                'TK_EQUALS': handle_equals,
188
                'TK_OPERATOR': handle_operator,
189
                'TK_COMMA': handle_comma,
190
                'TK_BLOCK_COMMENT': handle_block_comment,
191
                'TK_COMMENT': handle_comment,
192
                'TK_DOT': handle_dot,
193
                'TK_UNKNOWN': handle_unknown,
194
                'TK_EOF': handle_eof
195
            };
196
197
            function create_flags(flags_base, mode) {
198
                var next_indent_level = 0;
199
                if (flags_base) {
200
                    next_indent_level = flags_base.indentation_level;
201
                    if (!output.just_added_newline() &&
202
                        flags_base.line_indent_level > next_indent_level) {
203
                        next_indent_level = flags_base.line_indent_level;
204
                    }
205
                }
206
207
                var next_flags = {
208
                    mode: mode,
209
                    parent: flags_base,
210
                    last_text: flags_base ? flags_base.last_text : '', // last token text
211
                    last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
212
                    declaration_statement: false,
213
                    declaration_assignment: false,
214
                    multiline_frame: false,
215
                    inline_frame: false,
216
                    if_block: false,
217
                    else_block: false,
218
                    do_block: false,
219
                    do_while: false,
220
                    import_block: false,
221
                    in_case_statement: false, // switch(..){ INSIDE HERE }
222
                    in_case: false, // we're on the exact line with "case 0:"
223
                    case_body: false, // the indented case-action block
224
                    indentation_level: next_indent_level,
225
                    line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
226
                    start_line_index: output.get_line_number(),
227
                    ternary_depth: 0
228
                };
229
                return next_flags;
230
            }
231
232
            // Some interpreters have unexpected results with foo = baz || bar;
233
            options = options ? options : {};
234
            opt = {};
235
236
            // compatibility
237
            if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
238
                opt.brace_style = options.braces_on_own_line ? "expand" : "collapse";
239
            }
240
            opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse");
241
242
            // graceful handling of deprecated option
243
            if (opt.brace_style === "expand-strict") {
244
                opt.brace_style = "expand";
245
            }
246
247
            opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
248
            opt.indent_char = options.indent_char ? options.indent_char : ' ';
249
            opt.eol = options.eol ? options.eol : 'auto';
250
            opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
251
            opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
252
            opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
253
            opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
254
            opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
255
            opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
256
            opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
257
            opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
258
            opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
259
            opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
260
            opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
261
            opt.e4x = (options.e4x === undefined) ? false : options.e4x;
262
            opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
263
            opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
264
            opt.operator_position = sanitizeOperatorPosition(options.operator_position);
265
266
            // For testing of beautify ignore:start directive
267
            opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;
268
269
            // force opt.space_after_anon_function to true if opt.jslint_happy
270
            if (opt.jslint_happy) {
271
                opt.space_after_anon_function = true;
272
            }
273
274
            if (options.indent_with_tabs) {
275
                opt.indent_char = '\t';
276
                opt.indent_size = 1;
277
            }
278
279
            if (opt.eol === 'auto') {
280
                opt.eol = '\n';
281
                if (js_source_text && acorn.lineBreak.test(js_source_text || '')) {
282
                    opt.eol = js_source_text.match(acorn.lineBreak)[0];
283
                }
284
            }
285
286
            opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
287
288
            //----------------------------------
289
            indent_string = '';
290
            while (opt.indent_size > 0) {
291
                indent_string += opt.indent_char;
292
                opt.indent_size -= 1;
293
            }
294
295
            var preindent_index = 0;
296
            if (js_source_text && js_source_text.length) {
297
                while ((js_source_text.charAt(preindent_index) === ' ' ||
298
                        js_source_text.charAt(preindent_index) === '\t')) {
299
                    baseIndentString += js_source_text.charAt(preindent_index);
300
                    preindent_index += 1;
301
                }
302
                js_source_text = js_source_text.substring(preindent_index);
303
            }
304
305
            last_type = 'TK_START_BLOCK'; // last token type
306
            last_last_text = ''; // pre-last token text
307
            output = new Output(indent_string, baseIndentString);
308
309
            // If testing the ignore directive, start with output disable set to true
310
            output.raw = opt.test_output_raw;
311
312
313
            // Stack of parsing/formatting states, including MODE.
314
            // We tokenize, parse, and output in an almost purely a forward-only stream of token input
315
            // and formatted output.  This makes the beautifier less accurate than full parsers
316
            // but also far more tolerant of syntax errors.
317
            //
318
            // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
319
            // MODE.BlockStatement on the the stack, even though it could be object literal.  If we later
320
            // encounter a ":", we'll switch to to MODE.ObjectLiteral.  If we then see a ";",
321
            // most full parsers would die, but the beautifier gracefully falls back to
322
            // MODE.BlockStatement and continues on.
323
            flag_store = [];
324
            set_mode(MODE.BlockStatement);
325
326
            this.beautify = function() {
327
328
                /*jshint onevar:true */
329
                var local_token, sweet_code;
330
                Tokenizer = new tokenizer(js_source_text, opt, indent_string);
331
                tokens = Tokenizer.tokenize();
332
                token_pos = 0;
333
334
                function get_local_token() {
335
                    local_token = get_token();
336
                    return local_token;
337
                }
338
339
                while (get_local_token()) {
340
                    for (var i = 0; i < local_token.comments_before.length; i++) {
341
                        // The cleanest handling of inline comments is to treat them as though they aren't there.
342
                        // Just continue formatting and the behavior should be logical.
343
                        // Also ignore unknown tokens.  Again, this should result in better behavior.
344
                        handle_token(local_token.comments_before[i]);
345
                    }
346
                    handle_token(local_token);
347
348
                    last_last_text = flags.last_text;
349
                    last_type = local_token.type;
350
                    flags.last_text = local_token.text;
351
352
                    token_pos += 1;
353
                }
354
355
                sweet_code = output.get_code();
356
                if (opt.end_with_newline) {
357
                    sweet_code += '\n';
358
                }
359
360
                if (opt.eol !== '\n') {
361
                    sweet_code = sweet_code.replace(/[\n]/g, opt.eol);
362
                }
363
364
                return sweet_code;
365
            };
366
367
            function handle_token(local_token) {
368
                var newlines = local_token.newlines;
369
                var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
370
371
                if (keep_whitespace) {
372
                    for (var i = 0; i < newlines; i += 1) {
373
                        print_newline(i > 0);
374
                    }
375
                } else {
376
                    if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
377
                        newlines = opt.max_preserve_newlines;
378
                    }
379
380
                    if (opt.preserve_newlines) {
381
                        if (local_token.newlines > 1) {
382
                            print_newline();
383
                            for (var j = 1; j < newlines; j += 1) {
384
                                print_newline(true);
385
                            }
386
                        }
387
                    }
388
                }
389
390
                current_token = local_token;
391
                handlers[current_token.type]();
392
            }
393
394
            // we could use just string.split, but
395
            // IE doesn't like returning empty strings
396
            function split_linebreaks(s) {
397
                //return s.split(/\x0d\x0a|\x0a/);
398
399
                s = s.replace(acorn.allLineBreaks, '\n');
400
                var out = [],
401
                    idx = s.indexOf("\n");
402
                while (idx !== -1) {
403
                    out.push(s.substring(0, idx));
404
                    s = s.substring(idx + 1);
405
                    idx = s.indexOf("\n");
406
                }
407
                if (s.length) {
408
                    out.push(s);
409
                }
410
                return out;
411
            }
412
413
            var newline_restricted_tokens = ['break', 'contiue', 'return', 'throw'];
414
415
            function allow_wrap_or_preserved_newline(force_linewrap) {
416
                force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
417
418
                // Never wrap the first token on a line
419
                if (output.just_added_newline()) {
420
                    return;
421
                }
422
423
                var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap;
424
                var operatorLogicApplies = in_array(flags.last_text, Tokenizer.positionable_operators) || in_array(current_token.text, Tokenizer.positionable_operators);
425
426
                if (operatorLogicApplies) {
427
                    var shouldPrintOperatorNewline = (
428
                            in_array(flags.last_text, Tokenizer.positionable_operators) &&
429
                            in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)
430
                        ) ||
431
                        in_array(current_token.text, Tokenizer.positionable_operators);
432
                    shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline;
433
                }
434
435
                if (shouldPreserveOrForce) {
436
                    print_newline(false, true);
437
                } else if (opt.wrap_line_length) {
438
                    if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) {
439
                        // These tokens should never have a newline inserted
440
                        // between them and the following expression.
441
                        return;
442
                    }
443
                    var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
444
                        (output.space_before_token ? 1 : 0);
445
                    if (proposed_line_length >= opt.wrap_line_length) {
446
                        print_newline(false, true);
447
                    }
448
                }
449
            }
450
451
            function print_newline(force_newline, preserve_statement_flags) {
452
                if (!preserve_statement_flags) {
453
                    if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
454
                        while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
455
                            restore_mode();
456
                        }
457
                    }
458
                }
459
460
                if (output.add_new_line(force_newline)) {
461
                    flags.multiline_frame = true;
462
                }
463
            }
464
465
            function print_token_line_indentation() {
466
                if (output.just_added_newline()) {
467
                    if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
468
                        output.current_line.push(current_token.whitespace_before);
469
                        output.space_before_token = false;
470
                    } else if (output.set_indent(flags.indentation_level)) {
471
                        flags.line_indent_level = flags.indentation_level;
472
                    }
473
                }
474
            }
475
476
            function print_token(printable_token) {
477
                if (output.raw) {
478
                    output.add_raw_token(current_token);
479
                    return;
480
                }
481
482
                if (opt.comma_first && last_type === 'TK_COMMA' &&
483
                    output.just_added_newline()) {
484
                    if (output.previous_line.last() === ',') {
485
                        var popped = output.previous_line.pop();
486
                        // if the comma was already at the start of the line,
487
                        // pull back onto that line and reprint the indentation
488
                        if (output.previous_line.is_empty()) {
489
                            output.previous_line.push(popped);
490
                            output.trim(true);
491
                            output.current_line.pop();
492
                            output.trim();
493
                        }
494
495
                        // add the comma in front of the next token
496
                        print_token_line_indentation();
497
                        output.add_token(',');
498
                        output.space_before_token = true;
499
                    }
500
                }
501
502
                printable_token = printable_token || current_token.text;
503
                print_token_line_indentation();
504
                output.add_token(printable_token);
505
            }
506
507
            function indent() {
508
                flags.indentation_level += 1;
509
            }
510
511
            function deindent() {
512
                if (flags.indentation_level > 0 &&
513
                    ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) {
514
                    flags.indentation_level -= 1;
515
516
                }
517
            }
518
519
            function set_mode(mode) {
520
                if (flags) {
521
                    flag_store.push(flags);
522
                    previous_flags = flags;
523
                } else {
524
                    previous_flags = create_flags(null, mode);
525
                }
526
527
                flags = create_flags(previous_flags, mode);
528
            }
529
530
            function is_array(mode) {
531
                return mode === MODE.ArrayLiteral;
532
            }
533
534
            function is_expression(mode) {
535
                return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
536
            }
537
538
            function restore_mode() {
539
                if (flag_store.length > 0) {
540
                    previous_flags = flags;
541
                    flags = flag_store.pop();
542
                    if (previous_flags.mode === MODE.Statement) {
543
                        output.remove_redundant_indentation(previous_flags);
544
                    }
545
                }
546
            }
547
548
            function start_of_object_property() {
549
                return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
550
                    (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
551
            }
552
553
            function start_of_statement() {
554
                if (
555
                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
556
                    (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
557
                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw']) && !current_token.wanted_newline) ||
558
                    (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(current_token.type === 'TK_RESERVED' && current_token.text === 'if')) ||
559
                    (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
560
                    (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement &&
561
                        !flags.in_case &&
562
                        !(current_token.text === '--' || current_token.text === '++') &&
563
                        last_last_text !== 'function' &&
564
                        current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
565
                    (flags.mode === MODE.ObjectLiteral && (
566
                        (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
567
                ) {
568
569
                    set_mode(MODE.Statement);
570
                    indent();
571
572
                    if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
573
                        flags.declaration_statement = true;
574
                    }
575
576
                    // Issue #276:
577
                    // If starting a new statement with [if, for, while, do], push to a new line.
578
                    // if (a) if (b) if(c) d(); else e(); else f();
579
                    if (!start_of_object_property()) {
580
                        allow_wrap_or_preserved_newline(
581
                            current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
582
                    }
583
584
                    return true;
585
                }
586
                return false;
587
            }
588
589
            function all_lines_start_with(lines, c) {
590
                for (var i = 0; i < lines.length; i++) {
591
                    var line = trim(lines[i]);
592
                    if (line.charAt(0) !== c) {
593
                        return false;
594
                    }
595
                }
596
                return true;
597
            }
598
599
            function each_line_matches_indent(lines, indent) {
600
                var i = 0,
601
                    len = lines.length,
602
                    line;
603
                for (; i < len; i++) {
604
                    line = lines[i];
605
                    // allow empty lines to pass through
606
                    if (line && line.indexOf(indent) !== 0) {
607
                        return false;
608
                    }
609
                }
610
                return true;
611
            }
612
613
            function is_special_word(word) {
614
                return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
615
            }
616
617
            function get_token(offset) {
618
                var index = token_pos + (offset || 0);
619
                return (index < 0 || index >= tokens.length) ? null : tokens[index];
620
            }
621
622
            function handle_start_expr() {
623
                if (start_of_statement()) {
624
                    // The conditional starts the statement if appropriate.
625
                }
626
627
                var next_mode = MODE.Expression;
628
                if (current_token.text === '[') {
629
630
                    if (last_type === 'TK_WORD' || flags.last_text === ')') {
631
                        // this is array index specifier, break immediately
632
                        // a[x], fn()[x]
633
                        if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) {
634
                            output.space_before_token = true;
635
                        }
636
                        set_mode(next_mode);
637
                        print_token();
638
                        indent();
639
                        if (opt.space_in_paren) {
640
                            output.space_before_token = true;
641
                        }
642
                        return;
643
                    }
644
645
                    next_mode = MODE.ArrayLiteral;
646
                    if (is_array(flags.mode)) {
647
                        if (flags.last_text === '[' ||
648
                            (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
649
                            // ], [ goes to new line
650
                            // }, [ goes to new line
651
                            if (!opt.keep_array_indentation) {
652
                                print_newline();
653
                            }
654
                        }
655
                    }
656
657
                } else {
658
                    if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
659
                        next_mode = MODE.ForInitializer;
660
                    } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
661
                        next_mode = MODE.Conditional;
662
                    } else {
663
                        // next_mode = MODE.Expression;
664
                    }
665
                }
666
667
                if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
668
                    print_newline();
669
                } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
670
                    // TODO: Consider whether forcing this is required.  Review failing tests when removed.
671
                    allow_wrap_or_preserved_newline(current_token.wanted_newline);
672
                    // do nothing on (( and )( and ][ and ]( and .(
673
                } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
674
                    output.space_before_token = true;
675
                } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
676
                    (flags.last_text === '*' && last_last_text === 'function')) {
677
                    // function() vs function ()
678
                    if (opt.space_after_anon_function) {
679
                        output.space_before_token = true;
680
                    }
681
                } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) {
682
                    if (opt.space_before_conditional) {
683
                        output.space_before_token = true;
684
                    }
685
                }
686
687
                // Should be a space between await and an IIFE
688
                if (current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await') {
689
                    output.space_before_token = true;
690
                }
691
692
                // Support of this kind of newline preservation.
693
                // a = (b &&
694
                //     (c || d));
695
                if (current_token.text === '(') {
696
                    if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
697
                        if (!start_of_object_property()) {
698
                            allow_wrap_or_preserved_newline();
699
                        }
700
                    }
701
                }
702
703
                // Support preserving wrapped arrow function expressions
704
                // a.b('c',
705
                //     () => d.e
706
                // )
707
                if (current_token.text === '(' && last_type !== 'TK_WORD' && last_type !== 'TK_RESERVED') {
708
                    allow_wrap_or_preserved_newline();
709
                }
710
711
                set_mode(next_mode);
712
                print_token();
713
                if (opt.space_in_paren) {
714
                    output.space_before_token = true;
715
                }
716
717
                // In all cases, if we newline while inside an expression it should be indented.
718
                indent();
719
            }
720
721
            function handle_end_expr() {
722
                // statements inside expressions are not valid syntax, but...
723
                // statements must all be closed when their container closes
724
                while (flags.mode === MODE.Statement) {
725
                    restore_mode();
726
                }
727
728
                if (flags.multiline_frame) {
729
                    allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
730
                }
731
732
                if (opt.space_in_paren) {
733
                    if (last_type === 'TK_START_EXPR' && !opt.space_in_empty_paren) {
734
                        // () [] no inner space in empty parens like these, ever, ref #320
735
                        output.trim();
736
                        output.space_before_token = false;
737
                    } else {
738
                        output.space_before_token = true;
739
                    }
740
                }
741
                if (current_token.text === ']' && opt.keep_array_indentation) {
742
                    print_token();
743
                    restore_mode();
744
                } else {
745
                    restore_mode();
746
                    print_token();
747
                }
748
                output.remove_redundant_indentation(previous_flags);
749
750
                // do {} while () // no statement required after
751
                if (flags.do_while && previous_flags.mode === MODE.Conditional) {
752
                    previous_flags.mode = MODE.Expression;
753
                    flags.do_block = false;
754
                    flags.do_while = false;
755
756
                }
757
            }
758
759
            function handle_start_block() {
760
                // Check if this is should be treated as a ObjectLiteral
761
                var next_token = get_token(1);
762
                var second_token = get_token(2);
763
                if (second_token && (
764
                        (in_array(second_token.text, [':', ',']) && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED'])) ||
765
                        (in_array(next_token.text, ['get', 'set']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
766
                    )) {
767
                    // We don't support TypeScript,but we didn't break it for a very long time.
768
                    // We'll try to keep not breaking it.
769
                    if (!in_array(last_last_text, ['class', 'interface'])) {
770
                        set_mode(MODE.ObjectLiteral);
771
                    } else {
772
                        set_mode(MODE.BlockStatement);
773
                    }
774
                } else if (last_type === 'TK_OPERATOR' && flags.last_text === '=>') {
775
                    // arrow function: (param1, paramN) => { statements }
776
                    set_mode(MODE.BlockStatement);
777
                } else if (in_array(last_type, ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR']) ||
778
                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw', 'import']))
779
                ) {
780
                    // Detecting shorthand function syntax is difficult by scanning forward,
781
                    //     so check the surrounding context.
782
                    // If the block is being returned, imported, passed as arg,
783
                    //     assigned with = or assigned in a nested object, treat as an ObjectLiteral.
784
                    set_mode(MODE.ObjectLiteral);
785
                } else {
786
                    set_mode(MODE.BlockStatement);
787
                }
788
789
                var empty_braces = !next_token.comments_before.length && next_token.text === '}';
790
                var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
791
                    last_type === 'TK_END_EXPR';
792
793
794
                if (opt.brace_style === "expand" ||
795
                    (opt.brace_style === "none" && current_token.wanted_newline)) {
796
                    if (last_type !== 'TK_OPERATOR' &&
797
                        (empty_anonymous_function ||
798
                            last_type === 'TK_EQUALS' ||
799
                            (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
800
                        output.space_before_token = true;
801
                    } else {
802
                        print_newline(false, true);
803
                    }
804
                } else { // collapse
805
                    if (opt.brace_style === 'collapse-preserve-inline') {
806
                        // search forward for a newline wanted inside this block
807
                        var index = 0;
808
                        var check_token = null;
809
                        flags.inline_frame = true;
810
                        do {
811
                            index += 1;
812
                            check_token = get_token(index);
813
                            if (check_token.wanted_newline) {
814
                                flags.inline_frame = false;
815
                                break;
816
                            }
817
                        } while (check_token.type !== 'TK_EOF' &&
818
                            !(check_token.type === 'TK_END_BLOCK' && check_token.opened === current_token));
819
                    }
820
821
                    if (is_array(previous_flags.mode) && (last_type === 'TK_START_EXPR' || last_type === 'TK_COMMA')) {
822
                        // if we're preserving inline,
823
                        // allow newline between comma and next brace.
824
                        if (last_type === 'TK_COMMA' || opt.space_in_paren) {
825
                            output.space_before_token = true;
826
                        }
827
828
                        if (opt.brace_style === 'collapse-preserve-inline' &&
829
                            (last_type === 'TK_COMMA' || (last_type === 'TK_START_EXPR' && flags.inline_frame))) {
830
                            allow_wrap_or_preserved_newline();
831
                            previous_flags.multiline_frame = previous_flags.multiline_frame || flags.multiline_frame;
832
                            flags.multiline_frame = false;
833
                        }
834
                    } else if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
835
                        if (last_type === 'TK_START_BLOCK') {
836
                            print_newline();
837
                        } else {
838
                            output.space_before_token = true;
839
                        }
840
                    }
841
                }
842
                print_token();
843
                indent();
844
            }
845
846
            function handle_end_block() {
847
                // statements must all be closed when their container closes
848
                while (flags.mode === MODE.Statement) {
849
                    restore_mode();
850
                }
851
                var empty_braces = last_type === 'TK_START_BLOCK';
852
853
                if (opt.brace_style === "expand") {
854
                    if (!empty_braces) {
855
                        print_newline();
856
                    }
857
                } else {
858
                    // skip {}
859
                    if (!empty_braces) {
860
                        if (flags.inline_frame) {
861
                            output.space_before_token = true;
862
                        } else if (is_array(flags.mode) && opt.keep_array_indentation) {
863
                            // we REALLY need a newline here, but newliner would skip that
864
                            opt.keep_array_indentation = false;
865
                            print_newline();
866
                            opt.keep_array_indentation = true;
867
868
                        } else {
869
                            print_newline();
870
                        }
871
                    }
872
                }
873
                restore_mode();
874
                print_token();
875
            }
876
877
            function handle_word() {
878
                if (current_token.type === 'TK_RESERVED') {
879
                    if (in_array(current_token.text, ['set', 'get']) && flags.mode !== MODE.ObjectLiteral) {
880
                        current_token.type = 'TK_WORD';
881
                    } else if (in_array(current_token.text, ['as', 'from']) && !flags.import_block) {
882
                        current_token.type = 'TK_WORD';
883
                    } else if (flags.mode === MODE.ObjectLiteral) {
884
                        var next_token = get_token(1);
885
                        if (next_token.text === ':') {
886
                            current_token.type = 'TK_WORD';
887
                        }
888
                    }
889
                }
890
891
                if (start_of_statement()) {
892
                    // The conditional starts the statement if appropriate.
893
                } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
894
                    (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
895
                    last_type !== 'TK_EQUALS' &&
896
                    (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {
897
898
                    print_newline();
899
                }
900
901
                if (flags.do_block && !flags.do_while) {
902
                    if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
903
                        // do {} ## while ()
904
                        output.space_before_token = true;
905
                        print_token();
906
                        output.space_before_token = true;
907
                        flags.do_while = true;
908
                        return;
909
                    } else {
910
                        // do {} should always have while as the next word.
911
                        // if we don't see the expected while, recover
912
                        print_newline();
913
                        flags.do_block = false;
914
                    }
915
                }
916
917
                // if may be followed by else, or not
918
                // Bare/inline ifs are tricky
919
                // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
920
                if (flags.if_block) {
921
                    if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
922
                        flags.else_block = true;
923
                    } else {
924
                        while (flags.mode === MODE.Statement) {
925
                            restore_mode();
926
                        }
927
                        flags.if_block = false;
928
                        flags.else_block = false;
929
                    }
930
                }
931
932
                if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
933
                    print_newline();
934
                    if (flags.case_body || opt.jslint_happy) {
935
                        // switch cases following one another
936
                        deindent();
937
                        flags.case_body = false;
938
                    }
939
                    print_token();
940
                    flags.in_case = true;
941
                    flags.in_case_statement = true;
942
                    return;
943
                }
944
945
                if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
946
                    if (in_array(flags.last_text, ['}', ';']) || (output.just_added_newline() && !in_array(flags.last_text, ['[', '{', ':', '=', ',']))) {
947
                        // make sure there is a nice clean space of at least one blank line
948
                        // before a new function definition
949
                        if (!output.just_added_blankline() && !current_token.comments_before.length) {
950
                            print_newline();
951
                            print_newline(true);
952
                        }
953
                    }
954
                    if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
955
                        if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
956
                            output.space_before_token = true;
957
                        } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
958
                            output.space_before_token = true;
959
                        } else {
960
                            print_newline();
961
                        }
962
                    } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
963
                        // foo = function
964
                        output.space_before_token = true;
965
                    } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
966
                        // (function
967
                    } else {
968
                        print_newline();
969
                    }
970
                }
971
972
                if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
973
                    if (!start_of_object_property()) {
974
                        allow_wrap_or_preserved_newline();
975
                    }
976
                }
977
978
                if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['function', 'get', 'set'])) {
979
                    print_token();
980
                    flags.last_word = current_token.text;
981
                    return;
982
                }
983
984
                prefix = 'NONE';
985
986
                if (last_type === 'TK_END_BLOCK') {
987
988
                    if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally', 'from']))) {
989
                        prefix = 'NEWLINE';
990
                    } else {
991
                        if (opt.brace_style === "expand" ||
992
                            opt.brace_style === "end-expand" ||
993
                            (opt.brace_style === "none" && current_token.wanted_newline)) {
994
                            prefix = 'NEWLINE';
995
                        } else {
996
                            prefix = 'SPACE';
997
                            output.space_before_token = true;
998
                        }
999
                    }
1000
                } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
1001
                    // TODO: Should this be for STATEMENT as well?
1002
                    prefix = 'NEWLINE';
1003
                } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
1004
                    prefix = 'SPACE';
1005
                } else if (last_type === 'TK_STRING') {
1006
                    prefix = 'NEWLINE';
1007
                } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
1008
                    (flags.last_text === '*' && last_last_text === 'function')) {
1009
                    prefix = 'SPACE';
1010
                } else if (last_type === 'TK_START_BLOCK') {
1011
                    if (flags.inline_frame) {
1012
                        prefix = 'SPACE';
1013
                    } else {
1014
                        prefix = 'NEWLINE';
1015
                    }
1016
                } else if (last_type === 'TK_END_EXPR') {
1017
                    output.space_before_token = true;
1018
                    prefix = 'NEWLINE';
1019
                }
1020
1021
                if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
1022
                    if (flags.last_text === 'else' || flags.last_text === 'export') {
1023
                        prefix = 'SPACE';
1024
                    } else {
1025
                        prefix = 'NEWLINE';
1026
                    }
1027
1028
                }
1029
1030
                if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
1031
                    if (!(last_type === 'TK_END_BLOCK' && previous_flags.mode === MODE.BlockStatement) ||
1032
                        opt.brace_style === "expand" ||
1033
                        opt.brace_style === "end-expand" ||
1034
                        (opt.brace_style === "none" && current_token.wanted_newline)) {
1035
                        print_newline();
1036
                    } else {
1037
                        output.trim(true);
1038
                        var line = output.current_line;
1039
                        // If we trimmed and there's something other than a close block before us
1040
                        // put a newline back in.  Handles '} // comment' scenario.
1041
                        if (line.last() !== '}') {
1042
                            print_newline();
1043
                        }
1044
                        output.space_before_token = true;
1045
                    }
1046
                } else if (prefix === 'NEWLINE') {
1047
                    if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
1048
                        // no newline between 'return nnn'
1049
                        output.space_before_token = true;
1050
                    } else if (last_type !== 'TK_END_EXPR') {
1051
                        if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
1052
                            // no need to force newline on 'var': for (var x = 0...)
1053
                            if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
1054
                                // no newline for } else if {
1055
                                output.space_before_token = true;
1056
                            } else {
1057
                                print_newline();
1058
                            }
1059
                        }
1060
                    } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
1061
                        print_newline();
1062
                    }
1063
                } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
1064
                    print_newline(); // }, in lists get a newline treatment
1065
                } else if (prefix === 'SPACE') {
1066
                    output.space_before_token = true;
1067
                }
1068
                print_token();
1069
                flags.last_word = current_token.text;
1070
1071
                if (current_token.type === 'TK_RESERVED') {
1072
                    if (current_token.text === 'do') {
1073
                        flags.do_block = true;
1074
                    } else if (current_token.text === 'if') {
1075
                        flags.if_block = true;
1076
                    } else if (current_token.text === 'import') {
1077
                        flags.import_block = true;
1078
                    } else if (flags.import_block && current_token.type === 'TK_RESERVED' && current_token.text === 'from') {
1079
                        flags.import_block = false;
1080
                    }
1081
                }
1082
            }
1083
1084
            function handle_semicolon() {
1085
                if (start_of_statement()) {
1086
                    // The conditional starts the statement if appropriate.
1087
                    // Semicolon can be the start (and end) of a statement
1088
                    output.space_before_token = false;
1089
                }
1090
                while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
1091
                    restore_mode();
1092
                }
1093
1094
                // hacky but effective for the moment
1095
                if (flags.import_block) {
1096
                    flags.import_block = false;
1097
                }
1098
                print_token();
1099
            }
1100
1101
            function handle_string() {
1102
                if (start_of_statement()) {
1103
                    // The conditional starts the statement if appropriate.
1104
                    // One difference - strings want at least a space before
1105
                    output.space_before_token = true;
1106
                } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || flags.inline_frame) {
1107
                    output.space_before_token = true;
1108
                } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
1109
                    if (!start_of_object_property()) {
1110
                        allow_wrap_or_preserved_newline();
1111
                    }
1112
                } else {
1113
                    print_newline();
1114
                }
1115
                print_token();
1116
            }
1117
1118
            function handle_equals() {
1119
                if (start_of_statement()) {
1120
                    // The conditional starts the statement if appropriate.
1121
                }
1122
1123
                if (flags.declaration_statement) {
1124
                    // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
1125
                    flags.declaration_assignment = true;
1126
                }
1127
                output.space_before_token = true;
1128
                print_token();
1129
                output.space_before_token = true;
1130
            }
1131
1132
            function handle_comma() {
1133
                print_token();
1134
                output.space_before_token = true;
1135
                if (flags.declaration_statement) {
1136
                    if (is_expression(flags.parent.mode)) {
1137
                        // do not break on comma, for(var a = 1, b = 2)
1138
                        flags.declaration_assignment = false;
1139
                    }
1140
1141
                    if (flags.declaration_assignment) {
1142
                        flags.declaration_assignment = false;
1143
                        print_newline(false, true);
1144
                    } else if (opt.comma_first) {
1145
                        // for comma-first, we want to allow a newline before the comma
1146
                        // to turn into a newline after the comma, which we will fixup later
1147
                        allow_wrap_or_preserved_newline();
1148
                    }
1149
                } else if (flags.mode === MODE.ObjectLiteral ||
1150
                    (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
1151
                    if (flags.mode === MODE.Statement) {
1152
                        restore_mode();
1153
                    }
1154
1155
                    if (!flags.inline_frame) {
1156
                        print_newline();
1157
                    }
1158
                } else if (opt.comma_first) {
1159
                    // EXPR or DO_BLOCK
1160
                    // for comma-first, we want to allow a newline before the comma
1161
                    // to turn into a newline after the comma, which we will fixup later
1162
                    allow_wrap_or_preserved_newline();
1163
                }
1164
            }
1165
1166
            function handle_operator() {
1167
                if (start_of_statement()) {
1168
                    // The conditional starts the statement if appropriate.
1169
                }
1170
1171
                if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
1172
                    // "return" had a special handling in TK_WORD. Now we need to return the favor
1173
                    output.space_before_token = true;
1174
                    print_token();
1175
                    return;
1176
                }
1177
1178
                // hack for actionscript's import .*;
1179
                if (current_token.text === '*' && last_type === 'TK_DOT') {
1180
                    print_token();
1181
                    return;
1182
                }
1183
1184
                if (current_token.text === '::') {
1185
                    // no spaces around exotic namespacing syntax operator
1186
                    print_token();
1187
                    return;
1188
                }
1189
1190
                // Allow line wrapping between operators when operator_position is
1191
                //   set to before or preserve
1192
                if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) {
1193
                    allow_wrap_or_preserved_newline();
1194
                }
1195
1196
                if (current_token.text === ':' && flags.in_case) {
1197
                    flags.case_body = true;
1198
                    indent();
1199
                    print_token();
1200
                    print_newline();
1201
                    flags.in_case = false;
1202
                    return;
1203
                }
1204
1205
                var space_before = true;
1206
                var space_after = true;
1207
                var in_ternary = false;
1208
                var isGeneratorAsterisk = current_token.text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function';
1209
                var isUnary = in_array(current_token.text, ['-', '+']) && (
1210
                    in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) ||
1211
                    in_array(flags.last_text, Tokenizer.line_starters) ||
1212
                    flags.last_text === ','
1213
                );
1214
1215
                if (current_token.text === ':') {
1216
                    if (flags.ternary_depth === 0) {
1217
                        // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
1218
                        space_before = false;
1219
                    } else {
1220
                        flags.ternary_depth -= 1;
1221
                        in_ternary = true;
1222
                    }
1223
                } else if (current_token.text === '?') {
1224
                    flags.ternary_depth += 1;
1225
                }
1226
1227
                // let's handle the operator_position option prior to any conflicting logic
1228
                if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, Tokenizer.positionable_operators)) {
1229
                    var isColon = current_token.text === ':';
1230
                    var isTernaryColon = (isColon && in_ternary);
1231
                    var isOtherColon = (isColon && !in_ternary);
1232
1233
                    switch (opt.operator_position) {
1234
                        case OPERATOR_POSITION.before_newline:
1235
                            // if the current token is : and it's not a ternary statement then we set space_before to false
1236
                            output.space_before_token = !isOtherColon;
1237
1238
                            print_token();
1239
1240
                            if (!isColon || isTernaryColon) {
1241
                                allow_wrap_or_preserved_newline();
1242
                            }
1243
1244
                            output.space_before_token = true;
1245
                            return;
1246
1247
                        case OPERATOR_POSITION.after_newline:
1248
                            // if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
1249
                            //   then print a newline.
1250
1251
                            output.space_before_token = true;
1252
1253
                            if (!isColon || isTernaryColon) {
1254
                                if (get_token(1).wanted_newline) {
1255
                                    print_newline(false, true);
1256
                                } else {
1257
                                    allow_wrap_or_preserved_newline();
1258
                                }
1259
                            } else {
1260
                                output.space_before_token = false;
1261
                            }
1262
1263
                            print_token();
1264
1265
                            output.space_before_token = true;
1266
                            return;
1267
1268
                        case OPERATOR_POSITION.preserve_newline:
1269
                            if (!isOtherColon) {
1270
                                allow_wrap_or_preserved_newline();
1271
                            }
1272
1273
                            // if we just added a newline, or the current token is : and it's not a ternary statement,
1274
                            //   then we set space_before to false
1275
                            space_before = !(output.just_added_newline() || isOtherColon);
1276
1277
                            output.space_before_token = space_before;
1278
                            print_token();
1279
                            output.space_before_token = true;
1280
                            return;
1281
                    }
1282
                }
1283
1284
                if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) {
1285
                    // unary operators (and binary +/- pretending to be unary) special cases
1286
1287
                    space_before = false;
1288
                    space_after = false;
1289
1290
                    // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
1291
                    // if there is a newline between -- or ++ and anything else we should preserve it.
1292
                    if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
1293
                        print_newline(false, true);
1294
                    }
1295
1296
                    if (flags.last_text === ';' && is_expression(flags.mode)) {
1297
                        // for (;; ++i)
1298
                        //        ^^^
1299
                        space_before = true;
1300
                    }
1301
1302
                    if (last_type === 'TK_RESERVED') {
1303
                        space_before = true;
1304
                    } else if (last_type === 'TK_END_EXPR') {
1305
                        space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
1306
                    } else if (last_type === 'TK_OPERATOR') {
1307
                        // a++ + ++b;
1308
                        // a - -b
1309
                        space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
1310
                        // + and - are not unary when preceeded by -- or ++ operator
1311
                        // a-- + b
1312
                        // a * +b
1313
                        // a - -b
1314
                        if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
1315
                            space_after = true;
1316
                        }
1317
                    }
1318
1319
1320
                    if (((flags.mode === MODE.BlockStatement && !flags.inline_frame) || flags.mode === MODE.Statement) &&
1321
                        (flags.last_text === '{' || flags.last_text === ';')) {
1322
                        // { foo; --i }
1323
                        // foo(); --bar;
1324
                        print_newline();
1325
                    }
1326
                } else if (isGeneratorAsterisk) {
1327
                    space_before = false;
1328
                    space_after = false;
1329
                }
1330
                output.space_before_token = output.space_before_token || space_before;
1331
                print_token();
1332
                output.space_before_token = space_after;
1333
            }
1334
1335
            function handle_block_comment() {
1336
                if (output.raw) {
1337
                    output.add_raw_token(current_token);
1338
                    if (current_token.directives && current_token.directives.preserve === 'end') {
1339
                        // If we're testing the raw output behavior, do not allow a directive to turn it off.
1340
                        output.raw = opt.test_output_raw;
1341
                    }
1342
                    return;
1343
                }
1344
1345
                if (current_token.directives) {
1346
                    print_newline(false, true);
1347
                    print_token();
1348
                    if (current_token.directives.preserve === 'start') {
1349
                        output.raw = true;
1350
                    }
1351
                    print_newline(false, true);
1352
                    return;
1353
                }
1354
1355
                // inline block
1356
                if (!acorn.isNewLine(current_token.text.charCodeAt(0)) && !current_token.wanted_newline) {
1357
                    output.space_before_token = true;
1358
                    print_token();
1359
                    output.space_before_token = true;
1360
                    return;
1361
                }
1362
1363
                var lines = split_linebreaks(current_token.text);
1364
                var j; // iterator for this case
1365
                var javadoc = false;
1366
                var starless = false;
1367
                var lastIndent = current_token.whitespace_before;
1368
                var lastIndentLength = lastIndent.length;
1369
1370
                // block comment starts with a new line
1371
                print_newline(false, true);
1372
                if (lines.length > 1) {
1373
                    javadoc = all_lines_start_with(lines.slice(1), '*');
1374
                    starless = each_line_matches_indent(lines.slice(1), lastIndent);
1375
                }
1376
1377
                // first line always indented
1378
                print_token(lines[0]);
1379
                for (j = 1; j < lines.length; j++) {
1380
                    print_newline(false, true);
1381
                    if (javadoc) {
1382
                        // javadoc: reformat and re-indent
1383
                        print_token(' ' + ltrim(lines[j]));
1384
                    } else if (starless && lines[j].length > lastIndentLength) {
1385
                        // starless: re-indent non-empty content, avoiding trim
1386
                        print_token(lines[j].substring(lastIndentLength));
1387
                    } else {
1388
                        // normal comments output raw
1389
                        output.add_token(lines[j]);
1390
                    }
1391
                }
1392
1393
                // for comments of more than one line, make sure there's a new line after
1394
                print_newline(false, true);
1395
            }
1396
1397
            function handle_comment() {
1398
                if (current_token.wanted_newline) {
1399
                    print_newline(false, true);
1400
                } else {
1401
                    output.trim(true);
1402
                }
1403
1404
                output.space_before_token = true;
1405
                print_token();
1406
                print_newline(false, true);
1407
            }
1408
1409
            function handle_dot() {
1410
                if (start_of_statement()) {
1411
                    // The conditional starts the statement if appropriate.
1412
                }
1413
1414
                if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
1415
                    output.space_before_token = true;
1416
                } else {
1417
                    // allow preserved newlines before dots in general
1418
                    // force newlines on dots after close paren when break_chained - for bar().baz()
1419
                    allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
1420
                }
1421
1422
                print_token();
1423
            }
1424
1425
            function handle_unknown() {
1426
                print_token();
1427
1428
                if (current_token.text[current_token.text.length - 1] === '\n') {
1429
                    print_newline();
1430
                }
1431
            }
1432
1433
            function handle_eof() {
1434
                // Unwind any open statements
1435
                while (flags.mode === MODE.Statement) {
1436
                    restore_mode();
1437
                }
1438
            }
1439
        }
1440
1441
1442
        function OutputLine(parent) {
1443
            var _character_count = 0;
1444
            // use indent_count as a marker for lines that have preserved indentation
1445
            var _indent_count = -1;
1446
1447
            var _items = [];
1448
            var _empty = true;
1449
1450
            this.set_indent = function(level) {
1451
                _character_count = parent.baseIndentLength + level * parent.indent_length;
1452
                _indent_count = level;
1453
            };
1454
1455
            this.get_character_count = function() {
1456
                return _character_count;
1457
            };
1458
1459
            this.is_empty = function() {
1460
                return _empty;
1461
            };
1462
1463
            this.last = function() {
1464
                if (!this._empty) {
1465
                    return _items[_items.length - 1];
1466
                } else {
1467
                    return null;
1468
                }
1469
            };
1470
1471
            this.push = function(input) {
1472
                _items.push(input);
1473
                _character_count += input.length;
1474
                _empty = false;
1475
            };
1476
1477
            this.pop = function() {
1478
                var item = null;
1479
                if (!_empty) {
1480
                    item = _items.pop();
1481
                    _character_count -= item.length;
1482
                    _empty = _items.length === 0;
1483
                }
1484
                return item;
1485
            };
1486
1487
            this.remove_indent = function() {
1488
                if (_indent_count > 0) {
1489
                    _indent_count -= 1;
1490
                    _character_count -= parent.indent_length;
1491
                }
1492
            };
1493
1494
            this.trim = function() {
1495
                while (this.last() === ' ') {
1496
                    _items.pop();
1497
                    _character_count -= 1;
1498
                }
1499
                _empty = _items.length === 0;
1500
            };
1501
1502
            this.toString = function() {
1503
                var result = '';
1504
                if (!this._empty) {
1505
                    if (_indent_count >= 0) {
1506
                        result = parent.indent_cache[_indent_count];
1507
                    }
1508
                    result += _items.join('');
1509
                }
1510
                return result;
1511
            };
1512
        }
1513
1514
        function Output(indent_string, baseIndentString) {
1515
            baseIndentString = baseIndentString || '';
1516
            this.indent_cache = [baseIndentString];
1517
            this.baseIndentLength = baseIndentString.length;
1518
            this.indent_length = indent_string.length;
1519
            this.raw = false;
1520
1521
            var lines = [];
1522
            this.baseIndentString = baseIndentString;
1523
            this.indent_string = indent_string;
1524
            this.previous_line = null;
1525
            this.current_line = null;
1526
            this.space_before_token = false;
1527
1528
            this.add_outputline = function() {
1529
                this.previous_line = this.current_line;
1530
                this.current_line = new OutputLine(this);
1531
                lines.push(this.current_line);
1532
            };
1533
1534
            // initialize
1535
            this.add_outputline();
1536
1537
1538
            this.get_line_number = function() {
1539
                return lines.length;
1540
            };
1541
1542
            // Using object instead of string to allow for later expansion of info about each line
1543
            this.add_new_line = function(force_newline) {
1544
                if (this.get_line_number() === 1 && this.just_added_newline()) {
1545
                    return false; // no newline on start of file
1546
                }
1547
1548
                if (force_newline || !this.just_added_newline()) {
1549
                    if (!this.raw) {
1550
                        this.add_outputline();
1551
                    }
1552
                    return true;
1553
                }
1554
1555
                return false;
1556
            };
1557
1558
            this.get_code = function() {
1559
                var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
1560
                return sweet_code;
1561
            };
1562
1563
            this.set_indent = function(level) {
1564
                // Never indent your first output indent at the start of the file
1565
                if (lines.length > 1) {
1566
                    while (level >= this.indent_cache.length) {
1567
                        this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
1568
                    }
1569
1570
                    this.current_line.set_indent(level);
1571
                    return true;
1572
                }
1573
                this.current_line.set_indent(0);
1574
                return false;
1575
            };
1576
1577
            this.add_raw_token = function(token) {
1578
                for (var x = 0; x < token.newlines; x++) {
1579
                    this.add_outputline();
1580
                }
1581
                this.current_line.push(token.whitespace_before);
1582
                this.current_line.push(token.text);
1583
                this.space_before_token = false;
1584
            };
1585
1586
            this.add_token = function(printable_token) {
1587
                this.add_space_before_token();
1588
                this.current_line.push(printable_token);
1589
            };
1590
1591
            this.add_space_before_token = function() {
1592
                if (this.space_before_token && !this.just_added_newline()) {
1593
                    this.current_line.push(' ');
1594
                }
1595
                this.space_before_token = false;
1596
            };
1597
1598
            this.remove_redundant_indentation = function(frame) {
1599
                // This implementation is effective but has some issues:
1600
                //     - can cause line wrap to happen too soon due to indent removal
1601
                //           after wrap points are calculated
1602
                // These issues are minor compared to ugly indentation.
1603
1604
                if (frame.multiline_frame ||
1605
                    frame.mode === MODE.ForInitializer ||
1606
                    frame.mode === MODE.Conditional) {
1607
                    return;
1608
                }
1609
1610
                // remove one indent from each line inside this section
1611
                var index = frame.start_line_index;
1612
1613
                var output_length = lines.length;
1614
                while (index < output_length) {
1615
                    lines[index].remove_indent();
1616
                    index++;
1617
                }
1618
            };
1619
1620
            this.trim = function(eat_newlines) {
1621
                eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
1622
1623
                this.current_line.trim(indent_string, baseIndentString);
1624
1625
                while (eat_newlines && lines.length > 1 &&
1626
                    this.current_line.is_empty()) {
1627
                    lines.pop();
1628
                    this.current_line = lines[lines.length - 1];
1629
                    this.current_line.trim();
1630
                }
1631
1632
                this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
1633
            };
1634
1635
            this.just_added_newline = function() {
1636
                return this.current_line.is_empty();
1637
            };
1638
1639
            this.just_added_blankline = function() {
1640
                if (this.just_added_newline()) {
1641
                    if (lines.length === 1) {
1642
                        return true; // start of the file and newline = blank
1643
                    }
1644
1645
                    var line = lines[lines.length - 2];
1646
                    return line.is_empty();
1647
                }
1648
                return false;
1649
            };
1650
        }
1651
1652
1653
        var Token = function(type, text, newlines, whitespace_before, parent) {
1654
            this.type = type;
1655
            this.text = text;
1656
            this.comments_before = [];
1657
            this.newlines = newlines || 0;
1658
            this.wanted_newline = newlines > 0;
1659
            this.whitespace_before = whitespace_before || '';
1660
            this.parent = parent || null;
1661
            this.opened = null;
1662
            this.directives = null;
1663
        };
1664
1665
        function tokenizer(input, opts) {
1666
1667
            var whitespace = "\n\r\t ".split('');
1668
            var digit = /[0-9]/;
1669
            var digit_bin = /[01]/;
1670
            var digit_oct = /[01234567]/;
1671
            var digit_hex = /[0123456789abcdefABCDEF]/;
1672
1673
            this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ');
1674
            var punct = this.positionable_operators.concat(
1675
                // non-positionable operators - these do not follow operator position settings
1676
                '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~'.split(' '));
1677
1678
            // words which should always start on new line.
1679
            this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
1680
            var reserved_words = this.line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']);
1681
1682
            //  /* ... */ comment ends with nearest */ or end of file
1683
            var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;
1684
1685
            // comment ends just before nearest linefeed or end of file
1686
            var comment_pattern = /([^\n\r\u2028\u2029]*)/g;
1687
1688
            var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
1689
            var directive_pattern = / (\w+)[:](\w+)/g;
1690
            var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;
1691
1692
            var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g;
1693
1694
            var n_newlines, whitespace_before_token, in_html_comment, tokens, parser_pos;
1695
            var input_length;
1696
1697
            this.tokenize = function() {
1698
                // cache the source's length.
1699
                input_length = input.length;
1700
                parser_pos = 0;
1701
                in_html_comment = false;
1702
                tokens = [];
1703
1704
                var next, last;
1705
                var token_values;
1706
                var open = null;
1707
                var open_stack = [];
1708
                var comments = [];
1709
1710
                while (!(last && last.type === 'TK_EOF')) {
1711
                    token_values = tokenize_next();
1712
                    next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
1713
                    while (next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
1714
                        if (next.type === 'TK_BLOCK_COMMENT') {
1715
                            next.directives = token_values[2];
1716
                        }
1717
                        comments.push(next);
1718
                        token_values = tokenize_next();
1719
                        next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
1720
                    }
1721
1722
                    if (comments.length) {
1723
                        next.comments_before = comments;
1724
                        comments = [];
1725
                    }
1726
1727
                    if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
1728
                        next.parent = last;
1729
                        open_stack.push(open);
1730
                        open = next;
1731
                    } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
1732
                        (open && (
1733
                            (next.text === ']' && open.text === '[') ||
1734
                            (next.text === ')' && open.text === '(') ||
1735
                            (next.text === '}' && open.text === '{')))) {
1736
                        next.parent = open.parent;
1737
                        next.opened = open;
1738
1739
                        open = open_stack.pop();
1740
                    }
1741
1742
                    tokens.push(next);
1743
                    last = next;
1744
                }
1745
1746
                return tokens;
1747
            };
1748
1749
            function get_directives(text) {
1750
                if (!text.match(directives_block_pattern)) {
1751
                    return null;
1752
                }
1753
1754
                var directives = {};
1755
                directive_pattern.lastIndex = 0;
1756
                var directive_match = directive_pattern.exec(text);
1757
1758
                while (directive_match) {
1759
                    directives[directive_match[1]] = directive_match[2];
1760
                    directive_match = directive_pattern.exec(text);
1761
                }
1762
1763
                return directives;
1764
            }
1765
1766
            function tokenize_next() {
1767
                var resulting_string;
1768
                var whitespace_on_this_line = [];
1769
1770
                n_newlines = 0;
1771
                whitespace_before_token = '';
1772
1773
                if (parser_pos >= input_length) {
1774
                    return ['', 'TK_EOF'];
1775
                }
1776
1777
                var last_token;
1778
                if (tokens.length) {
1779
                    last_token = tokens[tokens.length - 1];
1780
                } else {
1781
                    // For the sake of tokenizing we can pretend that there was on open brace to start
1782
                    last_token = new Token('TK_START_BLOCK', '{');
1783
                }
1784
1785
1786
                var c = input.charAt(parser_pos);
1787
                parser_pos += 1;
1788
1789
                while (in_array(c, whitespace)) {
1790
1791
                    if (acorn.isNewLine(c.charCodeAt(0))) {
1792
                        if (!(c === '\n' && input.charAt(parser_pos - 2) === '\r')) {
1793
                            n_newlines += 1;
1794
                            whitespace_on_this_line = [];
1795
                        }
1796
                    } else {
1797
                        whitespace_on_this_line.push(c);
1798
                    }
1799
1800
                    if (parser_pos >= input_length) {
1801
                        return ['', 'TK_EOF'];
1802
                    }
1803
1804
                    c = input.charAt(parser_pos);
1805
                    parser_pos += 1;
1806
                }
1807
1808
                if (whitespace_on_this_line.length) {
1809
                    whitespace_before_token = whitespace_on_this_line.join('');
1810
                }
1811
1812
                if (digit.test(c) || (c === '.' && digit.test(input.charAt(parser_pos)))) {
1813
                    var allow_decimal = true;
1814
                    var allow_e = true;
1815
                    var local_digit = digit;
1816
1817
                    if (c === '0' && parser_pos < input_length && /[XxOoBb]/.test(input.charAt(parser_pos))) {
1818
                        // switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
1819
                        allow_decimal = false;
1820
                        allow_e = false;
1821
                        if (/[Bb]/.test(input.charAt(parser_pos))) {
1822
                            local_digit = digit_bin;
1823
                        } else if (/[Oo]/.test(input.charAt(parser_pos))) {
1824
                            local_digit = digit_oct;
1825
                        } else {
1826
                            local_digit = digit_hex;
1827
                        }
1828
                        c += input.charAt(parser_pos);
1829
                        parser_pos += 1;
1830
                    } else if (c === '.') {
1831
                        // Already have a decimal for this literal, don't allow another
1832
                        allow_decimal = false;
1833
                    } else {
1834
                        // we know this first loop will run.  It keeps the logic simpler.
1835
                        c = '';
1836
                        parser_pos -= 1;
1837
                    }
1838
1839
                    // Add the digits
1840
                    while (parser_pos < input_length && local_digit.test(input.charAt(parser_pos))) {
1841
                        c += input.charAt(parser_pos);
1842
                        parser_pos += 1;
1843
1844
                        if (allow_decimal && parser_pos < input_length && input.charAt(parser_pos) === '.') {
1845
                            c += input.charAt(parser_pos);
1846
                            parser_pos += 1;
1847
                            allow_decimal = false;
1848
                        } else if (allow_e && parser_pos < input_length && /[Ee]/.test(input.charAt(parser_pos))) {
1849
                            c += input.charAt(parser_pos);
1850
                            parser_pos += 1;
1851
1852
                            if (parser_pos < input_length && /[+-]/.test(input.charAt(parser_pos))) {
1853
                                c += input.charAt(parser_pos);
1854
                                parser_pos += 1;
1855
                            }
1856
1857
                            allow_e = false;
1858
                            allow_decimal = false;
1859
                        }
1860
                    }
1861
1862
                    return [c, 'TK_WORD'];
1863
                }
1864
1865
                if (acorn.isIdentifierStart(input.charCodeAt(parser_pos - 1))) {
1866
                    if (parser_pos < input_length) {
1867
                        while (acorn.isIdentifierChar(input.charCodeAt(parser_pos))) {
1868
                            c += input.charAt(parser_pos);
1869
                            parser_pos += 1;
1870
                            if (parser_pos === input_length) {
1871
                                break;
1872
                            }
1873
                        }
1874
                    }
1875
1876
                    if (!(last_token.type === 'TK_DOT' ||
1877
                            (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) &&
1878
                        in_array(c, reserved_words)) {
1879
                        if (c === 'in') { // hack for 'in' operator
1880
                            return [c, 'TK_OPERATOR'];
1881
                        }
1882
                        return [c, 'TK_RESERVED'];
1883
                    }
1884
1885
                    return [c, 'TK_WORD'];
1886
                }
1887
1888
                if (c === '(' || c === '[') {
1889
                    return [c, 'TK_START_EXPR'];
1890
                }
1891
1892
                if (c === ')' || c === ']') {
1893
                    return [c, 'TK_END_EXPR'];
1894
                }
1895
1896
                if (c === '{') {
1897
                    return [c, 'TK_START_BLOCK'];
1898
                }
1899
1900
                if (c === '}') {
1901
                    return [c, 'TK_END_BLOCK'];
1902
                }
1903
1904
                if (c === ';') {
1905
                    return [c, 'TK_SEMICOLON'];
1906
                }
1907
1908
                if (c === '/') {
1909
                    var comment = '';
1910
                    var comment_match;
1911
                    // peek for comment /* ... */
1912
                    if (input.charAt(parser_pos) === '*') {
1913
                        parser_pos += 1;
1914
                        block_comment_pattern.lastIndex = parser_pos;
1915
                        comment_match = block_comment_pattern.exec(input);
1916
                        comment = '/*' + comment_match[0];
1917
                        parser_pos += comment_match[0].length;
1918
                        var directives = get_directives(comment);
1919
                        if (directives && directives.ignore === 'start') {
1920
                            directives_end_ignore_pattern.lastIndex = parser_pos;
1921
                            comment_match = directives_end_ignore_pattern.exec(input);
1922
                            comment += comment_match[0];
1923
                            parser_pos += comment_match[0].length;
1924
                        }
1925
                        comment = comment.replace(acorn.allLineBreaks, '\n');
1926
                        return [comment, 'TK_BLOCK_COMMENT', directives];
1927
                    }
1928
                    // peek for comment // ...
1929
                    if (input.charAt(parser_pos) === '/') {
1930
                        parser_pos += 1;
1931
                        comment_pattern.lastIndex = parser_pos;
1932
                        comment_match = comment_pattern.exec(input);
1933
                        comment = '//' + comment_match[0];
1934
                        parser_pos += comment_match[0].length;
1935
                        return [comment, 'TK_COMMENT'];
1936
                    }
1937
1938
                }
1939
1940
                var startXmlRegExp = /^<([-a-zA-Z:0-9_.]+|{.+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{.+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.+?}))*\s*(\/?)\s*>/;
1941
1942
                if (c === '`' || c === "'" || c === '"' || // string
1943
                    (
1944
                        (c === '/') || // regexp
1945
                        (opts.e4x && c === "<" && input.slice(parser_pos - 1).match(startXmlRegExp)) // xml
1946
                    ) && ( // regex and xml can only appear in specific locations during parsing
1947
                        (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
1948
                        (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
1949
                            last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
1950
                        (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
1951
                            'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
1952
                        ]))
1953
                    )) {
1954
1955
                    var sep = c,
1956
                        esc = false,
1957
                        has_char_escapes = false;
1958
1959
                    resulting_string = c;
1960
1961
                    if (sep === '/') {
1962
                        //
1963
                        // handle regexp
1964
                        //
1965
                        var in_char_class = false;
1966
                        while (parser_pos < input_length &&
1967
                            ((esc || in_char_class || input.charAt(parser_pos) !== sep) &&
1968
                                !acorn.isNewLine(input.charCodeAt(parser_pos)))) {
1969
                            resulting_string += input.charAt(parser_pos);
1970
                            if (!esc) {
1971
                                esc = input.charAt(parser_pos) === '\\';
1972
                                if (input.charAt(parser_pos) === '[') {
1973
                                    in_char_class = true;
1974
                                } else if (input.charAt(parser_pos) === ']') {
1975
                                    in_char_class = false;
1976
                                }
1977
                            } else {
1978
                                esc = false;
1979
                            }
1980
                            parser_pos += 1;
1981
                        }
1982
                    } else if (opts.e4x && sep === '<') {
1983
                        //
1984
                        // handle e4x xml literals
1985
                        //
1986
1987
                        var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{.+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{.+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.+?}))*\s*(\/?)\s*>/g;
1988
                        var xmlStr = input.slice(parser_pos - 1);
1989
                        var match = xmlRegExp.exec(xmlStr);
1990
                        if (match && match.index === 0) {
1991
                            var rootTag = match[2];
1992
                            var depth = 0;
1993
                            while (match) {
1994
                                var isEndTag = !!match[1];
1995
                                var tagName = match[2];
1996
                                var isSingletonTag = (!!match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
1997
                                if (tagName === rootTag && !isSingletonTag) {
1998
                                    if (isEndTag) {
1999
                                        --depth;
2000
                                    } else {
2001
                                        ++depth;
2002
                                    }
2003
                                }
2004
                                if (depth <= 0) {
2005
                                    break;
2006
                                }
2007
                                match = xmlRegExp.exec(xmlStr);
2008
                            }
2009
                            var xmlLength = match ? match.index + match[0].length : xmlStr.length;
2010
                            xmlStr = xmlStr.slice(0, xmlLength);
2011
                            parser_pos += xmlLength - 1;
2012
                            xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n');
2013
                            return [xmlStr, "TK_STRING"];
2014
                        }
2015
                    } else {
2016
                        //
2017
                        // handle string
2018
                        //
2019
                        var parse_string = function(delimiter, allow_unescaped_newlines, start_sub) {
2020
                            // Template strings can travers lines without escape characters.
2021
                            // Other strings cannot
2022
                            var current_char;
2023
                            while (parser_pos < input_length) {
2024
                                current_char = input.charAt(parser_pos);
2025
                                if (!(esc || (current_char !== delimiter &&
2026
                                        (allow_unescaped_newlines || !acorn.isNewLine(current_char.charCodeAt(0)))))) {
2027
                                    break;
2028
                                }
2029
2030
                                // Handle \r\n linebreaks after escapes or in template strings
2031
                                if ((esc || allow_unescaped_newlines) && acorn.isNewLine(current_char.charCodeAt(0))) {
2032
                                    if (current_char === '\r' && input.charAt(parser_pos + 1) === '\n') {
2033
                                        parser_pos += 1;
2034
                                        current_char = input.charAt(parser_pos);
2035
                                    }
2036
                                    resulting_string += '\n';
2037
                                } else {
2038
                                    resulting_string += current_char;
2039
                                }
2040
                                if (esc) {
2041
                                    if (current_char === 'x' || current_char === 'u') {
2042
                                        has_char_escapes = true;
2043
                                    }
2044
                                    esc = false;
2045
                                } else {
2046
                                    esc = current_char === '\\';
2047
                                }
2048
2049
                                parser_pos += 1;
2050
2051
                                if (start_sub && resulting_string.indexOf(start_sub, resulting_string.length - start_sub.length) !== -1) {
2052
                                    if (delimiter === '`') {
2053
                                        parse_string('}', allow_unescaped_newlines, '`');
2054
                                    } else {
2055
                                        parse_string('`', allow_unescaped_newlines, '${');
2056
                                    }
2057
                                }
2058
                            }
2059
                        };
2060
2061
                        if (sep === '`') {
2062
                            parse_string('`', true, '${');
2063
                        } else {
2064
                            parse_string(sep);
2065
                        }
2066
                    }
2067
2068
                    if (has_char_escapes && opts.unescape_strings) {
2069
                        resulting_string = unescape_string(resulting_string);
2070
                    }
2071
2072
                    if (parser_pos < input_length && input.charAt(parser_pos) === sep) {
2073
                        resulting_string += sep;
2074
                        parser_pos += 1;
2075
2076
                        if (sep === '/') {
2077
                            // regexps may have modifiers /regexp/MOD , so fetch those, too
2078
                            // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
2079
                            while (parser_pos < input_length && acorn.isIdentifierStart(input.charCodeAt(parser_pos))) {
2080
                                resulting_string += input.charAt(parser_pos);
2081
                                parser_pos += 1;
2082
                            }
2083
                        }
2084
                    }
2085
                    return [resulting_string, 'TK_STRING'];
2086
                }
2087
2088
                if (c === '#') {
2089
2090
                    if (tokens.length === 0 && input.charAt(parser_pos) === '!') {
2091
                        // shebang
2092
                        resulting_string = c;
2093
                        while (parser_pos < input_length && c !== '\n') {
2094
                            c = input.charAt(parser_pos);
2095
                            resulting_string += c;
2096
                            parser_pos += 1;
2097
                        }
2098
                        return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
2099
                    }
2100
2101
2102
2103
                    // Spidermonkey-specific sharp variables for circular references
2104
                    // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
2105
                    // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
2106
                    var sharp = '#';
2107
                    if (parser_pos < input_length && digit.test(input.charAt(parser_pos))) {
2108
                        do {
2109
                            c = input.charAt(parser_pos);
2110
                            sharp += c;
2111
                            parser_pos += 1;
2112
                        } while (parser_pos < input_length && c !== '#' && c !== '=');
2113
                        if (c === '#') {
2114
                            //
2115
                        } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
2116
                            sharp += '[]';
2117
                            parser_pos += 2;
2118
                        } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
2119
                            sharp += '{}';
2120
                            parser_pos += 2;
2121
                        }
2122
                        return [sharp, 'TK_WORD'];
2123
                    }
2124
                }
2125
2126
                if (c === '<' && (input.charAt(parser_pos) === '?' || input.charAt(parser_pos) === '%')) {
2127
                    template_pattern.lastIndex = parser_pos - 1;
2128
                    var template_match = template_pattern.exec(input);
2129
                    if (template_match) {
2130
                        c = template_match[0];
2131
                        parser_pos += c.length - 1;
2132
                        c = c.replace(acorn.allLineBreaks, '\n');
2133
                        return [c, 'TK_STRING'];
2134
                    }
2135
                }
2136
2137
                if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
2138
                    parser_pos += 3;
2139
                    c = '<!--';
2140
                    while (!acorn.isNewLine(input.charCodeAt(parser_pos)) && parser_pos < input_length) {
2141
                        c += input.charAt(parser_pos);
2142
                        parser_pos++;
2143
                    }
2144
                    in_html_comment = true;
2145
                    return [c, 'TK_COMMENT'];
2146
                }
2147
2148
                if (c === '-' && in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
2149
                    in_html_comment = false;
2150
                    parser_pos += 2;
2151
                    return ['-->', 'TK_COMMENT'];
2152
                }
2153
2154
                if (c === '.') {
2155
                    return [c, 'TK_DOT'];
2156
                }
2157
2158
                if (in_array(c, punct)) {
2159
                    while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
2160
                        c += input.charAt(parser_pos);
2161
                        parser_pos += 1;
2162
                        if (parser_pos >= input_length) {
2163
                            break;
2164
                        }
2165
                    }
2166
2167
                    if (c === ',') {
2168
                        return [c, 'TK_COMMA'];
2169
                    } else if (c === '=') {
2170
                        return [c, 'TK_EQUALS'];
2171
                    } else {
2172
                        return [c, 'TK_OPERATOR'];
2173
                    }
2174
                }
2175
2176
                return [c, 'TK_UNKNOWN'];
2177
            }
2178
2179
2180
            function unescape_string(s) {
2181
                var esc = false,
2182
                    out = '',
2183
                    pos = 0,
2184
                    s_hex = '',
2185
                    escaped = 0,
2186
                    c;
2187
2188
                while (esc || pos < s.length) {
2189
2190
                    c = s.charAt(pos);
2191
                    pos++;
2192
2193
                    if (esc) {
2194
                        esc = false;
2195
                        if (c === 'x') {
2196
                            // simple hex-escape \x24
2197
                            s_hex = s.substr(pos, 2);
2198
                            pos += 2;
2199
                        } else if (c === 'u') {
2200
                            // unicode-escape, \u2134
2201
                            s_hex = s.substr(pos, 4);
2202
                            pos += 4;
2203
                        } else {
2204
                            // some common escape, e.g \n
2205
                            out += '\\' + c;
2206
                            continue;
2207
                        }
2208
                        if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) {
2209
                            // some weird escaping, bail out,
2210
                            // leaving whole string intact
2211
                            return s;
2212
                        }
2213
2214
                        escaped = parseInt(s_hex, 16);
2215
2216
                        if (escaped >= 0x00 && escaped < 0x20) {
2217
                            // leave 0x00...0x1f escaped
2218
                            if (c === 'x') {
2219
                                out += '\\x' + s_hex;
2220
                            } else {
2221
                                out += '\\u' + s_hex;
2222
                            }
2223
                            continue;
2224
                        } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
2225
                            // single-quote, apostrophe, backslash - escape these
2226
                            out += '\\' + String.fromCharCode(escaped);
2227
                        } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) {
2228
                            // we bail out on \x7f..\xff,
2229
                            // leaving whole string escaped,
2230
                            // as it's probably completely binary
2231
                            return s;
2232
                        } else {
2233
                            out += String.fromCharCode(escaped);
2234
                        }
2235
                    } else if (c === '\\') {
2236
                        esc = true;
2237
                    } else {
2238
                        out += c;
2239
                    }
2240
                }
2241
                return out;
2242
            }
2243
        }
2244
2245
        var beautifier = new Beautifier(js_source_text, options);
2246
        return beautifier.beautify();
2247
2248
    }
2249
2250
	return {
2251
		js_beautify: js_beautify
2252
	};
2253
});
(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/nls/root/messages.js (+43 lines)
Lines 290-295 define({ Link Here
290
	'jsDocAnyTypeDesc': ' - No type information',
290
	'jsDocAnyTypeDesc': ' - No type information',
291
	'jsDocAnyTypeDoc': 'Represents that no type information is known. The type is described as the \'any\' type.',
291
	'jsDocAnyTypeDoc': 'Represents that no type information is known. The type is described as the \'any\' type.',
292
	
292
	
293
	'beautifierPluginName' : 'JSBeautify plugin for Tern',
294
	'beautifierPluginDescription' : 'Provides JS formatting for Tern',
295
	'javascriptFormatter' : 'Javascript Formatter',
296
297
	// JS formatting settings
298
	'javascriptFormattingSettings' : 'Formatting Settings for Javascript',
299
	'javascriptFormatting' : 'Javascript Formatting',
300
	'js_indent_size' : 'Indention size:',
301
	'js_indent_char' : 'Indentation character:',
302
	'js_eol' : 'Charater(s) to use as line terminators:',
303
	'js_end_with_newline' : 'End ouput with newline:',
304
	'indentation_unix' : 'Unix',
305
	'indentation_mac' : 'Mac',
306
	'indentation_windows' : 'Windows',
307
	'indentation_space' : 'space',
308
	'indentation_tab' : 'tab',
309
	'jsFormattingSettings' : 'Formatting Settings for Javascript',
310
	'indent_level': 'Initial indentation level:',
311
	'before_newline' : 'Before new line',
312
	'after_newline' : 'After new line',
313
	'preserve_newline' : 'Preserve new line',
314
	'collapse_preserve_inline' : 'Collapse Preserve inline',
315
	'collapse' : 'Collapse',
316
	'expand' : 'Expand',
317
	'end_expand' : 'End expand',
318
	'none' : 'None',
319
	'js_preserve_newlines' : 'Preserve line-breaks:',
320
	'js_max_preserve_newlines' : 'Number of line-breaks to be preserved in one chunk:',
321
	'space_in_paren' : 'Add padding space within paren:',
322
	'space_in_empty_paren' : 'Add padding space in empty paren:',
323
	'space_after_anon_function' : "Add a space before an anonymous function's parens",
324
	'js_brace_style' : 'Brace Style:',
325
	'break_chained_methods' : 'Break chained method calls across subsequent lines:',
326
	'keep_array_indentation' : 'Preserve array indentation:',
327
	'space_before_conditional' : 'Space before condition:',
328
	'unescape_strings' : 'Decode printable characters encoded in xNN notation:',
329
	'js_wrap_line_length' : 'Wrap lines at next opportunity after N characters: (0 for unlimited)',
330
	'e4x' : 'Pass E4X xml literal through untouched:',
331
	'comma_first' : 'Put commas at the beginning of new line instead of end:',
332
	'operator_position' : 'Position for operator:',
333
293
	// Other messages
334
	// Other messages
294
	'unknownError': 'An unknown error occurred.',
335
	'unknownError': 'An unknown error occurred.',
295
	'failedDeleteRequest': 'Failed to delete file from Tern: ${0}',
336
	'failedDeleteRequest': 'Failed to delete file from Tern: ${0}',
Lines 326-331 define({ Link Here
326
	'serverNotStarted': 'The server has not been started. Request: \'${0}\'',
367
	'serverNotStarted': 'The server has not been started. Request: \'${0}\'',
327
	'failedToComputeProblems': 'Failed to compute ESLint problems/markers',
368
	'failedToComputeProblems': 'Failed to compute ESLint problems/markers',
328
	'failedToComputeOutline': 'Failed to compute outline',
369
	'failedToComputeOutline': 'Failed to compute outline',
370
	'failedToFormat' : 'Failed to format',
371
	'failedToFormatNoServer' : 'Failed to format, server not started',
329
	
372
	
330
	//Templates
373
	//Templates
331
	'eslintRuleEnableDisable': 'Enable or disable ESLint rule using the ```ruleid:0/1/2``` form.\n\nExample use:\n\n>```/* eslint semi:1, no-console:0, no-redeclare:2 */```',
374
	'eslintRuleEnableDisable': 'Enable or disable ESLint rule using the ```ruleid:0/1/2``` form.\n\nExample use:\n\n>```/* eslint semi:1, no-console:0, no-redeclare:2 */```',
(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/ternDefaults.js (-3 / +8 lines)
Lines 44-52 define([ Link Here
44
	"javascript/ternPlugins/redis",
44
	"javascript/ternPlugins/redis",
45
	"javascript/ternPlugins/refs",
45
	"javascript/ternPlugins/refs",
46
	"javascript/ternPlugins/templates",
46
	"javascript/ternPlugins/templates",
47
	"javascript/ternPlugins/quickfixes"
47
	"javascript/ternPlugins/quickfixes",
48
	"javascript/ternPlugins/beautifier"
48
], function(Messages, ecma5, ecma6, ecma7, browser, chai) {
49
], function(Messages, ecma5, ecma6, ecma7, browser, chai) {
49
	
50
	var defs = [ecma5, ecma6, ecma7, browser, chai];
50
	var defs = [ecma5, ecma6, ecma7, browser, chai];
51
	var defNames = ["ecma5", "ecma6", "ecma7", "browser", "chai"]; //these are in the same order to avoid a walk of the array
51
	var defNames = ["ecma5", "ecma6", "ecma7", "browser", "chai"]; //these are in the same order to avoid a walk of the array
52
	
52
	
Lines 112-118 define([ Link Here
112
				"name": Messages["templatesPlugin"],
112
				"name": Messages["templatesPlugin"],
113
				"description": Messages["templatesPluginDescription"],
113
				"description": Messages["templatesPluginDescription"],
114
				"version": "1.0"
114
				"version": "1.0"
115
			}
115
			},
116
			"beautifier": {
117
				"name": Messages["beautifierPluginName"],
118
				"description": Messages["beautifierPluginDescription"],
119
				"version": "1.0"
120
			},
116
		},
121
		},
117
		optional: {
122
		optional: {
118
			"amqp": {
123
			"amqp": {
(-)a/bundles/org.eclipse.orion.client.ui/web/orion/settings/nls/root/messages.js (-2 / +2 lines)
Lines 1-6 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * @license
2
 * @license
3
 * Copyright (c) 2012 IBM Corporation and others.
3
 * Copyright (c) 2012, 2016 IBM Corporation and others.
4
 * All rights reserved. This program and the accompanying materials are made
4
 * All rights reserved. This program and the accompanying materials are made
5
 * available under the terms of the Eclipse Public License v1.0
5
 * available under the terms of the Eclipse Public License v1.0
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
Lines 283-287 define({//Default message bundle Link Here
283
    "editorTheme selection background": "Selection background",
283
    "editorTheme selection background": "Selection background",
284
    'customizeTheme': 'Custom Style...',
284
    'customizeTheme': 'Custom Style...',
285
    'moreEditorSettings': 'Editor Settings...',
285
    'moreEditorSettings': 'Editor Settings...',
286
    'JavascriptSettingWarning' : '${0} Warning: Global settings for ${2} are overriden by the settings defined in: ${1}'
286
    'SettingWarning' : '${0} Warning: Global settings for \'${2}\' are overriden by the settings defined in: ${1}'
287
});
287
});
(-)a/bundles/org.eclipse.orion.client.webtools/web/webtools/plugins/webToolsPlugin.js (-2 / +238 lines)
Lines 26-34 define(['orion/plugin', Link Here
26
'webtools/cssQuickFixes',
26
'webtools/cssQuickFixes',
27
'webtools/cssResultManager',
27
'webtools/cssResultManager',
28
'orion/editor/stylers/text_css/syntax',
28
'orion/editor/stylers/text_css/syntax',
29
'i18n!webtools/nls/messages'
29
'i18n!webtools/nls/messages',
30
'webtools/htmlFormatter',
31
'webtools/cssFormatter',
30
], function(PluginProvider, mServiceRegistry, ScriptResolver, HtmlAstManager, htmlHover, htmlContentAssist, htmlOccurrences, htmlOutliner,
32
], function(PluginProvider, mServiceRegistry, ScriptResolver, HtmlAstManager, htmlHover, htmlContentAssist, htmlOccurrences, htmlOutliner,
31
            mHTML, cssContentAssist, mCssValidator, mCssOutliner, cssHover, cssQuickFixes, cssResultManager, mCSS, messages) {
33
            mHTML, cssContentAssist, mCssValidator, mCssOutliner, cssHover, cssQuickFixes, cssResultManager, mCSS, messages, HtmlFormatter, CssFormatter) {
32
34
33
	/**
35
	/**
34
	 * Plug-in headers
36
	 * Plug-in headers
Lines 220-225 define(['orion/plugin', Link Here
220
    		}
222
    		}
221
    	);
223
    	);
222
224
225
		var htmlFormatter = new HtmlFormatter.HtmlFormatter();
226
		provider.registerServiceProvider("orion.edit.format",
227
			htmlFormatter,
228
			{
229
				contentType: ["text/html"], //$NON-NLS-1$
230
				id: "orion.format.html.formatter", //$NON-NLS-1$
231
				name: messages["htmlFormatter"],//$NON-NLS-1$
232
			}
233
		);
234
235
		var cssFormatter = new CssFormatter.CssFormatter();
236
		provider.registerServiceProvider("orion.edit.format",
237
			cssFormatter,
238
			{
239
				contentType: ["text/css"],//$NON-NLS-1$
240
				id: "orion.format.css.formatter", //$NON-NLS-1$
241
				name: messages["cssFormatter"],//$NON-NLS-1$
242
			}
243
		);
244
245
		provider.registerService("orion.cm.managedservice", cssFormatter, {pid: 'jsbeautify.config.css'}); //$NON-NLS-1$ //$NON-NLS-2$
246
247
		// html formatter needs to listen to all formatting preference changes because html source can contain css and javascript
248
		provider.registerService("orion.cm.managedservice", htmlFormatter, {pid: 'jsbeautify.config.html'}); //$NON-NLS-1$ //$NON-NLS-2$
249
		provider.registerService("orion.cm.managedservice", htmlFormatter, {pid: 'jsbeautify.config.css'}); //$NON-NLS-1$ //$NON-NLS-2$
250
		provider.registerService("orion.cm.managedservice", htmlFormatter, {pid: 'jsbeautify.config.js'}); //$NON-NLS-1$ //$NON-NLS-2$
251
		
252
		/**
253
		 * CSS formatting settings
254
		 */
255
		var unix = "\n";
256
		var mac = "\r";
257
		var windows = "\n\r";
258
		var eof_characters = [
259
			{label: messages.indentation_unix,  value: unix},
260
			{label: messages.indentation_mac, value: mac},
261
			{label: messages.indentation_windows, value: windows}
262
		];
263
264
		var space = ' ';
265
		var tab= '\t';
266
		var indentation_characters = [
267
			{label: messages.indentation_space,  value: space},
268
			{label: messages.indentation_tab,  value: tab},
269
		];
270
		var collapse_preserve_inline = 'collapse-preserve-inline';
271
		var collapse = 'collapse';
272
		var expand = 'expand';
273
		var end_expand = 'end-expand';
274
		var none = 'none';
275
		var brace_styles = [
276
			{ label: messages.collapse_preserve_inline , value: collapse_preserve_inline},
277
			{ label: messages.collapse , value: collapse},
278
			{ label: messages.expand , value: expand},
279
			{ label: messages.end_expand , value: end_expand},
280
			{ label: messages.none , value: none},
281
		];
282
283
		provider.registerServiceProvider("orion.core.setting", {}, {
284
			settings: [
285
				{
286
					pid: 'jsbeautify.config.css',
287
					name: messages['cssFormattingSettings'],
288
					tags: 'beautify css formatting'.split(' '),
289
					category: 'cssFormatting',
290
					categoryLabel: messages['cssFormatting'],
291
					properties: [
292
						{
293
							id: 'css_indent_size',  //$NON-NLS-1$
294
							name: messages['css_indent_size'],
295
							type: 'number', //$NON-NLS-1$
296
							defaultValue: 4
297
						},
298
						{
299
							id: 'css_indent_char',  //$NON-NLS-1$
300
							name: messages['css_indent_char'],
301
							type: 'string', //$NON-NLS-1$
302
							defaultValue: space,
303
							options: indentation_characters
304
						},
305
						{
306
							id: 'css_eol',  //$NON-NLS-1$
307
							name: messages['css_eol'],
308
							type: 'string', //$NON-NLS-1$
309
							defaultValue: unix,
310
							options: eof_characters
311
						},
312
						{
313
							id: 'css_end_with_newline',  //$NON-NLS-1$
314
							name: messages['css_end_with_newline'],
315
							type: 'boolean', //$NON-NLS-1$
316
							defaultValue: false
317
						},
318
						{
319
							id: 'selector_separator_newline',  //$NON-NLS-1$
320
							name: messages['selector_separator_newline'],
321
							type: 'boolean', //$NON-NLS-1$
322
							defaultValue: true
323
						},
324
						{
325
							id: 'newline_between_rules',  //$NON-NLS-1$
326
							name: messages['newline_between_rules'],
327
							type: 'boolean', //$NON-NLS-1$
328
							defaultValue: true
329
						},
330
						{
331
							id: 'space_around_selector_separator',  //$NON-NLS-1$
332
							name: messages['space_around_selector_separator'],
333
							type: 'boolean', //$NON-NLS-1$
334
							defaultValue: false
335
						}
336
					]
337
				}]
338
		});
339
340
		/**
341
		 * Html formatting settings
342
		 */
343
		var keep = 'keep';
344
		var separate = 'separate';
345
		var normal = 'normal';
346
		var indent_scripts_values = [
347
			{ label: messages.normal, value: normal},
348
			{ label: messages.keep, value: keep},
349
			{ label: messages.separate, value: separate},
350
		];
351
		var auto = 'auto';
352
		var force = 'force';
353
		var wrap_attributes_values = [
354
			{ label: messages.auto, value: auto},
355
			{ label: messages.force, value: force},
356
		];
357
		provider.registerServiceProvider("orion.core.setting", {}, {
358
			settings: [
359
				{
360
					pid: 'jsbeautify.config.html',
361
					name: messages['htmlFormattingOptions'],
362
					tags: 'beautify html formatting'.split(' '),
363
					category: 'htmlFormatting',
364
					categoryLabel: messages['htmlFormatting'],
365
					properties: [
366
						{
367
							id: 'html_indent_size',  //$NON-NLS-1$
368
							name: messages['html_indent_size'],
369
							type: 'number', //$NON-NLS-1$
370
							defaultValue: 4
371
						},
372
						{
373
							id: 'html_indent_char',  //$NON-NLS-1$
374
							name: messages['html_indent_char'],
375
							type: 'string', //$NON-NLS-1$
376
							defaultValue: space,
377
							options: indentation_characters
378
						},
379
						{
380
							id: 'html_eol',  //$NON-NLS-1$
381
							name: messages['html_eol'],
382
							type: 'string', //$NON-NLS-1$
383
							defaultValue: unix,
384
							options: eof_characters
385
						},
386
						{
387
							id: 'html_end_with_newline',  //$NON-NLS-1$
388
							name: messages['html_end_with_newline'],
389
							type: 'boolean', //$NON-NLS-1$
390
							defaultValue: false
391
						},
392
						{
393
							id: 'html_preserve_new_lines',  //$NON-NLS-1$
394
							name: messages['html_preserve_new_lines'],
395
							type: 'boolean', //$NON-NLS-1$
396
							defaultValue: true
397
						},
398
						{
399
							id: 'html_max_preserve_new_lines',  //$NON-NLS-1$
400
							name: messages['html_max_preserve_new_lines'],
401
							type: 'number', //$NON-NLS-1$
402
							defaultValue: 32786
403
						},
404
						{
405
							id: 'html_brace_style',  //$NON-NLS-1$
406
							name: messages['html_brace_style'],
407
							type: 'boolean', //$NON-NLS-1$
408
							defaultValue: collapse,
409
							options: brace_styles
410
						},
411
						{
412
							id: 'html_wrap_line_length',  //$NON-NLS-1$
413
							name: messages['html_wrap_line_length'],
414
							type: 'number', //$NON-NLS-1$
415
							defaultValue: 0
416
						},
417
						{
418
							id: 'indent_inner_html',  //$NON-NLS-1$
419
							name: messages['indent_inner_html'],
420
							type: 'boolean', //$NON-NLS-1$
421
							defaultValue: false
422
						},
423
						{
424
							id: 'indent_handlebars',  //$NON-NLS-1$
425
							name: messages['indent_handlebars'],
426
							type: 'boolean', //$NON-NLS-1$
427
							defaultValue: false
428
						},
429
						{
430
							id: 'wrap_attributes',  //$NON-NLS-1$
431
							name: messages['wrap_attributes'],
432
							type: 'string', //$NON-NLS-1$
433
							defaultValue: 'auto',
434
							options: wrap_attributes_values
435
						},
436
						{
437
							id: 'wrap_attributes_indent_size',  //$NON-NLS-1$
438
							name: messages['wrap_attributes_indent_size'],
439
							type: 'number', //$NON-NLS-1$
440
							defaultValue: 1
441
						},
442
						{
443
							id: 'extra_liners',  //$NON-NLS-1$
444
							name: messages['extra_liners'],
445
							type: 'string', //$NON-NLS-1$
446
							defaultValue: 'head,body,html'
447
						},
448
						{
449
							id: 'indent-scripts',  //$NON-NLS-1$
450
							name: messages['indent_scripts'],
451
							type: 'string', //$NON-NLS-1$
452
							defaultValue: normal,
453
							options: indent_scripts_values
454
						}
455
					]
456
				}]
457
		});
458
223
        /**
459
        /**
224
    	 * CSSLint settings
460
    	 * CSSLint settings
225
    	 */
461
    	 */
(-)a/bundles/org.eclipse.orion.client.ui/web/orion/editorView.js (-2 / +4 lines)
Lines 45-51 define([ Link Here
45
	'orion/metrics',
45
	'orion/metrics',
46
	'orion/commonPreferences',
46
	'orion/commonPreferences',
47
	'embeddedEditor/helper/memoryFileSysConst',
47
	'embeddedEditor/helper/memoryFileSysConst',
48
	'orion/objects'
48
	'orion/objects',
49
	'orion/formatter'	
49
], function(
50
], function(
50
	messages,
51
	messages,
51
	mEditor, mEventTarget, mTextView, mTextModelFactory, mEditorFeatures, mHoverFactory, mContentAssist,
52
	mEditor, mEventTarget, mTextView, mTextModelFactory, mEditorFeatures, mHoverFactory, mContentAssist,
Lines 54-60 define([ Link Here
54
	mDispatcher, EditorContext, Highlight,
55
	mDispatcher, EditorContext, Highlight,
55
	mMarkOccurrences, mSyntaxchecker, LiveEditSession,
56
	mMarkOccurrences, mSyntaxchecker, LiveEditSession,
56
	mProblems, mBlamer, mDiffer,
57
	mProblems, mBlamer, mDiffer,
57
	mKeyBinding, util, Deferred, mContextMenu, mMetrics, mCommonPreferences, memoryFileSysConst, objects
58
	mKeyBinding, util, Deferred, mContextMenu, mMetrics, mCommonPreferences, memoryFileSysConst, objects, mFormatter
58
) {
59
) {
59
	var inMemoryFilePattern = memoryFileSysConst.MEMORY_FILE_PATTERN;
60
	var inMemoryFilePattern = memoryFileSysConst.MEMORY_FILE_PATTERN;
60
	var Dispatcher = mDispatcher.Dispatcher;
61
	var Dispatcher = mDispatcher.Dispatcher;
Lines 511-516 define([ Link Here
511
512
512
			this.blamer = new mBlamer.Blamer(serviceRegistry, inputManager, editor);
513
			this.blamer = new mBlamer.Blamer(serviceRegistry, inputManager, editor);
513
			this.differ = new mDiffer.Differ(serviceRegistry, inputManager, editor);
514
			this.differ = new mDiffer.Differ(serviceRegistry, inputManager, editor);
515
			this.formatter = new mFormatter.Formatter(serviceRegistry, inputManager, editor);
514
516
515
			this.problemService = new mProblems.ProblemService(serviceRegistry, this.problemsServiceID);
517
			this.problemService = new mProblems.ProblemService(serviceRegistry, this.problemsServiceID);
516
			var markerService = serviceRegistry.getService(this.problemsServiceID);
518
			var markerService = serviceRegistry.getService(this.problemsServiceID);
(-)a/bundles/org.eclipse.orion.client.ui/web/orion/settings/ui/PluginSettings.js (-2 / +49 lines)
Lines 449-454 define([ Link Here
449
			}.bind(this));
449
			}.bind(this));
450
		}.bind(this));
450
		}.bind(this));
451
	}
451
	}
452
	
453
	function getFormattingSettings(fileClient, projectPath, callback) {
454
		var jsbeautifyrc = projectPath + SettingsList.prototype.JSBEAUTIFYRC;
455
		return fileClient.read(jsbeautifyrc, false, false, {readIfExists: true}).then(function(contents) {
456
			if (contents !== null) {
457
				callback({contents: contents, name: SettingsList.prototype.JSBEAUTIFYRC, location: jsbeautifyrc});
458
				return;
459
			}
460
		}.bind(this));
461
	}
452
462
453
	SettingsList.prototype = {
463
	SettingsList.prototype = {
454
		/**
464
		/**
Lines 480-485 define([ Link Here
480
		 * The package.json file name
490
		 * The package.json file name
481
		 */
491
		 */
482
		PACKAGE_JSON : 'package.json',
492
		PACKAGE_JSON : 'package.json',
493
		/**
494
		 * The .jsbeautifyrc file name
495
		 * @see https://github.com/beautify-web/js-beautify/blob/master/README.md
496
		 */
497
		JSBEAUTIFYRC : '.jsbeautifyrc',
483
		
498
		
484
		_makeSection: function(parent, sectionId, title, hasMultipleSections) {
499
		_makeSection: function(parent, sectionId, title, hasMultipleSections) {
485
			var that = this;
500
			var that = this;
Lines 537-543 define([ Link Here
537
								if (!this.destroyed) {
552
								if (!this.destroyed) {
538
									var infoText = document.createElement("div"); //$NON-NLS-0$
553
									var infoText = document.createElement("div"); //$NON-NLS-0$
539
									infoText.classList.add("setting-info"); //$NON-NLS-0$
554
									infoText.classList.add("setting-info"); //$NON-NLS-0$
540
									infoText.textContent = messages.JavascriptSettingWarning;
555
									infoText.textContent = messages.SettingWarning;
541
									var icon = document.createElement("span"); //$NON-NLS-0$
556
									var icon = document.createElement("span"); //$NON-NLS-0$
542
									icon.classList.add("core-sprite-warning"); //$NON-NLS-0$
557
									icon.classList.add("core-sprite-warning"); //$NON-NLS-0$
543
									icon.classList.add("icon-inline"); //$NON-NLS-0$
558
									icon.classList.add("icon-inline"); //$NON-NLS-0$
Lines 559-565 define([ Link Here
559
					}
574
					}
560
					break;
575
					break;
561
				}
576
				}
562
			}
577
				case "javascriptFormatting" : {
578
					if (pageParams.resource && pageParams.resource.length !== 0) {
579
						// resource name starts with /file/ and ends with '/'
580
						projectName = pageParams.resource.substring(6, pageParams.resource.length - 1);
581
						getFormattingSettings(
582
							this.fileClient,
583
							pageParams.resource,
584
							function(file) {
585
								if (!this.destroyed) {
586
									var infoText = document.createElement("div"); //$NON-NLS-0$
587
									infoText.classList.add("setting-info"); //$NON-NLS-0$
588
									infoText.textContent = messages.SettingWarning;
589
									var icon = document.createElement("span"); //$NON-NLS-0$
590
									icon.classList.add("core-sprite-warning"); //$NON-NLS-0$
591
									icon.classList.add("icon-inline"); //$NON-NLS-0$
592
									icon.classList.add("imageSprite"); //$NON-NLS-0$
593
									var link = document.createElement("a"); //$NON-NLS-0$
594
									link.href = editTemplate.expand({resource: file.location});
595
									link.appendChild(document.createTextNode(file.name));
596
									var projectText = document.createElement("span"); //$NON-NLS-0$
597
									projectText.textContent = projectName;
598
									lib.processDOMNodes(infoText, [icon, link, projectText]);
599
									if (parent.firstChild) {
600
										parent.insertBefore(infoText, parent.firstChild);
601
									} else {
602
										parent.appendChild(infoText);
603
									}
604
									return true;
605
								}
606
							}.bind(this));
607
					}
608
				}
609
		}
563
			for (var i=0; i<settings.length; i++) {
610
			for (var i=0; i<settings.length; i++) {
564
				var setting = settings[i];
611
				var setting = settings[i];
565
				var sectionId = 'settings.section.'; //$NON-NLS-1$
612
				var sectionId = 'settings.section.'; //$NON-NLS-1$
(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/javascriptProject.js (-12 / +37 lines)
Lines 20-55 define([ Link Here
20
				fileName === project.ESLINTRC_JSON || fileName === project.PACKAGE_JSON) {
20
				fileName === project.ESLINTRC_JSON || fileName === project.PACKAGE_JSON) {
21
				delete project.map.eslint;
21
				delete project.map.eslint;
22
			}
22
			}
23
			if (fileName === project.JSBEAUTIFYRC) {
24
				delete project.map.formatting;
25
			}
23
		},
26
		},
24
		/**
27
		/**
25
		 * @callback
28
		 * @callback
26
		 */
29
		 */
27
		onModified: function onModified(project, qualifiedName, fileName) {
30
		onModified: function onModified(project, qualifiedName, fileName) {
28
			this._update(project, fileName);			
31
			this._update(project, fileName);
29
		},
32
		},
30
		/**
33
		/**
31
		 * @callback
34
		 * @callback
32
		 */
35
		 */
33
		onDeleted: function onDeleted(project, qualifiedName, fileName) {
36
		onDeleted: function onDeleted(project, qualifiedName, fileName) {
34
			this._update(project, fileName);			
37
			this._update(project, fileName);
35
		},
38
		},
36
		/**
39
		/**
37
		 * @callback
40
		 * @callback
38
		 */
41
		 */
39
		onCreated: function onCreated(project, qualifiedName, fileName) {
42
		onCreated: function onCreated(project, qualifiedName, fileName) {
40
			this._update(project, fileName);			
43
			this._update(project, fileName);
41
		},
44
		},
42
		/**
45
		/**
43
		 * @callback
46
		 * @callback
44
		 */
47
		 */
45
		onMoved: function onMoved(project, qualifiedName, fileName, toQualified, toName) {
48
		onMoved: function onMoved(project, qualifiedName, fileName, toQualified, toName) {
46
			this._update(project, fileName);			
49
			this._update(project, fileName);
47
		},
50
		},
48
		/**
51
		/**
49
		 * @callback
52
		 * @callback
50
		 */
53
		 */
51
		onProjectChanged: function onProjectChanged(project, evnt, projectName) {
54
		onProjectChanged: function onProjectChanged(project, evnt, projectName) {
52
			delete project.map.eslint;
55
			delete project.map.eslint;
56
			delete project.map.formatting;
53
		}
57
		}
54
	};
58
	};
55
	
59
	
Lines 114-120 define([ Link Here
114
	 * The node_modules folder name
118
	 * The node_modules folder name
115
	 */
119
	 */
116
	JavaScriptProject.prototype.NODE_MODULES = 'node_modules';
120
	JavaScriptProject.prototype.NODE_MODULES = 'node_modules';
117
	
121
	/**
122
	 * The .jsbeautifyrc file name
123
	 * @see https://github.com/beautify-web/js-beautify/blob/master/README.md
124
	 */
125
	JavaScriptProject.prototype.JSBEAUTIFYRC = '.jsbeautifyrc';
126
118
	/**
127
	/**
119
	 * @description Adds a handler for the given file name to the mapping of handlers
128
	 * @description Adds a handler for the given file name to the mapping of handlers
120
	 * @function
129
	 * @function
Lines 154-160 define([ Link Here
154
						this.ecma = v.ecmaVersion;
163
						this.ecma = v.ecmaVersion;
155
					}
164
					}
156
				} catch(err) {
165
				} catch(err) {
157
					this.ecma = 6;					
166
					this.ecma = 6;
158
				}
167
				}
159
			}
168
			}
160
			return this.ecma;
169
			return this.ecma;
Lines 269-285 define([ Link Here
269
		//TODO support loading YML and YAML files
278
		//TODO support loading YML and YAML files
270
		var vals;
279
		var vals;
271
		return this.getFile(this.ESLINTRC_JS).then(function(file) {
280
		return this.getFile(this.ESLINTRC_JS).then(function(file) {
272
			vals = readAndMap(this.map, file);
281
			vals = readAndMap(this.map, file, "eslint");
273
			if(vals) {
282
			if(vals) {
274
				return vals;
283
				return vals;
275
			} 
284
			} 
276
			return this.getFile(this.ESLINTRC_JSON).then(function(file) {
285
			return this.getFile(this.ESLINTRC_JSON).then(function(file) {
277
				vals = readAndMap(this.map, file);
286
				vals = readAndMap(this.map, file, "eslint");
278
				if(vals) {
287
				if(vals) {
279
					return vals;
288
					return vals;
280
				}
289
				}
281
				return this.getFile(this.ESLINTRC).then(function(file) {
290
				return this.getFile(this.ESLINTRC).then(function(file) {
282
					vals = readAndMap(this.map, file);
291
					vals = readAndMap(this.map, file, "eslint");
283
					if(vals) {
292
					if(vals) {
284
						return vals;
293
						return vals;
285
					}
294
					}
Lines 298-310 define([ Link Here
298
		}.bind(this));
307
		}.bind(this));
299
	};
308
	};
300
	
309
	
301
	function readAndMap(map, file) {
310
	/**
311
	 * @name JavaScriptProject.prototype.getFormattingOptions
312
	 * @description Returns project-specific formatting options (if any)
313
	 * @function
314
	 * @returns {Deferred} A deferred that will resolve to the project-specific formatting options or null
315
	 * @see https://github.com/beautify-web/js-beautify
316
	 */
317
	JavaScriptProject.prototype.getFormattingOptions = function getESlintOptions() {
318
		if(this.map.formatting) {
319
			return new Deferred().resolve(this.map.formatting);
320
		}
321
		return this.getFile(this.JSBEAUTIFYRC).then(function(file) {
322
			return readAndMap(this.map, file, "formatting");
323
		}.bind(this));
324
	};
325
326
	function readAndMap(map, file, key) {
302
		if(file && file.contents) {
327
		if(file && file.contents) {
303
			try {
328
			try {
304
				var vals = JSON.parse(file.contents);
329
				var vals = JSON.parse(file.contents);
305
				if(Object.keys(vals).length > 0) {
330
				if(Object.keys(vals).length > 0) {
306
					map.eslint = vals;
331
					map[key]= vals;
307
					return map.eslint;
332
					return map[key];
308
				}
333
				}
309
			} catch(e) {
334
			} catch(e) {
310
				//fall through and return null
335
				//fall through and return null
(-)a/bundles/org.eclipse.orion.client.ui/web/orion/editorCommands.js (+27 lines)
Lines 149-154 define([ Link Here
149
		this.editorPreferences = options.editorPreferences;
149
		this.editorPreferences = options.editorPreferences;
150
		this.differ = options.differ;
150
		this.differ = options.differ;
151
		this.blamer = options.blamer;
151
		this.blamer = options.blamer;
152
		this.formatter = options.formatter;
152
		var that = this;
153
		var that = this;
153
		this.listener = {
154
		this.listener = {
154
			onServiceAdded: function(event) {
155
			onServiceAdded: function(event) {
Lines 177-182 define([ Link Here
177
			this._createClipboardCommands();
178
			this._createClipboardCommands();
178
			this._createDelimiterCommands();
179
			this._createDelimiterCommands();
179
			this._createEncodingCommand();
180
			this._createEncodingCommand();
181
			this._createFormatterCommand();
180
			this._createSaveCommand();
182
			this._createSaveCommand();
181
			this._createOpenFolderCommand();
183
			this._createOpenFolderCommand();
182
			this._createOpenRecentCommand();
184
			this._createOpenRecentCommand();
Lines 223-228 define([ Link Here
223
			this.localSettings = target.localSettings;
225
			this.localSettings = target.localSettings;
224
			this.differ = target.differ;
226
			this.differ = target.differ;
225
			this.blamer = target.blamer;
227
			this.blamer = target.blamer;
228
			this.formatter = target.formatter;
226
			this.textSearcher = target.textSearcher;
229
			this.textSearcher = target.textSearcher;
227
			
230
			
228
			if (this._recreateEditCommands) {
231
			if (this._recreateEditCommands) {
Lines 264-269 define([ Link Here
264
			commandRegistry.registerCommandContribution(this.saveToolbarId || this.toolbarId, "orion.edit.save", 2, this.saveToolbarId ? "orion.menuBarFileGroup/orion.edit.saveGroup" : null, false, new mKeyBinding.KeyBinding('s', true), null, this); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-3$
267
			commandRegistry.registerCommandContribution(this.saveToolbarId || this.toolbarId, "orion.edit.save", 2, this.saveToolbarId ? "orion.menuBarFileGroup/orion.edit.saveGroup" : null, false, new mKeyBinding.KeyBinding('s', true), null, this); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-3$
265
			commandRegistry.registerCommandContribution(this.editToolbarId || this.pageNavId, "orion.edit.gotoLine", 3, this.editToolbarId ? "orion.menuBarEditGroup/orion.findGroup" : null, !this.editToolbarId, new mKeyBinding.KeyBinding('l', !util.isMac, false, false, util.isMac), new mCommandRegistry.URLBinding("gotoLine", "line"), this); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-5$
268
			commandRegistry.registerCommandContribution(this.editToolbarId || this.pageNavId, "orion.edit.gotoLine", 3, this.editToolbarId ? "orion.menuBarEditGroup/orion.findGroup" : null, !this.editToolbarId, new mKeyBinding.KeyBinding('l', !util.isMac, false, false, util.isMac), new mCommandRegistry.URLBinding("gotoLine", "line"), this); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-5$
266
			commandRegistry.registerCommandContribution(this.editToolbarId || this.pageNavId, "orion.edit.find", 0, this.editToolbarId ? "orion.menuBarEditGroup/orion.findGroup" : null, !this.editToolbarId, new mKeyBinding.KeyBinding('f', true), new mCommandRegistry.URLBinding("find", "find"), this); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-5$
269
			commandRegistry.registerCommandContribution(this.editToolbarId || this.pageNavId, "orion.edit.find", 0, this.editToolbarId ? "orion.menuBarEditGroup/orion.findGroup" : null, !this.editToolbarId, new mKeyBinding.KeyBinding('f', true), new mCommandRegistry.URLBinding("find", "find"), this); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-5$
270
			commandRegistry.registerCommandContribution(this.editToolbarId || this.pageNavId , "orion.edit.format", 1, this.editToolbarId ? "orion.menuBarEditGroup/orion.edit.formatGroup" : null, !this.editToolbarId, new mKeyBinding.KeyBinding('f', false, true, true), new mCommandRegistry.URLBinding("format", "format"), this); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-5$
267
			commandRegistry.registerCommandContribution(this.toolbarId, "orion.keyAssist", 0, "orion.menuBarToolsGroup", false, new mKeyBinding.KeyBinding(191, false, true, true)); //$NON-NLS-1$ //$NON-NLS-0$ //$NON-NLS-2$
271
			commandRegistry.registerCommandContribution(this.toolbarId, "orion.keyAssist", 0, "orion.menuBarToolsGroup", false, new mKeyBinding.KeyBinding(191, false, true, true)); //$NON-NLS-1$ //$NON-NLS-0$ //$NON-NLS-2$
268
			commandRegistry.registerCommandContribution(this.toolbarId , "orion.edit.showTooltip", 1, "orion.menuBarToolsGroup", false, new mKeyBinding.KeyBinding(113), null, this);//$NON-NLS-1$ //$NON-NLS-2$ 
272
			commandRegistry.registerCommandContribution(this.toolbarId , "orion.edit.showTooltip", 1, "orion.menuBarToolsGroup", false, new mKeyBinding.KeyBinding(113), null, this);//$NON-NLS-1$ //$NON-NLS-2$ 
269
			commandRegistry.registerCommandContribution(this.toolbarId , "orion.edit.blame", 2, "orion.menuBarToolsGroup", false, new mKeyBinding.KeyBinding('b', true, true), new mCommandRegistry.URLBinding("blame", "blame"), this); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-5$
273
			commandRegistry.registerCommandContribution(this.toolbarId , "orion.edit.blame", 2, "orion.menuBarToolsGroup", false, new mKeyBinding.KeyBinding('b', true, true), new mCommandRegistry.URLBinding("blame", "blame"), this); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-5$
Lines 318-323 define([ Link Here
318
			commandRegistry.registerCommandContribution(this.editorContextMenuId, "orion.edit.gotoLine", index++, "orion.editorContextMenuGroup/orion.findGroup", false); //$NON-NLS-1$ //$NON-NLS-2$
322
			commandRegistry.registerCommandContribution(this.editorContextMenuId, "orion.edit.gotoLine", index++, "orion.editorContextMenuGroup/orion.findGroup", false); //$NON-NLS-1$ //$NON-NLS-2$
319
			commandRegistry.registerCommandContribution(this.editorContextMenuId, "orion.quickSearch", index++, "orion.editorContextMenuGroup/orion.findGroup", false); //$NON-NLS-1$ //$NON-NLS-2$
323
			commandRegistry.registerCommandContribution(this.editorContextMenuId, "orion.quickSearch", index++, "orion.editorContextMenuGroup/orion.findGroup", false); //$NON-NLS-1$ //$NON-NLS-2$
320
			commandRegistry.registerCommandContribution(this.editorContextMenuId, "orion.openSearch", index++, "orion.editorContextMenuGroup/orion.findGroup", false); //$NON-NLS-1$ //$NON-NLS-2$
324
			commandRegistry.registerCommandContribution(this.editorContextMenuId, "orion.openSearch", index++, "orion.editorContextMenuGroup/orion.findGroup", false); //$NON-NLS-1$ //$NON-NLS-2$
325
			commandRegistry.registerCommandContribution(this.editorContextMenuId , "orion.edit.format", index++, "orion.editorContextMenuGroup/orion.edit.formatGroup", false); //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$
321
326
322
			// 'Tools' cascade
327
			// 'Tools' cascade
323
			commandRegistry.addCommandGroup(this.editorContextMenuId, "orion.editorContextMenuToolsGroup", 400, messages["Tools"], "orion.editorContextMenuGroup"); //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
328
			commandRegistry.addCommandGroup(this.editorContextMenuId, "orion.editorContextMenuToolsGroup", 400, messages["Tools"], "orion.editorContextMenuGroup"); //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
Lines 910-915 define([ Link Here
910
			this.commandService.addCommand(blameCommand);
915
			this.commandService.addCommand(blameCommand);
911
		},
916
		},
912
917
918
		_createFormatterCommand: function(){
919
			var that = this;
920
			var formatterCommand = new mCommands.Command({
921
				name: messages.Format,
922
				tooltip: messages.FormatTooltip,
923
				id: "orion.edit.format", //$NON-NLS-0$
924
				parameters: new mCommandRegistry.ParametersDescription([new mCommandRegistry.CommandParameter('formatter', 'boolean')], {clientCollect: true}), //$NON-NLS-1$ //$NON-NLS-0$
925
				visibleWhen: /** @callback */ function(items, data) {
926
					var editor = data.handler.editor || that.editor;
927
					var formatter = data.handler.formatter || that.formatter;
928
					return editor && editor.installed && formatter && formatter.isVisible();
929
				},
930
				callback: function(data) {
931
					var editor = this.editor || that.editor;
932
					var formatter = this.formatter || that.formatter;
933
					formatter.doFormat();
934
					editor.focus();
935
				}
936
			});
937
			this.commandService.addCommand(formatterCommand);
938
		},
939
913
		_createDiffCommand: function(){
940
		_createDiffCommand: function(){
914
			var that = this;
941
			var that = this;
915
			var diffCommand = new mCommands.Command({
942
			var diffCommand = new mCommands.Command({
(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/javascriptPlugin.js (-6 / +187 lines)
Lines 21-26 define([ Link Here
21
'javascript/scriptResolver',
21
'javascript/scriptResolver',
22
'javascript/astManager',
22
'javascript/astManager',
23
'javascript/quickFixes',
23
'javascript/quickFixes',
24
'javascript/javascriptFormatter',
24
'javascript/javascriptProject',
25
'javascript/javascriptProject',
25
'javascript/contentAssist/ternAssist',
26
'javascript/contentAssist/ternAssist',
26
'javascript/contentAssist/ternProjectAssist',
27
'javascript/contentAssist/ternProjectAssist',
Lines 45-51 define([ Link Here
45
'i18n!javascript/nls/messages',
46
'i18n!javascript/nls/messages',
46
'orion/i18nUtil',
47
'orion/i18nUtil',
47
'orion/URL-shim'
48
'orion/URL-shim'
48
], function(PluginProvider, mServiceRegistry, Deferred, ScriptResolver, ASTManager, QuickFixes, JavaScriptProject, TernAssist, TernProjectAssist,
49
], function(PluginProvider, mServiceRegistry, Deferred, ScriptResolver, ASTManager, QuickFixes, JavaScriptFormatter, JavaScriptProject, TernAssist, TernProjectAssist,
49
			EslintValidator, TernProjectValidator, Occurrences, Hover, Outliner, CUProvider, TernProjectManager, Util, Logger, GenerateDocCommand, OpenDeclCommand, OpenImplCommand,
50
			EslintValidator, TernProjectValidator, Occurrences, Hover, Outliner, CUProvider, TernProjectManager, Util, Logger, GenerateDocCommand, OpenDeclCommand, OpenImplCommand,
50
			RenameCommand, RefsCommand, mJS, mJSON, mJSONSchema, mEJS, javascriptMessages, i18nUtil) {
51
			RenameCommand, RefsCommand, mJS, mJSON, mJSONSchema, mEJS, javascriptMessages, i18nUtil) {
51
52
Lines 69-75 define([ Link Here
69
    		               }, {id: "application/json", //$NON-NLS-1$
70
    		               }, {id: "application/json", //$NON-NLS-1$
70
    		            	   "extends": "text/plain", //$NON-NLS-1$ //$NON-NLS-1$
71
    		            	   "extends": "text/plain", //$NON-NLS-1$ //$NON-NLS-1$
71
    		            	   name: "JSON", //$NON-NLS-1$
72
    		            	   name: "JSON", //$NON-NLS-1$
72
    		            	   extension: ["json", "pref", "tern-project", "eslintrc"], //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
73
    		            	   extension: ["json", "pref", "tern-project", "eslintrc", "jsbeautifyrc"], //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
73
    		            	   imageClass: "file-sprite-javascript modelDecorationSprite" //$NON-NLS-1$
74
    		            	   imageClass: "file-sprite-javascript modelDecorationSprite" //$NON-NLS-1$
74
    		               }, {id: "application/x-ejs", //$NON-NLS-1$
75
    		               }, {id: "application/x-ejs", //$NON-NLS-1$
75
    		            	   "extends": "text/plain", //$NON-NLS-1$ //$NON-NLS-1$
76
    		            	   "extends": "text/plain", //$NON-NLS-1$ //$NON-NLS-1$
Lines 680-686 define([ Link Here
680
                    ]
681
                    ]
681
    			}
682
    			}
682
    	);
683
    	);
683
    	
684
684
    	provider.registerServiceProvider("orion.edit.command",  //$NON-NLS-1$
685
    	provider.registerServiceProvider("orion.edit.command",  //$NON-NLS-1$
685
    			quickFixComputer,
686
    			quickFixComputer,
686
    			{
687
    			{
Lines 1496-1502 define([ Link Here
1496
				 	        	   name: javascriptMessages['prefBestPractices'],
1497
				 	        	   name: javascriptMessages['prefBestPractices'],
1497
				 	        	   tags: "validation javascript js eslint".split(" "),  //$NON-NLS-1$  //$NON-NLS-1$
1498
				 	        	   tags: "validation javascript js eslint".split(" "),  //$NON-NLS-1$  //$NON-NLS-1$
1498
				 	        	   category: 'javascript', //$NON-NLS-1$
1499
				 	        	   category: 'javascript', //$NON-NLS-1$
1499
 				 	        	   categoryLabel: javascriptMessages['javascriptValidation'],
1500
				 	        	   categoryLabel: javascriptMessages['javascriptValidation'],
1500
				 	        	   properties: [
1501
				 	        	   properties: [
1501
				 	        	   				{
1502
				 	        	   				{
1502
			 	        	                		id: "no-eq-null",  //$NON-NLS-1$
1503
			 	        	                		id: "no-eq-null",  //$NON-NLS-1$
Lines 1753-1761 define([ Link Here
1753
				 	        	                	defaultValue: warning,
1754
				 	        	                	defaultValue: warning,
1754
				 	        	                	options: severities
1755
				 	        	                	options: severities
1755
				 	        	                }
1756
				 	        	                }
1756
]
1757
										]
1757
				 	        	}]
1758
				 	        	}]
1758
    			});
1759
			});
1760
1761
		var jsFormatter = new JavaScriptFormatter.JavaScriptFormatter(ternWorker, jsProject);
1762
		provider.registerServiceProvider("orion.edit.format",
1763
			jsFormatter,
1764
			{
1765
				name: javascriptMessages["javascriptFormatter"],
1766
				contentType: ["application/javascript"],
1767
				id: "orion.format.js.formatter",
1768
			}
1769
		);
1770
		// register preferences for formatting when modified (updated call)
1771
		provider.registerService("orion.cm.managedservice", jsFormatter, {pid: 'jsbeautify.config.js'}); //$NON-NLS-1$ //$NON-NLS-2$
1772
1773
		var unix = "\n";
1774
		var mac = "\r";
1775
		var windows = "\n\r";
1776
		var eof_characters = [
1777
			{label: javascriptMessages.indentation_unix,  value: unix},
1778
			{label: javascriptMessages.indentation_mac, value: mac},
1779
			{label: javascriptMessages.indentation_windows, value: windows}
1780
		];
1781
1782
		var space = ' ';
1783
		var tab= '\t';
1784
		var indentation_characters = [
1785
			{label: javascriptMessages.indentation_space,  value: space},
1786
			{label: javascriptMessages.indentation_tab,  value: tab},
1787
		];
1788
		
1789
		var before_newline = 'before-newline';
1790
		var after_newline = 'after-newline';
1791
		var preserve_newline = 'preserve-newline';
1792
		var operator_positions = [
1793
			{ label: javascriptMessages.before_newline, value: before_newline},
1794
			{ label: javascriptMessages.after_newline, value: after_newline},
1795
			{ label: javascriptMessages.preserve_newline, value: preserve_newline},
1796
		];
1797
		
1798
		var collapse_preserve_inline = 'collapse-preserve-inline';
1799
		var collapse = 'collapse';
1800
		var expand = 'expand';
1801
		var end_expand = 'end-expand';
1802
		var none = 'none';
1803
		var brace_styles = [
1804
			{ label: javascriptMessages.collapse_preserve_inline , value: collapse_preserve_inline},
1805
			{ label: javascriptMessages.collapse , value: collapse},
1806
			{ label: javascriptMessages.expand , value: expand},
1807
			{ label: javascriptMessages.end_expand , value: end_expand},
1808
			{ label: javascriptMessages.none , value: none},
1809
		];
1810
		provider.registerServiceProvider("orion.core.setting", {}, {
1811
			settings: [
1812
				{
1813
					pid: 'jsbeautify.config.js',//$NON-NLS-1$
1814
					name: javascriptMessages['javascriptFormattingSettings'],//$NON-NLS-1$
1815
					tags: 'beautify javascript js formatting'.split(' '),//$NON-NLS-1$//$NON-NLS-2$
1816
					category: 'javascriptFormatting',//$NON-NLS-1$
1817
					categoryLabel: javascriptMessages['javascriptFormatting'],//$NON-NLS-1$
1818
					properties: [
1819
						{
1820
							id: 'js_indent_size',  //$NON-NLS-1$
1821
							name: javascriptMessages['js_indent_size'],//$NON-NLS-1$
1822
							type: 'number', //$NON-NLS-1$
1823
							defaultValue: 4
1824
						},
1825
						{
1826
							id: 'js_indent_char',  //$NON-NLS-1$
1827
							name: javascriptMessages['js_indent_char'],//$NON-NLS-1$
1828
							type: 'string', //$NON-NLS-1$
1829
							defaultValue: space,
1830
							options: indentation_characters
1831
						},
1832
						{
1833
							id: 'js_eol',  //$NON-NLS-1$
1834
							name: javascriptMessages['js_eol'],//$NON-NLS-1$
1835
							type: 'string', //$NON-NLS-1$
1836
							defaultValue: unix,
1837
							options: eof_characters
1838
						},
1839
						{
1840
							id: 'js_end_with_newline',  //$NON-NLS-1$
1841
							name: javascriptMessages['js_end_with_newline'],//$NON-NLS-1$
1842
							type: 'boolean', //$NON-NLS-1$
1843
							defaultValue: false
1844
						},
1845
						{
1846
							id: 'js_preserve_newlines',  //$NON-NLS-1$
1847
							name: javascriptMessages['js_preserve_newlines'],//$NON-NLS-1$
1848
							type: 'boolean', //$NON-NLS-1$
1849
							defaultValue: true
1850
						},
1851
						{
1852
							id: 'js_max_preserve_newlines',  //$NON-NLS-1$
1853
							name: javascriptMessages['js_max_preserve_newlines'],//$NON-NLS-1$
1854
							type: 'number', //$NON-NLS-1$
1855
							defaultValue: 10
1856
						},
1857
						{
1858
							id: 'js_brace_style',  //$NON-NLS-1$
1859
							name: javascriptMessages['js_brace_style'],//$NON-NLS-1$
1860
							type: 'boolean', //$NON-NLS-1$
1861
							defaultValue: collapse,
1862
							options: brace_styles
1863
						},
1864
						{
1865
							id: 'js_wrap_line_length',  //$NON-NLS-1$
1866
							name: javascriptMessages['js_wrap_line_length'],//$NON-NLS-1$
1867
							type: 'number', //$NON-NLS-1$
1868
							defaultValue: 0
1869
						},
1870
						{
1871
							id: 'indent_level',  //$NON-NLS-1$
1872
							name: javascriptMessages['indent_level'],//$NON-NLS-1$
1873
							type: 'number', //$NON-NLS-1$
1874
							defaultValue: 0
1875
						},
1876
						{
1877
							id: 'space_in_paren',  //$NON-NLS-1$
1878
							name: javascriptMessages['space_in_paren'],//$NON-NLS-1$
1879
							type: 'boolean', //$NON-NLS-1$
1880
							defaultValue: false
1881
						},
1882
						{
1883
							id: 'space_in_empty_paren',  //$NON-NLS-1$
1884
							name: javascriptMessages['space_in_empty_paren'],//$NON-NLS-1$
1885
							type: 'boolean', //$NON-NLS-1$
1886
							defaultValue: false
1887
						},
1888
						{
1889
							id: 'space_after_anon_function',  //$NON-NLS-1$
1890
							name: javascriptMessages['space_after_anon_function'],//$NON-NLS-1$
1891
							type: 'boolean', //$NON-NLS-1$
1892
							defaultValue: false
1893
						},
1894
						{
1895
							id: 'break_chained_methods',  //$NON-NLS-1$
1896
							name: javascriptMessages['break_chained_methods'],//$NON-NLS-1$
1897
							type: 'boolean', //$NON-NLS-1$
1898
							defaultValue: false
1899
						},
1900
						{
1901
							id: 'keep_array_indentation',  //$NON-NLS-1$
1902
							name: javascriptMessages['keep_array_indentation'],//$NON-NLS-1$
1903
							type: 'boolean', //$NON-NLS-1$
1904
							defaultValue: false
1905
						},
1906
						{
1907
							id: 'space_before_conditional',  //$NON-NLS-1$
1908
							name: javascriptMessages['space_before_conditional'],//$NON-NLS-1$
1909
							type: 'boolean', //$NON-NLS-1$
1910
							defaultValue: true
1911
						},
1912
						{
1913
							id: 'unescape_strings',  //$NON-NLS-1$
1914
							name: javascriptMessages['unescape_strings'],//$NON-NLS-1$
1915
							type: 'boolean', //$NON-NLS-1$
1916
							defaultValue: false
1917
						},
1918
						{
1919
							id: 'e4x',  //$NON-NLS-1$
1920
							name: javascriptMessages['e4x'],//$NON-NLS-1$
1921
							type: 'boolean', //$NON-NLS-1$
1922
							defaultValue: false
1923
						},
1924
						{
1925
							id: 'comma_first',  //$NON-NLS-1$
1926
							name: javascriptMessages['comma_first'],//$NON-NLS-1$
1927
							type: 'boolean', //$NON-NLS-1$
1928
							defaultValue: false
1929
						},
1930
						{
1931
							id: 'operator_position',  //$NON-NLS-1$
1932
							name: javascriptMessages['operator_position'],//$NON-NLS-1$
1933
							type: 'string', //$NON-NLS-1$
1934
							defaultValue: before_newline,
1935
							options: operator_positions
1936
						},
1937
					]
1938
				}]
1939
		});
1759
1940
1760
    	/**
1941
    	/**
1761
    	 * Register syntax styling for js, json and json schema content
1942
    	 * Register syntax styling for js, json and json schema content

Return to bug 496437