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