##// END OF EJS Templates
js: migrate to polymer 3.x
ergo -
r3173:b3d618e2 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,50 +1,60 b''
1 {
1 {
2 "name": "rhodecode-enterprise",
2 "name": "rhodecode-enterprise",
3 "version": "1.0.0",
3 "version": "1.0.0",
4 "private": true,
4 "private": true,
5 "description" : "RhodeCode JS packaged",
5 "description" : "RhodeCode JS packaged",
6 "license": "SEE LICENSE IN LICENSE.txt",
6 "license": "SEE LICENSE IN LICENSE.txt",
7 "repository" : {
7 "repository" : {
8 "type" : "hg",
8 "type" : "hg",
9 "url" : "https://code.rhodecode.com/rhodecode-enterprise-ce"
9 "url" : "https://code.rhodecode.com/rhodecode-enterprise-ce"
10 },
10 },
11 "devDependencies": {
11 "devDependencies": {
12 "appenlight-client": "git+https://git@github.com/AppEnlight/appenlight-client-js.git#0.5.1",
12 "appenlight-client": "git+https://git@github.com/AppEnlight/appenlight-client-js.git#0.5.1",
13 "bower": "^1.8.4",
13 "bower": "^1.8.4",
14 "clipboard": "^2.0.1",
14 "clipboard": "^2.0.1",
15 "exports-loader": "^0.6.4",
15 "exports-loader": "^0.6.4",
16 "favico.js": "^0.3.10",
16 "favico.js": "^0.3.10",
17 "grunt": "^0.4.5",
17 "grunt": "^0.4.5",
18 "grunt-cli": "^1.3.1",
18 "grunt-cli": "^1.3.1",
19 "grunt-contrib-concat": "^0.5.1",
19 "grunt-contrib-concat": "^0.5.1",
20 "grunt-contrib-copy": "^1.0.0",
20 "grunt-contrib-copy": "^1.0.0",
21 "grunt-contrib-jshint": "^0.12.0",
21 "grunt-contrib-jshint": "^0.12.0",
22 "grunt-contrib-less": "^1.1.0",
22 "grunt-contrib-less": "^1.1.0",
23 "grunt-contrib-watch": "^0.6.1",
23 "grunt-contrib-watch": "^0.6.1",
24 "grunt-webpack": "^3.1.3",
24 "grunt-webpack": "^3.1.3",
25 "jquery": "1.11.3",
25 "jquery": "1.11.3",
26 "jshint": "^2.9.1-rc3",
26 "jshint": "^2.9.1-rc3",
27 "moment": "^2.18.1",
27 "moment": "^2.18.1",
28 "mousetrap": "^1.6.1",
28 "mousetrap": "^1.6.1",
29 "qrious": "^4.0.2",
29 "qrious": "^4.0.2",
30 "sticky-sidebar": "3.3.1",
30 "sticky-sidebar": "3.3.1",
31 "waypoints": "4.0.1",
31 "waypoints": "4.0.1",
32 "webpack": "4.23.1",
32 "webpack": "4.23.1",
33 "webpack-cli": "3.1.2",
33 "webpack-cli": "3.1.2",
34 "babel-core": "^6.26.3",
34 "babel-core": "^6.26.3",
35 "babel-loader": "^7.1.2",
35 "babel-loader": "^7.1.2",
36 "babel-plugin-transform-object-rest-spread": "^6.26.0",
36 "babel-plugin-transform-object-rest-spread": "^6.26.0",
37 "babel-preset-env": "^1.6.0",
37 "babel-preset-env": "^1.6.0",
38 "copy-webpack-plugin": "^4.4.2",
38 "copy-webpack-plugin": "^4.4.2",
39 "css-loader": "^0.28.11",
39 "css-loader": "^0.28.11",
40 "exports-loader": "^0.6.4",
40 "exports-loader": "^0.6.4",
41 "html-loader": "^0.4.4",
41 "html-loader": "^0.4.4",
42 "html-webpack-plugin": "^3.2.0",
42 "html-webpack-plugin": "^3.2.0",
43 "imports-loader": "^0.7.1",
43 "imports-loader": "^0.7.1",
44 "polymer-webpack-loader": "^2.0.1",
44 "polymer-webpack-loader": "^2.0.1",
45 "style-loader": "^0.21.0",
45 "style-loader": "^0.21.0",
46 "webpack-uglify-js-plugin": "^1.1.9",
46 "webpack-uglify-js-plugin": "^1.1.9",
47 "raw-loader": "1.0.0-beta.0",
47 "raw-loader": "1.0.0-beta.0",
48 "ts-loader": "^1.3.3"
48 "ts-loader": "^1.3.3",
49 "@webcomponents/webcomponentsjs": "^2.0.0",
50 "@polymer/polymer": "^3.0.0",
51 "@polymer/paper-button": "^3.0.0",
52 "@polymer/paper-spinner": "^3.0.0",
53 "@polymer/paper-tooltip": "^3.0.0",
54 "@polymer/paper-toast": "^3.0.0",
55 "@polymer/paper-toggle-button": "^3.0.0",
56 "@polymer/iron-ajax": "^3.0.0",
57 "@polymer/iron-autogrow-textarea": "^3.0.0",
58 "@polymer/iron-a11y-keys": "^3.0.0"
49 }
59 }
50 }
60 }
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
This diff has been collapsed as it changes many lines, (1003 lines changed) Show them Hide them
@@ -1,610 +1,577 b''
1 <link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
1 import {Polymer, html} from '@polymer/polymer/polymer-legacy';
2 <link rel="import" href="../../../../../../bower_components/iron-ajax/iron-ajax.html">
2 import '@polymer/iron-ajax/iron-ajax.js';
3
4 <!--
5
6 `<channelstream-connection>` allows you to connect and interact with channelstream server
7 abstracting websocket/long-polling connections from you.
8
9 In typical use, just slap some `<channelstream-connection>` at the top of your body:
10
3
11 <body>
4 const elemTemplate = html`
12 <channelstream-connection
5 <iron-ajax
13 username="{{user.username}}"
14 connect-url="http://127.0.0.1:8000/demo/connect"
15 disconnect-url="http://127.0.0.1:8000/disconnect"
16 subscribe-url="http://127.0.0.1:8000/demo/subscribe"
17 message-url="http://127.0.0.1:8000/demo/message"
18 long-poll-url="http://127.0.0.1:8000/listen"
19 websocket-url="http://127.0.0.1:8000/ws"
20 channels-url='["channel1", "channel2"]' />
21
22 Then you can do `channelstreamElem.connect()` to kick off your connection.
23 This element also handles automatic reconnections.
24
25 ## Default handlers
26
27 By default element has a listener attached that will fire `startListening()` handler on `channelstream-connected` event.
28
29 By default element has a listener attached that will fire `retryConnection()` handler on `channelstream-connect-error` event,
30 this handler will forever try to re-establish connection to the server incrementing intervals between retries up to 1 minute.
31
32 -->
33 <dom-module id="channelstream-connection">
34 <template>
35
36 <iron-ajax
37 id="ajaxConnect"
6 id="ajaxConnect"
38 url=""
7 url=""
39 handle-as="json"
8 handle-as="json"
40 method="post"
9 method="post"
41 content-type="application/json"
10 content-type="application/json"
42 loading="{{loadingConnect}}"
11 loading="{{loadingConnect}}"
43 last-response="{{connectLastResponse}}"
12 last-response="{{connectLastResponse}}"
44 on-response="_handleConnect"
13 on-response="_handleConnect"
45 on-error="_handleConnectError"
14 on-error="_handleConnectError"
46 debounce-duration="100"></iron-ajax>
15 debounce-duration="100"></iron-ajax>
47
16
48 <iron-ajax
17 <iron-ajax
49 id="ajaxDisconnect"
18 id="ajaxDisconnect"
50 url=""
19 url=""
51 handle-as="json"
20 handle-as="json"
52 method="post"
21 method="post"
53 content-type="application/json"
22 content-type="application/json"
54 loading="{{loadingDisconnect}}"
23 loading="{{loadingDisconnect}}"
55 last-response="{{_disconnectLastResponse}}"
24 last-response="{{_disconnectLastResponse}}"
56 on-response="_handleDisconnect"
25 on-response="_handleDisconnect"
57 debounce-duration="100"></iron-ajax>
26 debounce-duration="100"></iron-ajax>
58
27
59 <iron-ajax
28 <iron-ajax
60 id="ajaxSubscribe"
29 id="ajaxSubscribe"
61 url=""
30 url=""
62 handle-as="json"
31 handle-as="json"
63 method="post"
32 method="post"
64 content-type="application/json"
33 content-type="application/json"
65 loading="{{loadingSubscribe}}"
34 loading="{{loadingSubscribe}}"
66 last-response="{{subscribeLastResponse}}"
35 last-response="{{subscribeLastResponse}}"
67 on-response="_handleSubscribe"
36 on-response="_handleSubscribe"
68 debounce-duration="100"></iron-ajax>
37 debounce-duration="100"></iron-ajax>
69
38
70 <iron-ajax
39 <iron-ajax
71 id="ajaxUnsubscribe"
40 id="ajaxUnsubscribe"
72 url=""
41 url=""
73 handle-as="json"
42 handle-as="json"
74 method="post"
43 method="post"
75 content-type="application/json"
44 content-type="application/json"
76 loading="{{loadingUnsubscribe}}"
45 loading="{{loadingUnsubscribe}}"
77 last-response="{{unsubscribeLastResponse}}"
46 last-response="{{unsubscribeLastResponse}}"
78 on-response="_handleUnsubscribe"
47 on-response="_handleUnsubscribe"
79 debounce-duration="100"></iron-ajax>
48 debounce-duration="100"></iron-ajax>
80
49
81 <iron-ajax
50 <iron-ajax
82 id="ajaxMessage"
51 id="ajaxMessage"
83 url=""
52 url=""
84 handle-as="json"
53 handle-as="json"
85 method="post"
54 method="post"
86 content-type="application/json"
55 content-type="application/json"
87 loading="{{loadingMessage}}"
56 loading="{{loadingMessage}}"
88 last-response="{{messageLastResponse}}"
57 last-response="{{messageLastResponse}}"
89 on-response="_handleMessage"
58 on-response="_handleMessage"
90 on-error="_handleMessageError"
59 on-error="_handleMessageError"
91 debounce-duration="100"></iron-ajax>
60 debounce-duration="100"></iron-ajax>
92
61
93 <iron-ajax
62 <iron-ajax
94 id="ajaxListen"
63 id="ajaxListen"
95 url=""
64 url=""
96 handle-as="text"
65 handle-as="text"
97 loading="{{loadingListen}}"
66 loading="{{loadingListen}}"
98 last-response="{{listenLastResponse}}"
67 last-response="{{listenLastResponse}}"
99 on-request="_handleListenOpen"
68 on-request="_handleListenOpen"
100 on-error="_handleListenError"
69 on-error="_handleListenError"
101 on-response="_handleListenMessageEvent"
70 on-response="_handleListenMessageEvent"
102 debounce-duration="100"></iron-ajax>
71 debounce-duration="100"></iron-ajax>
103
72 `
104 </template>
105 <script>
106 Polymer({
107 is: 'channelstream-connection',
108
73
109 /**
74 Polymer({
110 * Fired when `channels` array changes.
75 is: 'channelstream-connection',
111 *
112 * @event channelstream-channels-changed
113 */
114
76
115 /**
77 _template: elemTemplate,
116 * Fired when `connect()` method succeeds.
117 *
118 * @event channelstream-connected
119 */
120
78
121 /**
79 /**
122 * Fired when `connect` fails.
80 * Fired when `channels` array changes.
123 *
81 *
124 * @event channelstream-connect-error
82 * @event channelstream-channels-changed
125 */
83 */
126
127 /**
128 * Fired when `disconnect()` succeeds.
129 *
130 * @event channelstream-disconnected
131 */
132
84
133 /**
85 /**
134 * Fired when `message()` succeeds.
86 * Fired when `connect()` method succeeds.
135 *
87 *
136 * @event channelstream-message-sent
88 * @event channelstream-connected
137 */
89 */
138
139 /**
140 * Fired when `message()` fails.
141 *
142 * @event channelstream-message-error
143 */
144
90
145 /**
91 /**
146 * Fired when `subscribe()` succeeds.
92 * Fired when `connect` fails.
147 *
93 *
148 * @event channelstream-subscribed
94 * @event channelstream-connect-error
149 */
95 */
150
151 /**
152 * Fired when `subscribe()` fails.
153 *
154 * @event channelstream-subscribe-error
155 */
156
96
157 /**
97 /**
158 * Fired when `unsubscribe()` succeeds.
98 * Fired when `disconnect()` succeeds.
159 *
99 *
160 * @event channelstream-unsubscribed
100 * @event channelstream-disconnected
161 */
101 */
162
163 /**
164 * Fired when `unsubscribe()` fails.
165 *
166 * @event channelstream-unsubscribe-error
167 */
168
102
169 /**
103 /**
170 * Fired when listening connection receives a message.
104 * Fired when `message()` succeeds.
171 *
105 *
172 * @event channelstream-listen-message
106 * @event channelstream-message-sent
173 */
107 */
174
175 /**
176 * Fired when listening connection is opened.
177 *
178 * @event channelstream-listen-opened
179 */
180
108
181 /**
109 /**
182 * Fired when listening connection is closed.
110 * Fired when `message()` fails.
183 *
111 *
184 * @event channelstream-listen-closed
112 * @event channelstream-message-error
185 */
113 */
186
114
187 /**
115 /**
188 * Fired when listening connection suffers an error.
116 * Fired when `subscribe()` succeeds.
189 *
117 *
190 * @event channelstream-listen-error
118 * @event channelstream-subscribed
191 */
119 */
192
120
193 properties: {
121 /**
194 isReady: Boolean,
122 * Fired when `subscribe()` fails.
195 /** List of channels user should be subscribed to. */
123 *
196 channels: {
124 * @event channelstream-subscribe-error
197 type: Array,
125 */
198 value: function () {
126
199 return []
127 /**
200 },
128 * Fired when `unsubscribe()` succeeds.
201 notify: true
129 *
202 },
130 * @event channelstream-unsubscribed
203 /** Username of connecting user. */
131 */
204 username: {
132
205 type: String,
133 /**
206 value: 'Anonymous',
134 * Fired when `unsubscribe()` fails.
207 reflectToAttribute: true
135 *
208 },
136 * @event channelstream-unsubscribe-error
209 /** Connection identifier. */
137 */
210 connectionId: {
138
211 type: String,
139 /**
212 reflectToAttribute: true
140 * Fired when listening connection receives a message.
213 },
141 *
214 /** Websocket instance. */
142 * @event channelstream-listen-message
215 websocket: {
143 */
216 type: Object,
217 value: null
218 },
219 /** Websocket connection url. */
220 websocketUrl: {
221 type: String,
222 value: ''
223 },
224 /** URL used in `connect()`. */
225 connectUrl: {
226 type: String,
227 value: ''
228 },
229 /** URL used in `disconnect()`. */
230 disconnectUrl: {
231 type: String,
232 value: ''
233 },
234 /** URL used in `subscribe()`. */
235 subscribeUrl: {
236 type: String,
237 value: ''
238 },
239 /** URL used in `unsubscribe()`. */
240 unsubscribeUrl: {
241 type: String,
242 value: ''
243 },
244 /** URL used in `message()`. */
245 messageUrl: {
246 type: String,
247 value: ''
248 },
249 /** Long-polling connection url. */
250 longPollUrl: {
251 type: String,
252 value: ''
253 },
254 /** Long-polling connection url. */
255 shouldReconnect: {
256 type: Boolean,
257 value: true
258 },
259 /** Should send heartbeats. */
260 heartbeats: {
261 type: Boolean,
262 value: true
263 },
264 /** How much should every retry interval increase (in milliseconds) */
265 increaseBounceIv: {
266 type: Number,
267 value: 2000
268 },
269 _currentBounceIv: {
270 type: Number,
271 reflectToAttribute: true,
272 value: 0
273 },
274 /** Should use websockets or long-polling by default */
275 useWebsocket: {
276 type: Boolean,
277 reflectToAttribute: true,
278 value: true
279 },
280 connected: {
281 type: Boolean,
282 reflectToAttribute: true,
283 value: false
284 }
285 },
286
144
287 observers: [
145 /**
288 '_handleChannelsChange(channels.splices)'
146 * Fired when listening connection is opened.
289 ],
147 *
148 * @event channelstream-listen-opened
149 */
290
150
291 listeners: {
151 /**
292 'channelstream-connected': 'startListening',
152 * Fired when listening connection is closed.
293 'channelstream-connect-error': 'retryConnection',
153 *
294 },
154 * @event channelstream-listen-closed
155 */
295
156
296 /**
157 /**
297 * Mutators hold functions that you can set locally to change the data
158 * Fired when listening connection suffers an error.
298 * that the client is sending to all endpoints
159 *
299 * you can call it like `elem.mutators('connect', yourFunc())`
160 * @event channelstream-listen-error
300 * mutators will be executed in order they were pushed onto arrays
161 */
301 *
302 */
303 mutators: {
304 connect: function () {
305 return []
306 }(),
307 message: function () {
308 return []
309 }(),
310 subscribe: function () {
311 return []
312 }(),
313 unsubscribe: function () {
314 return []
315 }(),
316 disconnect: function () {
317 return []
318 }()
319 },
320 ready: function () {
321 this.isReady = true;
322 },
323
162
324 /**
163 properties: {
325 * Connects user and fetches connection id from the server.
164 isReady: Boolean,
326 *
165 /** List of channels user should be subscribed to. */
327 */
166 channels: {
328 connect: function () {
167 type: Array,
329 var request = this.$['ajaxConnect'];
168 value: function () {
330 request.url = this.connectUrl;
169 return []
331 request.body = {
332 username: this.username,
333 channels: this.channels
334 };
335 for (var i = 0; i < this.mutators.connect.length; i++) {
336 this.mutators.connect[i](request);
337 }
338 request.generateRequest()
339 },
340 /**
341 * Overwrite with custom function that will
342 */
343 addMutator: function (type, func) {
344 this.mutators[type].push(func);
345 },
170 },
346 /**
171 notify: true
347 * Subscribes user to channels.
172 },
348 *
173 /** Username of connecting user. */
349 */
174 username: {
350 subscribe: function (channels) {
175 type: String,
351 var request = this.$['ajaxSubscribe'];
176 value: 'Anonymous',
352 request.url = this.subscribeUrl;
177 reflectToAttribute: true
353 request.body = {
178 },
354 channels: channels,
179 /** Connection identifier. */
355 conn_id: this.connectionId
180 connectionId: {
356 };
181 type: String,
357 for (var i = 0; i < this.mutators.subscribe.length; i++) {
182 reflectToAttribute: true
358 this.mutators.subscribe[i](request);
183 },
359 }
184 /** Websocket instance. */
360 if (request.body.channels.length) {
185 websocket: {
361 request.generateRequest();
186 type: Object,
362 }
187 value: null
363 },
188 },
364 /**
189 /** Websocket connection url. */
365 * Unsubscribes user from channels.
190 websocketUrl: {
366 *
191 type: String,
367 */
192 value: ''
368 unsubscribe: function (unsubscribe) {
193 },
369 var request = this.$['ajaxUnsubscribe'];
194 /** URL used in `connect()`. */
195 connectUrl: {
196 type: String,
197 value: ''
198 },
199 /** URL used in `disconnect()`. */
200 disconnectUrl: {
201 type: String,
202 value: ''
203 },
204 /** URL used in `subscribe()`. */
205 subscribeUrl: {
206 type: String,
207 value: ''
208 },
209 /** URL used in `unsubscribe()`. */
210 unsubscribeUrl: {
211 type: String,
212 value: ''
213 },
214 /** URL used in `message()`. */
215 messageUrl: {
216 type: String,
217 value: ''
218 },
219 /** Long-polling connection url. */
220 longPollUrl: {
221 type: String,
222 value: ''
223 },
224 /** Long-polling connection url. */
225 shouldReconnect: {
226 type: Boolean,
227 value: true
228 },
229 /** Should send heartbeats. */
230 heartbeats: {
231 type: Boolean,
232 value: true
233 },
234 /** How much should every retry interval increase (in milliseconds) */
235 increaseBounceIv: {
236 type: Number,
237 value: 2000
238 },
239 _currentBounceIv: {
240 type: Number,
241 reflectToAttribute: true,
242 value: 0
243 },
244 /** Should use websockets or long-polling by default */
245 useWebsocket: {
246 type: Boolean,
247 reflectToAttribute: true,
248 value: true
249 },
250 connected: {
251 type: Boolean,
252 reflectToAttribute: true,
253 value: false
254 }
255 },
370
256
371 request.url = this.unsubscribeUrl;
257 observers: [
372 request.body = {
258 '_handleChannelsChange(channels.splices)'
373 channels: unsubscribe,
259 ],
374 conn_id: this.connectionId
375 };
376 for (var i = 0; i < this.mutators.unsubscribe.length; i++) {
377 this.mutators.unsubscribe[i](request);
378 }
379 request.generateRequest()
380 },
381
260
382 /**
261 listeners: {
383 * calculates list of channels we should add user to based on difference
262 'channelstream-connected': 'startListening',
384 * between channels property and passed channel list
263 'channelstream-connect-error': 'retryConnection',
385 */
264 },
386 calculateSubscribe: function (channels) {
265
387 var currentlySubscribed = this.channels;
266 /**
388 var toSubscribe = [];
267 * Mutators hold functions that you can set locally to change the data
389 for (var i = 0; i < channels.length; i++) {
268 * that the client is sending to all endpoints
390 if (currentlySubscribed.indexOf(channels[i]) === -1) {
269 * you can call it like `elem.mutators('connect', yourFunc())`
391 toSubscribe.push(channels[i]);
270 * mutators will be executed in order they were pushed onto arrays
392 }
271 *
393 }
272 */
394 return toSubscribe
273 mutators: {
395 },
274 connect: function () {
396 /**
275 return []
397 * calculates list of channels we should remove user from based difference
276 }(),
398 * between channels property and passed channel list
277 message: function () {
399 */
278 return []
400 calculateUnsubscribe: function (channels) {
279 }(),
401 var currentlySubscribed = this.channels;
280 subscribe: function () {
402 var toUnsubscribe = [];
281 return []
403 for (var i = 0; i < channels.length; i++) {
282 }(),
404 if (currentlySubscribed.indexOf(channels[i]) !== -1) {
283 unsubscribe: function () {
405 toUnsubscribe.push(channels[i]);
284 return []
406 }
285 }(),
407 }
286 disconnect: function () {
408 return toUnsubscribe
287 return []
409 },
288 }()
410 /**
289 },
411 * Marks the connection as expired.
290 ready: function () {
412 *
291 this.isReady = true;
413 */
292 },
414 disconnect: function () {
293
415 var request = this.$['ajaxDisconnect'];
294 /**
416 request.url = this.disconnectUrl;
295 * Connects user and fetches connection id from the server.
417 request.params = {
296 *
418 conn_id: this.connectionId
297 */
419 };
298 connect: function () {
420 for (var i = 0; i < this.mutators.disconnect.length; i++) {
299 var request = this.$['ajaxConnect'];
421 this.mutators.disconnect[i](request);
300 request.url = this.connectUrl;
422 }
301 request.body = {
423 // mark connection as expired
302 username: this.username,
424 request.generateRequest();
303 channels: this.channels
425 // disconnect existing connection
304 };
426 this.closeConnection();
305 for (var i = 0; i < this.mutators.connect.length; i++) {
427 },
306 this.mutators.connect[i](request);
307 }
308 request.generateRequest()
309 },
310 /**
311 * Overwrite with custom function that will
312 */
313 addMutator: function (type, func) {
314 this.mutators[type].push(func);
315 },
316 /**
317 * Subscribes user to channels.
318 *
319 */
320 subscribe: function (channels) {
321 var request = this.$['ajaxSubscribe'];
322 request.url = this.subscribeUrl;
323 request.body = {
324 channels: channels,
325 conn_id: this.connectionId
326 };
327 for (var i = 0; i < this.mutators.subscribe.length; i++) {
328 this.mutators.subscribe[i](request);
329 }
330 if (request.body.channels.length) {
331 request.generateRequest();
332 }
333 },
334 /**
335 * Unsubscribes user from channels.
336 *
337 */
338 unsubscribe: function (unsubscribe) {
339 var request = this.$['ajaxUnsubscribe'];
340
341 request.url = this.unsubscribeUrl;
342 request.body = {
343 channels: unsubscribe,
344 conn_id: this.connectionId
345 };
346 for (var i = 0; i < this.mutators.unsubscribe.length; i++) {
347 this.mutators.unsubscribe[i](request);
348 }
349 request.generateRequest()
350 },
428
351
429 /**
352 /**
430 * Sends a message to the server.
353 * calculates list of channels we should add user to based on difference
431 *
354 * between channels property and passed channel list
432 */
355 */
433 message: function (message) {
356 calculateSubscribe: function (channels) {
434 var request = this.$['ajaxMessage'];
357 var currentlySubscribed = this.channels;
435 request.url = this.messageUrl;
358 var toSubscribe = [];
436 request.body = message;
359 for (var i = 0; i < channels.length; i++) {
437 for (var i = 0; i < this.mutators.message.length; i++) {
360 if (currentlySubscribed.indexOf(channels[i]) === -1) {
438 this.mutators.message[i](request)
361 toSubscribe.push(channels[i]);
439 }
362 }
440 request.generateRequest();
363 }
441 },
364 return toSubscribe
442 /**
365 },
443 * Opens "long lived" (websocket/longpoll) connection to the channelstream server.
366 /**
444 *
367 * calculates list of channels we should remove user from based difference
445 */
368 * between channels property and passed channel list
446 startListening: function (event) {
369 */
447 this.fire('start-listening', {});
370 calculateUnsubscribe: function (channels) {
448 if (this.useWebsocket) {
371 var currentlySubscribed = this.channels;
449 this.useWebsocket = window.WebSocket ? true : false;
372 var toUnsubscribe = [];
450 }
373 for (var i = 0; i < channels.length; i++) {
451 if (this.useWebsocket) {
374 if (currentlySubscribed.indexOf(channels[i]) !== -1) {
452 this.openWebsocket();
375 toUnsubscribe.push(channels[i]);
453 }
376 }
454 else {
377 }
455 this.openLongPoll();
378 return toUnsubscribe
456 }
379 },
457 },
380 /**
458 /**
381 * Marks the connection as expired.
459 * Opens websocket connection.
382 *
460 *
383 */
461 */
384 disconnect: function () {
462 openWebsocket: function () {
385 var request = this.$['ajaxDisconnect'];
463 var url = this.websocketUrl + '?conn_id=' + this.connectionId;
386 request.url = this.disconnectUrl;
464 this.websocket = new WebSocket(url);
387 request.params = {
465 this.websocket.onopen = this._handleListenOpen.bind(this);
388 conn_id: this.connectionId
466 this.websocket.onclose = this._handleListenCloseEvent.bind(this);
389 };
467 this.websocket.onerror = this._handleListenErrorEvent.bind(this);
390 for (var i = 0; i < this.mutators.disconnect.length; i++) {
468 this.websocket.onmessage = this._handleListenMessageEvent.bind(this);
391 this.mutators.disconnect[i](request);
469 },
392 }
470 /**
393 // mark connection as expired
471 * Opens long-poll connection.
394 request.generateRequest();
472 *
395 // disconnect existing connection
473 */
396 this.closeConnection();
474 openLongPoll: function () {
397 },
475 var request = this.$['ajaxListen'];
476 request.url = this.longPollUrl + '?conn_id=' + this.connectionId;
477 request.generateRequest()
478 },
479 /**
480 * Retries `connect()` call while incrementing interval between tries up to 1 minute.
481 *
482 */
483 retryConnection: function () {
484 if (!this.shouldReconnect) {
485 return;
486 }
487 if (this._currentBounceIv < 60000) {
488 this._currentBounceIv = this._currentBounceIv + this.increaseBounceIv;
489 }
490 else {
491 this._currentBounceIv = 60000;
492 }
493 setTimeout(this.connect.bind(this), this._currentBounceIv);
494 },
495 /**
496 * Closes listening connection.
497 *
498 */
499 closeConnection: function () {
500 var request = this.$['ajaxListen'];
501 if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
502 this.websocket.onclose = null;
503 this.websocket.onerror = null;
504 this.websocket.close();
505 }
506 if (request.loading) {
507 request.lastRequest.abort();
508 }
509 this.connected = false;
510 },
511
398
512 _handleChannelsChange: function (event) {
399 /**
513 // do not fire the event if set() didn't mutate anything
400 * Sends a message to the server.
514 // is this a reliable way to do it?
401 *
515 if (!this.isReady || event === undefined) {
402 */
516 return
403 message: function (message) {
517 }
404 var request = this.$['ajaxMessage'];
518 this.fire('channelstream-channels-changed', event)
405 request.url = this.messageUrl;
519 },
406 request.body = message;
520
407 for (var i = 0; i < this.mutators.message.length; i++) {
521 _handleListenOpen: function (event) {
408 this.mutators.message[i](request)
522 this.connected = true;
409 }
523 this.fire('channelstream-listen-opened', event);
410 request.generateRequest();
524 this.createHeartBeats();
411 },
525 },
412 /**
526
413 * Opens "long lived" (websocket/longpoll) connection to the channelstream server.
527 createHeartBeats: function () {
414 *
528 if (typeof self._heartbeat === 'undefined' && this.websocket !== null
415 */
529 && this.heartbeats) {
416 startListening: function (event) {
530 self._heartbeat = setInterval(this._sendHeartBeat.bind(this), 10000);
417 this.fire('start-listening', {});
531 }
418 if (this.useWebsocket) {
532 },
419 this.useWebsocket = window.WebSocket ? true : false;
533
420 }
534 _sendHeartBeat: function () {
421 if (this.useWebsocket) {
535 if (this.websocket.readyState === WebSocket.OPEN && this.heartbeats) {
422 this.openWebsocket();
536 this.websocket.send(JSON.stringify({type: 'heartbeat'}));
423 }
537 }
424 else {
538 },
425 this.openLongPoll();
539
426 }
540 _handleListenError: function (event) {
427 },
541 this.connected = false;
428 /**
542 this.retryConnection();
429 * Opens websocket connection.
543 },
430 *
544 _handleConnectError: function (event) {
431 */
545 this.connected = false;
432 openWebsocket: function () {
546 this.fire('channelstream-connect-error', event.detail);
433 var url = this.websocketUrl + '?conn_id=' + this.connectionId;
547 },
434 this.websocket = new WebSocket(url);
548
435 this.websocket.onopen = this._handleListenOpen.bind(this);
549 _handleListenMessageEvent: function (event) {
436 this.websocket.onclose = this._handleListenCloseEvent.bind(this);
550 var data = null;
437 this.websocket.onerror = this._handleListenErrorEvent.bind(this);
551 // comes from iron-ajax
438 this.websocket.onmessage = this._handleListenMessageEvent.bind(this);
552 if (event.detail) {
439 },
553 data = JSON.parse(event.detail.response)
440 /**
554 // comes from websocket
441 * Opens long-poll connection.
555 setTimeout(this.openLongPoll.bind(this), 0);
442 *
556 } else {
443 */
557 data = JSON.parse(event.data)
444 openLongPoll: function () {
558 }
445 var request = this.$['ajaxListen'];
559 this.fire('channelstream-listen-message', data);
446 request.url = this.longPollUrl + '?conn_id=' + this.connectionId;
447 request.generateRequest()
448 },
449 /**
450 * Retries `connect()` call while incrementing interval between tries up to 1 minute.
451 *
452 */
453 retryConnection: function () {
454 if (!this.shouldReconnect) {
455 return;
456 }
457 if (this._currentBounceIv < 60000) {
458 this._currentBounceIv = this._currentBounceIv + this.increaseBounceIv;
459 }
460 else {
461 this._currentBounceIv = 60000;
462 }
463 setTimeout(this.connect.bind(this), this._currentBounceIv);
464 },
465 /**
466 * Closes listening connection.
467 *
468 */
469 closeConnection: function () {
470 var request = this.$['ajaxListen'];
471 if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
472 this.websocket.onclose = null;
473 this.websocket.onerror = null;
474 this.websocket.close();
475 }
476 if (request.loading) {
477 request.lastRequest.abort();
478 }
479 this.connected = false;
480 },
560
481
561 },
482 _handleChannelsChange: function (event) {
483 // do not fire the event if set() didn't mutate anything
484 // is this a reliable way to do it?
485 if (!this.isReady || event === undefined) {
486 return
487 }
488 this.fire('channelstream-channels-changed', event)
489 },
562
490
563 _handleListenCloseEvent: function (event) {
491 _handleListenOpen: function (event) {
564 this.connected = false;
492 this.connected = true;
565 this.fire('channelstream-listen-closed', event.detail);
493 this.fire('channelstream-listen-opened', event);
566 this.retryConnection();
494 this.createHeartBeats();
567 },
495 },
568
496
569 _handleListenErrorEvent: function (event) {
497 createHeartBeats: function () {
570 this.connected = false;
498 if (typeof self._heartbeat === 'undefined' && this.websocket !== null
571 this.fire('channelstream-listen-error', {})
499 && this.heartbeats) {
572 },
500 self._heartbeat = setInterval(this._sendHeartBeat.bind(this), 10000);
501 }
502 },
573
503
574 _handleConnect: function (event) {
504 _sendHeartBeat: function () {
575 this.currentBounceIv = 0;
505 if (this.websocket.readyState === WebSocket.OPEN && this.heartbeats) {
576 this.connectionId = event.detail.response.conn_id;
506 this.websocket.send(JSON.stringify({type: 'heartbeat'}));
577 this.fire('channelstream-connected', event.detail.response);
507 }
578 },
508 },
579
509
580 _handleDisconnect: function (event) {
510 _handleListenError: function (event) {
581 this.connected = false;
511 this.connected = false;
582 this.fire('channelstream-disconnected', {});
512 this.retryConnection();
583 },
513 },
514 _handleConnectError: function (event) {
515 this.connected = false;
516 this.fire('channelstream-connect-error', event.detail);
517 },
584
518
585 _handleMessage: function (event) {
519 _handleListenMessageEvent: function (event) {
586 this.fire('channelstream-message-sent', event.detail.response);
520 var data = null;
587 },
521 // comes from iron-ajax
588 _handleMessageError: function (event) {
522 if (event.detail) {
589 this.fire('channelstream-message-error', event.detail);
523 data = JSON.parse(event.detail.response)
590 },
524 // comes from websocket
525 setTimeout(this.openLongPoll.bind(this), 0);
526 } else {
527 data = JSON.parse(event.data)
528 }
529 this.fire('channelstream-listen-message', data);
591
530
592 _handleSubscribe: function (event) {
531 },
593 this.fire('channelstream-subscribed', event.detail.response);
532
594 },
533 _handleListenCloseEvent: function (event) {
534 this.connected = false;
535 this.fire('channelstream-listen-closed', event.detail);
536 this.retryConnection();
537 },
538
539 _handleListenErrorEvent: function (event) {
540 this.connected = false;
541 this.fire('channelstream-listen-error', {})
542 },
595
543
596 _handleSubscribeError: function (event) {
544 _handleConnect: function (event) {
597 this.fire('channelstream-subscribe-error', event.detail);
545 this.currentBounceIv = 0;
598 },
546 this.connectionId = event.detail.response.conn_id;
547 this.fire('channelstream-connected', event.detail.response);
548 },
599
549
600 _handleUnsubscribe: function (event) {
550 _handleDisconnect: function (event) {
601 this.fire('channelstream-unsubscribed', event.detail.response);
551 this.connected = false;
602 },
552 this.fire('channelstream-disconnected', {});
553 },
603
554
604 _handleUnsubscribeError: function (event) {
555 _handleMessage: function (event) {
605 this.fire('channelstream-unsubscribe-error', event.detail);
556 this.fire('channelstream-message-sent', event.detail.response);
606 }
557 },
607 });
558 _handleMessageError: function (event) {
559 this.fire('channelstream-message-error', event.detail);
560 },
561
562 _handleSubscribe: function (event) {
563 this.fire('channelstream-subscribed', event.detail.response);
564 },
608
565
609 </script>
566 _handleSubscribeError: function (event) {
610 </dom-module>
567 this.fire('channelstream-subscribe-error', event.detail);
568 },
569
570 _handleUnsubscribe: function (event) {
571 this.fire('channelstream-unsubscribed', event.detail.response);
572 },
573
574 _handleUnsubscribeError: function (event) {
575 this.fire('channelstream-unsubscribe-error', event.detail);
576 }
577 });
@@ -1,7 +1,7 b''
1 import '../../../../../bower_components/iron-ajax/iron-ajax.html';
1 import '@polymer/iron-ajax/iron-ajax.js';
2 import './root-styles.gen.html';
2 import './root-styles.gen.html';
3 import './channelstream-connection/channelstream-connection.html';
3 import './channelstream-connection/channelstream-connection.js';
4 import './rhodecode-toast/rhodecode-toast.html';
4 import './rhodecode-toast/rhodecode-toast.js';
5 import './rhodecode-toggle/rhodecode-toggle.html';
5 import './rhodecode-toggle/rhodecode-toggle.js';
6 import './rhodecode-unsafe-html/rhodecode-unsafe-html.html';
6 import './rhodecode-unsafe-html/rhodecode-unsafe-html.js';
7 import './rhodecode-app/rhodecode-app.html';
7 import './rhodecode-app/rhodecode-app.js';
@@ -1,195 +1,194 b''
1 <link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
1 import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
2 <link rel="import" href="../channelstream-connection/channelstream-connection.html">
2 import '../channelstream-connection/channelstream-connection.js';
3 <link rel="import" href="../rhodecode-toast/rhodecode-toast.html">
3 import '../rhodecode-toast/rhodecode-toast.js';
4 <link rel="import" href="../rhodecode-favicon/rhodecode-favicon.html">
4 import '../rhodecode-favicon/rhodecode-favicon.js';
5
6 var ccLog = Logger.get('RhodeCodeApp');
7 ccLog.setLevel(Logger.OFF);
5
8
6 <dom-module id="rhodecode-app">
9 export class RhodecodeApp extends PolymerElement {
7 <template>
10
11 static get is() {
12 return 'rhodecode-app';
13 }
14
15 static get template(){
16 return html`
8 <channelstream-connection
17 <channelstream-connection
9 id="channelstream-connection"
18 id="channelstream-connection"
10 on-channelstream-listen-message="receivedMessage"
19 on-channelstream-listen-message="receivedMessage"
11 on-channelstream-connected="handleConnected"
20 on-channelstream-connected="handleConnected"
12 on-channelstream-subscribed="handleSubscribed">
21 on-channelstream-subscribed="handleSubscribed">
13 </channelstream-connection>
22 </channelstream-connection>
14 <rhodecode-favicon></rhodecode-favicon>
23 <rhodecode-favicon></rhodecode-favicon>
15 </template>
24 `
16 <script>
25 }
17 var ccLog = Logger.get('RhodeCodeApp');
18 ccLog.setLevel(Logger.OFF);
19
20 class RhodecodeApp extends Polymer.Element {
21
22 static get is() {
23 return 'rhodecode-app';
24 }
25
26
26 connectedCallback() {
27 connectedCallback() {
27 super.connectedCallback();
28 super.connectedCallback();
28 ccLog.debug('rhodeCodeApp created');
29 ccLog.debug('rhodeCodeApp created');
29 $.Topic('/notifications').subscribe(this.handleNotifications.bind(this));
30 $.Topic('/notifications').subscribe(this.handleNotifications.bind(this));
30 $.Topic('/favicon/update').subscribe(this.faviconUpdate.bind(this));
31 $.Topic('/favicon/update').subscribe(this.faviconUpdate.bind(this));
31 $.Topic('/connection_controller/subscribe').subscribe(
32 $.Topic('/connection_controller/subscribe').subscribe(
32 this.subscribeToChannelTopic.bind(this));
33 this.subscribeToChannelTopic.bind(this));
33 // this event can be used to coordinate plugins to do their
34 // this event can be used to coordinate plugins to do their
34 // initialization before channelstream is kicked off
35 // initialization before channelstream is kicked off
35 $.Topic('/__MAIN_APP__').publish({});
36 $.Topic('/__MAIN_APP__').publish({});
36
37 for (var i = 0; i < alertMessagePayloads.length; i++) {
38 $.Topic('/notifications').publish(alertMessagePayloads[i]);
39 }
40 this.initPlugins();
41 // after rest of application loads and topics get fired, launch connection
42 $(document).ready(function () {
43 this.kickoffChannelstreamPlugin();
44 }.bind(this));
45 }
46
37
47 initPlugins() {
38 for (var i = 0; i < alertMessagePayloads.length; i++) {
48 for (var i = 0; i < window.APPLICATION_PLUGINS.length; i++) {
39 $.Topic('/notifications').publish(alertMessagePayloads[i]);
49 var pluginDef = window.APPLICATION_PLUGINS[i];
40 }
50 if (pluginDef.component) {
41 this.initPlugins();
51 var pluginElem = document.createElement(pluginDef.component);
42 // after rest of application loads and topics get fired, launch connection
52 this.shadowRoot.appendChild(pluginElem);
43 $(document).ready(function () {
53 if (typeof pluginElem.init !== 'undefined') {
44 this.kickoffChannelstreamPlugin();
54 pluginElem.init();
45 }.bind(this));
55 }
46 }
56 }
57 }
58 }
59
60 /** proxy to channelstream connection */
61 getChannelStreamConnection() {
62 return this.$['channelstream-connection'];
63 }
64
65 handleNotifications(data) {
66 var elem = document.getElementById('notifications');
67 if (elem) {
68 elem.handleNotification(data);
69 }
70
71 }
72
47
73 faviconUpdate(data) {
48 initPlugins() {
74 this.shadowRoot.querySelector('rhodecode-favicon').counter = data.count;
49 for (var i = 0; i < window.APPLICATION_PLUGINS.length; i++) {
75 }
50 var pluginDef = window.APPLICATION_PLUGINS[i];
76
51 if (pluginDef.component) {
77 /** opens connection to ws server */
52 var pluginElem = document.createElement(pluginDef.component);
78 kickoffChannelstreamPlugin(data) {
53 this.shadowRoot.appendChild(pluginElem);
79 ccLog.debug('kickoffChannelstreamPlugin');
54 if (typeof pluginElem.init !== 'undefined') {
80 var channels = ['broadcast'];
55 pluginElem.init();
81 var addChannels = this.checkViewChannels();
82 for (var i = 0; i < addChannels.length; i++) {
83 channels.push(addChannels[i]);
84 }
85 if (window.CHANNELSTREAM_SETTINGS && CHANNELSTREAM_SETTINGS.enabled) {
86 var channelstreamConnection = this.getChannelStreamConnection();
87 channelstreamConnection.connectUrl = CHANNELSTREAM_URLS.connect;
88 channelstreamConnection.subscribeUrl = CHANNELSTREAM_URLS.subscribe;
89 channelstreamConnection.websocketUrl = CHANNELSTREAM_URLS.ws + '/ws';
90 channelstreamConnection.longPollUrl = CHANNELSTREAM_URLS.longpoll + '/listen';
91 // some channels might already be registered by topic
92 for (var i = 0; i < channels.length; i++) {
93 channelstreamConnection.push('channels', channels[i]);
94 }
95 // append any additional channels registered in other plugins
96 $.Topic('/connection_controller/subscribe').processPrepared();
97 channelstreamConnection.connect();
98 }
56 }
99 }
57 }
58 }
59 }
100
60
101 checkViewChannels() {
61 /** proxy to channelstream connection */
102 // subscribe to different channels data is sent.
62 getChannelStreamConnection() {
63 return this.$['channelstream-connection'];
64 }
103
65
104 var channels = [];
66 handleNotifications(data) {
105 // subscribe to PR repo channel for PR's'
67 var elem = document.getElementById('notifications');
106 if (templateContext.pull_request_data.pull_request_id) {
68 if (elem) {
107 var channelName = '/repo$' + templateContext.repo_name + '$/pr/' +
69 elem.handleNotification(data);
108 String(templateContext.pull_request_data.pull_request_id);
70 }
109 channels.push(channelName);
110 }
111
71
112 if (templateContext.commit_data.commit_id) {
72 }
113 var channelName = '/repo$' + templateContext.repo_name + '$/commit/' +
73
114 String(templateContext.commit_data.commit_id);
74 faviconUpdate(data) {
115 channels.push(channelName);
75 this.shadowRoot.querySelector('rhodecode-favicon').counter = data.count;
116 }
76 }
117
77
118 return channels;
78 /** opens connection to ws server */
79 kickoffChannelstreamPlugin(data) {
80 ccLog.debug('kickoffChannelstreamPlugin');
81 var channels = ['broadcast'];
82 var addChannels = this.checkViewChannels();
83 for (var i = 0; i < addChannels.length; i++) {
84 channels.push(addChannels[i]);
85 }
86 if (window.CHANNELSTREAM_SETTINGS && CHANNELSTREAM_SETTINGS.enabled) {
87 var channelstreamConnection = this.getChannelStreamConnection();
88 channelstreamConnection.connectUrl = CHANNELSTREAM_URLS.connect;
89 channelstreamConnection.subscribeUrl = CHANNELSTREAM_URLS.subscribe;
90 channelstreamConnection.websocketUrl = CHANNELSTREAM_URLS.ws + '/ws';
91 channelstreamConnection.longPollUrl = CHANNELSTREAM_URLS.longpoll + '/listen';
92 // some channels might already be registered by topic
93 for (var i = 0; i < channels.length; i++) {
94 channelstreamConnection.push('channels', channels[i]);
119 }
95 }
96 // append any additional channels registered in other plugins
97 $.Topic('/connection_controller/subscribe').processPrepared();
98 channelstreamConnection.connect();
99 }
100 }
101
102 checkViewChannels() {
103 // subscribe to different channels data is sent.
120
104
121 /** subscribes users from channels in channelstream */
105 var channels = [];
122 subscribeToChannelTopic(channels) {
106 // subscribe to PR repo channel for PR's'
123 var channelstreamConnection = this.getChannelStreamConnection();
107 if (templateContext.pull_request_data.pull_request_id) {
124 var toSubscribe = channelstreamConnection.calculateSubscribe(channels);
108 var channelName = '/repo$' + templateContext.repo_name + '$/pr/' +
125 ccLog.debug('subscribeToChannelTopic', toSubscribe);
109 String(templateContext.pull_request_data.pull_request_id);
126 if (toSubscribe.length > 0) {
110 channels.push(channelName);
127 // if we are connected then subscribe
111 }
128 if (channelstreamConnection.connected) {
112
129 channelstreamConnection.subscribe(toSubscribe);
113 if (templateContext.commit_data.commit_id) {
130 }
114 var channelName = '/repo$' + templateContext.repo_name + '$/commit/' +
131 // not connected? just push channels onto the stack
115 String(templateContext.commit_data.commit_id);
132 else {
116 channels.push(channelName);
133 for (var i = 0; i < toSubscribe.length; i++) {
117 }
134 channelstreamConnection.push('channels', toSubscribe[i]);
118
135 }
119 return channels;
136 }
120 }
121
122 /** subscribes users from channels in channelstream */
123 subscribeToChannelTopic(channels) {
124 var channelstreamConnection = this.getChannelStreamConnection();
125 var toSubscribe = channelstreamConnection.calculateSubscribe(channels);
126 ccLog.debug('subscribeToChannelTopic', toSubscribe);
127 if (toSubscribe.length > 0) {
128 // if we are connected then subscribe
129 if (channelstreamConnection.connected) {
130 channelstreamConnection.subscribe(toSubscribe);
131 }
132 // not connected? just push channels onto the stack
133 else {
134 for (var i = 0; i < toSubscribe.length; i++) {
135 channelstreamConnection.push('channels', toSubscribe[i]);
137 }
136 }
138 }
137 }
138 }
139 }
139
140
140 /** publish received messages into correct topic */
141 /** publish received messages into correct topic */
141 receivedMessage(event) {
142 receivedMessage(event) {
142 for (var i = 0; i < event.detail.length; i++) {
143 for (var i = 0; i < event.detail.length; i++) {
143 var message = event.detail[i];
144 var message = event.detail[i];
144 if (message.message.topic) {
145 if (message.message.topic) {
145 ccLog.debug('publishing', message.message.topic);
146 ccLog.debug('publishing', message.message.topic);
146 $.Topic(message.message.topic).publish(message);
147 $.Topic(message.message.topic).publish(message);
147 }
148 }
148 else if (message.type === 'presence') {
149 else if (message.type === 'presence') {
149 $.Topic('/connection_controller/presence').publish(message);
150 $.Topic('/connection_controller/presence').publish(message);
150 }
151 else {
152 ccLog.warn('unhandled message', message);
153 }
154 }
155 }
151 }
152 else {
153 ccLog.warn('unhandled message', message);
154 }
155 }
156 }
156
157
157 handleConnected(event) {
158 handleConnected(event) {
158 var channelstreamConnection = this.getChannelStreamConnection();
159 var channelstreamConnection = this.getChannelStreamConnection();
159 channelstreamConnection.set('channelsState',
160 channelstreamConnection.set('channelsState',
160 event.detail.channels_info);
161 event.detail.channels_info);
161 channelstreamConnection.set('userState', event.detail.state);
162 channelstreamConnection.set('userState', event.detail.state);
162 channelstreamConnection.set('channels', event.detail.channels);
163 channelstreamConnection.set('channels', event.detail.channels);
163 this.propagageChannelsState();
164 this.propagageChannelsState();
164 }
165 }
165
166
166 handleSubscribed(event) {
167 handleSubscribed(event) {
167 var channelstreamConnection = this.getChannelStreamConnection();
168 var channelstreamConnection = this.getChannelStreamConnection();
168 var channelInfo = event.detail.channels_info;
169 var channelInfo = event.detail.channels_info;
169 var channelKeys = Object.keys(event.detail.channels_info);
170 var channelKeys = Object.keys(event.detail.channels_info);
170 for (var i = 0; i < channelKeys.length; i++) {
171 for (var i = 0; i < channelKeys.length; i++) {
171 var key = channelKeys[i];
172 var key = channelKeys[i];
172 channelstreamConnection.set(['channelsState', key], channelInfo[key]);
173 channelstreamConnection.set(['channelsState', key], channelInfo[key]);
173 }
174 }
174 channelstreamConnection.set('channels', event.detail.channels);
175 channelstreamConnection.set('channels', event.detail.channels);
175 this.propagageChannelsState();
176 this.propagageChannelsState();
176 }
177 }
177
178
178 /** propagates channel states on topics */
179 /** propagates channel states on topics */
179 propagageChannelsState(event) {
180 propagageChannelsState(event) {
180 var channelstreamConnection = this.getChannelStreamConnection();
181 var channelstreamConnection = this.getChannelStreamConnection();
181 var channel_data = channelstreamConnection.channelsState;
182 var channel_data = channelstreamConnection.channelsState;
182 var channels = channelstreamConnection.channels;
183 var channels = channelstreamConnection.channels;
183 for (var i = 0; i < channels.length; i++) {
184 for (var i = 0; i < channels.length; i++) {
184 var key = channels[i];
185 var key = channels[i];
185 $.Topic('/connection_controller/channel_update').publish(
186 $.Topic('/connection_controller/channel_update').publish(
186 {channel: key, state: channel_data[key]}
187 {channel: key, state: channel_data[key]}
187 );
188 );
188 }
189 }
189 }
190 }
190
191
191 }
192 }
192
193
193 customElements.define(RhodecodeApp.is, RhodecodeApp);
194 customElements.define(RhodecodeApp.is, RhodecodeApp);
194 </script>
195 </dom-module>
@@ -1,38 +1,33 b''
1 <link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
1 import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
2
2
3 <dom-module id="rhodecode-favicon">
3 export class RhodecodeFavicon extends PolymerElement {
4 <template>
5 </template>
6 <script>
7 class RhodecodeFavicon extends Polymer.Element {
8
4
9 static get is() { return 'rhodecode-favicon'; }
5 static get is() {
6 return 'rhodecode-favicon';
7 }
10
8
11 static get properties() {
9 static get properties() {
12 return {
10 return {
13 favicon: Object,
11 favicon: Object,
14 counter: {
12 counter: {
15 type: Number,
13 type: Number,
16 observer: '_handleCounter'
14 observer: '_handleCounter'
17 }
18 }
19 }
15 }
20
16 }
21 connectedCallback() {
17 }
22 super.connectedCallback();
23 this.favicon = new Favico({
24 type: 'rectangle',
25 animation: 'none'
26 });
27 }
28
18
29 _handleCounter(newVal, oldVal) {
19 connectedCallback() {
30 this.favicon.badge(this.counter);
20 super.connectedCallback();
31 }
21 this.favicon = new Favico({
22 type: 'rectangle',
23 animation: 'none'
24 });
25 }
32
26
33 }
27 _handleCounter(newVal, oldVal) {
34 customElements.define(RhodecodeFavicon.is, RhodecodeFavicon);
28 this.favicon.badge(this.counter);
29 }
35
30
31 }
36
32
37 </script>
33 customElements.define(RhodecodeFavicon.is, RhodecodeFavicon);
38 </dom-module>
@@ -1,233 +1,231 b''
1 <link rel="import" href="../../../../../../bower_components/paper-button/paper-button.html">
1 import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
2 <link rel="import"
2 import '@polymer/paper-toggle-button/paper-toggle-button.js';
3 href="../../../../../../bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
3 import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
4 <link rel="import" href="../rhodecode-unsafe-html/rhodecode-unsafe-html.html">
4 import {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
5 <dom-module id="rhodecode-toast">
5 import '../rhodecode-unsafe-html/rhodecode-unsafe-html.js';
6 <template>
6
7 export class RhodecodeToast extends mixinBehaviors([IronA11yKeysBehavior], PolymerElement) {
8
9 static get is() {
10 return 'rhodecode-toast';
11 }
12
13 static get template(){
14 return html`
7 <style include="shared-styles">
15 <style include="shared-styles">
8 /* inset border for buttons - does not work in ie */
16 /* inset border for buttons - does not work in ie */
9 /* rounded borders */
17 /* rounded borders */
10 /* rounded borders - bottom only */
18 /* rounded borders - bottom only */
11 /* rounded borders - top only */
19 /* rounded borders - top only */
12 /* text shadow */
20 /* text shadow */
13 /* centers text in a circle - input diameter of circle and color */
21 /* centers text in a circle - input diameter of circle and color */
14 /* pill version of the circle */
22 /* pill version of the circle */
15 .absolute-center {
23 .absolute-center {
16 margin: auto;
24 margin: auto;
17 position: absolute;
25 position: absolute;
18 top: 0;
26 top: 0;
19 left: 0;
27 left: 0;
20 bottom: 0;
28 bottom: 0;
21 right: 0;
29 right: 0;
22 }
30 }
23
31
24 .top-left-rounded-corner {
32 .top-left-rounded-corner {
25 -webkit-border-top-left-radius: 2px;
33 -webkit-border-top-left-radius: 2px;
26 -khtml-border-radius-topleft: 2px;
34 -khtml-border-radius-topleft: 2px;
27 border-top-left-radius: 2px;
35 border-top-left-radius: 2px;
28 }
36 }
29
37
30 .top-right-rounded-corner {
38 .top-right-rounded-corner {
31 -webkit-border-top-right-radius: 2px;
39 -webkit-border-top-right-radius: 2px;
32 -khtml-border-radius-topright: 2px;
40 -khtml-border-radius-topright: 2px;
33 border-top-right-radius: 2px;
41 border-top-right-radius: 2px;
34 }
42 }
35
43
36 .bottom-left-rounded-corner {
44 .bottom-left-rounded-corner {
37 -webkit-border-bottom-left-radius: 2px;
45 -webkit-border-bottom-left-radius: 2px;
38 -khtml-border-radius-bottomleft: 2px;
46 -khtml-border-radius-bottomleft: 2px;
39 border-bottom-left-radius: 2px;
47 border-bottom-left-radius: 2px;
40 }
48 }
41
49
42 .bottom-right-rounded-corner {
50 .bottom-right-rounded-corner {
43 -webkit-border-bottom-right-radius: 2px;
51 -webkit-border-bottom-right-radius: 2px;
44 -khtml-border-radius-bottomright: 2px;
52 -khtml-border-radius-bottomright: 2px;
45 border-bottom-right-radius: 2px;
53 border-bottom-right-radius: 2px;
46 }
54 }
47
55
48 .top-left-rounded-corner-mid {
56 .top-left-rounded-corner-mid {
49 -webkit-border-top-left-radius: 2px;
57 -webkit-border-top-left-radius: 2px;
50 -khtml-border-radius-topleft: 2px;
58 -khtml-border-radius-topleft: 2px;
51 border-top-left-radius: 2px;
59 border-top-left-radius: 2px;
52 }
60 }
53
61
54 .top-right-rounded-corner-mid {
62 .top-right-rounded-corner-mid {
55 -webkit-border-top-right-radius: 2px;
63 -webkit-border-top-right-radius: 2px;
56 -khtml-border-radius-topright: 2px;
64 -khtml-border-radius-topright: 2px;
57 border-top-right-radius: 2px;
65 border-top-right-radius: 2px;
58 }
66 }
59
67
60 .bottom-left-rounded-corner-mid {
68 .bottom-left-rounded-corner-mid {
61 -webkit-border-bottom-left-radius: 2px;
69 -webkit-border-bottom-left-radius: 2px;
62 -khtml-border-radius-bottomleft: 2px;
70 -khtml-border-radius-bottomleft: 2px;
63 border-bottom-left-radius: 2px;
71 border-bottom-left-radius: 2px;
64 }
72 }
65
73
66 .bottom-right-rounded-corner-mid {
74 .bottom-right-rounded-corner-mid {
67 -webkit-border-bottom-right-radius: 2px;
75 -webkit-border-bottom-right-radius: 2px;
68 -khtml-border-radius-bottomright: 2px;
76 -khtml-border-radius-bottomright: 2px;
69 border-bottom-right-radius: 2px;
77 border-bottom-right-radius: 2px;
70 }
78 }
71
79
72 .alert {
80 .alert {
73 margin: 10px 0;
81 margin: 10px 0;
74 }
82 }
75
83
76 .toast-close {
84 .toast-close {
77 margin: 0;
85 margin: 0;
78 float: right;
86 float: right;
79 cursor: pointer;
87 cursor: pointer;
80 }
88 }
81
89
82 .toast-message-holder {
90 .toast-message-holder {
83 background: rgba(255, 255, 255, 0.25);
91 background: rgba(255, 255, 255, 0.25);
84 }
92 }
85
93
86 .toast-message-holder.fixed {
94 .toast-message-holder.fixed {
87 position: fixed;
95 position: fixed;
88 padding: 10px 0;
96 padding: 10px 0;
89 margin-left: 10px;
97 margin-left: 10px;
90 margin-right: 10px;
98 margin-right: 10px;
91 top: 0;
99 top: 0;
92 left: 0;
100 left: 0;
93 right: 0;
101 right: 0;
94 z-index: 100;
102 z-index: 100;
95 }
103 }
96 </style>
104 </style>
97
105
98 <template is="dom-if" if="[[hasToasts]]">
106 <template is="dom-if" if="[[hasToasts]]">
99 <div class$="container toast-message-holder [[conditionalClass(isFixed)]]">
107 <div class$="container toast-message-holder [[conditionalClass(isFixed)]]">
100 <template is="dom-repeat" items="[[toasts]]">
108 <template is="dom-repeat" items="[[toasts]]">
101 <div class$="alert alert-[[item.level]]">
109 <div class$="alert alert-[[item.level]]">
102 <div on-click="dismissNotification" class="toast-close" index-pos="[[index]]">
110 <div on-click="dismissNotification" class="toast-close" index-pos="[[index]]">
103 <span>[[_gettext('Close')]]</span>
111 <span>[[_gettext('Close')]]</span>
104 </div>
112 </div>
105 <rhodecode-unsafe-html text="[[item.message]]"></rhodecode-unsafe-html>
113 <rhodecode-unsafe-html text="[[item.message]]"></rhodecode-unsafe-html>
106 </div>
114 </div>
107 </template>
115 </template>
108 </div>
116 </div>
109 </template>
117 </template>
110 </template>
118 `
111
119 }
112 <script>
113
114 class RhodecodeToast extends Polymer.mixinBehaviors([Polymer.IronA11yKeysBehavior], Polymer.Element) {
115
116 static get is() {
117 return 'rhodecode-toast';
118 }
119
120
120 static get properties() {
121 static get properties() {
121 return {
122 return {
122 toasts: {
123 toasts: {
123 type: Array,
124 type: Array,
124 value() {
125 value() {
125 return []
126 return []
126 }
127 },
128 isFixed: {
129 type: Boolean,
130 value: false
131 },
132 hasToasts: {
133 type: Boolean,
134 computed: '_computeHasToasts(toasts.*)'
135 },
136 keyEventTarget: {
137 type: Object,
138 value() {
139 return document.body;
140 }
141 }
142 }
127 }
143 }
128 },
144
129 isFixed: {
145 get keyBindings() {
130 type: Boolean,
146 return {
131 value: false
147 'esc:keyup': '_hideOnEsc'
132 },
133 hasToasts: {
134 type: Boolean,
135 computed: '_computeHasToasts(toasts.*)'
136 },
137 keyEventTarget: {
138 type: Object,
139 value() {
140 return document.body;
148 }
141 }
149 }
142 }
143 }
144 }
150
145
151 static get observers() {
146 get keyBindings() {
152 return [
147 return {
153 '_changedToasts(toasts.splices)'
148 'esc:keyup': '_hideOnEsc'
154 ]
149 }
155 }
150 }
156
151
157 _hideOnEsc(event) {
152 static get observers() {
158 return this.dismissNotifications();
153 return [
159 }
154 '_changedToasts(toasts.splices)'
155 ]
156 }
160
157
161 _computeHasToasts() {
158 _hideOnEsc(event) {
162 return this.toasts.length > 0;
159 return this.dismissNotifications();
163 }
160 }
164
161
165 _debouncedCalc() {
162 _computeHasToasts() {
166 // calculate once in a while
163 return this.toasts.length > 0;
167 this.debounce('debouncedCalc', this.toastInWindow, 25);
164 }
168 }
169
165
170 conditionalClass() {
166 _debouncedCalc() {
171 return this.isFixed ? 'fixed' : '';
167 // calculate once in a while
172 }
168 this.debounce('debouncedCalc', this.toastInWindow, 25);
169 }
173
170
174 toastInWindow() {
171 conditionalClass() {
175 if (!this._headerNode) {
172 return this.isFixed ? 'fixed' : '';
176 return true
173 }
177 }
178 var headerHeight = this._headerNode.offsetHeight;
179 var scrollPosition = window.scrollY;
180
174
181 if (this.isFixed) {
175 toastInWindow() {
182 this.isFixed = 1 <= scrollPosition;
176 if (!this._headerNode) {
183 }
177 return true
184 else {
178 }
185 this.isFixed = headerHeight <= scrollPosition;
179 var headerHeight = this._headerNode.offsetHeight;
186 }
180 var scrollPosition = window.scrollY;
187 }
188
181
189 connectedCallback() {
182 if (this.isFixed) {
190 super.connectedCallback();
183 this.isFixed = 1 <= scrollPosition;
191 this._headerNode = document.querySelector('.header', document);
184 }
192 this.listen(window, 'scroll', '_debouncedCalc');
185 else {
193 this.listen(window, 'resize', '_debouncedCalc');
186 this.isFixed = headerHeight <= scrollPosition;
194 this._debouncedCalc();
187 }
195 }
188 }
196
189
197 _changedToasts(newValue, oldValue) {
190 connectedCallback() {
198 $.Topic('/favicon/update').publish({count: this.toasts.length});
191 super.connectedCallback();
199 }
192 this._headerNode = document.querySelector('.header', document);
193 this.listen(window, 'scroll', '_debouncedCalc');
194 this.listen(window, 'resize', '_debouncedCalc');
195 this._debouncedCalc();
196 }
200
197
201 dismissNotification(e) {
198 _changedToasts(newValue, oldValue) {
202 $.Topic('/favicon/update').publish({count: this.toasts.length - 1});
199 $.Topic('/favicon/update').publish({count: this.toasts.length});
203 var idx = e.target.parentNode.indexPos
200 }
204 this.splice('toasts', idx, 1);
205
201
206 }
202 dismissNotification(e) {
203 $.Topic('/favicon/update').publish({count: this.toasts.length - 1});
204 var idx = e.target.parentNode.indexPos
205 this.splice('toasts', idx, 1);
207
206
208 dismissNotifications() {
207 }
209 $.Topic('/favicon/update').publish({count: 0});
208
210 this.splice('toasts', 0);
209 dismissNotifications() {
211 }
210 $.Topic('/favicon/update').publish({count: 0});
211 this.splice('toasts', 0);
212 }
212
213
213 handleNotification(data) {
214 handleNotification(data) {
214 if (!templateContext.rhodecode_user.notification_status && !data.message.force) {
215 if (!templateContext.rhodecode_user.notification_status && !data.message.force) {
215 // do not act if notifications are disabled
216 // do not act if notifications are disabled
216 return
217 return
217 }
218 }
218 this.push('toasts', {
219 this.push('toasts', {
219 level: data.message.level,
220 level: data.message.level,
220 message: data.message.message
221 message: data.message.message
221 });
222 });
222 }
223 }
223
224
224 _gettext(x){
225 _gettext(x){
225 return _gettext(x)
226 return _gettext(x)
226 }
227 }
227
228
228 }
229 }
229
230
230 customElements.define(RhodecodeToast.is, RhodecodeToast);
231 customElements.define(RhodecodeToast.is, RhodecodeToast);
231
232 </script>
233 </dom-module>
@@ -1,61 +1,57 b''
1 <link rel="import"
1 import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
2 href="../../../../../../bower_components/paper-toggle-button/paper-toggle-button.html">
2 import '@polymer/paper-toggle-button/paper-toggle-button.js';
3 <link rel="import" href="../../../../../../bower_components/paper-spinner/paper-spinner.html">
3 import '@polymer/paper-spinner/paper-spinner.js';
4 <link rel="import" href="../../../../../../bower_components/paper-tooltip/paper-tooltip.html">
4 import '@polymer/paper-tooltip/paper-tooltip.js';
5
5
6 <dom-module id="rhodecode-toggle">
6 export class RhodecodeToggle extends PolymerElement {
7
7
8 <style include="shared-styles">
8 static get is() {
9 .rc-toggle {
9 return 'rhodecode-toggle';
10 float: left;
10 }
11 position: relative;
12 }
13
11
14 .rc-toggle paper-spinner {
12 static get template() {
15 position: absolute;
13 return html`
16 top: 0;
14 <style include="shared-styles">
17 left: -30px;
15 .rc-toggle {
18 width: 20px;
16 float: left;
19 height: 20px;
17 position: relative;
20 }
18 }
21 </style>
19
22
20 .rc-toggle paper-spinner {
23 <template>
21 position: absolute;
22 top: 0;
23 left: -30px;
24 width: 20px;
25 height: 20px;
26 }
27 </style>
24 <div class="rc-toggle">
28 <div class="rc-toggle">
25 <paper-toggle-button checked={{checked}}>[[labelStatus(checked)]]</paper-toggle-button>
29 <paper-toggle-button checked={{checked}}>[[labelStatus(checked)]]
30 </paper-toggle-button>
26 <paper-tooltip>[[tooltipText]]</paper-tooltip>
31 <paper-tooltip>[[tooltipText]]</paper-tooltip>
27 <template is="dom-if" if="[[shouldShow(noSpinner)]]">
32 <template is="dom-if" if="[[shouldShow(noSpinner)]]">
28 <paper-spinner active=[[active]]></paper-spinner>
33 <paper-spinner active=[[active]]></paper-spinner>
29 </template>
34 </template>
30 </div>
35 </div>
31 </template>
36 `;
32
37 }
33 <script>
34
35 class RhodecodeToggle extends Polymer.Element {
36
37 static get is() {
38 return 'rhodecode-toggle';
39 }
40
38
41 static get properties() {
39 static get properties() {
42 return {
40 return {
43 noSpinner: {type: Boolean, value: false, reflectToAttribute: true},
41 noSpinner: {type: Boolean, value: false, reflectToAttribute: true},
44 tooltipText: {type: String, value: "Click to toggle", reflectToAttribute: true},
42 tooltipText: {type: String, value: "Click to toggle", reflectToAttribute: true},
45 checked: {type: Boolean, value: false, reflectToAttribute: true},
43 checked: {type: Boolean, value: false, reflectToAttribute: true},
46 active: {type: Boolean, value: false, reflectToAttribute: true, notify: true}
44 active: {type: Boolean, value: false, reflectToAttribute: true, notify: true}
47 }
45 }
48 }
46 }
49
47
50 shouldShow() {
48 shouldShow() {
51 return !this.noSpinner
49 return !this.noSpinner
52 }
50 }
53
51
54 labelStatus(isActive) {
52 labelStatus(isActive) {
55 return this.checked ? 'Enabled' : "Disabled"
53 return this.checked ? 'Enabled' : "Disabled"
56 }
54 }
57 }
55 }
58
56
59 customElements.define(RhodecodeToggle.is, RhodecodeToggle);
57 customElements.define(RhodecodeToggle.is, RhodecodeToggle);
60 </script>
61 </dom-module>
@@ -1,31 +1,30 b''
1 <link rel="import" href="../../../../../../bower_components/polymer/polymer-element.html">
1 import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
2
3 export class RhodecodeUnsafeHtml extends PolymerElement {
2
4
3 <dom-module id="rhodecode-unsafe-html">
5 static get is() {
4 <template>
6 return 'rhodecode-unsafe-html';
7 }
8
9 static get template() {
10 return html`
5 <style include="shared-styles"></style>
11 <style include="shared-styles"></style>
6 <slot></slot>
12 <slot></slot>
7 </template>
13 `;
8 <script>
14 }
9 class RhodecodeUnsafeHtml extends Polymer.Element {
10
11 static get is() {
12 return 'rhodecode-unsafe-html';
13 }
14
15
15 static get properties() {
16 static get properties() {
16 return {
17 return {
17 text: {
18 text: {
18 type: String,
19 type: String,
19 observer: '_handleText'
20 observer: '_handleText'
20 }
21 }
22 }
23
24 _handleText(newVal, oldVal) {
25 this.innerHTML = this.text;
26 }
21 }
27 }
22 }
23 }
28
24
29 customElements.define(RhodecodeUnsafeHtml.is, RhodecodeUnsafeHtml);
25 _handleText(newVal, oldVal) {
30 </script>
26 this.innerHTML = this.text;
31 </dom-module>
27 }
28 }
29
30 customElements.define(RhodecodeUnsafeHtml.is, RhodecodeUnsafeHtml);
General Comments 0
You need to be logged in to leave comments. Login now