js: migrate to polymer 3.x
This commit is contained in:
parent
d75cc12c5c
commit
b76a293536
15 changed files with 1404 additions and 1180 deletions
12
package.json
12
package.json
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue