├── .DS_Store
├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── META-INF
└── MANIFEST.MF
├── README.textile
├── build.properties
├── gson.jar
├── icons
└── sample.gif
├── js.jar
├── plugin.xml
└── src
└── io
└── emmet
├── Emmet.java
├── IEmmetEditor.java
├── IEmmetFile.java
├── IUserData.java
├── SelectionData.java
├── TabStop.java
├── TabStopGroup.java
├── TabStopStructure.java
├── actions
├── AbstractMenuItem.java
├── Action.java
└── Menu.java
├── eclipse
├── AutoCompleteDialog.java
├── EclipseEmmetEditor.java
├── EclipseEmmetFile.java
├── EclipseEmmetHelper.java
├── EclipseEmmetPlugin.java
├── EclipseTemplateProcessor.java
├── EclipseUserData.java
├── EditorTypeInvestigator.java
├── EmmetContextType.java
├── MainMenuContribution.java
├── Startup.java
├── TabKeyHandler.java
├── WrapWithAbbreviationDialog.java
├── handlers
│ ├── ActionRunner.java
│ ├── DefaultAction.java
│ ├── ExpandAbbreviationAction.java
│ ├── InsertFormattedLineBreakAction.java
│ └── WrapWithAbbreviationAction.java
└── preferences
│ ├── EclipseEmmetPreferencePage.java
│ ├── EmmetAbbreviationsPreferencesPage.java
│ ├── EmmetSnippetsPreferencesPage.java
│ ├── EmmetVariablesPreferencePage.java
│ ├── LabelFieldEditor.java
│ ├── PreferenceConstants.java
│ ├── PreferenceInitializer.java
│ ├── SpacerFieldEditor.java
│ ├── SpinnerFieldEditor.java
│ ├── StatusInfo.java
│ ├── TemplateContentProvider.java
│ ├── TemplateHelper.java
│ ├── VariablePreferencePage.java
│ └── output
│ ├── CSSOutputPreferencePage.java
│ ├── DefaultOutputPreferencePage.java
│ ├── HAMLOutputPreferencePage.java
│ ├── HTMLOutputPreferencePage.java
│ ├── OutputProfile.java
│ ├── XMLOutputPreferencePage.java
│ └── XSLOutputPreferencePage.java
├── emmet-app.js
├── file-interface.js
├── java-wrapper.js
├── json2.js
└── snippets.json
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergeche/eclipse-zencoding/146d682fdc6914b900401a8e265659ce07cef8d1/.DS_Store
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
20 |
19 |
21 |
22 | "Read more about current Emmet syntax":http://code.google.com/p/zen-coding/wiki/ZenHTMLSelectorsEn
23 |
24 | h2. Installation
25 |
26 | # Go to _Help > Install New Software..._ in your Eclipse IDE
27 | # Add "http://emmet.io/eclipse/updates/":http://emmet.io/eclipse/updates/ in update sites
28 | # Check _Emmet for Eclipse_ group in available plugins list, click Next button and follow the installation instructions
29 | # Restart Eclipse
30 |
31 | h2. Plugin Overview
32 |
33 | This plugin provides the features:
34 |
35 | * Expand abbreviations by Tab key
36 | * Tab stops and linked mode support
37 | * Simple install and update process
38 | * Change action shortcuts in Eclipse's Keys preferences page
39 | * Works across all Eclipse editors
40 | * Preferences support to fine-tune output for each syntax and add new abbreviations and snippets
41 |
42 | !http://media.chikuyonok.ru/eclipse/prefs.png!
43 |
44 | *Aptana 3 users:* since Aptana 3 can also expand snippets by Tab key, there might be collisions in expanded result (for example, for @div@ tag). You can remove unused snippets for Aptana bundles in order to make Emmet plugin work properly.
45 |
46 | h3. Contributions
47 |
48 | "Django snippets":https://github.com/andreyfedoseev/zen-coding-django-snippets
--------------------------------------------------------------------------------
/build.properties:
--------------------------------------------------------------------------------
1 | source.. = src/
2 | output.. = bin/
3 | bin.includes = plugin.xml,\
4 | META-INF/,\
5 | .,\
6 | icons/,\
7 | js.jar,\
8 | gson.jar
9 |
--------------------------------------------------------------------------------
/gson.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergeche/eclipse-zencoding/146d682fdc6914b900401a8e265659ce07cef8d1/gson.jar
--------------------------------------------------------------------------------
/icons/sample.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergeche/eclipse-zencoding/146d682fdc6914b900401a8e265659ce07cef8d1/icons/sample.gif
--------------------------------------------------------------------------------
/js.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergeche/eclipse-zencoding/146d682fdc6914b900401a8e265659ce07cef8d1/js.jar
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | emmet.require('actions').get
)
92 | * @param vargs
93 | * @return
94 | */
95 | public Object execJSFunction(String name, Object... vargs) {
96 | // temporary register all variables
97 | Object wrappedObj;
98 | StringBuilder jsArgs = new StringBuilder();
99 | for (int i = 0; i < vargs.length; i++) {
100 | wrappedObj = Context.javaToJS(vargs[i], scope);
101 | ScriptableObject.putProperty(scope, "__javaParam" + i, wrappedObj);
102 | if (i > 0) {
103 | jsArgs.append(',');
104 | }
105 | jsArgs.append("__javaParam" + i);
106 | }
107 |
108 | // evaluate code
109 | Object result = cx.evaluateString(scope, name + "(" + jsArgs.toString() + ");", "start
11 | * and end
indexes. If there's no selection, should return
12 | * list with both indexes referring to current caret position
13 | * @example
14 | * 15 | * SelectionData selection = editor.getSelectionRange(); 16 | * System.out.println(selection.getStart() + ", " + selection.getEnd()); 17 | *18 | */ 19 | public SelectionData getSelectionRange(); 20 | 21 | /** 22 | * Creates selection from
start
to end
character
23 | * indexes.
24 | * @example
25 | * editor.createSelection(10, 40);26 | */ 27 | public void createSelection(int start, int end); 28 | 29 | /** 30 | * Returns current line's start and end indexes as object with
start
31 | * and end
properties
32 | * @return {Object}
33 | * @example
34 | * 35 | * SelectionData range = editor.getCurrentLineRange(); 36 | * System.out.println(range.getStart() + ", " + range.getEnd(); 37 | *38 | */ 39 | public SelectionData getCurrentLineRange(); 40 | 41 | /** 42 | * Returns current caret position 43 | */ 44 | public int getCaretPos(); 45 | 46 | /** 47 | * Set new caret position 48 | */ 49 | public void setCaretPos(int pos); 50 | 51 | /** 52 | * Returns content of current line 53 | */ 54 | public String getCurrentLine(); 55 | 56 | /** 57 | * Replace current editor's content. If
value
contains
58 | * caret_placeholder
, the editor will put caret into
59 | * this position.
60 | * @param value Content you want to paste
61 | */
62 | public void replaceContent(String value);
63 |
64 | /**
65 | * Place the value
content at start
string
66 | * index of current content. If value
contains
67 | * caret_placeholder
, the editor will put caret into
68 | * this position.
69 | * @param value Content you want to paste
70 | * @param start Start index of editor's content
71 | */
72 | public void replaceContent(String value, int start);
73 |
74 | /**
75 | * Replace editor's content part (from start
to
76 | * end
index) with value
. If value
77 | * contains caret_placeholder
, the editor will put caret into
78 | * this position.
79 | *
80 | * @param value Content you want to paste
81 | * @param start Start index of editor's content
82 | * @param end End index of editor's content
83 | */
84 | public void replaceContent(String value, int start, int end);
85 |
86 | /**
87 | * Replace editor's content part (from start
to
88 | * end
index) with value
. If value
89 | * contains caret_placeholder
, the editor will put caret into
90 | * this position.
91 | *
92 | * @param value Content you want to paste
93 | * @param start Start index of editor's content
94 | * @param end End index of editor's content
95 | * @param no_indent Do not indent pasted value
96 | */
97 | public void replaceContent(String value, int start, int end, boolean no_indent);
98 |
99 | /**
100 | * Returns editor's content
101 | */
102 | public String getContent();
103 |
104 | /**
105 | * Returns current editor's syntax mode
106 | */
107 | public String getSyntax();
108 |
109 | /**
110 | * Returns current output profile name
111 | */
112 | public String getProfileName();
113 |
114 | /**
115 | * Ask user to enter something
116 | * @param title Dialog title
117 | * @return Entered data
118 | * @since 0.65
119 | */
120 | public String prompt(String title);
121 |
122 | /**
123 | * Returns current selection
124 | * @since 0.65
125 | */
126 | public String getSelection();
127 |
128 | /**
129 | * Returns current editor's file path
130 | * @since 0.65
131 | */
132 | public String getFilePath();
133 | }
134 |
--------------------------------------------------------------------------------
/src/io/emmet/IEmmetFile.java:
--------------------------------------------------------------------------------
1 | package io.emmet;
2 |
3 | /**
4 | * Emmet File interface is used by JS core to read and locate files
5 | * @author sergey
6 | *
7 | */
8 | public interface IEmmetFile {
9 | /**
10 | * Read file content and return it
11 | * @param path File's relative or absolute path
12 | */
13 | public String read(String path);
14 |
15 | /**
16 | * Locate fileName
file that relates to editorFile
.
17 | * File name may be absolute or relative path
18 | * editorFile
as a start pointfileName
to start point and test iffileName
cannot be located
38 | */
39 | public String locateFile(String editorFile, String fileName);
40 |
41 | /**
42 | * Creates absolute path by concatenating parent
and file_name
.
43 | * If parent
points to file, its parent directory is used
44 | * @param parent
45 | * @param fileName
46 | */
47 | public String createPath(String parent, String fileName);
48 |
49 | /**
50 | * Saves content
as file
51 | * @param file File's absolute path
52 | * @param content File content
53 | */
54 | public void save(String file, String content);
55 |
56 | /**
57 | * Returns file extension in lower case
58 | * @param file
59 | */
60 | public String getExt(String file);
61 | }
62 |
--------------------------------------------------------------------------------
/src/io/emmet/IUserData.java:
--------------------------------------------------------------------------------
1 | package io.emmet;
2 |
3 | public interface IUserData {
4 | public void load(Emmet ctx);
5 | public void loadExtensions(Emmet ctx);
6 | }
7 |
--------------------------------------------------------------------------------
/src/io/emmet/SelectionData.java:
--------------------------------------------------------------------------------
1 | package io.emmet;
2 |
3 |
4 | public class SelectionData {
5 | private int start = 0;
6 | private int end = 0;
7 |
8 | public SelectionData() {
9 |
10 | }
11 |
12 | public SelectionData(int start, int end) {
13 | updateRange(start, end);
14 | }
15 |
16 | public void setStart(int start) {
17 | this.start = start;
18 | }
19 |
20 | public int getStart() {
21 | return start;
22 | }
23 |
24 | public void setEnd(int end) {
25 | this.end = end;
26 | }
27 |
28 | public int getEnd() {
29 | return end;
30 | }
31 |
32 | /**
33 | * Updates selection's start and end indexes
34 | */
35 | public void updateRange(int start, int end) {
36 | this.start = Math.min(start, end);
37 | this.end = Math.max(start, end);
38 | }
39 |
40 | /**
41 | * Updates selection ranges by passing start offset and selection length
42 | * (commonly used notation in most editors)
43 | */
44 | public void updateRangeWithLength(int start, int length) {
45 | int end = start + length;
46 | updateRange(start, end);
47 | }
48 |
49 | /**
50 | * Returns selection length
51 | */
52 | public int getLength() {
53 | return end - start;
54 | }
55 |
56 | public String toString() {
57 | return "selection start: " + start + ", end: " + end;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/io/emmet/TabStop.java:
--------------------------------------------------------------------------------
1 | package io.emmet;
2 |
3 |
4 | public class TabStop {
5 | private int start = 0;
6 | private int end = 0;
7 |
8 | public TabStop(int start, int end) {
9 | this.start = start;
10 | this.end = end;
11 | }
12 |
13 | public void setStart(int start) {
14 | this.start = start;
15 | }
16 |
17 | public int getStart() {
18 | return start;
19 | }
20 |
21 | public void setEnd(int end) {
22 | this.end = end;
23 | }
24 |
25 | public int getEnd() {
26 | return end;
27 | }
28 |
29 | public boolean isZeroWidth() {
30 | return start == end;
31 | }
32 |
33 | public int getLength() {
34 | return end - start;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/io/emmet/TabStopGroup.java:
--------------------------------------------------------------------------------
1 | package io.emmet;
2 |
3 |
4 | import java.util.ArrayList;
5 |
6 | public class TabStopGroup {
7 | private ArrayListnull
if none.
34 | */
35 | private String message;
36 |
37 | /**
38 | * The input value; the empty string by default.
39 | */
40 | private String value = "";//$NON-NLS-1$
41 |
42 | /**
43 | * Ok button widget.
44 | */
45 | private Button okButton;
46 |
47 | /**
48 | * Input text widget.
49 | */
50 | private Text text;
51 |
52 | /**
53 | * Maximum proposals to be displayed in content assist
54 | */
55 | private int maxProposals = 10;
56 |
57 | private ArrayList
63 | * Note that the open
method blocks for input dialogs.
64 | *
null
to create a top-level
68 | * shell
69 | * @param dialogTitle
70 | * the dialog title, or null
if none
71 | * @param dialogMessage
72 | * the dialog message, or null
if none
73 | * @param initialValue
74 | * the initial input value, or null
if none
75 | * (equivalent to the empty string)
76 | */
77 | public AutoCompleteDialog(Shell parentShell, String dialogTitle,
78 | String dialogMessage, String initialValue) {
79 | super(parentShell);
80 | title = dialogTitle;
81 | message = dialogMessage;
82 | if (initialValue == null) {
83 | value = "";//$NON-NLS-1$
84 | } else {
85 | value = initialValue;
86 | }
87 |
88 | setProposals(new ArrayListhowMany
times
235 | */
236 | public String repeatString(String str, int howMany) {
237 | StringBuilder result = new StringBuilder();
238 |
239 | for (int i = 0; i < howMany; i++) {
240 | result.append(str);
241 | }
242 |
243 | return result.toString();
244 | }
245 |
246 | public String getNewline() {
247 | return TextUtilities.getDefaultLineDelimiter(doc);
248 | }
249 |
250 | /**
251 | * Indents text with padding
252 | * @param {String} text Text to indent
253 | * @param {String|Number} pad Padding size (number) or padding itself (string)
254 | * @return {String}
255 | */
256 | public String padString(String text, String pad) {
257 | StringBuilder result = new StringBuilder();
258 | String newline = getNewline();
259 | String lines[] = text.split("\\r\\n|\\n\\r|\\r|\\n", -1);
260 |
261 | if (lines.length > 0) {
262 | result.append(lines[0]);
263 | for (int i = 1; i < lines.length; i++) {
264 | result.append(newline + pad + lines[i]);
265 | }
266 | } else {
267 | result.append(text);
268 | }
269 |
270 | return result.toString();
271 | }
272 |
273 | @Override
274 | public String getContent() {
275 | return doc.get();
276 | }
277 |
278 | @Override
279 | public String getSyntax() {
280 | String syntax = EditorTypeInvestigator.getSyntax(this);
281 | if (syntax == null)
282 | syntax = EditorTypeInvestigator.TYPE_HTML;
283 | return syntax;
284 | }
285 |
286 | @Override
287 | public String getProfileName() {
288 | return EditorTypeInvestigator.getOutputProfile(this);
289 | }
290 |
291 | public String prompt(String type, String title) {
292 |
293 | final Display currentDisplay = Display.getCurrent();
294 | String defaultValueArg = "";
295 |
296 | /**
297 | * Answer
298 | */
299 | class Answer {
300 | public String result = "";
301 | }
302 |
303 | final String message = title;
304 | final String dialogType = type;
305 | final String defaultValue = defaultValueArg;
306 | final Answer a = new Answer();
307 |
308 | if (currentDisplay != null) {
309 | currentDisplay.syncExec(new Runnable() {
310 |
311 | public void run() {
312 | Shell shell = currentDisplay.getActiveShell();
313 |
314 | if (shell != null) {
315 | AutoCompleteDialog dialog = dialogFactory(dialogType, message, defaultValue);
316 | int dialogResult = dialog.open();
317 | if (dialogResult == Window.OK) {
318 | a.result = dialog.getValue();
319 | addProposal(message, a.result);
320 | } else {
321 | a.result = "";
322 | }
323 | }
324 | }
325 | });
326 | }
327 |
328 | return a.result;
329 | }
330 |
331 | @Override
332 | public String prompt(String title) {
333 | return prompt(DIALOG_PROMPT, title);
334 | }
335 |
336 | public String promptWrap(String title) {
337 | return prompt(DIALOG_WRAP_WITH_ABBREVIATION, title);
338 | }
339 |
340 | private AutoCompleteDialog dialogFactory(String type, String message, String defaultValue) {
341 | AutoCompleteDialog dialog;
342 | if (type == DIALOG_WRAP_WITH_ABBREVIATION) {
343 | dialog = new WrapWithAbbreviationDialog(null, "Emmet Prompt", message, defaultValue);
344 | } else {
345 | dialog = new AutoCompleteDialog(null, "Emmet Prompt", message, defaultValue);
346 | }
347 |
348 | dialog.setProposals(getProposals(message));
349 | return dialog;
350 | }
351 |
352 | private ArrayList"%20"
and "%5B" and "%5D"
and "%7B" "%7D"
.
287 | *
288 | * @param input
289 | * @return String
290 | * @since 3.0.2
291 | */
292 | public static String urlEncodeFilename(char[] input) {
293 |
294 | if (input == null) {
295 | return null;
296 | }
297 |
298 | StringBuffer retu = new StringBuffer(input.length);
299 | for (int i = 0; i < input.length; i++) {
300 | if (input[i] == ' ') {
301 | retu.append("%20"); //$NON-NLS-1$
302 | } else if (input[i] == '[') {
303 | retu.append("%5B"); //$NON-NLS-1$
304 | } else if (input[i] == ']') {
305 | retu.append("%5D"); //$NON-NLS-1$
306 | } else if (input[i] == '{') {
307 | retu.append("%7B"); //$NON-NLS-1$
308 | } else if (input[i] == '}') {
309 | retu.append("%7D"); //$NON-NLS-1$
310 | } else if (input[i] == '`') {
311 | retu.append("%60"); //$NON-NLS-1$
312 | } else if (input[i] == '+') {
313 | retu.append("%2B"); //$NON-NLS-1$
314 | } else {
315 | retu.append(input[i]);
316 | }
317 | }
318 | return retu.toString();
319 | }
320 |
321 | /**
322 | * Returns string representation of passed editor
323 | * @param editor
324 | * @return
325 | */
326 | public static String getEditorString(EclipseEmmetEditor editor) {
327 | return editor.getEditor().toString().toLowerCase();
328 | }
329 |
330 | /**
331 | * Test if current editor belongs to Aptana
332 | * @param editor
333 | * @return
334 | */
335 | public static boolean isApatana(EclipseEmmetEditor editor) {
336 | return getEditorString(editor).indexOf(".aptana.") != -1;
337 | }
338 |
339 | }
340 |
--------------------------------------------------------------------------------
/src/io/emmet/eclipse/EclipseEmmetPlugin.java:
--------------------------------------------------------------------------------
1 | package io.emmet.eclipse;
2 |
3 | import io.emmet.Emmet;
4 | import io.emmet.eclipse.preferences.PreferenceConstants;
5 |
6 | import org.eclipse.jface.resource.ImageDescriptor;
7 | import org.eclipse.jface.util.IPropertyChangeListener;
8 | import org.eclipse.jface.util.PropertyChangeEvent;
9 | import org.eclipse.ui.plugin.AbstractUIPlugin;
10 | import org.osgi.framework.BundleContext;
11 |
12 |
13 | /**
14 | * The activator class controls the plug-in life cycle
15 | */
16 | public class EclipseEmmetPlugin extends AbstractUIPlugin {
17 |
18 | // The plug-in ID
19 | public static final String PLUGIN_ID = "io.emmet.eclipse"; //$NON-NLS-1$
20 |
21 | // The shared instance
22 | private static EclipseEmmetPlugin plugin;
23 |
24 | /**
25 | * The constructor
26 | */
27 | public EclipseEmmetPlugin() {
28 | }
29 |
30 | /*
31 | * (non-Javadoc)
32 | * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
33 | */
34 | public void start(BundleContext context) throws Exception {
35 | super.start(context);
36 | plugin = this;
37 |
38 | Emmet.setUserDataDelegate(new EclipseUserData());
39 |
40 | // XXX maybe there's a better place for such listener?
41 | getDefault().getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() {
42 | @Override
43 | public void propertyChange(PropertyChangeEvent event) {
44 | if (event.getProperty() == PreferenceConstants.P_EXTENSIONS_PATH) {
45 | Emmet.reset();
46 | }
47 | }
48 | });
49 | }
50 |
51 | /*
52 | * (non-Javadoc)
53 | * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
54 | */
55 | public void stop(BundleContext context) throws Exception {
56 | plugin = null;
57 | super.stop(context);
58 | }
59 |
60 | /**
61 | * Returns the shared instance
62 | *
63 | * @return the shared instance
64 | */
65 | public static EclipseEmmetPlugin getDefault() {
66 | return plugin;
67 | }
68 |
69 | /**
70 | * Returns an image descriptor for the image file at the given
71 | * plug-in relative path
72 | *
73 | * @param path the path
74 | * @return the image descriptor
75 | */
76 | public static ImageDescriptor getImageDescriptor(String path) {
77 | return imageDescriptorFromPlugin(PLUGIN_ID, path);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/io/emmet/eclipse/EclipseTemplateProcessor.java:
--------------------------------------------------------------------------------
1 | package io.emmet.eclipse;
2 |
3 | /**
4 | * Processes Eclipse template and converts it to Emmet abbreviation/snippet
5 | * @author sergey
6 | *
7 | */
8 | public class EclipseTemplateProcessor {
9 | /**
10 | * Convert Eclipse template to Emmet entry
11 | * @param template
12 | * @return
13 | */
14 | public static String process(String template) {
15 | StringBuffer result = new StringBuffer();
16 |
17 | char ch;
18 | char nextCh;
19 | int i = 0;
20 | int len = template.length();
21 | int varEnd;
22 | String varName;
23 |
24 | while (i < len) {
25 | ch = template.charAt(i);
26 | nextCh = (i < len - 1) ? template.charAt(i + 1) : '\0';
27 |
28 | if (ch == '$') {
29 | if (nextCh == '$') { // escaping dollar sign
30 | result.append("\\$");
31 | i++;
32 | } else if (nextCh == '{') { // variable start
33 | varEnd = template.indexOf('}', i);
34 | if (varEnd != -1) {
35 | varName = template.substring(i + 2, varEnd);
36 | if (varName.equals("cursor")) {
37 | result.append('|');
38 | } else {
39 | // Leave variables as is because filters can provide
40 | // value substitutions
41 | result.append("${" + varName + "}");
42 | }
43 | i = varEnd;
44 | } else {
45 | result.append(ch);
46 | }
47 |
48 | } else { // just a dollar sign, escape it
49 | result.append("\\$");
50 | }
51 | } else {
52 | result.append(ch);
53 | }
54 |
55 | i++;
56 | }
57 |
58 | return result.toString();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/io/emmet/eclipse/EclipseUserData.java:
--------------------------------------------------------------------------------
1 | package io.emmet.eclipse;
2 |
3 | import io.emmet.IUserData;
4 | import io.emmet.Emmet;
5 | import io.emmet.eclipse.preferences.PreferenceConstants;
6 | import io.emmet.eclipse.preferences.TemplateHelper;
7 | import io.emmet.eclipse.preferences.output.OutputProfile;
8 |
9 | import java.io.File;
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 |
13 | import org.eclipse.jface.preference.IPreferenceStore;
14 | import org.eclipse.jface.text.templates.Template;
15 | import org.eclipse.jface.text.templates.persistence.TemplateStore;
16 |
17 |
18 | import com.google.gson.Gson;
19 |
20 | public class EclipseUserData implements IUserData {
21 |
22 | @Override
23 | public void load(Emmet ctx) {
24 | // since JSON with variable data types and fields in Java is pretty hard,
25 | // we will collect user data into a simple hash array and then convert to
26 | // desired structure in JS
27 | HashMap30 | * This page is used to modify preferences only. They 31 | * are stored in the preference store that belongs to 32 | * the main plug-in class. That way, preferences can 33 | * be accessed directly via the preference store. 34 | */ 35 | 36 | public class EclipseEmmetPreferencePage 37 | extends FieldEditorPreferencePage 38 | implements IWorkbenchPreferencePage { 39 | 40 | public EclipseEmmetPreferencePage() { 41 | super(GRID); 42 | setPreferenceStore(EclipseEmmetPlugin.getDefault().getPreferenceStore()); 43 | setDescription("Common Emmet preferences"); 44 | 45 | 46 | } 47 | 48 | /** 49 | * Creates the field editors. Field editors are abstractions of 50 | * the common GUI blocks needed to manipulate various types 51 | * of preferences. Each field editor knows how to save and 52 | * restore itself. 53 | */ 54 | public void createFieldEditors() { 55 | addField( 56 | new BooleanFieldEditor( 57 | PreferenceConstants.P_TAB_EXPAND, 58 | "&Expand abbreviations by Tab key", 59 | getFieldEditorParent())); 60 | 61 | addField( 62 | new BooleanFieldEditor( 63 | PreferenceConstants.P_UPGRADE_EDITORS, 64 | "&Upgrade web editors", 65 | getFieldEditorParent())); 66 | 67 | addField(new LabelFieldEditor( 68 | "This option provides better newline insertion behaviour for \n" + 69 | "Web editors (Eclipse WTP's HTML, XML, XSL and CSS; Aptana's CSS editor).\n" + 70 | "For CSS editor, you can specify 'close_css_brace' variable \n" + 71 | "(see Variable section) with the value that will be automatically \n" + 72 | "inserted instead of closing brace of CSS rule defition.", getFieldEditorParent())); 73 | 74 | addField(new DirectoryFieldEditor( 75 | PreferenceConstants.P_EXTENSIONS_PATH, 76 | "E&xtensions path", 77 | getFieldEditorParent())); 78 | 79 | } 80 | 81 | /* (non-Javadoc) 82 | * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) 83 | */ 84 | public void init(IWorkbench workbench) { 85 | } 86 | 87 | private void updatePreferences() { 88 | IPreferenceStore store = EclipseEmmetPlugin.getDefault().getPreferenceStore(); 89 | TabKeyHandler.setEnabled(store.getBoolean(PreferenceConstants.P_TAB_EXPAND)); 90 | } 91 | 92 | @Override 93 | public boolean performOk() { 94 | boolean result = super.performOk(); 95 | if (result) { 96 | updatePreferences(); 97 | } 98 | return result; 99 | } 100 | 101 | @Override 102 | protected void performDefaults() { 103 | super.performDefaults(); 104 | updatePreferences(); 105 | } 106 | 107 | protected void contributeButtons(Composite parent) { 108 | Button resetButton = new Button(parent, SWT.PUSH); 109 | resetButton.setText("Reload engine"); 110 | Dialog.applyDialogFont(resetButton); 111 | 112 | resetButton.addSelectionListener(new SelectionAdapter() { 113 | public void widgetSelected(SelectionEvent e) { 114 | Emmet.reset(); 115 | } 116 | }); 117 | 118 | ((GridLayout) parent.getLayout()).numColumns++; 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /src/io/emmet/eclipse/preferences/EmmetAbbreviationsPreferencesPage.java: -------------------------------------------------------------------------------- 1 | package io.emmet.eclipse.preferences; 2 | 3 | import io.emmet.Emmet; 4 | import io.emmet.eclipse.EclipseEmmetPlugin; 5 | 6 | import org.eclipse.ui.IWorkbenchPreferencePage; 7 | import org.eclipse.ui.texteditor.templates.TemplatePreferencePage; 8 | 9 | 10 | public class EmmetAbbreviationsPreferencesPage extends TemplatePreferencePage implements 11 | IWorkbenchPreferencePage { 12 | 13 | public EmmetAbbreviationsPreferencesPage() { 14 | setPreferenceStore(EclipseEmmetPlugin.getDefault().getPreferenceStore()); 15 | setTemplateStore(TemplateHelper.getTemplateStore("abbreviations")); 16 | setContextTypeRegistry(TemplateHelper.getContextTypeRegistry()); 17 | setDescription("Abbreviations for Emmet are building blocks for (X)HTML tags. " + 18 | "Abbreviation should look like opening XHTML tag, e.g.:\n" + 19 | "
0
) indicating that the
24 | * editor should perform validation after every key stroke.
25 | *
26 | * @see #setValidateStrategy
27 | */
28 | public static final int VALIDATE_ON_KEY_STROKE = 0;
29 |
30 | /**
31 | * Validation strategy constant (value 1
) indicating that the
32 | * editor should perform validation only when the text widget loses focus.
33 | *
34 | * @see #setValidateStrategy
35 | */
36 | public static final int VALIDATE_ON_FOCUS_LOST = 1;
37 |
38 | /**
39 | * Text limit constant (value -1
) indicating unlimited text
40 | * limit and width.
41 | */
42 | public static int UNLIMITED = -1;
43 |
44 | /**
45 | * Cached valid state.
46 | */
47 | private boolean isValid;
48 |
49 | /**
50 | * Old text value.
51 | *
52 | * @since 3.4 this field is protected.
53 | */
54 | protected int oldValue;
55 |
56 | /**
57 | * Width of text field in characters; initially unlimited.
58 | */
59 | private int widthInChars = UNLIMITED;
60 |
61 | /**
62 | * Text limit of text field in characters; initially unlimited.
63 | */
64 | private int textLimit = UNLIMITED;
65 |
66 | private int increment = 1;
67 | private int minValue = 0;
68 | private int maxValue = Integer.MAX_VALUE;
69 |
70 | /**
71 | * The error message, or null
if none.
72 | */
73 | private String errorMessage;
74 |
75 | /**
76 | * The validation strategy; VALIDATE_ON_KEY_STROKE
by default.
77 | */
78 | private int validateStrategy = VALIDATE_ON_KEY_STROKE;
79 |
80 | public SpinnerFieldEditor(String name, String labelText, int width, Composite parent) {
81 | init(name, labelText);
82 | isValid = false;
83 | errorMessage = JFaceResources.getString("IntegerFieldEditor.errorMessage");//$NON-NLS-1$
84 | widthInChars = width;
85 | createControl(parent);
86 | }
87 |
88 | @Override
89 | protected void adjustForNumColumns(int numColumns) {
90 | GridData gd = (GridData) spinner.getLayoutData();
91 | gd.horizontalSpan = numColumns - 1;
92 | gd.grabExcessHorizontalSpace = false;
93 | }
94 |
95 | /**
96 | * Checks whether the text input field contains a valid value or not.
97 | *
98 | * @return true
if the field value is valid,
99 | * and false
if invalid
100 | */
101 | protected boolean checkState() {
102 | if (spinner == null) {
103 | return false;
104 | }
105 |
106 | String numberString = spinner.getText();
107 | try {
108 | int number = Integer.valueOf(numberString).intValue();
109 | if (number >= minValue && number <= maxValue) {
110 | clearErrorMessage();
111 | return true;
112 | }
113 |
114 | showErrorMessage();
115 | return false;
116 |
117 | } catch (NumberFormatException e1) {
118 | showErrorMessage();
119 | }
120 |
121 | return false;
122 | }
123 |
124 | /**
125 | * Hook for subclasses to do specific state checks.
126 | *
127 | * The default implementation of this framework method does nothing and
128 | * returns true
. Subclasses should override this method to
129 | * specific state checks.
130 | *
true
if the field value is valid, and
133 | * false
if invalid
134 | */
135 | protected boolean doCheckState() {
136 | return true;
137 | }
138 |
139 | @Override
140 | protected void doFillIntoGrid(Composite parent, int numColumns) {
141 | getLabelControl(parent);
142 |
143 | spinner = getSpinnerControl(parent);
144 | GridData gd = new GridData();
145 | gd.horizontalSpan = numColumns - 1;
146 | if (widthInChars != UNLIMITED) {
147 | GC gc = new GC(spinner);
148 | try {
149 | Point extent = gc.textExtent("X");//$NON-NLS-1$
150 | gd.widthHint = widthInChars * extent.x;
151 | } finally {
152 | gc.dispose();
153 | }
154 | } else {
155 | gd.horizontalAlignment = GridData.FILL;
156 | gd.grabExcessHorizontalSpace = true;
157 | }
158 | spinner.setLayoutData(gd);
159 |
160 | }
161 |
162 | @Override
163 | protected void doLoad() {
164 | if (spinner != null) {
165 | int value = getPreferenceStore().getInt(getPreferenceName());
166 | spinner.setSelection(value);
167 | oldValue = value;
168 | }
169 | }
170 |
171 | @Override
172 | protected void doLoadDefault() {
173 | if (spinner != null) {
174 | int value = getPreferenceStore().getDefaultInt(getPreferenceName());
175 | spinner.setSelection(value);
176 | }
177 | valueChanged();
178 | }
179 |
180 | @Override
181 | protected void doStore() {
182 | getPreferenceStore().setValue(getPreferenceName(), spinner.getSelection());
183 | }
184 |
185 | /**
186 | * Returns the error message that will be displayed when and if
187 | * an error occurs.
188 | *
189 | * @return the error message, or null
if none
190 | */
191 | public String getErrorMessage() {
192 | return errorMessage;
193 | }
194 |
195 | @Override
196 | public int getNumberOfControls() {
197 | return 2;
198 | }
199 |
200 | /**
201 | * Returns the field editor's value.
202 | *
203 | * @return the current value
204 | */
205 | public int getIntValue() {
206 | if (spinner != null) {
207 | return spinner.getSelection();
208 | }
209 |
210 | return getPreferenceStore().getInt(getPreferenceName());
211 | }
212 |
213 | /**
214 | * Returns this field editor's text control.
215 | *
216 | * @return the text control, or null
if no
217 | * text field is created yet
218 | */
219 | protected Spinner getSpinnerControl() {
220 | return spinner;
221 | }
222 |
223 | /**
224 | * Returns this field editor's text control.
225 | * 226 | * The control is created if it does not yet exist 227 | *
228 | * 229 | * @param parent 230 | * the parent 231 | * @return the text control 232 | */ 233 | public Spinner getSpinnerControl(Composite parent) { 234 | if (spinner == null) { 235 | spinner = new Spinner(parent, SWT.SINGLE | SWT.BORDER); 236 | spinner.setFont(parent.getFont()); 237 | switch (validateStrategy) { 238 | case VALIDATE_ON_KEY_STROKE: 239 | spinner.addKeyListener(new KeyAdapter() { 240 | 241 | /* 242 | * (non-Javadoc) 243 | * @see org.eclipse.swt.events.KeyAdapter#keyReleased(org.eclipse.swt.events.KeyEvent) 244 | */ 245 | public void keyReleased(KeyEvent e) { 246 | valueChanged(); 247 | } 248 | }); 249 | spinner.addFocusListener(new FocusAdapter() { 250 | // Ensure that the value is checked on focus loss in case we 251 | // missed a keyRelease or user hasn't released key. 252 | // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=214716 253 | public void focusLost(FocusEvent e) { 254 | valueChanged(); 255 | } 256 | }); 257 | 258 | break; 259 | case VALIDATE_ON_FOCUS_LOST: 260 | spinner.addKeyListener(new KeyAdapter() { 261 | public void keyPressed(KeyEvent e) { 262 | clearErrorMessage(); 263 | } 264 | }); 265 | spinner.addFocusListener(new FocusAdapter() { 266 | public void focusGained(FocusEvent e) { 267 | refreshValidState(); 268 | } 269 | 270 | public void focusLost(FocusEvent e) { 271 | valueChanged(); 272 | clearErrorMessage(); 273 | } 274 | }); 275 | break; 276 | default: 277 | Assert.isTrue(false, "Unknown validate strategy");//$NON-NLS-1$ 278 | } 279 | spinner.addDisposeListener(new DisposeListener() { 280 | public void widgetDisposed(DisposeEvent event) { 281 | spinner = null; 282 | } 283 | }); 284 | if (textLimit > 0) {// Only set limits above 0 - see SWT spec 285 | spinner.setTextLimit(textLimit); 286 | } 287 | 288 | spinner.setIncrement(getIncrement()); 289 | spinner.setMinimum(getMinValue()); 290 | spinner.setMaximum(getMaxValue()); 291 | } else { 292 | checkParent(spinner, parent); 293 | } 294 | return spinner; 295 | } 296 | 297 | /* 298 | * (non-Javadoc) Method declared on FieldEditor. 299 | */ 300 | public boolean isValid() { 301 | return isValid; 302 | } 303 | 304 | /* 305 | * (non-Javadoc) Method declared on FieldEditor. 306 | */ 307 | protected void refreshValidState() { 308 | isValid = checkState(); 309 | } 310 | 311 | /** 312 | * Sets the error message that will be displayed when and if an error 313 | * occurs. 314 | * 315 | * @param message 316 | * the error message 317 | */ 318 | public void setErrorMessage(String message) { 319 | errorMessage = message; 320 | } 321 | 322 | /* (non-Javadoc) 323 | * Method declared on FieldEditor. 324 | */ 325 | public void setFocus() { 326 | if (spinner != null) { 327 | spinner.setFocus(); 328 | } 329 | } 330 | 331 | /** 332 | * Sets this field editor's value. 333 | * 334 | * @param value the new value, ornull
meaning the empty string
335 | */
336 | public void setIntValue(int value) {
337 | if (spinner != null) {
338 | if (spinner == null) {
339 | value = 0;//$NON-NLS-1$
340 | }
341 |
342 | oldValue = spinner.getSelection();
343 | if (oldValue != value) {
344 | spinner.setSelection(value);
345 | valueChanged();
346 | }
347 | }
348 | }
349 |
350 | /**
351 | * Sets this text field's text limit.
352 | *
353 | * @param limit the limit on the number of character in the text
354 | * input field, or UNLIMITED
for no limit
355 |
356 | */
357 | public void setTextLimit(int limit) {
358 | textLimit = limit;
359 | if (spinner != null) {
360 | spinner.setTextLimit(limit);
361 | }
362 | }
363 |
364 | /**
365 | * Sets the strategy for validating the text.
366 | *
367 | * Calling this method has no effect after createPartControl
368 | * is called. Thus this method is really only useful for subclasses to call
369 | * in their constructor. However, it has public visibility for backward
370 | * compatibility.
371 | *
VALIDATE_ON_KEY_STROKE
to perform
374 | * on the fly checking (the default), or VALIDATE_ON_FOCUS_LOST
to
375 | * perform validation only after the text has been typed in
376 | */
377 | public void setValidateStrategy(int value) {
378 | Assert.isTrue(value == VALIDATE_ON_FOCUS_LOST
379 | || value == VALIDATE_ON_KEY_STROKE);
380 | validateStrategy = value;
381 | }
382 |
383 | /**
384 | * Shows the error message set via setErrorMessage
.
385 | */
386 | public void showErrorMessage() {
387 | showErrorMessage(errorMessage);
388 | }
389 |
390 | /**
391 | * Informs this field editor's listener, if it has one, about a change to
392 | * the value (VALUE
property) provided that the old and new
393 | * values are different.
394 | * 395 | * This hook is not called when the text is initialized (or reset 396 | * to the default value) from the preference store. 397 | *
398 | */ 399 | protected void valueChanged() { 400 | setPresentsDefaultValue(false); 401 | boolean oldState = isValid; 402 | refreshValidState(); 403 | 404 | if (isValid != oldState) { 405 | fireStateChanged(IS_VALID, oldState, isValid); 406 | } 407 | 408 | int newValue = spinner.getSelection(); 409 | if (newValue != oldValue) { 410 | fireValueChanged(VALUE, oldValue, newValue); 411 | oldValue = newValue; 412 | } 413 | } 414 | 415 | /* 416 | * @see FieldEditor.setEnabled(boolean,Composite). 417 | */ 418 | public void setEnabled(boolean enabled, Composite parent) { 419 | super.setEnabled(enabled, parent); 420 | getSpinnerControl(parent).setEnabled(enabled); 421 | } 422 | 423 | public void setIncrement(int increment) { 424 | this.increment = increment; 425 | } 426 | 427 | public int getIncrement() { 428 | return increment; 429 | } 430 | 431 | public int getMinValue() { 432 | return minValue; 433 | } 434 | 435 | public int getMaxValue() { 436 | return maxValue; 437 | } 438 | 439 | /** 440 | * Sets the range of valid values for this field. 441 | * 442 | * @param min the minimum allowed value (inclusive) 443 | * @param max the maximum allowed value (inclusive) 444 | */ 445 | public void setValidRange(int min, int max) { 446 | minValue = min; 447 | maxValue = max; 448 | setErrorMessage(JFaceResources.format( 449 | "IntegerFieldEditor.errorMessageRange", //$NON-NLS-1$ 450 | new Object[] { new Integer(min), new Integer(max) })); 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /src/io/emmet/eclipse/preferences/StatusInfo.java: -------------------------------------------------------------------------------- 1 | package io.emmet.eclipse.preferences; 2 | 3 | import io.emmet.eclipse.EclipseEmmetPlugin; 4 | 5 | import org.eclipse.core.runtime.Assert; 6 | import org.eclipse.core.runtime.IStatus; 7 | 8 | 9 | /** 10 | * A settable IStatus. 11 | * Can be an error, warning, info or OKk. For error, info and warning states, 12 | * a message describes the problem. 13 | * 14 | * @since 3.0 15 | */ 16 | class StatusInfo implements IStatus { 17 | 18 | private String fStatusMessage; 19 | private int fSeverity; 20 | 21 | /** 22 | * Creates a status set to OK (no message) 23 | */ 24 | public StatusInfo() { 25 | this(OK, null); 26 | } 27 | 28 | /** 29 | * Creates a status . 30 | * @param severity The status severity: ERROR, WARNING, INFO and OK. 31 | * @param message The message of the status. Applies only for ERROR, 32 | * WARNING and INFO. 33 | */ 34 | public StatusInfo(int severity, String message) { 35 | fStatusMessage= message; 36 | fSeverity= severity; 37 | } 38 | 39 | /** 40 | * Returns if the status' severity is OK. 41 | * 42 | * @returntrue
if the status' severity is OK
43 | */
44 | public boolean isOK() {
45 | return fSeverity == IStatus.OK;
46 | }
47 |
48 | /**
49 | * Returns if the status' severity is WARNING.
50 | *
51 | * @return true
if the status' severity is WARNING
52 | */
53 | public boolean isWarning() {
54 | return fSeverity == IStatus.WARNING;
55 | }
56 |
57 | /**
58 | * Returns if the status' severity is INFO.
59 | *
60 | * @return true
if the status' severity is INFO
61 | */
62 | public boolean isInfo() {
63 | return fSeverity == IStatus.INFO;
64 | }
65 |
66 | /**
67 | * Returns if the status' severity is ERROR.
68 | *
69 | * @return true
if the status' severity is ERROR
70 | */
71 | public boolean isError() {
72 | return fSeverity == IStatus.ERROR;
73 | }
74 |
75 | /**
76 | * Returns the message.
77 | *
78 | * @return the message
79 | * @see IStatus#getMessage()
80 | */
81 | public String getMessage() {
82 | return fStatusMessage;
83 | }
84 |
85 | /**
86 | * Sets the status to ERROR.
87 | * @param errorMessage the error message (can be empty, but not null)
88 | */
89 | public void setError(String errorMessage) {
90 | Assert.isNotNull(errorMessage);
91 | fStatusMessage= errorMessage;
92 | fSeverity= IStatus.ERROR;
93 | }
94 |
95 | /**
96 | * Sets the status to WARNING.
97 | * @param warningMessage the warning message (can be empty, but not null)
98 | */
99 | public void setWarning(String warningMessage) {
100 | Assert.isNotNull(warningMessage);
101 | fStatusMessage= warningMessage;
102 | fSeverity= IStatus.WARNING;
103 | }
104 |
105 | /**
106 | * Sets the status to INFO.
107 | * @param infoMessage the info message (can be empty, but not null)
108 | */
109 | public void setInfo(String infoMessage) {
110 | Assert.isNotNull(infoMessage);
111 | fStatusMessage= infoMessage;
112 | fSeverity= IStatus.INFO;
113 | }
114 |
115 | /**
116 | * Sets the status to OK.
117 | */
118 | public void setOK() {
119 | fStatusMessage= null;
120 | fSeverity= IStatus.OK;
121 | }
122 |
123 | /*
124 | * @see IStatus#matches(int)
125 | */
126 | public boolean matches(int severityMask) {
127 | return (fSeverity & severityMask) != 0;
128 | }
129 |
130 | /**
131 | * Returns always false
.
132 | * @see IStatus#isMultiStatus()
133 | */
134 | public boolean isMultiStatus() {
135 | return false;
136 | }
137 |
138 | /*
139 | * @see IStatus#getSeverity()
140 | */
141 | public int getSeverity() {
142 | return fSeverity;
143 | }
144 |
145 | /*
146 | * @see IStatus#getPlugin()
147 | */
148 | public String getPlugin() {
149 | return EclipseEmmetPlugin.PLUGIN_ID;
150 | }
151 |
152 | /**
153 | * Returns always null
.
154 | * @see IStatus#getException()
155 | */
156 | public Throwable getException() {
157 | return null;
158 | }
159 |
160 | /**
161 | * Returns always the error severity.
162 | * @see IStatus#getCode()
163 | */
164 | public int getCode() {
165 | return fSeverity;
166 | }
167 |
168 | /**
169 | * Returns always null
.
170 | * @see IStatus#getChildren()
171 | */
172 | public IStatus[] getChildren() {
173 | return new IStatus[0];
174 | }
175 |
176 | }
--------------------------------------------------------------------------------
/src/io/emmet/eclipse/preferences/TemplateContentProvider.java:
--------------------------------------------------------------------------------
1 | package io.emmet.eclipse.preferences;
2 |
3 | import org.eclipse.jface.text.templates.persistence.TemplateStore;
4 | import org.eclipse.jface.viewers.IStructuredContentProvider;
5 | import org.eclipse.jface.viewers.Viewer;
6 |
7 | /**
8 | * A content provider for the template preference page's table viewer.
9 | *
10 | * @since 3.0
11 | */
12 | class TemplateContentProvider implements IStructuredContentProvider {
13 |
14 | /** The template store. */
15 | private TemplateStore fStore;
16 |
17 | /*
18 | * @see IStructuredContentProvider#getElements(Object)
19 | */
20 | public Object[] getElements(Object input) {
21 | return fStore.getTemplateData(false);
22 | }
23 |
24 | /*
25 | * @see IContentProvider#inputChanged(Viewer, Object, Object)
26 | */
27 | public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
28 | fStore= (TemplateStore) newInput;
29 | }
30 |
31 | /*
32 | * @see IContentProvider#dispose()
33 | */
34 | public void dispose() {
35 | fStore= null;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/io/emmet/eclipse/preferences/TemplateHelper.java:
--------------------------------------------------------------------------------
1 | package io.emmet.eclipse.preferences;
2 |
3 | import io.emmet.eclipse.EclipseEmmetPlugin;
4 | import io.emmet.eclipse.EmmetContextType;
5 |
6 | import java.io.IOException;
7 | import java.util.HashMap;
8 |
9 | import org.eclipse.jface.text.templates.ContextTypeRegistry;
10 | import org.eclipse.jface.text.templates.persistence.TemplateStore;
11 | import org.eclipse.ui.editors.text.templates.ContributionContextTypeRegistry;
12 | import org.eclipse.ui.editors.text.templates.ContributionTemplateStore;
13 |
14 |
15 | public class TemplateHelper {
16 | /** The template store list. */
17 | private static HashMapnull
if the edition failed
508 | * @since 3.1
509 | */
510 | protected Template editTemplate(Template template, boolean edit, boolean isNameModifiable) {
511 | EditVariableDialog dialog= new EditVariableDialog(getShell(), template, edit, isNameModifiable);
512 | if (dialog.open() == Window.OK) {
513 | return dialog.getTemplate();
514 | }
515 | return null;
516 | }
517 |
518 | private void selectionChanged1() {
519 | updateButtons();
520 | }
521 |
522 | /**
523 | * Updates the buttons.
524 | */
525 | protected void updateButtons() {
526 | IStructuredSelection selection= (IStructuredSelection) fTableViewer.getSelection();
527 | int selectionCount= selection.size();
528 | int itemCount= fTableViewer.getTable().getItemCount();
529 |
530 | fEditButton.setEnabled(selectionCount == 1);
531 | fRemoveButton.setEnabled(selectionCount > 0 && selectionCount <= itemCount);
532 | }
533 |
534 | private void add() {
535 | Template template= new Template("", "", CONTEXT_TYPE_ID, "", true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
536 |
537 | Template newTemplate= editTemplate(template, false, true);
538 | if (newTemplate != null) {
539 | TemplatePersistenceData data= new TemplatePersistenceData(newTemplate, true);
540 | fTemplateStore.add(data);
541 | fTableViewer.refresh();
542 | fTableViewer.setSelection(new StructuredSelection(data));
543 | }
544 | }
545 |
546 | private void remove() {
547 | IStructuredSelection selection= (IStructuredSelection) fTableViewer.getSelection();
548 |
549 | @SuppressWarnings("rawtypes")
550 | Iterator elements= selection.iterator();
551 | while (elements.hasNext()) {
552 | TemplatePersistenceData data= (TemplatePersistenceData) elements.next();
553 | fTemplateStore.delete(data);
554 | }
555 |
556 | fTableViewer.refresh();
557 | }
558 |
559 | /**
560 | * Sets the template store.
561 | *
562 | * @param store the new template store
563 | */
564 | public void setTemplateStore(TemplateStore store) {
565 | fTemplateStore= store;
566 | }
567 |
568 | /**
569 | * Returns the template store.
570 | *
571 | * @return the template store
572 | */
573 | public TemplateStore getTemplateStore() {
574 | return fTemplateStore;
575 | }
576 |
577 | private int computeMinimumColumnWidth(GC gc, String string) {
578 | return gc.stringExtent(string).x + 10; // pad 10 to accommodate table header trimmings
579 | }
580 |
581 | /*
582 | * @see PreferencePage#performDefaults()
583 | */
584 | protected void performDefaults() {
585 | fTemplateStore.restoreDefaults(false);
586 | fTableViewer.refresh();
587 | }
588 |
589 | /*
590 | * @see PreferencePage#performOk()
591 | */
592 | public boolean performOk() {
593 | try {
594 | fTemplateStore.save();
595 | } catch (IOException e) {
596 | openWriteErrorDialog(e);
597 | }
598 |
599 | return super.performOk();
600 | }
601 |
602 | private void openWriteErrorDialog(IOException ex) {
603 | IStatus status= new Status(IStatus.ERROR, EclipseEmmetPlugin.PLUGIN_ID, IStatus.OK, "Failed to write templates.", ex); //$NON-NLS-1$
604 | EclipseEmmetPlugin.getDefault().getLog().log(status);
605 | String title= "Error while saving variables";
606 | String message= "Error occured while saving variable preverences";
607 | MessageDialog.openError(getShell(), title, message);
608 | }
609 | }
610 |
--------------------------------------------------------------------------------
/src/io/emmet/eclipse/preferences/output/CSSOutputPreferencePage.java:
--------------------------------------------------------------------------------
1 | package io.emmet.eclipse.preferences.output;
2 |
3 | public class CSSOutputPreferencePage extends DefaultOutputPreferencePage {
4 |
5 | public CSSOutputPreferencePage() {
6 | super();
7 | setDescription("Output preferences for CSS syntax");
8 | setPrefSuffix("css");
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/io/emmet/eclipse/preferences/output/DefaultOutputPreferencePage.java:
--------------------------------------------------------------------------------
1 | package io.emmet.eclipse.preferences.output;
2 |
3 | import io.emmet.Emmet;
4 | import io.emmet.eclipse.EclipseEmmetPlugin;
5 | import io.emmet.eclipse.preferences.PreferenceConstants;
6 | import io.emmet.eclipse.preferences.PreferenceInitializer;
7 | import io.emmet.eclipse.preferences.SpacerFieldEditor;
8 | import io.emmet.eclipse.preferences.SpinnerFieldEditor;
9 |
10 | import org.eclipse.jface.preference.BooleanFieldEditor;
11 | import org.eclipse.jface.preference.FieldEditorPreferencePage;
12 | import org.eclipse.jface.preference.RadioGroupFieldEditor;
13 | import org.eclipse.jface.preference.StringFieldEditor;
14 | import org.eclipse.ui.IWorkbench;
15 | import org.eclipse.ui.IWorkbenchPreferencePage;
16 |
17 |
18 | public class DefaultOutputPreferencePage extends FieldEditorPreferencePage
19 | implements IWorkbenchPreferencePage {
20 |
21 | private String prefSuffix = "";
22 |
23 | public DefaultOutputPreferencePage() {
24 | super(FLAT);
25 | setPreferenceStore(EclipseEmmetPlugin.getDefault().getPreferenceStore());
26 | setDescription("Default output preferences for unknown syntaxes (like JavaScript, Python, etc.)");
27 | setPrefSuffix("default");
28 | }
29 |
30 | public String getPrefName(String prefix) {
31 | return PreferenceInitializer.getPrefName(prefix, getPrefSuffix());
32 | }
33 |
34 | /* (non-Javadoc)
35 | * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
36 | */
37 | @Override
38 | public void init(IWorkbench workbench) {
39 | }
40 |
41 | @Override
42 | protected void createFieldEditors() {
43 |
44 | addField(new RadioGroupFieldEditor(
45 | getPrefName(PreferenceConstants.P_PROFILE_TAG_CASE),
46 | "Tag case:",
47 | 3,
48 | new String[][] {
49 | { "&Lowercase", OutputProfile.LOWERCASE },
50 | { "&Uppercase", OutputProfile.UPPERCASE },
51 | { "&As is", OutputProfile.LEAVE }
52 | }, getFieldEditorParent(), true));
53 |
54 | addField(new RadioGroupFieldEditor(
55 | getPrefName(PreferenceConstants.P_PROFILE_ATTR_CASE),
56 | "Attribute case:",
57 | 3,
58 | new String[][] {
59 | { "L&owercase", OutputProfile.LOWERCASE },
60 | { "U&ppercase", OutputProfile.UPPERCASE },
61 | { "A&s is", OutputProfile.LEAVE }
62 | }, getFieldEditorParent(), true));
63 |
64 | addField(new RadioGroupFieldEditor(
65 | getPrefName(PreferenceConstants.P_PROFILE_ATTR_QUOTES),
66 | "Attribute quotes:",
67 | 2,
68 | new String[][] {
69 | { "S&ingle", OutputProfile.SINGE_QUOTES },
70 | { "&Double", OutputProfile.DOUBLE_QUOTES }
71 | }, getFieldEditorParent(), true));
72 |
73 | addField(new RadioGroupFieldEditor(
74 | getPrefName(PreferenceConstants.P_PROFILE_TAG_NEWLINE),
75 | "Each tag on new line:",
76 | 3,
77 | new String[][] {
78 | { "Yes", OutputProfile.TRUE },
79 | { "No", OutputProfile.FALSE },
80 | { "Decide", OutputProfile.DECIDE }
81 | }, getFieldEditorParent(), true));
82 |
83 | addField(new BooleanFieldEditor(
84 | getPrefName(PreferenceConstants.P_PROFILE_PLACE_CURSOR),
85 | "Place caret placeholders in expanded abbreviations",
86 | getFieldEditorParent()));
87 |
88 | addField(new BooleanFieldEditor(
89 | getPrefName(PreferenceConstants.P_PROFILE_INDENT),
90 | "Indent tags",
91 | getFieldEditorParent()));
92 |
93 | SpinnerFieldEditor inlineBreak = new SpinnerFieldEditor(
94 | getPrefName(PreferenceConstants.P_PROFILE_INLINE_BREAK),
95 | "How many inline elements should be to force line break",
96 | 6,
97 | getFieldEditorParent());
98 |
99 | inlineBreak.setValidRange(0, 99);
100 | addField(inlineBreak);
101 |
102 | addField(new RadioGroupFieldEditor(
103 | getPrefName(PreferenceConstants.P_PROFILE_SELF_CLOSING_TAG),
104 | "Self-closing style for writing empty elements:",
105 | 1,
106 | new String[][] {
107 | { "Disabled (file_name
file that relates to editor_file
.
36 | * File name may be absolute or relative path
37 | *
38 | * Dealing with absolute path.
39 | * Many modern editors has a "project" support as information unit, but you
40 | * should not rely on project path to find file with absolute path. First,
41 | * it requires user to create a project before using this method (and this
42 | * is not acutually Zen). Second, project path doesn't always points to
43 | * to website's document root folder: it may point, for example, to an
44 | * upper folder which contains server-side scripts.
45 | *
46 | * For better result, you should use the following algorithm in locating
47 | * absolute resources:
48 | * 1) Get parent folder for editor_file
as a start point
49 | * 2) Append required file_name
to start point and test if
50 | * file exists
51 | * 3) If it doesn't exists, move start point one level up (to parent folder)
52 | * and repeat step 2.
53 | *
54 | * @param {String} editor_file
55 | * @param {String} file_name
56 | * @return {String|null} Returns null if file_name
cannot be located
57 | */
58 | locateFile: function(editor_file, file_name) {
59 | var File = Packages.java.io.File;
60 | var f = new File(editor_file),
61 | result = '',
62 | tmp;
63 |
64 | // traverse upwards to find image uri
65 | while (f.getParent()) {
66 | tmp = new File(this.createPath(f.getParent(), file_name));
67 | if (tmp.exists()) {
68 | result = tmp.getCanonicalPath();
69 | break;
70 | }
71 |
72 | f = new File(f.getParent());
73 | }
74 |
75 | return result;
76 | },
77 |
78 | /**
79 | * Creates absolute path by concatenating parent
and file_name
.
80 | * If parent
points to file, its parent directory is used
81 | * @param {String} parent
82 | * @param {String} file_name
83 | * @return {String}
84 | */
85 | createPath: function(parent, file_name) {
86 | var File = Packages.java.io.File,
87 | f = new File(parent),
88 | result = '';
89 |
90 | if (f.exists()) {
91 | if (f.isFile()) {
92 | parent = f.getParent();
93 | }
94 |
95 | var req_file = new File(parent, file_name);
96 | result = req_file.getCanonicalPath();
97 | }
98 |
99 | return result;
100 | },
101 |
102 | /**
103 | * Saves content
as file
104 | * @param {String} file File's asolute path
105 | * @param {String} content File content
106 | */
107 | save: function(file, content) {
108 | content = content || '';
109 | file = String(file);
110 |
111 | var File = Packages.java.io.File,
112 | f = new File(file);
113 |
114 | if (file.indexOf('/') != -1) {
115 | var f_parent = new File(f.getParent());
116 | f_parent.mkdirs();
117 | }
118 |
119 | var stream = new Packages.java.io.FileOutputStream(file);
120 | for (var i = 0, il = content.length; i < il; i++) {
121 | stream.write(content.charCodeAt(i));
122 | }
123 |
124 | stream.close();
125 | },
126 |
127 | /**
128 | * Returns file extension in lower case
129 | * @param {String} file
130 | * @return {String}
131 | */
132 | getExt: function(file) {
133 | var m = (file || '').match(/\.([\w\-]+)$/);
134 | return m ? m[1].toLowerCase() : '';
135 | }
136 | };
137 | });
138 |
--------------------------------------------------------------------------------
/src/io/emmet/java-wrapper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Short-hand functions for Java wrapper
3 | * @author Sergey Chikuyonok (serge.che@gmail.com)
4 | * @link http://chikuyonok.ru
5 | */
6 |
7 | function require(name) {
8 | return emmet.require(name);
9 | }
10 |
11 | /**
12 | * Runs Emmet action
13 | * @param {IEmmetEditor} editor
14 | * @param {String} actionName
15 | * @return {Boolean}
16 | */
17 | function runEmmetAction(editor, actionName){
18 | var args = [editor];
19 | for (var i = 2, il = arguments.length; i < il; i++) {
20 | args.push(arguments[i]);
21 | }
22 |
23 | return require('actions').run(actionName, args);
24 | }
25 |
26 | function tryBoolean(val) {
27 | var strVal = String(val || '').toLowerCase();
28 | if (strVal == 'true')
29 | return true;
30 | if (strVal == 'false')
31 | return false;
32 |
33 | var intVal = parseInt(strVal, 10);
34 | if (!isNaN(intVal))
35 | return intVal;
36 |
37 | return strVal;
38 | }
39 |
40 | function previewWrapWithAbbreviation(editor, abbr) {
41 | abbr = String(abbr);
42 | if (!abbr)
43 | return null;
44 |
45 | var editorUtils = require('editorUtils');
46 | var utils = require('utils');
47 | var info = editorUtils.outputInfo(editor);
48 |
49 | var range = editor.getSelectionRange(),
50 | startOffset = range.start,
51 | endOffset = range.end;
52 |
53 | if (startOffset == endOffset) {
54 | // no selection, find tag pair
55 | range = require('html_matcher')(info.content, startOffset, info.profile);
56 |
57 | if (!range || range[0] == -1) // nothing to wrap
58 | return null;
59 |
60 | var narrowedSel = utils.narrowToNonSpace(info.content, range[0], range[1] - range[0]);
61 | startOffset = narrowedSel.start;
62 | endOffset = narrowedSel.end;
63 | }
64 |
65 | var newContent = utils.escapeText(info.content.substring(startOffset, endOffset));
66 | return require('wrapWithAbbreviation').wrap(abbr, editorUtils.unindent(editor, newContent), info.syntax, info.profile)
67 | || null;
68 | }
69 |
70 | function strToJSON(data) {
71 | try {
72 | return (new Function('return ' + String(data)))();
73 | } catch(e) {
74 | log('Error while parsing JSON: ' + e);
75 | return {};
76 | }
77 | }
78 |
79 | function javaLoadSystemSnippets(data) {
80 | if (data) {
81 | require('resources').setVocabulary(strToJSON(data), 'system');
82 | }
83 | }
84 |
85 | function javaLoadUserData(payload) {
86 | payload = strToJSON(payload);
87 | var profileMap = {
88 | 'tagCase': 'tag_case',
89 | 'attrCase': 'attr_case',
90 | 'attrQuotes': 'attr_quotes',
91 | 'tagNewline': 'tag_nl',
92 | 'placeCaret': 'place_cursor',
93 | 'indentTags': 'indent',
94 | 'inlineBreak': 'inline_break',
95 | 'selfClosing': 'self_closing_tag',
96 | 'filters': 'filters'
97 | };
98 |
99 | var validPayload = {};
100 |
101 | // prepare profiles
102 | if ('profiles' in payload) {
103 | validPayload.syntaxProfiles = {};
104 | _.each(payload.profiles, function(profile, syntax) {
105 | var p = {};
106 | _.each(profile, function(v, k) {
107 | p[profileMap[k]] = v;
108 | });
109 |
110 | validPayload.syntaxProfiles[syntax] = p;
111 | });
112 | }
113 |
114 | // prepare snippets
115 | var snippets = {};
116 | var addSnippets = function(data, type) {
117 | if (!data)
118 | return;
119 |
120 | _.each(data, function(item) {
121 | if (!(item[0] in snippets)) {
122 | snippets[item[0]] = {};
123 | }
124 |
125 | var syntaxCtx = snippets[item[0]];
126 | if (!syntaxCtx[type]) {
127 | syntaxCtx[type] = {};
128 | }
129 |
130 | syntaxCtx[type][item[1]] = item[2];
131 | });
132 | };
133 |
134 | addSnippets(payload.snippets, 'snippets');
135 | addSnippets(payload.abbreviations, 'abbreviations');
136 | validPayload.snippets = snippets;
137 |
138 | // prepare variables
139 | if ('variables' in payload) {
140 | validPayload.variables = {};
141 | _.each(payload.variables, function(item) {
142 | validPayload.variables[item[1]] = item[2];
143 | });
144 | }
145 |
146 | require('bootstrap').loadUserData(validPayload);
147 | }
148 |
149 | function javaLoadExtensions(payload) {
150 | require('bootstrap').loadExtensions(strToJSON(payload));
151 | }
152 |
153 | function javaExtractTabstops(text) {
154 | return require('tabStops').extract(text, {
155 | escape: function(ch) {
156 | return ch;
157 | }
158 | });
159 | }
160 |
161 | function log(message) {
162 | java.lang.System.out.println('JS: ' + message);
163 | }
--------------------------------------------------------------------------------
/src/io/emmet/json2.js:
--------------------------------------------------------------------------------
1 | /*
2 | json2.js
3 | 2011-10-19
4 |
5 | Public Domain.
6 |
7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 |
9 | See http://www.JSON.org/js.html
10 |
11 |
12 | This code should be minified before deployment.
13 | See http://javascript.crockford.com/jsmin.html
14 |
15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
16 | NOT CONTROL.
17 |
18 |
19 | This file creates a global JSON object containing two methods: stringify
20 | and parse.
21 |
22 | JSON.stringify(value, replacer, space)
23 | value any JavaScript value, usually an object or array.
24 |
25 | replacer an optional parameter that determines how object
26 | values are stringified for objects. It can be a
27 | function or an array of strings.
28 |
29 | space an optional parameter that specifies the indentation
30 | of nested structures. If it is omitted, the text will
31 | be packed without extra whitespace. If it is a number,
32 | it will specify the number of spaces to indent at each
33 | level. If it is a string (such as '\t' or ' '),
34 | it contains the characters used to indent at each level.
35 |
36 | This method produces a JSON text from a JavaScript value.
37 |
38 | When an object value is found, if the object contains a toJSON
39 | method, its toJSON method will be called and the result will be
40 | stringified. A toJSON method does not serialize: it returns the
41 | value represented by the name/value pair that should be serialized,
42 | or undefined if nothing should be serialized. The toJSON method
43 | will be passed the key associated with the value, and this will be
44 | bound to the value
45 |
46 | For example, this would serialize Dates as ISO strings.
47 |
48 | Date.prototype.toJSON = function (key) {
49 | function f(n) {
50 | // Format integers to have at least two digits.
51 | return n < 10 ? '0' + n : n;
52 | }
53 |
54 | return this.getUTCFullYear() + '-' +
55 | f(this.getUTCMonth() + 1) + '-' +
56 | f(this.getUTCDate()) + 'T' +
57 | f(this.getUTCHours()) + ':' +
58 | f(this.getUTCMinutes()) + ':' +
59 | f(this.getUTCSeconds()) + 'Z';
60 | };
61 |
62 | You can provide an optional replacer method. It will be passed the
63 | key and value of each member, with this bound to the containing
64 | object. The value that is returned from your method will be
65 | serialized. If your method returns undefined, then the member will
66 | be excluded from the serialization.
67 |
68 | If the replacer parameter is an array of strings, then it will be
69 | used to select the members to be serialized. It filters the results
70 | such that only members with keys listed in the replacer array are
71 | stringified.
72 |
73 | Values that do not have JSON representations, such as undefined or
74 | functions, will not be serialized. Such values in objects will be
75 | dropped; in arrays they will be replaced with null. You can use
76 | a replacer function to replace those with JSON values.
77 | JSON.stringify(undefined) returns undefined.
78 |
79 | The optional space parameter produces a stringification of the
80 | value that is filled with line breaks and indentation to make it
81 | easier to read.
82 |
83 | If the space parameter is a non-empty string, then that string will
84 | be used for indentation. If the space parameter is a number, then
85 | the indentation will be that many spaces.
86 |
87 | Example:
88 |
89 | text = JSON.stringify(['e', {pluribus: 'unum'}]);
90 | // text is '["e",{"pluribus":"unum"}]'
91 |
92 |
93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
95 |
96 | text = JSON.stringify([new Date()], function (key, value) {
97 | return this[key] instanceof Date ?
98 | 'Date(' + this[key] + ')' : value;
99 | });
100 | // text is '["Date(---current time---)"]'
101 |
102 |
103 | JSON.parse(text, reviver)
104 | This method parses a JSON text to produce an object or array.
105 | It can throw a SyntaxError exception.
106 |
107 | The optional reviver parameter is a function that can filter and
108 | transform the results. It receives each of the keys and values,
109 | and its return value is used instead of the original value.
110 | If it returns what it received, then the structure is not modified.
111 | If it returns undefined then the member is deleted.
112 |
113 | Example:
114 |
115 | // Parse the text. Values that look like ISO date strings will
116 | // be converted to Date objects.
117 |
118 | myData = JSON.parse(text, function (key, value) {
119 | var a;
120 | if (typeof value === 'string') {
121 | a =
122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
123 | if (a) {
124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
125 | +a[5], +a[6]));
126 | }
127 | }
128 | return value;
129 | });
130 |
131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
132 | var d;
133 | if (typeof value === 'string' &&
134 | value.slice(0, 5) === 'Date(' &&
135 | value.slice(-1) === ')') {
136 | d = new Date(value.slice(5, -1));
137 | if (d) {
138 | return d;
139 | }
140 | }
141 | return value;
142 | });
143 |
144 |
145 | This is a reference implementation. You are free to copy, modify, or
146 | redistribute.
147 | */
148 |
149 | /*jslint evil: true, regexp: true */
150 |
151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
154 | lastIndex, length, parse, prototype, push, replace, slice, stringify,
155 | test, toJSON, toString, valueOf
156 | */
157 |
158 |
159 | // Create a JSON object only if one does not already exist. We create the
160 | // methods in a closure to avoid creating global variables.
161 |
162 | var JSON;
163 | if (!JSON) {
164 | JSON = {};
165 | }
166 |
167 | (function () {
168 | 'use strict';
169 |
170 | function f(n) {
171 | // Format integers to have at least two digits.
172 | return n < 10 ? '0' + n : n;
173 | }
174 |
175 | if (typeof Date.prototype.toJSON !== 'function') {
176 |
177 | Date.prototype.toJSON = function (key) {
178 |
179 | return isFinite(this.valueOf())
180 | ? this.getUTCFullYear() + '-' +
181 | f(this.getUTCMonth() + 1) + '-' +
182 | f(this.getUTCDate()) + 'T' +
183 | f(this.getUTCHours()) + ':' +
184 | f(this.getUTCMinutes()) + ':' +
185 | f(this.getUTCSeconds()) + 'Z'
186 | : null;
187 | };
188 |
189 | String.prototype.toJSON =
190 | Number.prototype.toJSON =
191 | Boolean.prototype.toJSON = function (key) {
192 | return this.valueOf();
193 | };
194 | }
195 |
196 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
197 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
198 | gap,
199 | indent,
200 | meta = { // table of character substitutions
201 | '\b': '\\b',
202 | '\t': '\\t',
203 | '\n': '\\n',
204 | '\f': '\\f',
205 | '\r': '\\r',
206 | '"' : '\\"',
207 | '\\': '\\\\'
208 | },
209 | rep;
210 |
211 |
212 | function quote(string) {
213 |
214 | // If the string contains no control characters, no quote characters, and no
215 | // backslash characters, then we can safely slap some quotes around it.
216 | // Otherwise we must also replace the offending characters with safe escape
217 | // sequences.
218 |
219 | escapable.lastIndex = 0;
220 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
221 | var c = meta[a];
222 | return typeof c === 'string'
223 | ? c
224 | : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
225 | }) + '"' : '"' + string + '"';
226 | }
227 |
228 |
229 | function str(key, holder) {
230 |
231 | // Produce a string from holder[key].
232 |
233 | var i, // The loop counter.
234 | k, // The member key.
235 | v, // The member value.
236 | length,
237 | mind = gap,
238 | partial,
239 | value = holder[key];
240 |
241 | // If the value has a toJSON method, call it to obtain a replacement value.
242 |
243 | if (value && typeof value === 'object' &&
244 | typeof value.toJSON === 'function') {
245 | value = value.toJSON(key);
246 | }
247 |
248 | // If we were called with a replacer function, then call the replacer to
249 | // obtain a replacement value.
250 |
251 | if (typeof rep === 'function') {
252 | value = rep.call(holder, key, value);
253 | }
254 |
255 | // What happens next depends on the value's type.
256 |
257 | switch (typeof value) {
258 | case 'string':
259 | return quote(value);
260 |
261 | case 'number':
262 |
263 | // JSON numbers must be finite. Encode non-finite numbers as null.
264 |
265 | return isFinite(value) ? String(value) : 'null';
266 |
267 | case 'boolean':
268 | case 'null':
269 |
270 | // If the value is a boolean or null, convert it to a string. Note:
271 | // typeof null does not produce 'null'. The case is included here in
272 | // the remote chance that this gets fixed someday.
273 |
274 | return String(value);
275 |
276 | // If the type is 'object', we might be dealing with an object or an array or
277 | // null.
278 |
279 | case 'object':
280 |
281 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
282 | // so watch out for that case.
283 |
284 | if (!value) {
285 | return 'null';
286 | }
287 |
288 | // Make an array to hold the partial results of stringifying this object value.
289 |
290 | gap += indent;
291 | partial = [];
292 |
293 | // Is the value an array?
294 |
295 | if (Object.prototype.toString.apply(value) === '[object Array]') {
296 |
297 | // The value is an array. Stringify every element. Use null as a placeholder
298 | // for non-JSON values.
299 |
300 | length = value.length;
301 | for (i = 0; i < length; i += 1) {
302 | partial[i] = str(i, value) || 'null';
303 | }
304 |
305 | // Join all of the elements together, separated with commas, and wrap them in
306 | // brackets.
307 |
308 | v = partial.length === 0
309 | ? '[]'
310 | : gap
311 | ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
312 | : '[' + partial.join(',') + ']';
313 | gap = mind;
314 | return v;
315 | }
316 |
317 | // If the replacer is an array, use it to select the members to be stringified.
318 |
319 | if (rep && typeof rep === 'object') {
320 | length = rep.length;
321 | for (i = 0; i < length; i += 1) {
322 | if (typeof rep[i] === 'string') {
323 | k = rep[i];
324 | v = str(k, value);
325 | if (v) {
326 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
327 | }
328 | }
329 | }
330 | } else {
331 |
332 | // Otherwise, iterate through all of the keys in the object.
333 |
334 | for (k in value) {
335 | if (Object.prototype.hasOwnProperty.call(value, k)) {
336 | v = str(k, value);
337 | if (v) {
338 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
339 | }
340 | }
341 | }
342 | }
343 |
344 | // Join all of the member texts together, separated with commas,
345 | // and wrap them in braces.
346 |
347 | v = partial.length === 0
348 | ? '{}'
349 | : gap
350 | ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
351 | : '{' + partial.join(',') + '}';
352 | gap = mind;
353 | return v;
354 | }
355 | }
356 |
357 | // If the JSON object does not yet have a stringify method, give it one.
358 |
359 | if (typeof JSON.stringify !== 'function') {
360 | JSON.stringify = function (value, replacer, space) {
361 |
362 | // The stringify method takes a value and an optional replacer, and an optional
363 | // space parameter, and returns a JSON text. The replacer can be a function
364 | // that can replace values, or an array of strings that will select the keys.
365 | // A default replacer method can be provided. Use of the space parameter can
366 | // produce text that is more easily readable.
367 |
368 | var i;
369 | gap = '';
370 | indent = '';
371 |
372 | // If the space parameter is a number, make an indent string containing that
373 | // many spaces.
374 |
375 | if (typeof space === 'number') {
376 | for (i = 0; i < space; i += 1) {
377 | indent += ' ';
378 | }
379 |
380 | // If the space parameter is a string, it will be used as the indent string.
381 |
382 | } else if (typeof space === 'string') {
383 | indent = space;
384 | }
385 |
386 | // If there is a replacer, it must be a function or an array.
387 | // Otherwise, throw an error.
388 |
389 | rep = replacer;
390 | if (replacer && typeof replacer !== 'function' &&
391 | (typeof replacer !== 'object' ||
392 | typeof replacer.length !== 'number')) {
393 | throw new Error('JSON.stringify');
394 | }
395 |
396 | // Make a fake root object containing our value under the key of ''.
397 | // Return the result of stringifying the value.
398 |
399 | return str('', {'': value});
400 | };
401 | }
402 |
403 |
404 | // If the JSON object does not yet have a parse method, give it one.
405 |
406 | if (typeof JSON.parse !== 'function') {
407 | JSON.parse = function (text, reviver) {
408 |
409 | // The parse method takes a text and an optional reviver function, and returns
410 | // a JavaScript value if the text is a valid JSON text.
411 |
412 | var j;
413 |
414 | function walk(holder, key) {
415 |
416 | // The walk method is used to recursively walk the resulting structure so
417 | // that modifications can be made.
418 |
419 | var k, v, value = holder[key];
420 | if (value && typeof value === 'object') {
421 | for (k in value) {
422 | if (Object.prototype.hasOwnProperty.call(value, k)) {
423 | v = walk(value, k);
424 | if (v !== undefined) {
425 | value[k] = v;
426 | } else {
427 | delete value[k];
428 | }
429 | }
430 | }
431 | }
432 | return reviver.call(holder, key, value);
433 | }
434 |
435 |
436 | // Parsing happens in four stages. In the first stage, we replace certain
437 | // Unicode characters with escape sequences. JavaScript handles many characters
438 | // incorrectly, either silently deleting them, or treating them as line endings.
439 |
440 | text = String(text);
441 | cx.lastIndex = 0;
442 | if (cx.test(text)) {
443 | text = text.replace(cx, function (a) {
444 | return '\\u' +
445 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
446 | });
447 | }
448 |
449 | // In the second stage, we run the text against regular expressions that look
450 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
451 | // because they can cause invocation, and '=' because it can cause mutation.
452 | // But just to be safe, we want to reject all unexpected forms.
453 |
454 | // We split the second stage into 4 regexp operations in order to work around
455 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
456 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
457 | // replace all simple value tokens with ']' characters. Third, we delete all
458 | // open brackets that follow a colon or comma or that begin the text. Finally,
459 | // we look to see that the remaining characters are only whitespace or ']' or
460 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
461 |
462 | if (/^[\],:{}\s]*$/
463 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
464 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
465 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
466 |
467 | // In the third stage we use the eval function to compile the text into a
468 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
469 | // in JavaScript: it can begin a block or an object literal. We wrap the text
470 | // in parens to eliminate the ambiguity.
471 |
472 | j = eval('(' + text + ')');
473 |
474 | // In the optional fourth stage, we recursively walk the new structure, passing
475 | // each name/value pair to a reviver function for possible transformation.
476 |
477 | return typeof reviver === 'function'
478 | ? walk({'': j}, '')
479 | : j;
480 | }
481 |
482 | // If the text is not JSON parseable, then a SyntaxError is thrown.
483 |
484 | throw new SyntaxError('JSON.parse');
485 | };
486 | }
487 | }());
--------------------------------------------------------------------------------