js: migrate to polymer 3.x

This commit is contained in:
Marcin Lulek 2018-10-30 15:25:00 +01:00
parent d75cc12c5c
commit b76a293536
15 changed files with 1404 additions and 1180 deletions

View file

@ -45,6 +45,16 @@
"style-loader": "^0.21.0",
"webpack-uglify-js-plugin": "^1.1.9",
"raw-loader": "1.0.0-beta.0",
"ts-loader": "^1.3.3"
"ts-loader": "^1.3.3",
"@webcomponents/webcomponentsjs": "^2.0.0",
"@polymer/polymer": "^3.0.0",
"@polymer/paper-button": "^3.0.0",
"@polymer/paper-spinner": "^3.0.0",
"@polymer/paper-tooltip": "^3.0.0",
"@polymer/paper-toast": "^3.0.0",
"@polymer/paper-toggle-button": "^3.0.0",
"@polymer/iron-ajax": "^3.0.0",
"@polymer/iron-autogrow-textarea": "^3.0.0",
"@polymer/iron-a11y-keys": "^3.0.0"
}
}

View file

@ -4,6 +4,222 @@
let
sources = {
"@polymer/font-roboto-3.0.2" = {
name = "_at_polymer_slash_font-roboto";
packageName = "@polymer/font-roboto";
version = "3.0.2";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/font-roboto/-/font-roboto-3.0.2.tgz";
sha512 = "tx5TauYSmzsIvmSqepUPDYbs4/Ejz2XbZ1IkD7JEGqkdNUJlh+9KU85G56Tfdk/xjEZ8zorFfN09OSwiMrIQWA==";
};
};
"@polymer/iron-a11y-announcer-3.0.1" = {
name = "_at_polymer_slash_iron-a11y-announcer";
packageName = "@polymer/iron-a11y-announcer";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-a11y-announcer/-/iron-a11y-announcer-3.0.1.tgz";
sha512 = "Xiqmpz0AEEbMNGYPpbrXBIrcI/xaR4tn77pmSLfxVKGGwjEUR/YrRgyIwXp4EN7lvst1dFC8kyl2hLga0uDIVQ==";
};
};
"@polymer/iron-a11y-keys-3.0.1" = {
name = "_at_polymer_slash_iron-a11y-keys";
packageName = "@polymer/iron-a11y-keys";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-a11y-keys/-/iron-a11y-keys-3.0.1.tgz";
sha512 = "zmTi8xHeY4ZMJLAitW2hAmW5zXZ35hVy/eHQUFadAlOccuBK3oRRmoPRQRaZgpyJrCVFDAQRXXzzJtUDil/0CA==";
};
};
"@polymer/iron-a11y-keys-behavior-3.0.1" = {
name = "_at_polymer_slash_iron-a11y-keys-behavior";
packageName = "@polymer/iron-a11y-keys-behavior";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-a11y-keys-behavior/-/iron-a11y-keys-behavior-3.0.1.tgz";
sha512 = "lnrjKq3ysbBPT/74l0Fj0U9H9C35Tpw2C/tpJ8a+5g8Y3YJs1WSZYnEl1yOkw6sEyaxOq/1DkzH0+60gGu5/PQ==";
};
};
"@polymer/iron-ajax-3.0.1" = {
name = "_at_polymer_slash_iron-ajax";
packageName = "@polymer/iron-ajax";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-ajax/-/iron-ajax-3.0.1.tgz";
sha512 = "7+TPEAfWsRdhj1Y8UeF1759ktpVu+c3sG16rJiUC3wF9+woQ9xI1zUm2d59i7Yc3aDEJrR/Q8Y262KlOvyGVNg==";
};
};
"@polymer/iron-autogrow-textarea-3.0.1" = {
name = "_at_polymer_slash_iron-autogrow-textarea";
packageName = "@polymer/iron-autogrow-textarea";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-autogrow-textarea/-/iron-autogrow-textarea-3.0.1.tgz";
sha512 = "FgSL7APrOSL9Vu812sBCFlQ17hvnJsBAV2C2e1UAiaHhB+dyfLq8gGdGUpqVWuGJ50q4Y/49QwCNnLf85AdVYA==";
};
};
"@polymer/iron-behaviors-3.0.1" = {
name = "_at_polymer_slash_iron-behaviors";
packageName = "@polymer/iron-behaviors";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-behaviors/-/iron-behaviors-3.0.1.tgz";
sha512 = "IMEwcv1lhf1HSQxuyWOUIL0lOBwmeaoSTpgCJeP9IBYnuB1SPQngmfRuHKgK6/m9LQ9F9miC7p3HeQQUdKAE0w==";
};
};
"@polymer/iron-checked-element-behavior-3.0.1" = {
name = "_at_polymer_slash_iron-checked-element-behavior";
packageName = "@polymer/iron-checked-element-behavior";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-checked-element-behavior/-/iron-checked-element-behavior-3.0.1.tgz";
sha512 = "aDr0cbCNVq49q+pOqa6CZutFh+wWpwPMLpEth9swx+GkAj+gCURhuQkaUYhIo5f2egDbEioR1aeHMnPlU9dQZA==";
};
};
"@polymer/iron-fit-behavior-3.0.1" = {
name = "_at_polymer_slash_iron-fit-behavior";
packageName = "@polymer/iron-fit-behavior";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-fit-behavior/-/iron-fit-behavior-3.0.1.tgz";
sha512 = "/M0B1L30k31vmwNBaGuZcxzUAhJSHoGccb/DF0CDKI/hT8UlkTvcyemaWdOpmHHLgY52ceKIkRwA3AeXrKyvaQ==";
};
};
"@polymer/iron-flex-layout-3.0.1" = {
name = "_at_polymer_slash_iron-flex-layout";
packageName = "@polymer/iron-flex-layout";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-flex-layout/-/iron-flex-layout-3.0.1.tgz";
sha512 = "7gB869czArF+HZcPTVSgvA7tXYFze9EKckvM95NB7SqYF+NnsQyhoXgKnpFwGyo95lUjUW9TFDLUwDXnCYFtkw==";
};
};
"@polymer/iron-form-element-behavior-3.0.1" = {
name = "_at_polymer_slash_iron-form-element-behavior";
packageName = "@polymer/iron-form-element-behavior";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-form-element-behavior/-/iron-form-element-behavior-3.0.1.tgz";
sha512 = "G/e2KXyL5AY7mMjmomHkGpgS0uAf4ovNpKhkuUTRnMuMJuf589bKqE85KN4ovE1Tzhv2hJoh/igyD6ekHiYU1A==";
};
};
"@polymer/iron-meta-3.0.1" = {
name = "_at_polymer_slash_iron-meta";
packageName = "@polymer/iron-meta";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-meta/-/iron-meta-3.0.1.tgz";
sha512 = "pWguPugiLYmWFV9UWxLWzZ6gm4wBwQdDy4VULKwdHCqR7OP7u98h+XDdGZsSlDPv6qoryV/e3tGHlTIT0mbzJA==";
};
};
"@polymer/iron-overlay-behavior-3.0.2" = {
name = "_at_polymer_slash_iron-overlay-behavior";
packageName = "@polymer/iron-overlay-behavior";
version = "3.0.2";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.2.tgz";
sha512 = "j1qmt6mJHCwpe1mKOvqK5kcCUPQr5LSrlqpgRDbUuLgUfNJ/vGTipjrkBlfbEUagm5FEQdc1VLPLSQP6WVuP9g==";
};
};
"@polymer/iron-resizable-behavior-3.0.1" = {
name = "_at_polymer_slash_iron-resizable-behavior";
packageName = "@polymer/iron-resizable-behavior";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-resizable-behavior/-/iron-resizable-behavior-3.0.1.tgz";
sha512 = "FyHxRxFspVoRaeZSWpT3y0C9awomb4tXXolIJcZ7RvXhMP632V5lez+ch5G5SwK0LpnAPkg35eB0LPMFv+YMMQ==";
};
};
"@polymer/iron-validatable-behavior-3.0.1" = {
name = "_at_polymer_slash_iron-validatable-behavior";
packageName = "@polymer/iron-validatable-behavior";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/iron-validatable-behavior/-/iron-validatable-behavior-3.0.1.tgz";
sha512 = "wwpYh6wOa4fNI+jH5EYKC7TVPYQ2OfgQqocWat7GsNWcsblKYhLYbwsvEY5nO0n2xKqNfZzDLrUom5INJN7msQ==";
};
};
"@polymer/paper-behaviors-3.0.1" = {
name = "_at_polymer_slash_paper-behaviors";
packageName = "@polymer/paper-behaviors";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/paper-behaviors/-/paper-behaviors-3.0.1.tgz";
sha512 = "6knhj69fPJejv8qR0kCSUY+Q0XjaUf0OSnkjRjmTJPAwSrRYtgqE+l6P1FfA+py1X/cUjgne9EF5rMZAKJIg1g==";
};
};
"@polymer/paper-button-3.0.1" = {
name = "_at_polymer_slash_paper-button";
packageName = "@polymer/paper-button";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/paper-button/-/paper-button-3.0.1.tgz";
sha512 = "JRNBc+Oj9EWnmyLr7FcCr8T1KAnEHPh6mosln9BUdkM+qYaYsudSICh3cjTIbnj6AuF5OJidoLkM1dlyj0j6Zg==";
};
};
"@polymer/paper-ripple-3.0.1" = {
name = "_at_polymer_slash_paper-ripple";
packageName = "@polymer/paper-ripple";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/paper-ripple/-/paper-ripple-3.0.1.tgz";
sha512 = "dgOe12GyCF1VZBLUQqnzGWlf3xb255FajNCVB1VFj/AtskYtoamnafa7m3a+1vs+C8qbg4Benn5KwgxVDSW4cg==";
};
};
"@polymer/paper-spinner-3.0.1" = {
name = "_at_polymer_slash_paper-spinner";
packageName = "@polymer/paper-spinner";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/paper-spinner/-/paper-spinner-3.0.1.tgz";
sha512 = "MYIU6qWZnhZ5yNFOBzROPgBteGfxKEnDZ6bCgjrvUtJkBuQEz0MQZzSE/zmZc0oaJ9u5QK5xAFuYdudsGv7+sQ==";
};
};
"@polymer/paper-styles-3.0.1" = {
name = "_at_polymer_slash_paper-styles";
packageName = "@polymer/paper-styles";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/paper-styles/-/paper-styles-3.0.1.tgz";
sha512 = "y6hmObLqlCx602TQiSBKHqjwkE7xmDiFkoxdYGaNjtv4xcysOTdVJsDR/R9UHwIaxJ7gHlthMSykir1nv78++g==";
};
};
"@polymer/paper-toast-3.0.1" = {
name = "_at_polymer_slash_paper-toast";
packageName = "@polymer/paper-toast";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/paper-toast/-/paper-toast-3.0.1.tgz";
sha512 = "pizuogzObniDdICUc6dSLrnDt2VzzoRne1gCmbD6sfOATVv5tc8UfrqhA2iHngbNBEbniBiciS3iogdp5KTVUQ==";
};
};
"@polymer/paper-toggle-button-3.0.1" = {
name = "_at_polymer_slash_paper-toggle-button";
packageName = "@polymer/paper-toggle-button";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/paper-toggle-button/-/paper-toggle-button-3.0.1.tgz";
sha512 = "jadZB60fycT7YnSAH0H23LYo6/2HYmMZTtNr9LpdSIRFPLX6mqqxewex92cFz019bMKaRJgORn308hRlJo2u6A==";
};
};
"@polymer/paper-tooltip-3.0.1" = {
name = "_at_polymer_slash_paper-tooltip";
packageName = "@polymer/paper-tooltip";
version = "3.0.1";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/paper-tooltip/-/paper-tooltip-3.0.1.tgz";
sha512 = "yiUk09opTEnE1lK+tb501ENb+yQBi4p++Ep0eGJAHesVYKVMPNgPphVKkIizkDaU+n0SE+zXfTsRbYyOMDYXSg==";
};
};
"@polymer/polymer-3.1.0" = {
name = "_at_polymer_slash_polymer";
packageName = "@polymer/polymer";
version = "3.1.0";
src = fetchurl {
url = "https://registry.npmjs.org/@polymer/polymer/-/polymer-3.1.0.tgz";
sha512 = "hwN8IMERsFATz/9dSMxYHL+84J9uBkPuuarxJWlTsppZ4CAYTZKnepBfNrKoyNsafBmA3yXBiiKPPf+fJtza7A==";
};
};
"@types/clone-0.1.30" = {
name = "_at_types_slash_clone";
packageName = "@types/clone";
@ -193,6 +409,24 @@ let
sha512 = "mJ3QKWtCchL1vhU/kZlJnLPuQZnlDOdZsyP0bbLWPGdYsQDnSBvyTLhzwBA3QAMlzEL9V4JHygEmK6/OTEyytA==";
};
};
"@webcomponents/shadycss-1.5.2" = {
name = "_at_webcomponents_slash_shadycss";
packageName = "@webcomponents/shadycss";
version = "1.5.2";
src = fetchurl {
url = "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.5.2.tgz";
sha512 = "0OyrmVc7S+INtzoqP2ofAo+OdVn2Nj0Qvq4wD9FEGN7nMmLRxaD2mzy6hD6EslzxUSuGH302CDU4KXiY66SEqg==";
};
};
"@webcomponents/webcomponentsjs-2.1.3" = {
name = "_at_webcomponents_slash_webcomponentsjs";
packageName = "@webcomponents/webcomponentsjs";
version = "2.1.3";
src = fetchurl {
url = "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.1.3.tgz";
sha512 = "0UHJNY88lR3pnEYtBVT7F8cuuxOiITQGWJa0LxoELqkBSB7IabzJFOj5K99PajD3CGAsWpjB0CAeijfe376Y1w==";
};
};
"@xtuc/ieee754-1.2.0" = {
name = "_at_xtuc_slash_ieee754";
packageName = "@xtuc/ieee754";
@ -1454,13 +1688,13 @@ let
sha1 = "b534e7c734c4f81ec5fbe8aca2ad24354b962c6c";
};
};
"caniuse-db-1.0.30000899" = {
"caniuse-db-1.0.30000900" = {
name = "caniuse-db";
packageName = "caniuse-db";
version = "1.0.30000899";
version = "1.0.30000900";
src = fetchurl {
url = "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000899.tgz";
sha512 = "MSCUohyoLU4/PGapapw/PLQkmQ+sFgzX6e3tM6ue8HX9HW9rBD5gRiAYKhC8r0QkvUE0pWTA8Ze6f3jrzBizVg==";
url = "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000900.tgz";
sha512 = "fvicVRDlhHIQpt/bmbLl3hDHKUZb5ZP8O2OuZLz2fSEPlUBbvwwbhhqhGS617ldN6bDoo9A3+MQKQyFq0p7UXA==";
};
};
"caniuse-lite-1.0.30000899" = {
@ -7482,6 +7716,30 @@ let
version = "1.0.0";
src = ./..;
dependencies = [
sources."@polymer/font-roboto-3.0.2"
sources."@polymer/iron-a11y-announcer-3.0.1"
sources."@polymer/iron-a11y-keys-3.0.1"
sources."@polymer/iron-a11y-keys-behavior-3.0.1"
sources."@polymer/iron-ajax-3.0.1"
sources."@polymer/iron-autogrow-textarea-3.0.1"
sources."@polymer/iron-behaviors-3.0.1"
sources."@polymer/iron-checked-element-behavior-3.0.1"
sources."@polymer/iron-fit-behavior-3.0.1"
sources."@polymer/iron-flex-layout-3.0.1"
sources."@polymer/iron-form-element-behavior-3.0.1"
sources."@polymer/iron-meta-3.0.1"
sources."@polymer/iron-overlay-behavior-3.0.2"
sources."@polymer/iron-resizable-behavior-3.0.1"
sources."@polymer/iron-validatable-behavior-3.0.1"
sources."@polymer/paper-behaviors-3.0.1"
sources."@polymer/paper-button-3.0.1"
sources."@polymer/paper-ripple-3.0.1"
sources."@polymer/paper-spinner-3.0.1"
sources."@polymer/paper-styles-3.0.1"
sources."@polymer/paper-toast-3.0.1"
sources."@polymer/paper-toggle-button-3.0.1"
sources."@polymer/paper-tooltip-3.0.1"
sources."@polymer/polymer-3.1.0"
sources."@types/clone-0.1.30"
sources."@types/node-6.14.0"
sources."@types/parse5-2.2.34"
@ -7503,6 +7761,8 @@ let
sources."@webassemblyjs/wasm-parser-1.7.10"
sources."@webassemblyjs/wast-parser-1.7.10"
sources."@webassemblyjs/wast-printer-1.7.10"
sources."@webcomponents/shadycss-1.5.2"
sources."@webcomponents/webcomponentsjs-2.1.3"
sources."@xtuc/ieee754-1.2.0"
sources."@xtuc/long-4.2.1"
sources."abbrev-1.1.1"
@ -7716,7 +7976,7 @@ let
sources."browserslist-1.7.7"
];
})
sources."caniuse-db-1.0.30000899"
sources."caniuse-db-1.0.30000900"
sources."caniuse-lite-1.0.30000899"
sources."caseless-0.12.0"
sources."center-align-0.1.3"

View file

@ -1,610 +0,0 @@
<link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../../../../bower_components/iron-ajax/iron-ajax.html">
<!--
`<channelstream-connection>` allows you to connect and interact with channelstream server
abstracting websocket/long-polling connections from you.
In typical use, just slap some `<channelstream-connection>` at the top of your body:
<body>
<channelstream-connection
username="{{user.username}}"
connect-url="http://127.0.0.1:8000/demo/connect"
disconnect-url="http://127.0.0.1:8000/disconnect"
subscribe-url="http://127.0.0.1:8000/demo/subscribe"
message-url="http://127.0.0.1:8000/demo/message"
long-poll-url="http://127.0.0.1:8000/listen"
websocket-url="http://127.0.0.1:8000/ws"
channels-url='["channel1", "channel2"]' />
Then you can do `channelstreamElem.connect()` to kick off your connection.
This element also handles automatic reconnections.
## Default handlers
By default element has a listener attached that will fire `startListening()` handler on `channelstream-connected` event.
By default element has a listener attached that will fire `retryConnection()` handler on `channelstream-connect-error` event,
this handler will forever try to re-establish connection to the server incrementing intervals between retries up to 1 minute.
-->
<dom-module id="channelstream-connection">
<template>
<iron-ajax
id="ajaxConnect"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingConnect}}"
last-response="{{connectLastResponse}}"
on-response="_handleConnect"
on-error="_handleConnectError"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxDisconnect"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingDisconnect}}"
last-response="{{_disconnectLastResponse}}"
on-response="_handleDisconnect"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxSubscribe"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingSubscribe}}"
last-response="{{subscribeLastResponse}}"
on-response="_handleSubscribe"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxUnsubscribe"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingUnsubscribe}}"
last-response="{{unsubscribeLastResponse}}"
on-response="_handleUnsubscribe"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxMessage"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingMessage}}"
last-response="{{messageLastResponse}}"
on-response="_handleMessage"
on-error="_handleMessageError"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxListen"
url=""
handle-as="text"
loading="{{loadingListen}}"
last-response="{{listenLastResponse}}"
on-request="_handleListenOpen"
on-error="_handleListenError"
on-response="_handleListenMessageEvent"
debounce-duration="100"></iron-ajax>
</template>
<script>
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);
}
});
</script>
</dom-module>

View file

@ -0,0 +1,577 @@
import {Polymer, html} from '@polymer/polymer/polymer-legacy';
import '@polymer/iron-ajax/iron-ajax.js';
const elemTemplate = html`
<iron-ajax
id="ajaxConnect"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingConnect}}"
last-response="{{connectLastResponse}}"
on-response="_handleConnect"
on-error="_handleConnectError"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxDisconnect"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingDisconnect}}"
last-response="{{_disconnectLastResponse}}"
on-response="_handleDisconnect"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxSubscribe"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingSubscribe}}"
last-response="{{subscribeLastResponse}}"
on-response="_handleSubscribe"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxUnsubscribe"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingUnsubscribe}}"
last-response="{{unsubscribeLastResponse}}"
on-response="_handleUnsubscribe"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxMessage"
url=""
handle-as="json"
method="post"
content-type="application/json"
loading="{{loadingMessage}}"
last-response="{{messageLastResponse}}"
on-response="_handleMessage"
on-error="_handleMessageError"
debounce-duration="100"></iron-ajax>
<iron-ajax
id="ajaxListen"
url=""
handle-as="text"
loading="{{loadingListen}}"
last-response="{{listenLastResponse}}"
on-request="_handleListenOpen"
on-error="_handleListenError"
on-response="_handleListenMessageEvent"
debounce-duration="100"></iron-ajax>
`
Polymer({
is: 'channelstream-connection',
_template: elemTemplate,
/**
* 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);
}
});

View file

@ -1,7 +1,7 @@
import '../../../../../bower_components/iron-ajax/iron-ajax.html';
import '@polymer/iron-ajax/iron-ajax.js';
import './root-styles.gen.html';
import './channelstream-connection/channelstream-connection.html';
import './rhodecode-toast/rhodecode-toast.html';
import './rhodecode-toggle/rhodecode-toggle.html';
import './rhodecode-unsafe-html/rhodecode-unsafe-html.html';
import './rhodecode-app/rhodecode-app.html';
import './channelstream-connection/channelstream-connection.js';
import './rhodecode-toast/rhodecode-toast.js';
import './rhodecode-toggle/rhodecode-toggle.js';
import './rhodecode-unsafe-html/rhodecode-unsafe-html.js';
import './rhodecode-app/rhodecode-app.js';

View file

@ -1,195 +0,0 @@
<link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../channelstream-connection/channelstream-connection.html">
<link rel="import" href="../rhodecode-toast/rhodecode-toast.html">
<link rel="import" href="../rhodecode-favicon/rhodecode-favicon.html">
<dom-module id="rhodecode-app">
<template>
<channelstream-connection
id="channelstream-connection"
on-channelstream-listen-message="receivedMessage"
on-channelstream-connected="handleConnected"
on-channelstream-subscribed="handleSubscribed">
</channelstream-connection>
<rhodecode-favicon></rhodecode-favicon>
</template>
<script>
var ccLog = Logger.get('RhodeCodeApp');
ccLog.setLevel(Logger.OFF);
class RhodecodeApp extends Polymer.Element {
static get is() {
return 'rhodecode-app';
}
connectedCallback() {
super.connectedCallback();
ccLog.debug('rhodeCodeApp created');
$.Topic('/notifications').subscribe(this.handleNotifications.bind(this));
$.Topic('/favicon/update').subscribe(this.faviconUpdate.bind(this));
$.Topic('/connection_controller/subscribe').subscribe(
this.subscribeToChannelTopic.bind(this));
// this event can be used to coordinate plugins to do their
// initialization before channelstream is kicked off
$.Topic('/__MAIN_APP__').publish({});
for (var i = 0; i < alertMessagePayloads.length; i++) {
$.Topic('/notifications').publish(alertMessagePayloads[i]);
}
this.initPlugins();
// after rest of application loads and topics get fired, launch connection
$(document).ready(function () {
this.kickoffChannelstreamPlugin();
}.bind(this));
}
initPlugins() {
for (var i = 0; i < window.APPLICATION_PLUGINS.length; i++) {
var pluginDef = window.APPLICATION_PLUGINS[i];
if (pluginDef.component) {
var pluginElem = document.createElement(pluginDef.component);
this.shadowRoot.appendChild(pluginElem);
if (typeof pluginElem.init !== 'undefined') {
pluginElem.init();
}
}
}
}
/** proxy to channelstream connection */
getChannelStreamConnection() {
return this.$['channelstream-connection'];
}
handleNotifications(data) {
var elem = document.getElementById('notifications');
if (elem) {
elem.handleNotification(data);
}
}
faviconUpdate(data) {
this.shadowRoot.querySelector('rhodecode-favicon').counter = data.count;
}
/** opens connection to ws server */
kickoffChannelstreamPlugin(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.getChannelStreamConnection();
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() {
// subscribe to different channels data is sent.
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);
}
if (templateContext.commit_data.commit_id) {
var channelName = '/repo$' + templateContext.repo_name + '$/commit/' +
String(templateContext.commit_data.commit_id);
channels.push(channelName);
}
return channels;
}
/** subscribes users from channels in channelstream */
subscribeToChannelTopic(channels) {
var channelstreamConnection = this.getChannelStreamConnection();
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(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(event) {
var channelstreamConnection = this.getChannelStreamConnection();
channelstreamConnection.set('channelsState',
event.detail.channels_info);
channelstreamConnection.set('userState', event.detail.state);
channelstreamConnection.set('channels', event.detail.channels);
this.propagageChannelsState();
}
handleSubscribed(event) {
var channelstreamConnection = this.getChannelStreamConnection();
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(event) {
var channelstreamConnection = this.getChannelStreamConnection();
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]}
);
}
}
}
customElements.define(RhodecodeApp.is, RhodecodeApp);
</script>
</dom-module>

View file

@ -0,0 +1,194 @@
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
import '../channelstream-connection/channelstream-connection.js';
import '../rhodecode-toast/rhodecode-toast.js';
import '../rhodecode-favicon/rhodecode-favicon.js';
var ccLog = Logger.get('RhodeCodeApp');
ccLog.setLevel(Logger.OFF);
export class RhodecodeApp extends PolymerElement {
static get is() {
return 'rhodecode-app';
}
static get template(){
return html`
<channelstream-connection
id="channelstream-connection"
on-channelstream-listen-message="receivedMessage"
on-channelstream-connected="handleConnected"
on-channelstream-subscribed="handleSubscribed">
</channelstream-connection>
<rhodecode-favicon></rhodecode-favicon>
`
}
connectedCallback() {
super.connectedCallback();
ccLog.debug('rhodeCodeApp created');
$.Topic('/notifications').subscribe(this.handleNotifications.bind(this));
$.Topic('/favicon/update').subscribe(this.faviconUpdate.bind(this));
$.Topic('/connection_controller/subscribe').subscribe(
this.subscribeToChannelTopic.bind(this));
// this event can be used to coordinate plugins to do their
// initialization before channelstream is kicked off
$.Topic('/__MAIN_APP__').publish({});
for (var i = 0; i < alertMessagePayloads.length; i++) {
$.Topic('/notifications').publish(alertMessagePayloads[i]);
}
this.initPlugins();
// after rest of application loads and topics get fired, launch connection
$(document).ready(function () {
this.kickoffChannelstreamPlugin();
}.bind(this));
}
initPlugins() {
for (var i = 0; i < window.APPLICATION_PLUGINS.length; i++) {
var pluginDef = window.APPLICATION_PLUGINS[i];
if (pluginDef.component) {
var pluginElem = document.createElement(pluginDef.component);
this.shadowRoot.appendChild(pluginElem);
if (typeof pluginElem.init !== 'undefined') {
pluginElem.init();
}
}
}
}
/** proxy to channelstream connection */
getChannelStreamConnection() {
return this.$['channelstream-connection'];
}
handleNotifications(data) {
var elem = document.getElementById('notifications');
if (elem) {
elem.handleNotification(data);
}
}
faviconUpdate(data) {
this.shadowRoot.querySelector('rhodecode-favicon').counter = data.count;
}
/** opens connection to ws server */
kickoffChannelstreamPlugin(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.getChannelStreamConnection();
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() {
// subscribe to different channels data is sent.
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);
}
if (templateContext.commit_data.commit_id) {
var channelName = '/repo$' + templateContext.repo_name + '$/commit/' +
String(templateContext.commit_data.commit_id);
channels.push(channelName);
}
return channels;
}
/** subscribes users from channels in channelstream */
subscribeToChannelTopic(channels) {
var channelstreamConnection = this.getChannelStreamConnection();
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(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(event) {
var channelstreamConnection = this.getChannelStreamConnection();
channelstreamConnection.set('channelsState',
event.detail.channels_info);
channelstreamConnection.set('userState', event.detail.state);
channelstreamConnection.set('channels', event.detail.channels);
this.propagageChannelsState();
}
handleSubscribed(event) {
var channelstreamConnection = this.getChannelStreamConnection();
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(event) {
var channelstreamConnection = this.getChannelStreamConnection();
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]}
);
}
}
}
customElements.define(RhodecodeApp.is, RhodecodeApp);

View file

@ -1,38 +0,0 @@
<link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
<dom-module id="rhodecode-favicon">
<template>
</template>
<script>
class RhodecodeFavicon extends Polymer.Element {
static get is() { return 'rhodecode-favicon'; }
static get properties() {
return {
favicon: Object,
counter: {
type: Number,
observer: '_handleCounter'
}
}
}
connectedCallback() {
super.connectedCallback();
this.favicon = new Favico({
type: 'rectangle',
animation: 'none'
});
}
_handleCounter(newVal, oldVal) {
this.favicon.badge(this.counter);
}
}
customElements.define(RhodecodeFavicon.is, RhodecodeFavicon);
</script>
</dom-module>

View file

@ -0,0 +1,33 @@
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
export class RhodecodeFavicon extends PolymerElement {
static get is() {
return 'rhodecode-favicon';
}
static get properties() {
return {
favicon: Object,
counter: {
type: Number,
observer: '_handleCounter'
}
}
}
connectedCallback() {
super.connectedCallback();
this.favicon = new Favico({
type: 'rectangle',
animation: 'none'
});
}
_handleCounter(newVal, oldVal) {
this.favicon.badge(this.counter);
}
}
customElements.define(RhodecodeFavicon.is, RhodecodeFavicon);

View file

@ -1,233 +0,0 @@
<link rel="import" href="../../../../../../bower_components/paper-button/paper-button.html">
<link rel="import"
href="../../../../../../bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
<link rel="import" href="../rhodecode-unsafe-html/rhodecode-unsafe-html.html">
<dom-module id="rhodecode-toast">
<template>
<style include="shared-styles">
/* inset border for buttons - does not work in ie */
/* rounded borders */
/* rounded borders - bottom only */
/* rounded borders - top only */
/* text shadow */
/* centers text in a circle - input diameter of circle and color */
/* pill version of the circle */
.absolute-center {
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.top-left-rounded-corner {
-webkit-border-top-left-radius: 2px;
-khtml-border-radius-topleft: 2px;
border-top-left-radius: 2px;
}
.top-right-rounded-corner {
-webkit-border-top-right-radius: 2px;
-khtml-border-radius-topright: 2px;
border-top-right-radius: 2px;
}
.bottom-left-rounded-corner {
-webkit-border-bottom-left-radius: 2px;
-khtml-border-radius-bottomleft: 2px;
border-bottom-left-radius: 2px;
}
.bottom-right-rounded-corner {
-webkit-border-bottom-right-radius: 2px;
-khtml-border-radius-bottomright: 2px;
border-bottom-right-radius: 2px;
}
.top-left-rounded-corner-mid {
-webkit-border-top-left-radius: 2px;
-khtml-border-radius-topleft: 2px;
border-top-left-radius: 2px;
}
.top-right-rounded-corner-mid {
-webkit-border-top-right-radius: 2px;
-khtml-border-radius-topright: 2px;
border-top-right-radius: 2px;
}
.bottom-left-rounded-corner-mid {
-webkit-border-bottom-left-radius: 2px;
-khtml-border-radius-bottomleft: 2px;
border-bottom-left-radius: 2px;
}
.bottom-right-rounded-corner-mid {
-webkit-border-bottom-right-radius: 2px;
-khtml-border-radius-bottomright: 2px;
border-bottom-right-radius: 2px;
}
.alert {
margin: 10px 0;
}
.toast-close {
margin: 0;
float: right;
cursor: pointer;
}
.toast-message-holder {
background: rgba(255, 255, 255, 0.25);
}
.toast-message-holder.fixed {
position: fixed;
padding: 10px 0;
margin-left: 10px;
margin-right: 10px;
top: 0;
left: 0;
right: 0;
z-index: 100;
}
</style>
<template is="dom-if" if="[[hasToasts]]">
<div class$="container toast-message-holder [[conditionalClass(isFixed)]]">
<template is="dom-repeat" items="[[toasts]]">
<div class$="alert alert-[[item.level]]">
<div on-click="dismissNotification" class="toast-close" index-pos="[[index]]">
<span>[[_gettext('Close')]]</span>
</div>
<rhodecode-unsafe-html text="[[item.message]]"></rhodecode-unsafe-html>
</div>
</template>
</div>
</template>
</template>
<script>
class RhodecodeToast extends Polymer.mixinBehaviors([Polymer.IronA11yKeysBehavior], Polymer.Element) {
static get is() {
return 'rhodecode-toast';
}
static get properties() {
return {
toasts: {
type: Array,
value() {
return []
}
},
isFixed: {
type: Boolean,
value: false
},
hasToasts: {
type: Boolean,
computed: '_computeHasToasts(toasts.*)'
},
keyEventTarget: {
type: Object,
value() {
return document.body;
}
}
}
}
get keyBindings() {
return {
'esc:keyup': '_hideOnEsc'
}
}
static get observers() {
return [
'_changedToasts(toasts.splices)'
]
}
_hideOnEsc(event) {
return this.dismissNotifications();
}
_computeHasToasts() {
return this.toasts.length > 0;
}
_debouncedCalc() {
// calculate once in a while
this.debounce('debouncedCalc', this.toastInWindow, 25);
}
conditionalClass() {
return this.isFixed ? 'fixed' : '';
}
toastInWindow() {
if (!this._headerNode) {
return true
}
var headerHeight = this._headerNode.offsetHeight;
var scrollPosition = window.scrollY;
if (this.isFixed) {
this.isFixed = 1 <= scrollPosition;
}
else {
this.isFixed = headerHeight <= scrollPosition;
}
}
connectedCallback() {
super.connectedCallback();
this._headerNode = document.querySelector('.header', document);
this.listen(window, 'scroll', '_debouncedCalc');
this.listen(window, 'resize', '_debouncedCalc');
this._debouncedCalc();
}
_changedToasts(newValue, oldValue) {
$.Topic('/favicon/update').publish({count: this.toasts.length});
}
dismissNotification(e) {
$.Topic('/favicon/update').publish({count: this.toasts.length - 1});
var idx = e.target.parentNode.indexPos
this.splice('toasts', idx, 1);
}
dismissNotifications() {
$.Topic('/favicon/update').publish({count: 0});
this.splice('toasts', 0);
}
handleNotification(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
});
}
_gettext(x){
return _gettext(x)
}
}
customElements.define(RhodecodeToast.is, RhodecodeToast);
</script>
</dom-module>

View file

@ -0,0 +1,231 @@
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
import '@polymer/paper-toggle-button/paper-toggle-button.js';
import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
import {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
import '../rhodecode-unsafe-html/rhodecode-unsafe-html.js';
export class RhodecodeToast extends mixinBehaviors([IronA11yKeysBehavior], PolymerElement) {
static get is() {
return 'rhodecode-toast';
}
static get template(){
return html`
<style include="shared-styles">
/* inset border for buttons - does not work in ie */
/* rounded borders */
/* rounded borders - bottom only */
/* rounded borders - top only */
/* text shadow */
/* centers text in a circle - input diameter of circle and color */
/* pill version of the circle */
.absolute-center {
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.top-left-rounded-corner {
-webkit-border-top-left-radius: 2px;
-khtml-border-radius-topleft: 2px;
border-top-left-radius: 2px;
}
.top-right-rounded-corner {
-webkit-border-top-right-radius: 2px;
-khtml-border-radius-topright: 2px;
border-top-right-radius: 2px;
}
.bottom-left-rounded-corner {
-webkit-border-bottom-left-radius: 2px;
-khtml-border-radius-bottomleft: 2px;
border-bottom-left-radius: 2px;
}
.bottom-right-rounded-corner {
-webkit-border-bottom-right-radius: 2px;
-khtml-border-radius-bottomright: 2px;
border-bottom-right-radius: 2px;
}
.top-left-rounded-corner-mid {
-webkit-border-top-left-radius: 2px;
-khtml-border-radius-topleft: 2px;
border-top-left-radius: 2px;
}
.top-right-rounded-corner-mid {
-webkit-border-top-right-radius: 2px;
-khtml-border-radius-topright: 2px;
border-top-right-radius: 2px;
}
.bottom-left-rounded-corner-mid {
-webkit-border-bottom-left-radius: 2px;
-khtml-border-radius-bottomleft: 2px;
border-bottom-left-radius: 2px;
}
.bottom-right-rounded-corner-mid {
-webkit-border-bottom-right-radius: 2px;
-khtml-border-radius-bottomright: 2px;
border-bottom-right-radius: 2px;
}
.alert {
margin: 10px 0;
}
.toast-close {
margin: 0;
float: right;
cursor: pointer;
}
.toast-message-holder {
background: rgba(255, 255, 255, 0.25);
}
.toast-message-holder.fixed {
position: fixed;
padding: 10px 0;
margin-left: 10px;
margin-right: 10px;
top: 0;
left: 0;
right: 0;
z-index: 100;
}
</style>
<template is="dom-if" if="[[hasToasts]]">
<div class$="container toast-message-holder [[conditionalClass(isFixed)]]">
<template is="dom-repeat" items="[[toasts]]">
<div class$="alert alert-[[item.level]]">
<div on-click="dismissNotification" class="toast-close" index-pos="[[index]]">
<span>[[_gettext('Close')]]</span>
</div>
<rhodecode-unsafe-html text="[[item.message]]"></rhodecode-unsafe-html>
</div>
</template>
</div>
</template>
`
}
static get properties() {
return {
toasts: {
type: Array,
value() {
return []
}
},
isFixed: {
type: Boolean,
value: false
},
hasToasts: {
type: Boolean,
computed: '_computeHasToasts(toasts.*)'
},
keyEventTarget: {
type: Object,
value() {
return document.body;
}
}
}
}
get keyBindings() {
return {
'esc:keyup': '_hideOnEsc'
}
}
static get observers() {
return [
'_changedToasts(toasts.splices)'
]
}
_hideOnEsc(event) {
return this.dismissNotifications();
}
_computeHasToasts() {
return this.toasts.length > 0;
}
_debouncedCalc() {
// calculate once in a while
this.debounce('debouncedCalc', this.toastInWindow, 25);
}
conditionalClass() {
return this.isFixed ? 'fixed' : '';
}
toastInWindow() {
if (!this._headerNode) {
return true
}
var headerHeight = this._headerNode.offsetHeight;
var scrollPosition = window.scrollY;
if (this.isFixed) {
this.isFixed = 1 <= scrollPosition;
}
else {
this.isFixed = headerHeight <= scrollPosition;
}
}
connectedCallback() {
super.connectedCallback();
this._headerNode = document.querySelector('.header', document);
this.listen(window, 'scroll', '_debouncedCalc');
this.listen(window, 'resize', '_debouncedCalc');
this._debouncedCalc();
}
_changedToasts(newValue, oldValue) {
$.Topic('/favicon/update').publish({count: this.toasts.length});
}
dismissNotification(e) {
$.Topic('/favicon/update').publish({count: this.toasts.length - 1});
var idx = e.target.parentNode.indexPos
this.splice('toasts', idx, 1);
}
dismissNotifications() {
$.Topic('/favicon/update').publish({count: 0});
this.splice('toasts', 0);
}
handleNotification(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
});
}
_gettext(x){
return _gettext(x)
}
}
customElements.define(RhodecodeToast.is, RhodecodeToast);

View file

@ -1,61 +0,0 @@
<link rel="import"
href="../../../../../../bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../../../../../../bower_components/paper-spinner/paper-spinner.html">
<link rel="import" href="../../../../../../bower_components/paper-tooltip/paper-tooltip.html">
<dom-module id="rhodecode-toggle">
<style include="shared-styles">
.rc-toggle {
float: left;
position: relative;
}
.rc-toggle paper-spinner {
position: absolute;
top: 0;
left: -30px;
width: 20px;
height: 20px;
}
</style>
<template>
<div class="rc-toggle">
<paper-toggle-button checked={{checked}}>[[labelStatus(checked)]]</paper-toggle-button>
<paper-tooltip>[[tooltipText]]</paper-tooltip>
<template is="dom-if" if="[[shouldShow(noSpinner)]]">
<paper-spinner active=[[active]]></paper-spinner>
</template>
</div>
</template>
<script>
class RhodecodeToggle extends Polymer.Element {
static get is() {
return 'rhodecode-toggle';
}
static get properties() {
return {
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() {
return !this.noSpinner
}
labelStatus(isActive) {
return this.checked ? 'Enabled' : "Disabled"
}
}
customElements.define(RhodecodeToggle.is, RhodecodeToggle);
</script>
</dom-module>

View file

@ -0,0 +1,57 @@
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
import '@polymer/paper-toggle-button/paper-toggle-button.js';
import '@polymer/paper-spinner/paper-spinner.js';
import '@polymer/paper-tooltip/paper-tooltip.js';
export class RhodecodeToggle extends PolymerElement {
static get is() {
return 'rhodecode-toggle';
}
static get template() {
return html`
<style include="shared-styles">
.rc-toggle {
float: left;
position: relative;
}
.rc-toggle paper-spinner {
position: absolute;
top: 0;
left: -30px;
width: 20px;
height: 20px;
}
</style>
<div class="rc-toggle">
<paper-toggle-button checked={{checked}}>[[labelStatus(checked)]]
</paper-toggle-button>
<paper-tooltip>[[tooltipText]]</paper-tooltip>
<template is="dom-if" if="[[shouldShow(noSpinner)]]">
<paper-spinner active=[[active]]></paper-spinner>
</template>
</div>
`;
}
static get properties() {
return {
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() {
return !this.noSpinner
}
labelStatus(isActive) {
return this.checked ? 'Enabled' : "Disabled"
}
}
customElements.define(RhodecodeToggle.is, RhodecodeToggle);

View file

@ -1,31 +0,0 @@
<link rel="import" href="../../../../../../bower_components/polymer/polymer-element.html">
<dom-module id="rhodecode-unsafe-html">
<template>
<style include="shared-styles"></style>
<slot></slot>
</template>
<script>
class RhodecodeUnsafeHtml extends Polymer.Element {
static get is() {
return 'rhodecode-unsafe-html';
}
static get properties() {
return {
text: {
type: String,
observer: '_handleText'
}
}
}
_handleText(newVal, oldVal) {
this.innerHTML = this.text;
}
}
customElements.define(RhodecodeUnsafeHtml.is, RhodecodeUnsafeHtml);
</script>
</dom-module>

View file

@ -0,0 +1,30 @@
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
export class RhodecodeUnsafeHtml extends PolymerElement {
static get is() {
return 'rhodecode-unsafe-html';
}
static get template() {
return html`
<style include="shared-styles"></style>
<slot></slot>
`;
}
static get properties() {
return {
text: {
type: String,
observer: '_handleText'
}
}
}
_handleText(newVal, oldVal) {
this.innerHTML = this.text;
}
}
customElements.define(RhodecodeUnsafeHtml.is, RhodecodeUnsafeHtml);