##// END OF EJS Templates
frontend: introduce rhodecode-app for more complex cross element wiring
ergo -
r787:048ecbf3 default
parent child Browse files
Show More
@@ -0,0 +1,106 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:
10
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
37 id="ajaxConnect"
38 url=""
39 handle-as="json"
40 method="post"
41 content-type="application/json"
42 loading="{{loadingConnect}}"
43 last-response="{{connectLastResponse}}"
44 on-response="_handleConnect"
45 on-error="_handleConnectError"
46 debounce-duration="100"></iron-ajax>
47
48 <iron-ajax
49 id="ajaxDisconnect"
50 url=""
51 handle-as="json"
52 method="post"
53 content-type="application/json"
54 loading="{{loadingDisconnect}}"
55 last-response="{{_disconnectLastResponse}}"
56 on-response="_handleDisconnect"
57 debounce-duration="100"></iron-ajax>
58
59 <iron-ajax
60 id="ajaxSubscribe"
61 url=""
62 handle-as="json"
63 method="post"
64 content-type="application/json"
65 loading="{{loadingSubscribe}}"
66 last-response="{{subscribeLastResponse}}"
67 on-response="_handleSubscribe"
68 debounce-duration="100"></iron-ajax>
69
70 <iron-ajax
71 id="ajaxUnsubscribe"
72 url=""
73 handle-as="json"
74 method="post"
75 content-type="application/json"
76 loading="{{loadingUnsubscribe}}"
77 last-response="{{unsubscribeLastResponse}}"
78 on-response="_handleUnsubscribe"
79 debounce-duration="100"></iron-ajax>
80
81 <iron-ajax
82 id="ajaxMessage"
83 url=""
84 handle-as="json"
85 method="post"
86 content-type="application/json"
87 loading="{{loadingMessage}}"
88 last-response="{{messageLastResponse}}"
89 on-response="_handleMessage"
90 on-error="_handleMessageError"
91 debounce-duration="100"></iron-ajax>
92
93 <iron-ajax
94 id="ajaxListen"
95 url=""
96 handle-as="text"
97 loading="{{loadingListen}}"
98 last-response="{{listenLastResponse}}"
99 on-request="_handleListenOpen"
100 on-error="_handleListenError"
101 on-response="_handleListenMessageEvent"
102 debounce-duration="100"></iron-ajax>
103
104 </template>
105 <script src="channelstream-connection.js"></script>
106 </dom-module>
This diff has been collapsed as it changes many lines, (502 lines changed) Show them Hide them
@@ -0,0 +1,502 b''
1 Polymer({
2 is: 'channelstream-connection',
3
4 /**
5 * Fired when `channels` array changes.
6 *
7 * @event channelstream-channels-changed
8 */
9
10 /**
11 * Fired when `connect()` method succeeds.
12 *
13 * @event channelstream-connected
14 */
15
16 /**
17 * Fired when `connect` fails.
18 *
19 * @event channelstream-connect-error
20 */
21
22 /**
23 * Fired when `disconnect()` succeeds.
24 *
25 * @event channelstream-disconnected
26 */
27
28 /**
29 * Fired when `message()` succeeds.
30 *
31 * @event channelstream-message-sent
32 */
33
34 /**
35 * Fired when `message()` fails.
36 *
37 * @event channelstream-message-error
38 */
39
40 /**
41 * Fired when `subscribe()` succeeds.
42 *
43 * @event channelstream-subscribed
44 */
45
46 /**
47 * Fired when `subscribe()` fails.
48 *
49 * @event channelstream-subscribe-error
50 */
51
52 /**
53 * Fired when `unsubscribe()` succeeds.
54 *
55 * @event channelstream-unsubscribed
56 */
57
58 /**
59 * Fired when `unsubscribe()` fails.
60 *
61 * @event channelstream-unsubscribe-error
62 */
63
64 /**
65 * Fired when listening connection receives a message.
66 *
67 * @event channelstream-listen-message
68 */
69
70 /**
71 * Fired when listening connection is opened.
72 *
73 * @event channelstream-listen-opened
74 */
75
76 /**
77 * Fired when listening connection is closed.
78 *
79 * @event channelstream-listen-closed
80 */
81
82 /**
83 * Fired when listening connection suffers an error.
84 *
85 * @event channelstream-listen-error
86 */
87
88 properties: {
89 isReady: Boolean,
90 /** List of channels user should be subscribed to. */
91 channels: {
92 type: Array,
93 value: function () {
94 return []
95 },
96 notify: true
97 },
98 /** Username of connecting user. */
99 username: {
100 type: String,
101 value: 'Anonymous',
102 reflectToAttribute: true
103 },
104 /** Connection identifier. */
105 connectionId: {
106 type: String,
107 reflectToAttribute: true
108 },
109 /** Websocket instance. */
110 websocket: {
111 type: Object,
112 value: null
113 },
114 /** Websocket connection url. */
115 websocketUrl: {
116 type: String,
117 value: ''
118 },
119 /** URL used in `connect()`. */
120 connectUrl: {
121 type: String,
122 value: ''
123 },
124 /** URL used in `disconnect()`. */
125 disconnectUrl: {
126 type: String,
127 value: ''
128 },
129 /** URL used in `subscribe()`. */
130 subscribeUrl: {
131 type: String,
132 value: ''
133 },
134 /** URL used in `unsubscribe()`. */
135 unsubscribeUrl: {
136 type: String,
137 value: ''
138 },
139 /** URL used in `message()`. */
140 messageUrl: {
141 type: String,
142 value: ''
143 },
144 /** Long-polling connection url. */
145 longPollUrl: {
146 type: String,
147 value: ''
148 },
149 /** Long-polling connection url. */
150 shouldReconnect: {
151 type: Boolean,
152 value: true
153 },
154 /** Should send heartbeats. */
155 heartbeats: {
156 type: Boolean,
157 value: true
158 },
159 /** How much should every retry interval increase (in milliseconds) */
160 increaseBounceIv: {
161 type: Number,
162 value: 2000
163 },
164 _currentBounceIv: {
165 type: Number,
166 reflectToAttribute: true,
167 value: 0
168 },
169 /** Should use websockets or long-polling by default */
170 useWebsocket: {
171 type: Boolean,
172 reflectToAttribute: true,
173 value: true
174 },
175 connected: {
176 type: Boolean,
177 reflectToAttribute: true,
178 value: false
179 }
180 },
181
182 observers: [
183 '_handleChannelsChange(channels.splices)'
184 ],
185
186 listeners: {
187 'channelstream-connected': 'startListening',
188 'channelstream-connect-error': 'retryConnection',
189 },
190
191 /**
192 * Mutators hold functions that you can set locally to change the data
193 * that the client is sending to all endpoints
194 * you can call it like `elem.mutators('connect', yourFunc())`
195 * mutators will be executed in order they were pushed onto arrays
196 *
197 */
198 mutators: {
199 connect: function () {
200 return []
201 }(),
202 message: function () {
203 return []
204 }(),
205 subscribe: function () {
206 return []
207 }(),
208 unsubscribe: function () {
209 return []
210 }(),
211 disconnect: function () {
212 return []
213 }()
214 },
215 ready: function () {
216 this.isReady = true;
217 },
218
219 /**
220 * Connects user and fetches connection id from the server.
221 *
222 */
223 connect: function () {
224 var request = this.$['ajaxConnect'];
225 request.url = this.connectUrl;
226 request.body = {
227 username: this.username,
228 channels: this.channels
229 };
230 for (var i = 0; i < this.mutators.connect.length; i++) {
231 this.mutators.connect[i](request);
232 }
233 request.generateRequest()
234 },
235 /**
236 * Overwrite with custom function that will
237 */
238 addMutator: function (type, func) {
239 this.mutators[type].push(func);
240 },
241 /**
242 * Subscribes user to channels.
243 *
244 */
245 subscribe: function (channels) {
246 var request = this.$['ajaxSubscribe'];
247 request.url = this.subscribeUrl;
248 request.body = {
249 channels: channels,
250 conn_id: this.connectionId
251 };
252 for (var i = 0; i < this.mutators.subscribe.length; i++) {
253 this.mutators.subscribe[i](request);
254 }
255 if (request.body.channels.length) {
256 request.generateRequest();
257 }
258 },
259 /**
260 * Unsubscribes user from channels.
261 *
262 */
263 unsubscribe: function (unsubscribe) {
264 var request = this.$['ajaxUnsubscribe'];
265
266 request.url = this.unsubscribeUrl;
267 request.body = {
268 channels: unsubscribe,
269 conn_id: this.connectionId
270 };
271 for (var i = 0; i < this.mutators.unsubscribe.length; i++) {
272 this.mutators.unsubscribe[i](request);
273 }
274 request.generateRequest()
275 },
276
277 /**
278 * calculates list of channels we should add user to based on difference
279 * between channels property and passed channel list
280 */
281 calculateSubscribe: function (channels) {
282 var currentlySubscribed = this.channels;
283 var toSubscribe = [];
284 for (var i = 0; i < channels.length; i++) {
285 if (currentlySubscribed.indexOf(channels[i]) === -1) {
286 toSubscribe.push(channels[i]);
287 }
288 }
289 return toSubscribe
290 },
291 /**
292 * calculates list of channels we should remove user from based difference
293 * between channels property and passed channel list
294 */
295 calculateUnsubscribe: function (channels) {
296 var currentlySubscribed = this.channels;
297 var toUnsubscribe = [];
298 for (var i = 0; i < channels.length; i++) {
299 if (currentlySubscribed.indexOf(channels[i]) !== -1) {
300 toUnsubscribe.push(channels[i]);
301 }
302 }
303 return toUnsubscribe
304 },
305 /**
306 * Marks the connection as expired.
307 *
308 */
309 disconnect: function () {
310 var request = this.$['ajaxDisconnect'];
311 request.url = this.disconnectUrl;
312 request.params = {
313 conn_id: this.connectionId
314 };
315 for (var i = 0; i < this.mutators.disconnect.length; i++) {
316 this.mutators.disconnect[i](request);
317 }
318 // mark connection as expired
319 request.generateRequest();
320 // disconnect existing connection
321 this.closeConnection();
322 },
323
324 /**
325 * Sends a message to the server.
326 *
327 */
328 message: function (message) {
329 var request = this.$['ajaxMessage'];
330 request.url = this.messageUrl;
331 request.body = message;
332 for (var i = 0; i < this.mutators.message.length; i++) {
333 this.mutators.message[i](request)
334 }
335 request.generateRequest();
336 },
337 /**
338 * Opens "long lived" (websocket/longpoll) connection to the channelstream server.
339 *
340 */
341 startListening: function (event) {
342 this.fire('start-listening', {});
343 if (this.useWebsocket) {
344 this.useWebsocket = window.WebSocket ? true : false;
345 }
346 if (this.useWebsocket) {
347 this.openWebsocket();
348 }
349 else {
350 this.openLongPoll();
351 }
352 },
353 /**
354 * Opens websocket connection.
355 *
356 */
357 openWebsocket: function () {
358 var url = this.websocketUrl + '?conn_id=' + this.connectionId;
359 this.websocket = new WebSocket(url);
360 this.websocket.onopen = this._handleListenOpen.bind(this);
361 this.websocket.onclose = this._handleListenCloseEvent.bind(this);
362 this.websocket.onerror = this._handleListenErrorEvent.bind(this);
363 this.websocket.onmessage = this._handleListenMessageEvent.bind(this);
364 },
365 /**
366 * Opens long-poll connection.
367 *
368 */
369 openLongPoll: function () {
370 var request = this.$['ajaxListen'];
371 request.url = this.longPollUrl + '?conn_id=' + this.connectionId;
372 request.generateRequest()
373 },
374 /**
375 * Retries `connect()` call while incrementing interval between tries up to 1 minute.
376 *
377 */
378 retryConnection: function () {
379 if (!this.shouldReconnect) {
380 return;
381 }
382 if (this._currentBounceIv < 60000) {
383 this._currentBounceIv = this._currentBounceIv + this.increaseBounceIv;
384 }
385 else {
386 this._currentBounceIv = 60000;
387 }
388 setTimeout(this.connect.bind(this), this._currentBounceIv);
389 },
390 /**
391 * Closes listening connection.
392 *
393 */
394 closeConnection: function () {
395 var request = this.$['ajaxListen'];
396 if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
397 this.websocket.onclose = null;
398 this.websocket.onerror = null;
399 this.websocket.close();
400 }
401 if (request.loading) {
402 request.lastRequest.abort();
403 }
404 this.connected = false;
405 },
406
407 _handleChannelsChange: function (event) {
408 // do not fire the event if set() didn't mutate anything
409 // is this a reliable way to do it?
410 if (!this.isReady || event === undefined) {
411 return
412 }
413 this.fire('channelstream-channels-changed', event)
414 },
415
416 _handleListenOpen: function (event) {
417 this.connected = true;
418 this.fire('channelstream-listen-opened', event);
419 this.createHeartBeats();
420 },
421
422 createHeartBeats: function () {
423 if (typeof self._heartbeat === 'undefined' && this.websocket !== null
424 && this.heartbeats) {
425 self._heartbeat = setInterval(this._sendHeartBeat.bind(this), 10000);
426 }
427 },
428
429 _sendHeartBeat: function () {
430 if (this.websocket.readyState === WebSocket.OPEN && this.heartbeats) {
431 this.websocket.send(JSON.stringify({type: 'heartbeat'}));
432 }
433 },
434
435 _handleListenError: function (event) {
436 this.connected = false;
437 this.retryConnection();
438 },
439 _handleConnectError: function (event) {
440 this.connected = false;
441 this.fire('channelstream-connect-error', event.detail);
442 },
443
444 _handleListenMessageEvent: function (event) {
445 var data = null;
446 // comes from iron-ajax
447 if (event.detail) {
448 data = JSON.parse(event.detail.response)
449 // comes from websocket
450 setTimeout(this.openLongPoll.bind(this), 0);
451 } else {
452 data = JSON.parse(event.data)
453 }
454 this.fire('channelstream-listen-message', data);
455
456 },
457
458 _handleListenCloseEvent: function (event) {
459 this.connected = false;
460 this.fire('channelstream-listen-closed', event.detail);
461 this.retryConnection();
462 },
463
464 _handleListenErrorEvent: function (event) {
465 this.connected = false;
466 this.fire('channelstream-listen-error', {})
467 },
468
469 _handleConnect: function (event) {
470 this.currentBounceIv = 0;
471 this.connectionId = event.detail.response.conn_id;
472 this.fire('channelstream-connected', event.detail.response);
473 },
474
475 _handleDisconnect: function (event) {
476 this.connected = false;
477 this.fire('channelstream-disconnected', {});
478 },
479
480 _handleMessage: function (event) {
481 this.fire('channelstream-message-sent', event.detail.response);
482 },
483 _handleMessageError: function (event) {
484 this.fire('channelstream-message-error', event.detail);
485 },
486
487 _handleSubscribe: function (event) {
488 this.fire('channelstream-subscribed', event.detail.response);
489 },
490
491 _handleSubscribeError: function (event) {
492 this.fire('channelstream-subscribe-error', event.detail);
493 },
494
495 _handleUnsubscribe: function (event) {
496 this.fire('channelstream-unsubscribed', event.detail.response);
497 },
498
499 _handleUnsubscribeError: function (event) {
500 this.fire('channelstream-unsubscribe-error', event.detail);
501 }
502 });
@@ -0,0 +1,15 b''
1 <link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
2 <link rel="import" href="../channelstream-connection/channelstream-connection.html">
3
4 <dom-module id="rhodecode-app">
5 <template>
6 <rhodecode-toast id="notifications"></rhodecode-toast>
7 <channelstream-connection
8 id="channelstream-connection"
9 on-channelstream-listen-message="receivedMessage"
10 on-channelstream-connected="handleConnected"
11 on-channelstream-subscribed="handleSubscribed">
12 </channelstream-connection>
13 </template>
14 <script src="rhodecode-app.js"></script>
15 </dom-module>
@@ -0,0 +1,127 b''
1 ccLog = Logger.get('RhodeCodeApp');
2 ccLog.setLevel(Logger.OFF);
3
4 var rhodeCodeApp = Polymer({
5 is: 'rhodecode-app',
6 created: function () {
7 ccLog.debug('rhodeCodeApp created');
8 $.Topic('/notifications').subscribe(this.handleNotifications.bind(this));
9
10 $.Topic('/plugins/__REGISTER__').subscribe(
11 this.kickoffChannelstreamPlugin.bind(this)
12 );
13
14 $.Topic('/connection_controller/subscribe').subscribe(
15 this.subscribeToChannelTopic.bind(this));
16 },
17
18 /** proxy to channelstream connection */
19 getChannelStreamConnection: function () {
20 return this.$['channelstream-connection'];
21 },
22
23 handleNotifications: function (data) {
24 this.$['notifications'].handleNotification(data);
25 },
26
27 /** opens connection to ws server */
28 kickoffChannelstreamPlugin: function (data) {
29 ccLog.debug('kickoffChannelstreamPlugin');
30 var channels = ['broadcast'];
31 var addChannels = this.checkViewChannels();
32 for (var i = 0; i < addChannels.length; i++) {
33 channels.push(addChannels[i]);
34 }
35 var channelstreamConnection = this.$['channelstream-connection'];
36 channelstreamConnection.connectUrl = CHANNELSTREAM_URLS.connect;
37 channelstreamConnection.subscribeUrl = CHANNELSTREAM_URLS.subscribe;
38 channelstreamConnection.websocketUrl = CHANNELSTREAM_URLS.ws + '/ws';
39 channelstreamConnection.longPollUrl = CHANNELSTREAM_URLS.longpoll + '/listen';
40 // some channels might already be registered by topic
41 for (var i = 0; i < channels.length; i++) {
42 channelstreamConnection.push('channels', channels[i]);
43 }
44 // append any additional channels registered in other plugins
45 $.Topic('/connection_controller/subscribe').processPrepared();
46 channelstreamConnection.connect();
47 },
48
49 checkViewChannels: function () {
50 var channels = []
51 // subscribe to PR repo channel for PR's'
52 if (templateContext.pull_request_data.pull_request_id) {
53 var channelName = '/repo$' + templateContext.repo_name + '$/pr/' +
54 String(templateContext.pull_request_data.pull_request_id);
55 channels.push(channelName);
56 }
57 return channels;
58 },
59
60 /** subscribes users from channels in channelstream */
61 subscribeToChannelTopic: function (channels) {
62 var channelstreamConnection = this.$['channelstream-connection'];
63 var toSubscribe = channelstreamConnection.calculateSubscribe(channels);
64 ccLog.debug('subscribeToChannelTopic', toSubscribe);
65 if (toSubscribe.length > 0) {
66 // if we are connected then subscribe
67 if (channelstreamConnection.connected) {
68 channelstreamConnection.subscribe(toSubscribe);
69 }
70 // not connected? just push channels onto the stack
71 else {
72 for (var i = 0; i < toSubscribe.length; i++) {
73 channelstreamConnection.push('channels', toSubscribe[i]);
74 }
75 }
76 }
77 },
78
79 /** publish received messages into correct topic */
80 receivedMessage: function (event) {
81 for (var i = 0; i < event.detail.length; i++) {
82 var message = event.detail[i];
83 if (message.message.topic) {
84 ccLog.debug('publishing', message.message.topic);
85 $.Topic(message.message.topic).publish(message);
86 }
87 else if (message.type === 'presence'){
88 $.Topic('/connection_controller/presence').publish(message);
89 }
90 else {
91 ccLog.warn('unhandled message', message);
92 }
93 }
94 },
95
96 handleConnected: function (event) {
97 var channelstreamConnection = this.$['channelstream-connection'];
98 channelstreamConnection.set('channelsState',
99 event.detail.channels_info);
100 channelstreamConnection.set('userState', event.detail.state);
101 channelstreamConnection.set('channels', event.detail.channels);
102 this.propagageChannelsState();
103 },
104 handleSubscribed: function (event) {
105 var channelstreamConnection = this.$['channelstream-connection'];
106 var channelInfo = event.detail.channels_info;
107 var channelKeys = Object.keys(event.detail.channels_info);
108 for (var i = 0; i < channelKeys.length; i++) {
109 var key = channelKeys[i];
110 channelstreamConnection.set(['channelsState', key], channelInfo[key]);
111 }
112 channelstreamConnection.set('channels', event.detail.channels);
113 this.propagageChannelsState();
114 },
115 /** propagates channel states on topics */
116 propagageChannelsState: function (event) {
117 var channelstreamConnection = this.$['channelstream-connection'];
118 var channel_data = channelstreamConnection.channelsState;
119 var channels = channelstreamConnection.channels;
120 for (var i = 0; i < channels.length; i++) {
121 var key = channels[i];
122 $.Topic('/connection_controller/channel_update').publish(
123 {channel: key, state: channel_data[key]}
124 );
125 }
126 }
127 });
@@ -66,7 +66,6 b''
66 66 "<%= dirs.js.src %>/rhodecode/utils/topics.js",
67 67 "<%= dirs.js.src %>/rhodecode/widgets/multiselect.js",
68 68 "<%= dirs.js.src %>/rhodecode/init.js",
69 "<%= dirs.js.src %>/rhodecode/connection_controller.js",
70 69 "<%= dirs.js.src %>/rhodecode/codemirror.js",
71 70 "<%= dirs.js.src %>/rhodecode/comments.js",
72 71 "<%= dirs.js.src %>/rhodecode/constants.js",
@@ -81,7 +80,6 b''
81 80 "<%= dirs.js.src %>/rhodecode/select2_widgets.js",
82 81 "<%= dirs.js.src %>/rhodecode/tooltips.js",
83 82 "<%= dirs.js.src %>/rhodecode/users.js",
84 "<%= dirs.js.src %>/rhodecode/utils/notifications.js",
85 83 "<%= dirs.js.src %>/rhodecode/appenlight.js",
86 84 "<%= dirs.js.src %>/rhodecode.js"
87 85 ],
@@ -29,7 +29,9 b' from rhodecode.lib.ext_json import json'
29 29 def url_gen(request):
30 30 urls = {
31 31 'connect': request.route_url('channelstream_connect'),
32 'subscribe': request.route_url('channelstream_subscribe')
32 'subscribe': request.route_url('channelstream_subscribe'),
33 'longpoll': request.registry.settings.get('channelstream.longpoll_url', ''),
34 'ws': request.registry.settings.get('channelstream.ws_url', '')
33 35 }
34 36 return json.dumps(urls)
35 37
@@ -11,10 +11,6 b' Polymer({'
11 11 observers: [
12 12 '_changedToasts(toasts.splices)'
13 13 ],
14 ready: function(){
15
16 },
17
18 14 _changedToasts: function(newValue, oldValue){
19 15 this.$['p-toast'].notifyResize();
20 16 },
@@ -27,5 +23,16 b' Polymer({'
27 23 open: function(){
28 24 this.$['p-toast'].open();
29 25 },
26 handleNotification: function(data){
27 if (!templateContext.rhodecode_user.notification_status && !data.message.force) {
28 // do not act if notifications are disabled
29 return
30 }
31 this.push('toasts',{
32 level: data.message.level,
33 message: data.message.message
34 });
35 this.open();
36 },
30 37 _gettext: _gettext
31 38 });
@@ -1,6 +1,8 b''
1 1 <!-- required for stamped out templates that might use common elements -->
2 2 <link rel="import" href="../../../../../bower_components/iron-ajax/iron-ajax.html">
3 3 <link rel="import" href="shared-styles.html">
4 <link rel="import" href="channelstream-connection/channelstream-connection.html">
5 <link rel="import" href="rhodecode-app/rhodecode-app.html">
4 6 <link rel="import" href="rhodecode-toast/rhodecode-toast.html">
5 7 <link rel="import" href="rhodecode-toggle/rhodecode-toggle.html">
6 8 <link rel="import" href="rhodecode-unsafe-html/rhodecode-unsafe-html.html">
@@ -1,4 +1,7 b''
1 1 /plugins/__REGISTER__ - launched after the onDomReady() code from rhodecode.js is executed
2 2 /ui/plugins/code/anchor_focus - launched when rc starts to scroll on load to anchor on PR/Codeview
3 3 /ui/plugins/code/comment_form_built - launched when injectInlineForm() is executed and the form object is created
4 /notifications - shows new event notifications No newline at end of file
4 /notifications - shows new event notifications
5 /connection_controller/subscribe - subscribes user to new channels
6 /connection_controller/presence - receives presence change messages
7 /connection_controller/channel_update - receives channel states
@@ -146,7 +146,9 b" c.template_context['visual']['default_re"
146 146 <%def name="head_extra()"></%def>
147 147 ${self.head_extra()}
148 148 <script>
149 window.addEventListener("load", function(event) {
149 150 $.Topic('/plugins/__REGISTER__').prepareOrPublish({});
151 });
150 152 </script>
151 153 ## extra stuff
152 154 %if c.pre_code:
@@ -174,6 +176,6 b" c.template_context['visual']['default_re"
174 176 %if c.post_code:
175 177 ${c.post_code|n}
176 178 %endif
177 <rhodecode-toast id="notifications"></rhodecode-toast>
179 <rhodecode-app></rhodecode-app>
178 180 </body>
179 181 </html>
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now