aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main/js/xtext/services
diff options
context:
space:
mode:
Diffstat (limited to 'language-web/src/main/js/xtext/services')
-rw-r--r--language-web/src/main/js/xtext/services/ContentAssistService.js132
-rw-r--r--language-web/src/main/js/xtext/services/FormattingService.js52
-rw-r--r--language-web/src/main/js/xtext/services/HighlightingService.js33
-rw-r--r--language-web/src/main/js/xtext/services/HoverService.js59
-rw-r--r--language-web/src/main/js/xtext/services/LoadResourceService.js42
-rw-r--r--language-web/src/main/js/xtext/services/OccurrencesService.js39
-rw-r--r--language-web/src/main/js/xtext/services/SaveResourceService.js32
-rw-r--r--language-web/src/main/js/xtext/services/UpdateService.js159
-rw-r--r--language-web/src/main/js/xtext/services/ValidationService.js33
-rw-r--r--language-web/src/main/js/xtext/services/XtextService.js280
10 files changed, 0 insertions, 861 deletions
diff --git a/language-web/src/main/js/xtext/services/ContentAssistService.js b/language-web/src/main/js/xtext/services/ContentAssistService.js
deleted file mode 100644
index 1686570d..00000000
--- a/language-web/src/main/js/xtext/services/ContentAssistService.js
+++ /dev/null
@@ -1,132 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for content assist proposals. The proposals are returned as promise of
14 * a Deferred object.
15 */
16 function ContentAssistService(serviceUrl, resourceId, updateService) {
17 this.initialize(serviceUrl, 'assist', resourceId, updateService);
18 }
19
20 ContentAssistService.prototype = new XtextService();
21
22 ContentAssistService.prototype.invoke = function(editorContext, params, deferred) {
23 if (deferred === undefined) {
24 deferred = jQuery.Deferred();
25 }
26 var serverData = {
27 contentType: params.contentType
28 };
29 if (params.offset)
30 serverData.caretOffset = params.offset;
31 else
32 serverData.caretOffset = editorContext.getCaretOffset();
33 var selection = params.selection ? params.selection : editorContext.getSelection();
34 if (selection.start != serverData.caretOffset || selection.end != serverData.caretOffset) {
35 serverData.selectionStart = selection.start;
36 serverData.selectionEnd = selection.end;
37 }
38 var currentText;
39 var httpMethod = 'GET';
40 var onComplete = undefined;
41 var knownServerState = editorContext.getServerState();
42 if (params.sendFullText) {
43 serverData.fullText = editorContext.getText();
44 httpMethod = 'POST';
45 } else {
46 serverData.requiredStateId = knownServerState.stateId;
47 if (this._updateService) {
48 if (knownServerState.text === undefined || knownServerState.updateInProgress) {
49 var self = this;
50 this._updateService.addCompletionCallback(function() {
51 self.invoke(editorContext, params, deferred);
52 });
53 return deferred.promise();
54 }
55 knownServerState.updateInProgress = true;
56 onComplete = this._updateService.onComplete.bind(this._updateService);
57 currentText = editorContext.getText();
58 this._updateService.computeDelta(knownServerState.text, currentText, serverData);
59 if (serverData.deltaText !== undefined) {
60 httpMethod = 'POST';
61 }
62 }
63 }
64
65 var self = this;
66 self.sendRequest(editorContext, {
67 type: httpMethod,
68 data: serverData,
69
70 success: function(result) {
71 if (result.conflict) {
72 // The server has lost its session state and the resource is loaded from the server
73 if (self._increaseRecursionCount(editorContext)) {
74 if (onComplete) {
75 delete knownServerState.updateInProgress;
76 delete knownServerState.text;
77 delete knownServerState.stateId;
78 self._updateService.addCompletionCallback(function() {
79 self.invoke(editorContext, params, deferred);
80 });
81 self._updateService.invoke(editorContext, params);
82 } else {
83 var paramsCopy = {};
84 for (var p in params) {
85 if (params.hasOwnProperty(p))
86 paramsCopy[p] = params[p];
87 }
88 paramsCopy.sendFullText = true;
89 self.invoke(editorContext, paramsCopy, deferred);
90 }
91 } else {
92 deferred.reject(result.conflict);
93 }
94 return false;
95 }
96 if (onComplete && result.stateId !== undefined && result.stateId != editorContext.getServerState().stateId) {
97 var listeners = editorContext.updateServerState(currentText, result.stateId);
98 for (var i = 0; i < listeners.length; i++) {
99 self._updateService.addCompletionCallback(listeners[i], params);
100 }
101 }
102 deferred.resolve(result.entries);
103 },
104
105 error: function(xhr, textStatus, errorThrown) {
106 if (onComplete && xhr.status == 404 && !params.loadFromServer && knownServerState.text !== undefined) {
107 // The server has lost its session state and the resource is not loaded from the server
108 delete knownServerState.updateInProgress;
109 delete knownServerState.text;
110 delete knownServerState.stateId;
111 self._updateService.addCompletionCallback(function() {
112 self.invoke(editorContext, params, deferred);
113 });
114 self._updateService.invoke(editorContext, params);
115 return true;
116 }
117 deferred.reject(errorThrown);
118 },
119
120 complete: onComplete
121 }, !params.sendFullText);
122 var result = deferred.promise();
123 if (onComplete) {
124 result.always(function() {
125 knownServerState.updateInProgress = false;
126 });
127 }
128 return result;
129 };
130
131 return ContentAssistService;
132});
diff --git a/language-web/src/main/js/xtext/services/FormattingService.js b/language-web/src/main/js/xtext/services/FormattingService.js
deleted file mode 100644
index f59099ee..00000000
--- a/language-web/src/main/js/xtext/services/FormattingService.js
+++ /dev/null
@@ -1,52 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for formatting text.
14 */
15 function FormattingService(serviceUrl, resourceId, updateService) {
16 this.initialize(serviceUrl, 'format', resourceId, updateService);
17 };
18
19 FormattingService.prototype = new XtextService();
20
21 FormattingService.prototype._initServerData = function(serverData, editorContext, params) {
22 var selection = params.selection ? params.selection : editorContext.getSelection();
23 if (selection.end > selection.start) {
24 serverData.selectionStart = selection.start;
25 serverData.selectionEnd = selection.end;
26 }
27 return {
28 httpMethod: 'POST'
29 };
30 };
31
32 FormattingService.prototype._processResult = function(result, editorContext) {
33 // The text update may be asynchronous, so we have to compute the new text ourselves
34 var newText;
35 if (result.replaceRegion) {
36 var fullText = editorContext.getText();
37 var start = result.replaceRegion.offset;
38 var end = result.replaceRegion.offset + result.replaceRegion.length;
39 editorContext.setText(result.formattedText, start, end);
40 newText = fullText.substring(0, start) + result.formattedText + fullText.substring(end);
41 } else {
42 editorContext.setText(result.formattedText);
43 newText = result.formattedText;
44 }
45 var listeners = editorContext.updateServerState(newText, result.stateId);
46 for (var i = 0; i < listeners.length; i++) {
47 listeners[i]({});
48 }
49 };
50
51 return FormattingService;
52}); \ No newline at end of file
diff --git a/language-web/src/main/js/xtext/services/HighlightingService.js b/language-web/src/main/js/xtext/services/HighlightingService.js
deleted file mode 100644
index 5a5ac8ba..00000000
--- a/language-web/src/main/js/xtext/services/HighlightingService.js
+++ /dev/null
@@ -1,33 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for semantic highlighting.
14 */
15 function HighlightingService(serviceUrl, resourceId) {
16 this.initialize(serviceUrl, 'highlight', resourceId);
17 };
18
19 HighlightingService.prototype = new XtextService();
20
21 HighlightingService.prototype._checkPreconditions = function(editorContext, params) {
22 return this._state === undefined;
23 }
24
25 HighlightingService.prototype._onConflict = function(editorContext, cause) {
26 this.setState(undefined);
27 return {
28 suppressForcedUpdate: true
29 };
30 };
31
32 return HighlightingService;
33}); \ No newline at end of file
diff --git a/language-web/src/main/js/xtext/services/HoverService.js b/language-web/src/main/js/xtext/services/HoverService.js
deleted file mode 100644
index 03c5a52b..00000000
--- a/language-web/src/main/js/xtext/services/HoverService.js
+++ /dev/null
@@ -1,59 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for hover information.
14 */
15 function HoverService(serviceUrl, resourceId, updateService) {
16 this.initialize(serviceUrl, 'hover', resourceId, updateService);
17 };
18
19 HoverService.prototype = new XtextService();
20
21 HoverService.prototype._initServerData = function(serverData, editorContext, params) {
22 // In order to display hover info for a selected completion proposal while the content
23 // assist popup is shown, the selected proposal is passed as parameter
24 if (params.proposal && params.proposal.proposal)
25 serverData.proposal = params.proposal.proposal;
26 if (params.offset)
27 serverData.caretOffset = params.offset;
28 else
29 serverData.caretOffset = editorContext.getCaretOffset();
30 var selection = params.selection ? params.selection : editorContext.getSelection();
31 if (selection.start != serverData.caretOffset || selection.end != serverData.caretOffset) {
32 serverData.selectionStart = selection.start;
33 serverData.selectionEnd = selection.end;
34 }
35 };
36
37 HoverService.prototype._getSuccessCallback = function(editorContext, params, deferred) {
38 var delay = params.mouseHoverDelay;
39 if (!delay)
40 delay = 500;
41 var showTime = new Date().getTime() + delay;
42 return function(result) {
43 if (result.conflict || !result.title && !result.content) {
44 deferred.reject();
45 } else {
46 var remainingTimeout = Math.max(0, showTime - new Date().getTime());
47 setTimeout(function() {
48 if (!params.sendFullText && result.stateId !== undefined
49 && result.stateId != editorContext.getServerState().stateId)
50 deferred.reject();
51 else
52 deferred.resolve(result);
53 }, remainingTimeout);
54 }
55 };
56 };
57
58 return HoverService;
59}); \ No newline at end of file
diff --git a/language-web/src/main/js/xtext/services/LoadResourceService.js b/language-web/src/main/js/xtext/services/LoadResourceService.js
deleted file mode 100644
index b5a315c3..00000000
--- a/language-web/src/main/js/xtext/services/LoadResourceService.js
+++ /dev/null
@@ -1,42 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for loading resources. The resulting text is passed to the editor context.
14 */
15 function LoadResourceService(serviceUrl, resourceId, revert) {
16 this.initialize(serviceUrl, revert ? 'revert' : 'load', resourceId);
17 };
18
19 LoadResourceService.prototype = new XtextService();
20
21 LoadResourceService.prototype._initServerData = function(serverData, editorContext, params) {
22 return {
23 suppressContent: true,
24 httpMethod: this._serviceType == 'revert' ? 'POST' : 'GET'
25 };
26 };
27
28 LoadResourceService.prototype._getSuccessCallback = function(editorContext, params, deferred) {
29 return function(result) {
30 editorContext.setText(result.fullText);
31 editorContext.clearUndoStack();
32 editorContext.setDirty(result.dirty);
33 var listeners = editorContext.updateServerState(result.fullText, result.stateId);
34 for (var i = 0; i < listeners.length; i++) {
35 listeners[i](params);
36 }
37 deferred.resolve(result);
38 }
39 }
40
41 return LoadResourceService;
42}); \ No newline at end of file
diff --git a/language-web/src/main/js/xtext/services/OccurrencesService.js b/language-web/src/main/js/xtext/services/OccurrencesService.js
deleted file mode 100644
index 2e2d0b1a..00000000
--- a/language-web/src/main/js/xtext/services/OccurrencesService.js
+++ /dev/null
@@ -1,39 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for marking occurrences.
14 */
15 function OccurrencesService(serviceUrl, resourceId, updateService) {
16 this.initialize(serviceUrl, 'occurrences', resourceId, updateService);
17 };
18
19 OccurrencesService.prototype = new XtextService();
20
21 OccurrencesService.prototype._initServerData = function(serverData, editorContext, params) {
22 if (params.offset)
23 serverData.caretOffset = params.offset;
24 else
25 serverData.caretOffset = editorContext.getCaretOffset();
26 };
27
28 OccurrencesService.prototype._getSuccessCallback = function(editorContext, params, deferred) {
29 return function(result) {
30 if (result.conflict || !params.sendFullText && result.stateId !== undefined
31 && result.stateId != editorContext.getServerState().stateId)
32 deferred.reject();
33 else
34 deferred.resolve(result);
35 }
36 }
37
38 return OccurrencesService;
39}); \ No newline at end of file
diff --git a/language-web/src/main/js/xtext/services/SaveResourceService.js b/language-web/src/main/js/xtext/services/SaveResourceService.js
deleted file mode 100644
index 66cdaff5..00000000
--- a/language-web/src/main/js/xtext/services/SaveResourceService.js
+++ /dev/null
@@ -1,32 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for saving resources.
14 */
15 function SaveResourceService(serviceUrl, resourceId) {
16 this.initialize(serviceUrl, 'save', resourceId);
17 };
18
19 SaveResourceService.prototype = new XtextService();
20
21 SaveResourceService.prototype._initServerData = function(serverData, editorContext, params) {
22 return {
23 httpMethod: 'POST'
24 };
25 };
26
27 SaveResourceService.prototype._processResult = function(result, editorContext) {
28 editorContext.setDirty(false);
29 };
30
31 return SaveResourceService;
32}); \ No newline at end of file
diff --git a/language-web/src/main/js/xtext/services/UpdateService.js b/language-web/src/main/js/xtext/services/UpdateService.js
deleted file mode 100644
index b78d846d..00000000
--- a/language-web/src/main/js/xtext/services/UpdateService.js
+++ /dev/null
@@ -1,159 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for updating the server-side representation of a resource.
14 * This service only makes sense with a stateful server, where an update request is sent
15 * after each modification. This can greatly improve response times compared to the
16 * stateless alternative, where the full text content is sent with each service request.
17 */
18 function UpdateService(serviceUrl, resourceId) {
19 this.initialize(serviceUrl, 'update', resourceId, this);
20 this._completionCallbacks = [];
21 };
22
23 UpdateService.prototype = new XtextService();
24
25 /**
26 * Compute a delta between two versions of a text. If a difference is found, the result
27 * contains three properties:
28 * deltaText - the text to insert into s1
29 * deltaOffset - the text insertion offset
30 * deltaReplaceLength - the number of characters that shall be replaced by the inserted text
31 */
32 UpdateService.prototype.computeDelta = function(s1, s2, result) {
33 var start = 0, s1length = s1.length, s2length = s2.length;
34 while (start < s1length && start < s2length && s1.charCodeAt(start) === s2.charCodeAt(start)) {
35 start++;
36 }
37 if (start === s1length && start === s2length) {
38 return;
39 }
40 result.deltaOffset = start;
41 if (start === s1length) {
42 result.deltaText = s2.substring(start, s2length);
43 result.deltaReplaceLength = 0;
44 return;
45 } else if (start === s2length) {
46 result.deltaText = '';
47 result.deltaReplaceLength = s1length - start;
48 return;
49 }
50
51 var end1 = s1length - 1, end2 = s2length - 1;
52 while (end1 >= start && end2 >= start && s1.charCodeAt(end1) === s2.charCodeAt(end2)) {
53 end1--;
54 end2--;
55 }
56 result.deltaText = s2.substring(start, end2 + 1);
57 result.deltaReplaceLength = end1 - start + 1;
58 };
59
60 /**
61 * Invoke all completion callbacks and clear the list afterwards.
62 */
63 UpdateService.prototype.onComplete = function(xhr, textStatus) {
64 var callbacks = this._completionCallbacks;
65 this._completionCallbacks = [];
66 for (var i = 0; i < callbacks.length; i++) {
67 var callback = callbacks[i].callback;
68 var params = callbacks[i].params;
69 callback(params);
70 }
71 }
72
73 /**
74 * Add a callback to be invoked when the service call has completed.
75 */
76 UpdateService.prototype.addCompletionCallback = function(callback, params) {
77 this._completionCallbacks.push({callback: callback, params: params});
78 }
79
80 UpdateService.prototype.invoke = function(editorContext, params, deferred) {
81 if (deferred === undefined) {
82 deferred = jQuery.Deferred();
83 }
84 var knownServerState = editorContext.getServerState();
85 if (knownServerState.updateInProgress) {
86 var self = this;
87 this.addCompletionCallback(function() { self.invoke(editorContext, params, deferred) });
88 return deferred.promise();
89 }
90
91 var serverData = {
92 contentType: params.contentType
93 };
94 var currentText = editorContext.getText();
95 if (params.sendFullText || knownServerState.text === undefined) {
96 serverData.fullText = currentText;
97 } else {
98 this.computeDelta(knownServerState.text, currentText, serverData);
99 if (serverData.deltaText === undefined) {
100 if (params.forceUpdate) {
101 serverData.deltaText = '';
102 serverData.deltaOffset = editorContext.getCaretOffset();
103 serverData.deltaReplaceLength = 0;
104 } else {
105 deferred.resolve(knownServerState);
106 this.onComplete();
107 return deferred.promise();
108 }
109 }
110 serverData.requiredStateId = knownServerState.stateId;
111 }
112
113 knownServerState.updateInProgress = true;
114 var self = this;
115 self.sendRequest(editorContext, {
116 type: 'PUT',
117 data: serverData,
118
119 success: function(result) {
120 if (result.conflict) {
121 // The server has lost its session state and the resource is loaded from the server
122 if (knownServerState.text !== undefined) {
123 delete knownServerState.updateInProgress;
124 delete knownServerState.text;
125 delete knownServerState.stateId;
126 self.invoke(editorContext, params, deferred);
127 } else {
128 deferred.reject(result.conflict);
129 }
130 return false;
131 }
132 var listeners = editorContext.updateServerState(currentText, result.stateId);
133 for (var i = 0; i < listeners.length; i++) {
134 self.addCompletionCallback(listeners[i], params);
135 }
136 deferred.resolve(result);
137 },
138
139 error: function(xhr, textStatus, errorThrown) {
140 if (xhr.status == 404 && !params.loadFromServer && knownServerState.text !== undefined) {
141 // The server has lost its session state and the resource is not loaded from the server
142 delete knownServerState.updateInProgress;
143 delete knownServerState.text;
144 delete knownServerState.stateId;
145 self.invoke(editorContext, params, deferred);
146 return true;
147 }
148 deferred.reject(errorThrown);
149 },
150
151 complete: self.onComplete.bind(self)
152 }, true);
153 return deferred.promise().always(function() {
154 knownServerState.updateInProgress = false;
155 });
156 };
157
158 return UpdateService;
159}); \ No newline at end of file
diff --git a/language-web/src/main/js/xtext/services/ValidationService.js b/language-web/src/main/js/xtext/services/ValidationService.js
deleted file mode 100644
index 85c9953d..00000000
--- a/language-web/src/main/js/xtext/services/ValidationService.js
+++ /dev/null
@@ -1,33 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['xtext/services/XtextService', 'jquery'], function(XtextService, jQuery) {
11
12 /**
13 * Service class for validation.
14 */
15 function ValidationService(serviceUrl, resourceId) {
16 this.initialize(serviceUrl, 'validate', resourceId);
17 };
18
19 ValidationService.prototype = new XtextService();
20
21 ValidationService.prototype._checkPreconditions = function(editorContext, params) {
22 return this._state === undefined;
23 }
24
25 ValidationService.prototype._onConflict = function(editorContext, cause) {
26 this.setState(undefined);
27 return {
28 suppressForcedUpdate: true
29 };
30 };
31
32 return ValidationService;
33}); \ No newline at end of file
diff --git a/language-web/src/main/js/xtext/services/XtextService.js b/language-web/src/main/js/xtext/services/XtextService.js
deleted file mode 100644
index d3a4842f..00000000
--- a/language-web/src/main/js/xtext/services/XtextService.js
+++ /dev/null
@@ -1,280 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015, 2017 itemis AG (http://www.itemis.eu) and others.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-2.0.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10define(['jquery'], function(jQuery) {
11
12 var globalState = {};
13
14 /**
15 * Generic service implementation that can serve as superclass for specialized services.
16 */
17 function XtextService() {};
18
19 /**
20 * Initialize the request metadata for this service class. Two variants:
21 * - initialize(serviceUrl, serviceType, resourceId, updateService)
22 * - initialize(xtextServices, serviceType)
23 */
24 XtextService.prototype.initialize = function() {
25 this._serviceType = arguments[1];
26 if (typeof(arguments[0]) === 'string') {
27 this._requestUrl = arguments[0] + '/' + this._serviceType;
28 var resourceId = arguments[2];
29 if (resourceId)
30 this._encodedResourceId = encodeURIComponent(resourceId);
31 this._updateService = arguments[3];
32 } else {
33 var xtextServices = arguments[0];
34 if (xtextServices.options) {
35 this._requestUrl = xtextServices.options.serviceUrl + '/' + this._serviceType;
36 var resourceId = xtextServices.options.resourceId;
37 if (resourceId)
38 this._encodedResourceId = encodeURIComponent(resourceId);
39 }
40 this._updateService = xtextServices.updateService;
41 }
42 }
43
44 XtextService.prototype.setState = function(state) {
45 this._state = state;
46 }
47
48 /**
49 * Invoke the service with default service behavior.
50 */
51 XtextService.prototype.invoke = function(editorContext, params, deferred, callbacks) {
52 if (deferred === undefined) {
53 deferred = jQuery.Deferred();
54 }
55 if (jQuery.isFunction(this._checkPreconditions) && !this._checkPreconditions(editorContext, params)) {
56 deferred.reject();
57 return deferred.promise();
58 }
59 var serverData = {
60 contentType: params.contentType
61 };
62 var initResult;
63 if (jQuery.isFunction(this._initServerData))
64 initResult = this._initServerData(serverData, editorContext, params);
65 var httpMethod = 'GET';
66 if (initResult && initResult.httpMethod)
67 httpMethod = initResult.httpMethod;
68 var self = this;
69 if (!(initResult && initResult.suppressContent)) {
70 if (params.sendFullText) {
71 serverData.fullText = editorContext.getText();
72 httpMethod = 'POST';
73 } else {
74 var knownServerState = editorContext.getServerState();
75 if (knownServerState.updateInProgress) {
76 if (self._updateService) {
77 self._updateService.addCompletionCallback(function() {
78 self.invoke(editorContext, params, deferred);
79 });
80 } else {
81 deferred.reject();
82 }
83 return deferred.promise();
84 }
85 if (knownServerState.stateId !== undefined) {
86 serverData.requiredStateId = knownServerState.stateId;
87 }
88 }
89 }
90
91 var onSuccess;
92 if (jQuery.isFunction(this._getSuccessCallback)) {
93 onSuccess = this._getSuccessCallback(editorContext, params, deferred);
94 } else {
95 onSuccess = function(result) {
96 if (result.conflict) {
97 if (self._increaseRecursionCount(editorContext)) {
98 var onConflictResult;
99 if (jQuery.isFunction(self._onConflict)) {
100 onConflictResult = self._onConflict(editorContext, result.conflict);
101 }
102 if (!(onConflictResult && onConflictResult.suppressForcedUpdate) && !params.sendFullText
103 && result.conflict == 'invalidStateId' && self._updateService) {
104 self._updateService.addCompletionCallback(function() {
105 self.invoke(editorContext, params, deferred);
106 });
107 var knownServerState = editorContext.getServerState();
108 delete knownServerState.stateId;
109 delete knownServerState.text;
110 self._updateService.invoke(editorContext, params);
111 } else {
112 self.invoke(editorContext, params, deferred);
113 }
114 } else {
115 deferred.reject();
116 }
117 return false;
118 }
119 if (jQuery.isFunction(self._processResult)) {
120 var processedResult = self._processResult(result, editorContext);
121 if (processedResult) {
122 deferred.resolve(processedResult);
123 return true;
124 }
125 }
126 deferred.resolve(result);
127 };
128 }
129
130 var onError = function(xhr, textStatus, errorThrown) {
131 if (xhr.status == 404 && !params.loadFromServer && self._increaseRecursionCount(editorContext)) {
132 var onConflictResult;
133 if (jQuery.isFunction(self._onConflict)) {
134 onConflictResult = self._onConflict(editorContext, errorThrown);
135 }
136 var knownServerState = editorContext.getServerState();
137 if (!(onConflictResult && onConflictResult.suppressForcedUpdate)
138 && knownServerState.text !== undefined && self._updateService) {
139 self._updateService.addCompletionCallback(function() {
140 self.invoke(editorContext, params, deferred);
141 });
142 delete knownServerState.stateId;
143 delete knownServerState.text;
144 self._updateService.invoke(editorContext, params);
145 return true;
146 }
147 }
148 deferred.reject(errorThrown);
149 }
150
151 self.sendRequest(editorContext, {
152 type: httpMethod,
153 data: serverData,
154 success: onSuccess,
155 error: onError
156 }, !params.sendFullText);
157 return deferred.promise().always(function() {
158 self._recursionCount = undefined;
159 });
160 }
161
162 /**
163 * Send an HTTP request to invoke the service.
164 */
165 XtextService.prototype.sendRequest = function(editorContext, settings, needsSession) {
166 var self = this;
167 self.setState('started');
168 var corsEnabled = editorContext.xtextServices.options['enableCors'];
169 if(corsEnabled) {
170 settings.crossDomain = true;
171 settings.xhrFields = {withCredentials: true};
172 }
173 var onSuccess = settings.success;
174 settings.success = function(result) {
175 var accepted = true;
176 if (jQuery.isFunction(onSuccess)) {
177 accepted = onSuccess(result);
178 }
179 if (accepted || accepted === undefined) {
180 self.setState('finished');
181 if (editorContext.xtextServices) {
182 var successListeners = editorContext.xtextServices.successListeners;
183 if (successListeners) {
184 for (var i = 0; i < successListeners.length; i++) {
185 var listener = successListeners[i];
186 if (jQuery.isFunction(listener)) {
187 listener(self._serviceType, result);
188 }
189 }
190 }
191 }
192 }
193 };
194
195 var onError = settings.error;
196 settings.error = function(xhr, textStatus, errorThrown) {
197 var resolved = false;
198 if (jQuery.isFunction(onError)) {
199 resolved = onError(xhr, textStatus, errorThrown);
200 }
201 if (!resolved) {
202 self.setState(undefined);
203 self._reportError(editorContext, textStatus, errorThrown, xhr);
204 }
205 };
206
207 settings.async = true;
208 var requestUrl = self._requestUrl;
209 if (!settings.data.resource && self._encodedResourceId) {
210 if (requestUrl.indexOf('?') >= 0)
211 requestUrl += '&resource=' + self._encodedResourceId;
212 else
213 requestUrl += '?resource=' + self._encodedResourceId;
214 }
215
216 if (needsSession && globalState._initPending) {
217 // We have to wait until the initial request has finished to make sure the client has
218 // received a valid session id
219 if (!globalState._waitingRequests)
220 globalState._waitingRequests = [];
221 globalState._waitingRequests.push({requestUrl: requestUrl, settings: settings});
222 } else {
223 if (needsSession && !globalState._initDone) {
224 globalState._initPending = true;
225 var onComplete = settings.complete;
226 settings.complete = function(xhr, textStatus) {
227 if (jQuery.isFunction(onComplete)) {
228 onComplete(xhr, textStatus);
229 }
230 delete globalState._initPending;
231 globalState._initDone = true;
232 if (globalState._waitingRequests) {
233 for (var i = 0; i < globalState._waitingRequests.length; i++) {
234 var request = globalState._waitingRequests[i];
235 jQuery.ajax(request.requestUrl, request.settings);
236 }
237 delete globalState._waitingRequests;
238 }
239 }
240 }
241 jQuery.ajax(requestUrl, settings);
242 }
243 }
244
245 /**
246 * Use this in case of a conflict before retrying the service invocation. If the number
247 * of retries exceeds the limit, an error is reported and the function returns false.
248 */
249 XtextService.prototype._increaseRecursionCount = function(editorContext) {
250 if (this._recursionCount === undefined)
251 this._recursionCount = 1;
252 else
253 this._recursionCount++;
254
255 if (this._recursionCount >= 10) {
256 this._reportError(editorContext, 'warning', 'Xtext service request failed after 10 attempts.', {});
257 return false;
258 }
259 return true;
260 },
261
262 /**
263 * Report an error to the listeners.
264 */
265 XtextService.prototype._reportError = function(editorContext, severity, message, requestData) {
266 if (editorContext.xtextServices) {
267 var errorListeners = editorContext.xtextServices.errorListeners;
268 if (errorListeners) {
269 for (var i = 0; i < errorListeners.length; i++) {
270 var listener = errorListeners[i];
271 if (jQuery.isFunction(listener)) {
272 listener(this._serviceType, severity, message, requestData);
273 }
274 }
275 }
276 }
277 }
278
279 return XtextService;
280});