-

- 502 Bad Gateway | Backend server is unreachable -

-
-

Possible Cause

-
    -
  • The server is beeing restarted.
  • -
  • The server is overloaded.
  • -
  • The link may be incorrect.
  • -
-
-
-

Support

-

For support, go to Support. - It may be useful to include your log file; see the log file locations here. -

-
-
-

Documentation

-

For more information, see docs.rhodecode.com.

-
+
+ +
+
+

+ 502 Bad Gateway | Backend server is unreachable +

+
+

Possible Causes

+
    +
  • The server is being restarted.
  • +
  • The server is overloaded.
  • +
  • The link may be incorrect.
  • +
+
+
+

Support

+

For support, go to Support. + It may be useful to include your log file; see the log file locations here. +

+
+
+

Documentation

+

For more information, see docs.rhodecode.com.

- - diff --git a/rhodecode/public/css/code-block.less b/rhodecode/public/css/code-block.less --- a/rhodecode/public/css/code-block.less +++ b/rhodecode/public/css/code-block.less @@ -255,7 +255,7 @@ table.code-difftable { /** LINE NUMBERS **/ .lineno { - padding-left: 2px; + padding-left: 2px !important; padding-right: 2px; text-align: right; width: 32px; diff --git a/rhodecode/public/css/deform.less b/rhodecode/public/css/deform.less --- a/rhodecode/public/css/deform.less +++ b/rhodecode/public/css/deform.less @@ -12,6 +12,7 @@ .control-label { width: 200px; + padding: 10px; float: left; } .control-inputs { @@ -26,14 +27,37 @@ .form-group { clear: left; + margin-bottom: 20px; + + &:after { /* clear fix */ + content: " "; + display: block; + clear: left; + } } .form-control { width: 100%; + padding: 0.9em; + border: 1px solid #979797; + border-radius: 2px; + } + .form-control.select2-container { + padding: 0; /* padding already applied in .drop-menu a */ + } + + .form-control.readonly { + background: #eeeeee; + cursor: not-allowed; } .error-block { color: red; + margin: 0; + } + + .help-block { + margin: 0; } .deform-seq-container .control-inputs { @@ -62,7 +86,9 @@ } } - .form-control.select2-container { height: 40px; } + .form-control.select2-container { + height: 40px; + } .deform-two-field-sequence .deform-seq-container .deform-seq-item label { display: none; @@ -74,7 +100,7 @@ display: none; } .deform-two-field-sequence .deform-seq-container .deform-seq-item.form-group { - background: red; + margin: 0; } .deform-two-field-sequence .deform-seq-container .deform-seq-item .deform-seq-item-group .form-group { width: 45%; padding: 0 2px; float: left; clear: none; diff --git a/rhodecode/public/css/diff.less b/rhodecode/public/css/diff.less --- a/rhodecode/public/css/diff.less +++ b/rhodecode/public/css/diff.less @@ -6,14 +6,15 @@ div.diffblock .code-header .changeset_he // Line select and comment div.diffblock.margined.comm tr { td { - position: relative; + // IMPORTANT - never position:relative this as it causes insanely + // slow rendering } .add-comment-line { // Force td width for Firefox width: 20px; - - // TODO: anderson: fixing mouse-over bug. + + // TODO: anderson: fixing mouse-over bug. // why was it vertical-align baseline in first place?? vertical-align: top !important; // Force width and display for IE 9 @@ -23,14 +24,35 @@ div.diffblock.margined.comm tr { a { display: none; - position: absolute; - top: 2px; - left: 2px; + margin-top: 2px; + margin-left: 2px; color: @grey3; } } } + .comment-toggle { + position: relative; + min-width: 20px; + width: 20px; + color: @rcblue; + + .icon-comment { + position: absolute; + top: 2px; + left: 0; + z-index: 100; + visibility: hidden; + } + + &.active { + .icon-comment{ + visibility: visible; + } + cursor: pointer; + } + } + &.line { &:hover, &.hover{ .add-comment-line a{ @@ -47,7 +69,7 @@ div.diffblock.margined.comm tr { &.commenting { &, del, ins { background-image: none !important; - background-color: lighten(@alert4, 10%) !important; + background-color: lighten(@alert4, 10%) !important; } } } @@ -75,4 +97,4 @@ div.diffblock.margined.comm tr { clear: both; font-family: @text-semibold; } -} \ No newline at end of file +} diff --git a/rhodecode/public/css/main.less b/rhodecode/public/css/main.less --- a/rhodecode/public/css/main.less +++ b/rhodecode/public/css/main.less @@ -25,10 +25,8 @@ @import 'comments'; @import 'panels-bootstrap'; @import 'panels'; -@import 'toastr'; @import 'deform'; - //--- BASE ------------------// .noscript-error { top: 0; @@ -1101,6 +1099,44 @@ table.issuetracker { } } +table.integrations { + .td-icon { + width: 20px; + .integration-icon { + height: 20px; + width: 20px; + } + } +} + +.integrations { + a.integration-box { + color: @text-color; + &:hover { + .panel { + background: #fbfbfb; + } + } + .integration-icon { + width: 30px; + height: 30px; + margin-right: 20px; + float: left; + } + + .panel-body { + padding: 10px; + } + .panel { + margin-bottom: 10px; + } + h2 { + display: inline-block; + margin: 0; + min-width: 140px; + } + } +} //Permissions Settings #add_perm { @@ -2103,3 +2139,8 @@ input[type=radio] { padding: 0; border: none; } + +.toggle-ajax-spinner{ + height: 16px; + width: 16px; +} diff --git a/rhodecode/public/css/polymer.less b/rhodecode/public/css/polymer.less new file mode 100644 --- /dev/null +++ b/rhodecode/public/css/polymer.less @@ -0,0 +1,24 @@ +//Primary CSS +//--- IMPORTS ------------------// +@import 'helpers'; +@import 'mixins'; +@import 'variables'; +@import 'buttons'; +@import 'alerts'; + +:root { + --primary-color: @rcblue; + --light-primary-color: @rclightblue; + --dark-primary-color: @rcdarkblue; + --primary-text-color: @text-color; + + --paper-spinner-layer-1-color: @grey6; + --paper-spinner-layer-2-color: @grey5; + --paper-spinner-layer-3-color: @grey4; + --paper-spinner-layer-4-color: @grey3; +} + +.paper-toggle-button { + display: inline; +} + diff --git a/rhodecode/public/css/tables.less b/rhodecode/public/css/tables.less --- a/rhodecode/public/css/tables.less +++ b/rhodecode/public/css/tables.less @@ -117,7 +117,7 @@ table.dataTable { &.annotate{ padding-right: 0; - + div.annotatediv{ margin: 0 0.7em; } @@ -138,7 +138,7 @@ table.dataTable { &.td-journalaction { min-width: 300px; - .journal_action_params { + .journal_action_params { // waiting for feedback } } @@ -202,9 +202,11 @@ table.dataTable { &.td-tags { padding: .5em 1em .5em 0; + width: 140px; .tag { margin: 1px; + float: left; } } @@ -262,11 +264,11 @@ table.dataTable { width: 150px; height: 22px; overflow: hidden; - + .tag { display: inline-block; } - + &.truncate { height: 22px; max-height:2em; @@ -428,7 +430,7 @@ table.trending_language_tbl { } } -// Compare +// Compare table.compare_view_commits { margin-top: @space; @@ -486,7 +488,7 @@ table.compare_view_commits { td { padding-top: @space; } - + &:first-child td { padding-top: 0; } diff --git a/rhodecode/public/css/toastr.less b/rhodecode/public/css/toastr.less deleted file mode 100644 --- a/rhodecode/public/css/toastr.less +++ /dev/null @@ -1,268 +0,0 @@ -// Mix-ins -.borderRadius(@radius) { - -moz-border-radius: @radius; - -webkit-border-radius: @radius; - border-radius: @radius; -} - -.boxShadow(@boxShadow) { - -moz-box-shadow: @boxShadow; - -webkit-box-shadow: @boxShadow; - box-shadow: @boxShadow; -} - -.opacity(@opacity) { - @opacityPercent: @opacity * 100; - opacity: @opacity; - -ms-filter: ~"progid:DXImageTransform.Microsoft.Alpha(Opacity=@{opacityPercent})"; - filter: ~"alpha(opacity=@{opacityPercent})"; -} - -.wordWrap(@wordWrap: break-word) { - -ms-word-wrap: @wordWrap; - word-wrap: @wordWrap; -} - -// Variables -@black: #000000; -@grey: #999999; -@light-grey: #CCCCCC; -@white: #FFFFFF; -@near-black: #030303; -@green: #51A351; -@red: #BD362F; -@blue: #2F96B4; -@orange: #F89406; -@default-container-opacity: .8; - -// Styles -.toast-title { - font-weight: bold; -} - -.toast-message { - .wordWrap(); - - a, - label { - color: @near-black; - } - - a:hover { - color: @light-grey; - text-decoration: none; - } -} - -.toast-close-button { - position: relative; - right: -0.3em; - top: -0.3em; - float: right; - font-size: 20px; - font-weight: bold; - color: @black; - -webkit-text-shadow: 0 1px 0 rgba(255,255,255,1); - text-shadow: 0 1px 0 rgba(255,255,255,1); - .opacity(0.8); - - &:hover, - &:focus { - color: @black; - text-decoration: none; - cursor: pointer; - .opacity(0.4); - } -} - -/*Additional properties for button version - iOS requires the button element instead of an anchor tag. - If you want the anchor version, it requires `href="#"`.*/ -button.toast-close-button { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; -} - -//#endregion - -.toast-top-center { - top: 0; - right: 0; - width: 100%; -} - -.toast-bottom-center { - bottom: 0; - right: 0; - width: 100%; -} - -.toast-top-full-width { - top: 0; - right: 0; - width: 100%; -} - -.toast-bottom-full-width { - bottom: 0; - right: 0; - width: 100%; -} - -.toast-top-left { - top: 12px; - left: 12px; -} - -.toast-top-right { - top: 12px; - right: 12px; -} - -.toast-bottom-right { - right: 12px; - bottom: 12px; -} - -.toast-bottom-left { - bottom: 12px; - left: 12px; -} - -#toast-container { - position: fixed; - z-index: 999999; - // The container should not be clickable. - pointer-events: none; - * { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - } - - > div { - position: relative; - // The toast itself should be clickable. - pointer-events: auto; - overflow: hidden; - margin: 0 0 6px; - padding: 15px; - width: 300px; - .borderRadius(1px 1px 1px 1px); - background-position: 15px center; - background-repeat: no-repeat; - color: @near-black; - .opacity(@default-container-opacity); - } - - > :hover { - .opacity(1); - cursor: pointer; - } - - > .toast-info { - //background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important; - } - - > .toast-error { - //background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important; - } - - > .toast-success { - //background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important; - } - - > .toast-warning { - //background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important; - } - - /*overrides*/ - &.toast-top-center > div, - &.toast-bottom-center > div { - width: 400px; - margin-left: auto; - margin-right: auto; - } - - &.toast-top-full-width > div, - &.toast-bottom-full-width > div { - width: 96%; - margin-left: auto; - margin-right: auto; - } -} - -.toast { - border-color: @near-black; - border-style: solid; - border-width: 2px 2px 2px 25px; - background-color: @white; -} - -.toast-success { - border-color: @green; -} - -.toast-error { - border-color: @red; -} - -.toast-info { - border-color: @blue; -} - -.toast-warning { - border-color: @orange; -} - -.toast-progress { - position: absolute; - left: 0; - bottom: 0; - height: 4px; - background-color: @black; - .opacity(0.4); -} - -/*Responsive Design*/ - -@media all and (max-width: 240px) { - #toast-container { - - > div { - padding: 8px; - width: 11em; - } - - & .toast-close-button { - right: -0.2em; - top: -0.2em; - } - } -} - -@media all and (min-width: 241px) and (max-width: 480px) { - #toast-container { - > div { - padding: 8px; - width: 18em; - } - - & .toast-close-button { - right: -0.2em; - top: -0.2em; - } - } -} - -@media all and (min-width: 481px) and (max-width: 768px) { - #toast-container { - > div { - padding: 15px; - width: 25em; - } - } -} diff --git a/rhodecode/public/css/type.less b/rhodecode/public/css/type.less --- a/rhodecode/public/css/type.less +++ b/rhodecode/public/css/type.less @@ -261,7 +261,7 @@ mark, margin-bottom: 0; } - .links{ + .links { float: right; display: inline; margin: 0; @@ -270,7 +270,7 @@ mark, text-align: right; li:before { content: none; } - + li { float: right; } a { display: inline-block; margin-left: @textmargin/2; diff --git a/rhodecode/public/js/src/components/channelstream-connection/channelstream-connection.html b/rhodecode/public/js/src/components/channelstream-connection/channelstream-connection.html new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/channelstream-connection/channelstream-connection.html @@ -0,0 +1,106 @@ + + + + + + + + diff --git a/rhodecode/public/js/src/components/channelstream-connection/channelstream-connection.js b/rhodecode/public/js/src/components/channelstream-connection/channelstream-connection.js new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/channelstream-connection/channelstream-connection.js @@ -0,0 +1,502 @@ +Polymer({ + is: 'channelstream-connection', + + /** + * Fired when `channels` array changes. + * + * @event channelstream-channels-changed + */ + + /** + * Fired when `connect()` method succeeds. + * + * @event channelstream-connected + */ + + /** + * Fired when `connect` fails. + * + * @event channelstream-connect-error + */ + + /** + * Fired when `disconnect()` succeeds. + * + * @event channelstream-disconnected + */ + + /** + * Fired when `message()` succeeds. + * + * @event channelstream-message-sent + */ + + /** + * Fired when `message()` fails. + * + * @event channelstream-message-error + */ + + /** + * Fired when `subscribe()` succeeds. + * + * @event channelstream-subscribed + */ + + /** + * Fired when `subscribe()` fails. + * + * @event channelstream-subscribe-error + */ + + /** + * Fired when `unsubscribe()` succeeds. + * + * @event channelstream-unsubscribed + */ + + /** + * Fired when `unsubscribe()` fails. + * + * @event channelstream-unsubscribe-error + */ + + /** + * Fired when listening connection receives a message. + * + * @event channelstream-listen-message + */ + + /** + * Fired when listening connection is opened. + * + * @event channelstream-listen-opened + */ + + /** + * Fired when listening connection is closed. + * + * @event channelstream-listen-closed + */ + + /** + * Fired when listening connection suffers an error. + * + * @event channelstream-listen-error + */ + + properties: { + isReady: Boolean, + /** List of channels user should be subscribed to. */ + channels: { + type: Array, + value: function () { + return [] + }, + notify: true + }, + /** Username of connecting user. */ + username: { + type: String, + value: 'Anonymous', + reflectToAttribute: true + }, + /** Connection identifier. */ + connectionId: { + type: String, + reflectToAttribute: true + }, + /** Websocket instance. */ + websocket: { + type: Object, + value: null + }, + /** Websocket connection url. */ + websocketUrl: { + type: String, + value: '' + }, + /** URL used in `connect()`. */ + connectUrl: { + type: String, + value: '' + }, + /** URL used in `disconnect()`. */ + disconnectUrl: { + type: String, + value: '' + }, + /** URL used in `subscribe()`. */ + subscribeUrl: { + type: String, + value: '' + }, + /** URL used in `unsubscribe()`. */ + unsubscribeUrl: { + type: String, + value: '' + }, + /** URL used in `message()`. */ + messageUrl: { + type: String, + value: '' + }, + /** Long-polling connection url. */ + longPollUrl: { + type: String, + value: '' + }, + /** Long-polling connection url. */ + shouldReconnect: { + type: Boolean, + value: true + }, + /** Should send heartbeats. */ + heartbeats: { + type: Boolean, + value: true + }, + /** How much should every retry interval increase (in milliseconds) */ + increaseBounceIv: { + type: Number, + value: 2000 + }, + _currentBounceIv: { + type: Number, + reflectToAttribute: true, + value: 0 + }, + /** Should use websockets or long-polling by default */ + useWebsocket: { + type: Boolean, + reflectToAttribute: true, + value: true + }, + connected: { + type: Boolean, + reflectToAttribute: true, + value: false + } + }, + + observers: [ + '_handleChannelsChange(channels.splices)' + ], + + listeners: { + 'channelstream-connected': 'startListening', + 'channelstream-connect-error': 'retryConnection', + }, + + /** + * Mutators hold functions that you can set locally to change the data + * that the client is sending to all endpoints + * you can call it like `elem.mutators('connect', yourFunc())` + * mutators will be executed in order they were pushed onto arrays + * + */ + mutators: { + connect: function () { + return [] + }(), + message: function () { + return [] + }(), + subscribe: function () { + return [] + }(), + unsubscribe: function () { + return [] + }(), + disconnect: function () { + return [] + }() + }, + ready: function () { + this.isReady = true; + }, + + /** + * Connects user and fetches connection id from the server. + * + */ + connect: function () { + var request = this.$['ajaxConnect']; + request.url = this.connectUrl; + request.body = { + username: this.username, + channels: this.channels + }; + for (var i = 0; i < this.mutators.connect.length; i++) { + this.mutators.connect[i](request); + } + request.generateRequest() + }, + /** + * Overwrite with custom function that will + */ + addMutator: function (type, func) { + this.mutators[type].push(func); + }, + /** + * Subscribes user to channels. + * + */ + subscribe: function (channels) { + var request = this.$['ajaxSubscribe']; + request.url = this.subscribeUrl; + request.body = { + channels: channels, + conn_id: this.connectionId + }; + for (var i = 0; i < this.mutators.subscribe.length; i++) { + this.mutators.subscribe[i](request); + } + if (request.body.channels.length) { + request.generateRequest(); + } + }, + /** + * Unsubscribes user from channels. + * + */ + unsubscribe: function (unsubscribe) { + var request = this.$['ajaxUnsubscribe']; + + request.url = this.unsubscribeUrl; + request.body = { + channels: unsubscribe, + conn_id: this.connectionId + }; + for (var i = 0; i < this.mutators.unsubscribe.length; i++) { + this.mutators.unsubscribe[i](request); + } + request.generateRequest() + }, + + /** + * calculates list of channels we should add user to based on difference + * between channels property and passed channel list + */ + calculateSubscribe: function (channels) { + var currentlySubscribed = this.channels; + var toSubscribe = []; + for (var i = 0; i < channels.length; i++) { + if (currentlySubscribed.indexOf(channels[i]) === -1) { + toSubscribe.push(channels[i]); + } + } + return toSubscribe + }, + /** + * calculates list of channels we should remove user from based difference + * between channels property and passed channel list + */ + calculateUnsubscribe: function (channels) { + var currentlySubscribed = this.channels; + var toUnsubscribe = []; + for (var i = 0; i < channels.length; i++) { + if (currentlySubscribed.indexOf(channels[i]) !== -1) { + toUnsubscribe.push(channels[i]); + } + } + return toUnsubscribe + }, + /** + * Marks the connection as expired. + * + */ + disconnect: function () { + var request = this.$['ajaxDisconnect']; + request.url = this.disconnectUrl; + request.params = { + conn_id: this.connectionId + }; + for (var i = 0; i < this.mutators.disconnect.length; i++) { + this.mutators.disconnect[i](request); + } + // mark connection as expired + request.generateRequest(); + // disconnect existing connection + this.closeConnection(); + }, + + /** + * Sends a message to the server. + * + */ + message: function (message) { + var request = this.$['ajaxMessage']; + request.url = this.messageUrl; + request.body = message; + for (var i = 0; i < this.mutators.message.length; i++) { + this.mutators.message[i](request) + } + request.generateRequest(); + }, + /** + * Opens "long lived" (websocket/longpoll) connection to the channelstream server. + * + */ + startListening: function (event) { + this.fire('start-listening', {}); + if (this.useWebsocket) { + this.useWebsocket = window.WebSocket ? true : false; + } + if (this.useWebsocket) { + this.openWebsocket(); + } + else { + this.openLongPoll(); + } + }, + /** + * Opens websocket connection. + * + */ + openWebsocket: function () { + var url = this.websocketUrl + '?conn_id=' + this.connectionId; + this.websocket = new WebSocket(url); + this.websocket.onopen = this._handleListenOpen.bind(this); + this.websocket.onclose = this._handleListenCloseEvent.bind(this); + this.websocket.onerror = this._handleListenErrorEvent.bind(this); + this.websocket.onmessage = this._handleListenMessageEvent.bind(this); + }, + /** + * Opens long-poll connection. + * + */ + openLongPoll: function () { + var request = this.$['ajaxListen']; + request.url = this.longPollUrl + '?conn_id=' + this.connectionId; + request.generateRequest() + }, + /** + * Retries `connect()` call while incrementing interval between tries up to 1 minute. + * + */ + retryConnection: function () { + if (!this.shouldReconnect) { + return; + } + if (this._currentBounceIv < 60000) { + this._currentBounceIv = this._currentBounceIv + this.increaseBounceIv; + } + else { + this._currentBounceIv = 60000; + } + setTimeout(this.connect.bind(this), this._currentBounceIv); + }, + /** + * Closes listening connection. + * + */ + closeConnection: function () { + var request = this.$['ajaxListen']; + if (this.websocket && this.websocket.readyState === WebSocket.OPEN) { + this.websocket.onclose = null; + this.websocket.onerror = null; + this.websocket.close(); + } + if (request.loading) { + request.lastRequest.abort(); + } + this.connected = false; + }, + + _handleChannelsChange: function (event) { + // do not fire the event if set() didn't mutate anything + // is this a reliable way to do it? + if (!this.isReady || event === undefined) { + return + } + this.fire('channelstream-channels-changed', event) + }, + + _handleListenOpen: function (event) { + this.connected = true; + this.fire('channelstream-listen-opened', event); + this.createHeartBeats(); + }, + + createHeartBeats: function () { + if (typeof self._heartbeat === 'undefined' && this.websocket !== null + && this.heartbeats) { + self._heartbeat = setInterval(this._sendHeartBeat.bind(this), 10000); + } + }, + + _sendHeartBeat: function () { + if (this.websocket.readyState === WebSocket.OPEN && this.heartbeats) { + this.websocket.send(JSON.stringify({type: 'heartbeat'})); + } + }, + + _handleListenError: function (event) { + this.connected = false; + this.retryConnection(); + }, + _handleConnectError: function (event) { + this.connected = false; + this.fire('channelstream-connect-error', event.detail); + }, + + _handleListenMessageEvent: function (event) { + var data = null; + // comes from iron-ajax + if (event.detail) { + data = JSON.parse(event.detail.response) + // comes from websocket + setTimeout(this.openLongPoll.bind(this), 0); + } else { + data = JSON.parse(event.data) + } + this.fire('channelstream-listen-message', data); + + }, + + _handleListenCloseEvent: function (event) { + this.connected = false; + this.fire('channelstream-listen-closed', event.detail); + this.retryConnection(); + }, + + _handleListenErrorEvent: function (event) { + this.connected = false; + this.fire('channelstream-listen-error', {}) + }, + + _handleConnect: function (event) { + this.currentBounceIv = 0; + this.connectionId = event.detail.response.conn_id; + this.fire('channelstream-connected', event.detail.response); + }, + + _handleDisconnect: function (event) { + this.connected = false; + this.fire('channelstream-disconnected', {}); + }, + + _handleMessage: function (event) { + this.fire('channelstream-message-sent', event.detail.response); + }, + _handleMessageError: function (event) { + this.fire('channelstream-message-error', event.detail); + }, + + _handleSubscribe: function (event) { + this.fire('channelstream-subscribed', event.detail.response); + }, + + _handleSubscribeError: function (event) { + this.fire('channelstream-subscribe-error', event.detail); + }, + + _handleUnsubscribe: function (event) { + this.fire('channelstream-unsubscribed', event.detail.response); + }, + + _handleUnsubscribeError: function (event) { + this.fire('channelstream-unsubscribe-error', event.detail); + } +}); diff --git a/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.html b/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.html new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.html @@ -0,0 +1,15 @@ + + + + + + + diff --git a/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js b/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js @@ -0,0 +1,129 @@ +ccLog = Logger.get('RhodeCodeApp'); +ccLog.setLevel(Logger.OFF); + +var rhodeCodeApp = Polymer({ + is: 'rhodecode-app', + created: function () { + ccLog.debug('rhodeCodeApp created'); + $.Topic('/notifications').subscribe(this.handleNotifications.bind(this)); + + $.Topic('/plugins/__REGISTER__').subscribe( + this.kickoffChannelstreamPlugin.bind(this) + ); + + $.Topic('/connection_controller/subscribe').subscribe( + this.subscribeToChannelTopic.bind(this)); + }, + + /** proxy to channelstream connection */ + getChannelStreamConnection: function () { + return this.$['channelstream-connection']; + }, + + handleNotifications: function (data) { + this.$['notifications'].handleNotification(data); + }, + + /** opens connection to ws server */ + kickoffChannelstreamPlugin: function (data) { + ccLog.debug('kickoffChannelstreamPlugin'); + var channels = ['broadcast']; + var addChannels = this.checkViewChannels(); + for (var i = 0; i < addChannels.length; i++) { + channels.push(addChannels[i]); + } + if (window.CHANNELSTREAM_SETTINGS && CHANNELSTREAM_SETTINGS.enabled){ + var channelstreamConnection = this.$['channelstream-connection']; + channelstreamConnection.connectUrl = CHANNELSTREAM_URLS.connect; + channelstreamConnection.subscribeUrl = CHANNELSTREAM_URLS.subscribe; + channelstreamConnection.websocketUrl = CHANNELSTREAM_URLS.ws + '/ws'; + channelstreamConnection.longPollUrl = CHANNELSTREAM_URLS.longpoll + '/listen'; + // some channels might already be registered by topic + for (var i = 0; i < channels.length; i++) { + channelstreamConnection.push('channels', channels[i]); + } + // append any additional channels registered in other plugins + $.Topic('/connection_controller/subscribe').processPrepared(); + channelstreamConnection.connect(); + } + }, + + checkViewChannels: function () { + var channels = [] + // subscribe to PR repo channel for PR's' + if (templateContext.pull_request_data.pull_request_id) { + var channelName = '/repo$' + templateContext.repo_name + '$/pr/' + + String(templateContext.pull_request_data.pull_request_id); + channels.push(channelName); + } + return channels; + }, + + /** subscribes users from channels in channelstream */ + subscribeToChannelTopic: function (channels) { + var channelstreamConnection = this.$['channelstream-connection']; + var toSubscribe = channelstreamConnection.calculateSubscribe(channels); + ccLog.debug('subscribeToChannelTopic', toSubscribe); + if (toSubscribe.length > 0) { + // if we are connected then subscribe + if (channelstreamConnection.connected) { + channelstreamConnection.subscribe(toSubscribe); + } + // not connected? just push channels onto the stack + else { + for (var i = 0; i < toSubscribe.length; i++) { + channelstreamConnection.push('channels', toSubscribe[i]); + } + } + } + }, + + /** publish received messages into correct topic */ + receivedMessage: function (event) { + for (var i = 0; i < event.detail.length; i++) { + var message = event.detail[i]; + if (message.message.topic) { + ccLog.debug('publishing', message.message.topic); + $.Topic(message.message.topic).publish(message); + } + else if (message.type === 'presence'){ + $.Topic('/connection_controller/presence').publish(message); + } + else { + ccLog.warn('unhandled message', message); + } + } + }, + + handleConnected: function (event) { + var channelstreamConnection = this.$['channelstream-connection']; + channelstreamConnection.set('channelsState', + event.detail.channels_info); + channelstreamConnection.set('userState', event.detail.state); + channelstreamConnection.set('channels', event.detail.channels); + this.propagageChannelsState(); + }, + handleSubscribed: function (event) { + var channelstreamConnection = this.$['channelstream-connection']; + var channelInfo = event.detail.channels_info; + var channelKeys = Object.keys(event.detail.channels_info); + for (var i = 0; i < channelKeys.length; i++) { + var key = channelKeys[i]; + channelstreamConnection.set(['channelsState', key], channelInfo[key]); + } + channelstreamConnection.set('channels', event.detail.channels); + this.propagageChannelsState(); + }, + /** propagates channel states on topics */ + propagageChannelsState: function (event) { + var channelstreamConnection = this.$['channelstream-connection']; + var channel_data = channelstreamConnection.channelsState; + var channels = channelstreamConnection.channels; + for (var i = 0; i < channels.length; i++) { + var key = channels[i]; + $.Topic('/connection_controller/channel_update').publish( + {channel: key, state: channel_data[key]} + ); + } + } +}); diff --git a/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.html b/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.html new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.html @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.js b/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.js new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.js @@ -0,0 +1,38 @@ +Polymer({ + is: 'rhodecode-toast', + properties: { + toasts: { + type: Array, + value: function(){ + return [] + } + } + }, + observers: [ + '_changedToasts(toasts.splices)' + ], + _changedToasts: function(newValue, oldValue){ + this.$['p-toast'].notifyResize(); + }, + dismissNotifications: function(){ + this.$['p-toast'].close(); + }, + handleClosed: function(){ + this.splice('toasts', 0); + }, + open: function(){ + this.$['p-toast'].open(); + }, + handleNotification: function(data){ + if (!templateContext.rhodecode_user.notification_status && !data.message.force) { + // do not act if notifications are disabled + return + } + this.push('toasts',{ + level: data.message.level, + message: data.message.message + }); + this.open(); + }, + _gettext: _gettext +}); diff --git a/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.less b/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.less new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-toast/rhodecode-toast.less @@ -0,0 +1,29 @@ +@import '../../../../css/variables'; +@import '../../../../css/mixins'; + +paper-toast{ + width: 100%; + min-width: 400px; + padding: 0px; + --paper-toast-background-color: transparent; + --paper-toast-color: #000000; + .box-shadow(none); +} +paper-toast a{ + font-weight: bold +} + +.toast-message-holder { +} +.toast-close { + right: -5px; + position: absolute; + bottom: -45px; + paper-button{ + .box-shadow(0 2px 5px 0 rgba(0, 0, 0, 0.15)); + } +} +paper-toast .alert{ + margin: @padding 0 0 0; + .box-shadow(0 2px 5px 0 rgba(0, 0, 0, 0.15)); +} diff --git a/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.html b/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.html new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.js b/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.js new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.js @@ -0,0 +1,16 @@ +Polymer({ + is: 'rhodecode-toggle', + properties: { + noSpinner: { type: Boolean, value: false, reflectToAttribute:true}, + tooltipText: { type: String, value: "Click to toggle", reflectToAttribute:true}, + checked: { type: Boolean, value: false, reflectToAttribute:true}, + active: { type: Boolean, value: false, reflectToAttribute:true, notify:true} + }, + shouldShow: function(){ + return !this.noSpinner + }, + labelStatus: function(isActive){ + return this.checked? 'Enabled' : "Disabled" + } + +}); \ No newline at end of file diff --git a/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.less b/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.less new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-toggle/rhodecode-toggle.less @@ -0,0 +1,14 @@ +@import '../../../../css/variables'; + +.rc-toggle { + float: left; + position: relative; + + paper-spinner { + position: absolute; + top: 0; + left: -30px; + width: 20px; + height: 20px; + } +} \ No newline at end of file diff --git a/rhodecode/public/js/src/components/rhodecode-unsafe-html/rhodecode-unsafe-html.html b/rhodecode/public/js/src/components/rhodecode-unsafe-html/rhodecode-unsafe-html.html new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-unsafe-html/rhodecode-unsafe-html.html @@ -0,0 +1,9 @@ + + + + + + diff --git a/rhodecode/public/js/src/components/rhodecode-unsafe-html/rhodecode-unsafe-html.js b/rhodecode/public/js/src/components/rhodecode-unsafe-html/rhodecode-unsafe-html.js new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/rhodecode-unsafe-html/rhodecode-unsafe-html.js @@ -0,0 +1,12 @@ +Polymer({ + is: 'rhodecode-unsafe-html', + properties: { + text: { + type: String, + observer: '_handleText' + } + }, + _handleText: function(newVal, oldVal){ + this.innerHTML = this.text; + } +}) diff --git a/rhodecode/public/js/src/components/root-styles-prefix.html b/rhodecode/public/js/src/components/root-styles-prefix.html new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/root-styles-prefix.html @@ -0,0 +1,3 @@ + + + diff --git a/rhodecode/public/js/src/components/shared-components.html b/rhodecode/public/js/src/components/shared-components.html new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/shared-components.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/rhodecode/public/js/src/components/shared-styles.html b/rhodecode/public/js/src/components/shared-styles.html new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/components/shared-styles.html @@ -0,0 +1,5 @@ + + + diff --git a/rhodecode/public/js/src/plugins/toastr.js b/rhodecode/public/js/src/plugins/toastr.js deleted file mode 100644 --- a/rhodecode/public/js/src/plugins/toastr.js +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Toastr - * Copyright 2012-2015 - * Authors: John Papa, Hans Fjällemark, and Tim Ferrell. - * All Rights Reserved. - * Use, reproduction, distribution, and modification of this code is subject to the terms and - * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php - * - * ARIA Support: Greta Krafsig - * - * Project: https://github.com/CodeSeven/toastr - */ -/* global define */ -(function (define) { - define(['jquery'], function ($) { - return (function () { - var $container; - var listener; - var toastId = 0; - var toastType = { - error: 'error', - info: 'info', - success: 'success', - warning: 'warning' - }; - - var toastr = { - clear: clear, - remove: remove, - error: error, - getContainer: getContainer, - info: info, - options: {}, - subscribe: subscribe, - success: success, - version: '2.1.2', - warning: warning - }; - - var previousToast; - - return toastr; - - //////////////// - - function error(message, title, optionsOverride) { - return notify({ - type: toastType.error, - iconClass: getOptions().iconClasses.error, - message: message, - optionsOverride: optionsOverride, - title: title - }); - } - - function getContainer(options, create) { - if (!options) { options = getOptions(); } - $container = $('#' + options.containerId); - if ($container.length) { - return $container; - } - if (create) { - $container = createContainer(options); - } - return $container; - } - - function info(message, title, optionsOverride) { - return notify({ - type: toastType.info, - iconClass: getOptions().iconClasses.info, - message: message, - optionsOverride: optionsOverride, - title: title - }); - } - - function subscribe(callback) { - listener = callback; - } - - function success(message, title, optionsOverride) { - return notify({ - type: toastType.success, - iconClass: getOptions().iconClasses.success, - message: message, - optionsOverride: optionsOverride, - title: title - }); - } - - function warning(message, title, optionsOverride) { - return notify({ - type: toastType.warning, - iconClass: getOptions().iconClasses.warning, - message: message, - optionsOverride: optionsOverride, - title: title - }); - } - - function clear($toastElement, clearOptions) { - var options = getOptions(); - if (!$container) { getContainer(options); } - if (!clearToast($toastElement, options, clearOptions)) { - clearContainer(options); - } - } - - function remove($toastElement) { - var options = getOptions(); - if (!$container) { getContainer(options); } - if ($toastElement && $(':focus', $toastElement).length === 0) { - removeToast($toastElement); - return; - } - if ($container.children().length) { - $container.remove(); - } - } - - // internal functions - - function clearContainer (options) { - var toastsToClear = $container.children(); - for (var i = toastsToClear.length - 1; i >= 0; i--) { - clearToast($(toastsToClear[i]), options); - } - } - - function clearToast ($toastElement, options, clearOptions) { - var force = clearOptions && clearOptions.force ? clearOptions.force : false; - if ($toastElement && (force || $(':focus', $toastElement).length === 0)) { - $toastElement[options.hideMethod]({ - duration: options.hideDuration, - easing: options.hideEasing, - complete: function () { removeToast($toastElement); } - }); - return true; - } - return false; - } - - function createContainer(options) { - $container = $('
') - .attr('id', options.containerId) - .addClass(options.positionClass) - .attr('aria-live', 'polite') - .attr('role', 'alert'); - - $container.appendTo($(options.target)); - return $container; - } - - function getDefaults() { - return { - tapToDismiss: true, - toastClass: 'toast', - containerId: 'toast-container', - debug: false, - - showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery - showDuration: 300, - showEasing: 'swing', //swing and linear are built into jQuery - onShown: undefined, - hideMethod: 'fadeOut', - hideDuration: 1000, - hideEasing: 'swing', - onHidden: undefined, - closeMethod: false, - closeDuration: false, - closeEasing: false, - - extendedTimeOut: 1000, - iconClasses: { - error: 'toast-error', - info: 'toast-info', - success: 'toast-success', - warning: 'toast-warning' - }, - iconClass: 'toast-info', - positionClass: 'toast-top-right', - timeOut: 5000, // Set timeOut and extendedTimeOut to 0 to make it sticky - titleClass: 'toast-title', - messageClass: 'toast-message', - escapeHtml: false, - target: 'body', - closeHtml: '', - newestOnTop: true, - preventDuplicates: false, - progressBar: false - }; - } - - function publish(args) { - if (!listener) { return; } - listener(args); - } - - function notify(map) { - var options = getOptions(); - var iconClass = map.iconClass || options.iconClass; - - if (typeof (map.optionsOverride) !== 'undefined') { - options = $.extend(options, map.optionsOverride); - iconClass = map.optionsOverride.iconClass || iconClass; - } - - if (shouldExit(options, map)) { return; } - - toastId++; - - $container = getContainer(options, true); - - var intervalId = null; - var $toastElement = $('
'); - var $titleElement = $('
'); - var $messageElement = $('
'); - var $progressElement = $('
'); - var $closeElement = $(options.closeHtml); - var progressBar = { - intervalId: null, - hideEta: null, - maxHideTime: null - }; - var response = { - toastId: toastId, - state: 'visible', - startTime: new Date(), - options: options, - map: map - }; - - personalizeToast(); - - displayToast(); - - handleEvents(); - - publish(response); - - if (options.debug && console) { - console.log(response); - } - - return $toastElement; - - function escapeHtml(source) { - if (source == null) - source = ""; - - return new String(source) - .replace(/&/g, '&') - .replace(/"/g, '"') - .replace(/'/g, ''') - .replace(//g, '>'); - } - - function personalizeToast() { - setIcon(); - setTitle(); - setMessage(); - setCloseButton(); - setProgressBar(); - setSequence(); - } - - function handleEvents() { - $toastElement.hover(stickAround, delayedHideToast); - if (!options.onclick && options.tapToDismiss) { - $toastElement.click(hideToast); - } - - if (options.closeButton && $closeElement) { - $closeElement.click(function (event) { - if (event.stopPropagation) { - event.stopPropagation(); - } else if (event.cancelBubble !== undefined && event.cancelBubble !== true) { - event.cancelBubble = true; - } - hideToast(true); - }); - } - - if (options.onclick) { - $toastElement.click(function (event) { - options.onclick(event); - hideToast(); - }); - } - } - - function displayToast() { - $toastElement.hide(); - - $toastElement[options.showMethod]( - {duration: options.showDuration, easing: options.showEasing, complete: options.onShown} - ); - - if (options.timeOut > 0) { - intervalId = setTimeout(hideToast, options.timeOut); - progressBar.maxHideTime = parseFloat(options.timeOut); - progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime; - if (options.progressBar) { - progressBar.intervalId = setInterval(updateProgress, 10); - } - } - } - - function setIcon() { - if (map.iconClass) { - $toastElement.addClass(options.toastClass).addClass(iconClass); - } - } - - function setSequence() { - if (options.newestOnTop) { - $container.prepend($toastElement); - } else { - $container.append($toastElement); - } - } - - function setTitle() { - if (map.title) { - $titleElement.append(!options.escapeHtml ? map.title : escapeHtml(map.title)).addClass(options.titleClass); - $toastElement.append($titleElement); - } - } - - function setMessage() { - if (map.message) { - $messageElement.append(!options.escapeHtml ? map.message : escapeHtml(map.message)).addClass(options.messageClass); - $toastElement.append($messageElement); - } - } - - function setCloseButton() { - if (options.closeButton) { - $closeElement.addClass('toast-close-button').attr('role', 'button'); - $toastElement.prepend($closeElement); - } - } - - function setProgressBar() { - if (options.progressBar) { - $progressElement.addClass('toast-progress'); - $toastElement.prepend($progressElement); - } - } - - function shouldExit(options, map) { - if (options.preventDuplicates) { - if (map.message === previousToast) { - return true; - } else { - previousToast = map.message; - } - } - return false; - } - - function hideToast(override) { - var method = override && options.closeMethod !== false ? options.closeMethod : options.hideMethod; - var duration = override && options.closeDuration !== false ? - options.closeDuration : options.hideDuration; - var easing = override && options.closeEasing !== false ? options.closeEasing : options.hideEasing; - if ($(':focus', $toastElement).length && !override) { - return; - } - clearTimeout(progressBar.intervalId); - return $toastElement[method]({ - duration: duration, - easing: easing, - complete: function () { - removeToast($toastElement); - if (options.onHidden && response.state !== 'hidden') { - options.onHidden(); - } - response.state = 'hidden'; - response.endTime = new Date(); - publish(response); - } - }); - } - - function delayedHideToast() { - if (options.timeOut > 0 || options.extendedTimeOut > 0) { - intervalId = setTimeout(hideToast, options.extendedTimeOut); - progressBar.maxHideTime = parseFloat(options.extendedTimeOut); - progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime; - } - } - - function stickAround() { - clearTimeout(intervalId); - progressBar.hideEta = 0; - $toastElement.stop(true, true)[options.showMethod]( - {duration: options.showDuration, easing: options.showEasing} - ); - } - - function updateProgress() { - var percentage = ((progressBar.hideEta - (new Date().getTime())) / progressBar.maxHideTime) * 100; - $progressElement.width(percentage + '%'); - } - } - - function getOptions() { - return $.extend({}, getDefaults(), toastr.options); - } - - function removeToast($toastElement) { - if (!$container) { $container = getContainer(); } - if ($toastElement.is(':visible')) { - return; - } - $toastElement.remove(); - $toastElement = null; - if ($container.children().length === 0) { - $container.remove(); - previousToast = undefined; - } - } - - })(); - }); -}(typeof define === 'function' && define.amd ? define : function (deps, factory) { - if (typeof module !== 'undefined' && module.exports) { //Node - module.exports = factory(require('jquery')); - } else { - window.toastr = factory(window.jQuery); - } -})); diff --git a/rhodecode/public/js/src/rhodecode.js b/rhodecode/public/js/src/rhodecode.js --- a/rhodecode/public/js/src/rhodecode.js +++ b/rhodecode/public/js/src/rhodecode.js @@ -366,15 +366,9 @@ function offsetScroll(element, offset){ // At the time of development, Chrome didn't seem to support jquery's :target // element, so I had to scroll manually if (location.hash) { - var splitIx = location.hash.indexOf('/?/'); - if (splitIx !== -1){ - var loc = location.hash.slice(0, splitIx); - var remainder = location.hash.slice(splitIx + 2); - } - else{ - var loc = location.hash; - var remainder = null; - } + var result = splitDelimitedHash(location.hash); + var loc = result.loc; + var remainder = result.remainder; if (loc.length > 1){ var lineno = $(loc+'.lineno'); if (lineno.length > 0){ diff --git a/rhodecode/public/js/src/rhodecode/comments.js b/rhodecode/public/js/src/rhodecode/comments.js --- a/rhodecode/public/js/src/rhodecode/comments.js +++ b/rhodecode/public/js/src/rhodecode/comments.js @@ -38,7 +38,8 @@ var tableTr = function(cls, body){ var comment_id = fromHTML(body).children[0].id.split('comment-')[1]; var id = 'comment-tr-{0}'.format(comment_id); var _html = ('