diff --git a/.babelrc b/.babelrc
index 650f4ad2..f1e1eb33 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,3 +1,8 @@
{
- "plugins": ["syntax-dynamic-import"]
+ "plugins": [
+ "syntax-dynamic-import",
+ ["transform-react-jsx", {
+ "pragma": "createElement"
+ }]
+ ]
}
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
index 722fe5a8..03deba01 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -7,11 +7,22 @@ module.exports = {
"eslint:recommended",
"plugin:vue/recommended"
],
- "plugins": ["vue"],
+ "plugins": [
+ "vue",
+ "react"
+ ],
"parserOptions": {
"parser": "babel-eslint",
"ecmaVersion": 8,
- "sourceType": "module"
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "jsx": true
+ }
+ },
+ "settings": {
+ "react": {
+ "pragma": "createElement"
+ }
},
"globals": {
"import": false,
@@ -95,6 +106,7 @@ module.exports = {
"allowTemplateLiterals": true
}
],
+
"vue/html-indent": [
"warn",
"tab"
@@ -108,6 +120,32 @@ module.exports = {
"singleline": "never",
"multiline": "always"
}
- ]
+ ],
+
+ "jsx-quotes": ["error", "prefer-double"],
+ "react/jsx-boolean-value": "error",
+ "react/jsx-closing-bracket-location": ["error", "line-aligned"],
+ //"react/jsx-closing-tag-location": "error" -- stupid rule that doesn't allow line-aligned
+ "react/jsx-equals-spacing": "error",
+ "react/jsx-filename-extension": "error",
+ "react/jsx-first-prop-new-line": ["error", "multiline-multiprop"],
+ "react/jsx-indent": ["warn", "tab"],
+ "react/jsx-indent-props": ["warn", "tab"],
+ "react/jsx-key": "warn",
+ "react/jsx-no-bind": "error",
+ "react/jsx-no-comment-textnodes": "error",
+ "react/jsx-no-duplicate-props": "error",
+ "react/jsx-no-literals": ["warn"],
+ "react/jsx-no-target-blank": "error",
+ "react/jsx-sort-props": ["error", {
+ "callbacksLast": true,
+ "reservedFirst": true,
+ "noSortAlphabetically": true
+ }],
+ "react/jsx-tag-spacing": ["error", {
+ "beforeClosing": "never"
+ }],
+ "react/jsx-uses-react": "error",
+ "react/jsx-wrap-multilines": "error"
}
};
\ No newline at end of file
diff --git a/README.md b/README.md
index b9f1158a..9516bb29 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,7 @@ and add the following to your workspace settings:
{
"eslint.validate": [
"javascript",
+ "javascriptreact",
"vue"
]
}```
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index a97c842c..9f0a9d5c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -431,8 +431,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
- "dev": true,
- "optional": true
+ "dev": true
},
"asn1": {
"version": "0.2.3",
@@ -637,6 +636,17 @@
"babel-types": "6.26.0"
}
},
+ "babel-helper-builder-react-jsx": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz",
+ "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "esutils": "2.0.2"
+ }
+ },
"babel-helper-call-delegate": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
@@ -823,6 +833,12 @@
"integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=",
"dev": true
},
+ "babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
+ "dev": true
+ },
"babel-plugin-syntax-trailing-function-commas": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
@@ -1098,6 +1114,17 @@
"babel-runtime": "6.26.0"
}
},
+ "babel-plugin-transform-react-jsx": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz",
+ "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=",
+ "dev": true,
+ "requires": {
+ "babel-helper-builder-react-jsx": "6.26.0",
+ "babel-plugin-syntax-jsx": "6.18.0",
+ "babel-runtime": "6.26.0"
+ }
+ },
"babel-plugin-transform-regenerator": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
@@ -2755,6 +2782,15 @@
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true
},
+ "encoding": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "0.4.19"
+ }
+ },
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
@@ -3037,6 +3073,18 @@
}
}
},
+ "eslint-plugin-react": {
+ "version": "7.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz",
+ "integrity": "sha512-KC7Snr4YsWZD5flu6A5c0AcIZidzW3Exbqp7OT67OaD2AppJtlBr/GuPrW/vaQM/yfZotEvKAdrxrO+v8vwYJA==",
+ "dev": true,
+ "requires": {
+ "doctrine": "2.1.0",
+ "has": "1.0.1",
+ "jsx-ast-utils": "2.0.1",
+ "prop-types": "15.6.1"
+ }
+ },
"eslint-plugin-vue": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-4.4.0.tgz",
@@ -3525,6 +3573,29 @@
"websocket-driver": "0.7.0"
}
},
+ "fbjs": {
+ "version": "0.8.16",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz",
+ "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=",
+ "dev": true,
+ "requires": {
+ "core-js": "1.2.7",
+ "isomorphic-fetch": "2.2.1",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "promise": "7.3.1",
+ "setimmediate": "1.0.5",
+ "ua-parser-js": "0.7.17"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
+ "dev": true
+ }
+ }
+ },
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@@ -5004,6 +5075,16 @@
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true
},
+ "isomorphic-fetch": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+ "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+ "dev": true,
+ "requires": {
+ "node-fetch": "1.7.3",
+ "whatwg-fetch": "2.0.4"
+ }
+ },
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -5133,6 +5214,15 @@
"verror": "1.10.0"
}
},
+ "jsx-ast-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
+ "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
+ "dev": true,
+ "requires": {
+ "array-includes": "3.0.3"
+ }
+ },
"killable": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz",
@@ -5866,6 +5956,16 @@
"integrity": "sha512-nJmSswG4As/MkRq7QZFuH/sf/yuv8ODdMZrY4Bedjp77a5MK4A6s7YbBB64c9u79EBUOfXUXBvArmvzTD0X+6g==",
"dev": true
},
+ "node-fetch": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+ "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "dev": true,
+ "requires": {
+ "encoding": "0.1.12",
+ "is-stream": "1.1.0"
+ }
+ },
"node-forge": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz",
@@ -7184,7 +7284,6 @@
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"dev": true,
- "optional": true,
"requires": {
"asap": "2.0.6"
}
@@ -7195,6 +7294,17 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
+ "prop-types": {
+ "version": "15.6.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz",
+ "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==",
+ "dev": true,
+ "requires": {
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1"
+ }
+ },
"proxy-addr": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
@@ -9018,6 +9128,12 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
+ "ua-parser-js": {
+ "version": "0.7.17",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
+ "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==",
+ "dev": true
+ },
"uglify-es": {
"version": "3.3.9",
"resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
@@ -10067,6 +10183,12 @@
"integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
"dev": true
},
+ "whatwg-fetch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
+ "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==",
+ "dev": true
+ },
"whet.extend": {
"version": "0.9.9",
"resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz",
diff --git a/package.json b/package.json
index e6f6a979..f59ab63a 100755
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"main": "script.js",
"scripts": {
"start": "webpack-dev-server --config webpack.web.dev.js",
- "eslint": "eslint \"src/**/*.{js,vue}\"",
+ "eslint": "eslint \"src/**/*.{js,jsx,vue}\"",
"build": "webpack --config webpack.web.prod.js --define process.env.NODE_ENV='production'",
"build:babel": "webpack --config webpack.web.babel.js --define process.env.NODE_ENV='production'",
"build:prod": "webpack --config webpack.web.prod.js --define process.env.NODE_ENV='production'",
@@ -17,12 +17,14 @@
"babel-eslint": "^8.2.2",
"babel-loader": "^7.1.4",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
+ "babel-plugin-transform-react-jsx": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.10",
"eslint": "^4.18.2",
+ "eslint-plugin-react": "^7.7.0",
"eslint-plugin-vue": "^4.4.0",
"extract-loader": "^1.0.2",
"file-loader": "^1.1.11",
diff --git a/src/main.js b/src/main.js
index af78a561..4ffa7703 100644
--- a/src/main.js
+++ b/src/main.js
@@ -66,7 +66,7 @@ class FrankerFaceZ extends Module {
// ========================================================================
discoverModules() {
- const ctx = require.context('src/modules', true, /(?:^(?:\.\/)?[^/]+|index)\.js$/),
+ const ctx = require.context('src/modules', true, /(?:^(?:\.\/)?[^/]+|index)\.jsx?$/),
modules = this.populate(ctx, this.core_log);
this.core_log.info(`Loaded descriptions of ${Object.keys(modules).length} modules.`);
diff --git a/src/modules/chat/badges.js b/src/modules/chat/badges.jsx
similarity index 96%
rename from src/modules/chat/badges.js
rename to src/modules/chat/badges.jsx
index f675d3e6..14a74392 100644
--- a/src/modules/chat/badges.js
+++ b/src/modules/chat/badges.jsx
@@ -6,7 +6,7 @@
import {API_SERVER, IS_WEBKIT, WEBKIT_CSS as WEBKIT} from 'utilities/constants';
-import {createElement as e, ManagedStyle} from 'utilities/dom';
+import {createElement, ManagedStyle} from 'utilities/dom';
import {has} from 'utilities/object';
import Module from 'utilities/module';
@@ -270,16 +270,32 @@ export default class Badges extends Module {
if ( ! bd )
continue;
- out.push(e('div', {className: 'ffz-badge-tip'}, [
+ out.push(
+ {show_previews &&

}
+ {bd.title}
+
);
+
+ /*out.push(e('div', {className: 'ffz-badge-tip'}, [
show_previews && e('img', {
className: 'preview-image ffz-badge',
src: bd.image4x
}),
bd.title
- ]));
+ ]));*/
} else if ( p === 'ffz' ) {
- out.push(e('div', {className: 'ffz-badge-tip'}, [
+ out.push(
+ {show_previews &&
}
+ {d.title}
+
);
+
+ /*out.push(e('div', {className: 'ffz-badge-tip'}, [
show_previews && e('div', {
className: 'preview-image ffz-badge',
style: {
@@ -288,7 +304,7 @@ export default class Badges extends Module {
}
}),
d.title
- ]));
+ ]));*/
}
}
@@ -297,7 +313,7 @@ export default class Badges extends Module {
}
- render(msg, e) { // eslint-disable-line class-methods-use-this
+ render(msg, createElement) { // eslint-disable-line class-methods-use-this
const hidden_badges = this.parent.context.get('chat.badges.hidden') || {},
badge_style = this.parent.context.get('chat.badges.style'),
custom_mod = this.parent.context.get('chat.badges.custom-mod'),
@@ -444,7 +460,7 @@ export default class Badges extends Module {
if ( data.replaced )
props['data-replaced'] = data.replaced;
- out.push(e('span', props));
+ out.push(createElement('span', props));
}
return out;
diff --git a/src/modules/chat/emotes.js b/src/modules/chat/emotes.js
index c25897e3..52b29b30 100644
--- a/src/modules/chat/emotes.js
+++ b/src/modules/chat/emotes.js
@@ -171,6 +171,7 @@ export default class Emotes extends Module {
// ========================================================================
addDefaultSet(provider, set_id, data) {
+ const had_set = this.default_sets.includes(set_id);
if ( ! this.default_sets.sourceIncludes(provider, set_id) ) {
this.default_sets.push(provider, set_id);
this.refSet(set_id);
@@ -178,13 +179,20 @@ export default class Emotes extends Module {
if ( data )
this.loadSetData(set_id, data);
+
+ if ( ! had_set )
+ this.emit(':update-default-sets');
}
removeDefaultSet(provider, set_id) {
+ const had_set = this.default_sets.includes(set_id);
if ( this.default_sets.sourceIncludes(provider, set_id) ) {
this.default_sets.remove(provider, set_id);
this.unrefSet(set_id);
}
+
+ if ( had_set && ! this.default_sets.includes(set_id) )
+ this.emit(':update-default-sets');
}
refSet(set_id) {
diff --git a/src/modules/chat/tokenizers.js b/src/modules/chat/tokenizers.jsx
similarity index 87%
rename from src/modules/chat/tokenizers.js
rename to src/modules/chat/tokenizers.jsx
index 5ad70cca..893dfb3e 100644
--- a/src/modules/chat/tokenizers.js
+++ b/src/modules/chat/tokenizers.jsx
@@ -4,7 +4,7 @@
// Default Tokenizers
// ============================================================================
-import {sanitize, createElement as e} from 'utilities/dom';
+import {sanitize, createElement} from 'utilities/dom';
import {has, split_chars} from 'utilities/object';
const EMOTE_CLASS = 'chat-line__message--emote',
@@ -40,17 +40,16 @@ export const Links = {
type: 'link',
priority: 50,
- render(token, e) {
- return e('a', {
- className: 'ffz-tooltip',
- 'data-tooltip-type': 'link',
- 'data-url': token.url,
- 'data-is-mail': token.is_mail,
-
- rel: 'noopener',
- target: '_blank',
- href: token.url
- }, token.text);
+ render(token, createElement) {
+ return ({token.text});
},
tooltip(target, tip) {
@@ -209,10 +208,10 @@ export const Mentions = {
type: 'mention',
priority: 40,
- render(token, e) {
- return e('strong', {
- className: `chat-line__message-mention${token.me ? ' ffz--mention-me' : ''}`
- }, `${token.text}`);
+ render(token, createElement) {
+ return (
+ {token.text}
+ );
},
process(tokens, msg, user) {
@@ -279,16 +278,16 @@ export const Mentions = {
export const CheerEmotes = {
type: 'cheer',
- render(token, e) {
- return e('span', {
- className: `ffz-cheer ffz-tooltip`,
- 'data-tooltip-type': 'cheer',
- 'data-prefix': token.prefix,
- 'data-amount': this.i18n.formatNumber(token.amount),
- 'data-tier': token.tier,
- 'data-individuals': token.individuals ? JSON.stringify(token.individuals) : 'null',
- alt: token.text
- });
+ render(token, createElement) {
+ return ();
},
tooltip(target) {
@@ -300,16 +299,16 @@ export const CheerEmotes = {
length = individuals && individuals.length;
const out = [
- this.context.get('tooltip.emote-images') && e('div', {
- className: 'preview-image ffz-cheer-preview',
- 'data-prefix': prefix,
- 'data-tier': tier
- }),
+ this.context.get('tooltip.emote-images') && (),
this.i18n.t('tooltip.bits', '%{count|number} Bits', amount),
];
if ( length > 1 ) {
- out.push(e('br'));
+ out.push(
);
individuals.sort(i => -i[0]);
@@ -319,11 +318,11 @@ export const CheerEmotes = {
amount,
prefix,
tier
- }, e));
+ }, createElement));
}
if ( length > 12 ) {
- out.push(e('br'));
+ out.push(
);
out.push(this.i18n.t('tooltip.bits.more', '(and %{count} more)', length-12));
}
}
@@ -448,33 +447,33 @@ export const CheerEmotes = {
export const AddonEmotes = {
type: 'emote',
- render(token, e) {
+ render(token, createElement) {
const mods = token.modifiers || [], ml = mods.length,
- emote = e('img', {
- className: `${EMOTE_CLASS} ffz-tooltip${token.provider === 'ffz' ? ' ffz-emote' : ''}`,
- src: token.src,
- srcSet: token.srcSet,
- alt: token.text,
- 'data-tooltip-type': 'emote',
- 'data-provider': token.provider,
- 'data-id': token.id,
- 'data-set': token.set,
- 'data-modifiers': ml ? mods.map(x => x.id).join(' ') : null,
- 'data-modifier-info': ml ? JSON.stringify(mods.map(x => [x.set, x.id])) : null
- });
+ emote = (
x.id).join(' ') : null}
+ data-modifier-info={ml ? JSON.stringify(mods.map(x => [x.set, x.id])) : null}
+ />);
if ( ! ml )
return emote;
- return e('span', {
- className: `${EMOTE_CLASS} modified-emote`,
- 'data-provider': token.provider,
- 'data-id': token.id,
- 'data-set': token.set
- }, [
- emote,
- mods.map(t => e('span', null, this.tokenizers.emote.render(t, e)))
- ]);
+ return (
+ {emote}
+ {mods.map(t => {this.tokenizers.emote.render(t, createElement)})}
+ );
},
tooltip(target, tip) {
@@ -489,10 +488,10 @@ export const AddonEmotes = {
emote = emote_set && emote_set.emotes[emote_id];
if ( emote )
- return e('span', null, [
- this.tokenizers.emote.render(emote.token, e),
- ` - ${emote.hidden ? '???' : emote.name}`
- ]);
+ return (
+ {this.tokenizers.emote.render(emote.token, createElement)}
+ {` - ${emote.hidden ? '???' : emote.name}`}
+ );
})
}
@@ -540,25 +539,23 @@ export const AddonEmotes = {
}
return [
- preview && this.context.get('tooltip.emote-images') && e('img', {
- className: 'preview-image',
- src: preview,
- onLoad: tip.update
- }),
+ preview && this.context.get('tooltip.emote-images') && (
),
this.i18n.t('tooltip.emote', 'Emote: %{code}', {code: target.alt}),
- source && this.context.get('tooltip.emote-sources') && e('div', {
- className: 'pd-t-05',
- }, source),
+ source && this.context.get('tooltip.emote-sources') && (
+ {source}
+
),
- owner && this.context.get('tooltip.emote-sources') && e('div', {
- className: 'pd-t-05'
- }, owner),
+ owner && this.context.get('tooltip.emote-sources') && (
+ {owner}
+
),
- mods && e('div', {
- className: 'pd-t-1'
- }, mods)
+ mods && ({mods}
)
];
},
diff --git a/src/modules/main_menu/index.js b/src/modules/main_menu/index.js
index ee125e2d..cff46ddc 100644
--- a/src/modules/main_menu/index.js
+++ b/src/modules/main_menu/index.js
@@ -5,7 +5,7 @@
// ============================================================================
import Module from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
import {has, deep_copy} from 'utilities/object';
import {parse_path} from 'src/settings';
@@ -547,7 +547,7 @@ export default class MainMenu extends Module {
return;
this._vue = new this.vue.Vue({
- el: e('div'),
+ el: createElement('div'),
render: h => h('main-menu', this.getData())
});
diff --git a/src/modules/metadata.js b/src/modules/metadata.jsx
similarity index 88%
rename from src/modules/metadata.js
rename to src/modules/metadata.jsx
index 10b628c6..0b434128 100644
--- a/src/modules/metadata.js
+++ b/src/modules/metadata.jsx
@@ -4,7 +4,7 @@
// Channel Metadata
// ============================================================================
-import {createElement as e, ClickOutside} from 'utilities/dom';
+import {createElement, ClickOutside} from 'utilities/dom';
import {maybe_call} from 'utilities/object';
import {duration_to_string} from 'utilities/time';
@@ -277,35 +277,37 @@ export default class Metadata extends Module {
const fix = cls === 'tw-button--text';
if ( typeof icon === 'string' )
- icon = e('span', 'tw-button__icon tw-button__icon--left', e('figure', icon));
+ icon = ();
if ( def.popup && def.click ) {
- el = e('span', {
- className: `ffz-stat${fix ? ' ffz-fix-padding--left' : ''}`,
- 'data-key': key,
- tip_content: tooltip
- }, [
- btn = e('button', `tw-button ${cls}`, [
- icon,
- stat = e('span', 'ffz-stat-text tw-button__text')
- ]),
- popup = e('button', `tw-button ${cls} ffz-stat-arrow`,
- e('span', 'tw-button__icon tw-pd-x-0',
- e('figure', 'ffz-i-down-dir')
- )
- )
- ]);
+ el = (
+ {btn = ()}
+ {popup = ()}
+ );
} else
- btn = popup = el = e('button', {
- className: `ffz-stat${fix ? ' ffz-fix-padding' : ''} tw-button ${cls}`,
- 'data-key': key,
- tip_content: tooltip
- }, [
- icon,
- stat = e('span', 'ffz-stat-text tw-button__text'),
- def.popup && e('span', 'tw-button__icon tw-button__icon--right', e('figure', 'ffz-i-down-dir'))
- ]);
+ btn = popup = el = ();
if ( def.click )
btn.addEventListener('click', e => {
@@ -370,16 +372,16 @@ export default class Metadata extends Module {
} else {
if ( typeof icon === 'string' )
- icon = e('span', 'tw-stat__icon', e('figure', icon));
+ icon = ();
- el = e('div', {
- className: 'ffz-stat tw-stat',
- 'data-key': key,
- tip_content: tooltip
- }, [
- icon,
- stat = e('span', 'ffz-stat-text tw-stat__value')
- ]);
+ el = (
+ {icon}
+ {stat = }
+
)
}
el._ffz_order = order;
diff --git a/src/modules/tooltips.js b/src/modules/tooltips.js
index c955d49f..105da040 100644
--- a/src/modules/tooltips.js
+++ b/src/modules/tooltips.js
@@ -4,7 +4,7 @@
// Tooltip Handling
// ============================================================================
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
import {has, maybe_call} from 'utilities/object';
import Tooltip from 'utilities/tooltip';
@@ -20,8 +20,8 @@ export default class TooltipProvider extends Module {
this.types.json = target => {
const title = target.dataset.title;
return [
- title && e('strong', null, title),
- e('code', {
+ title && createElement('strong', null, title),
+ createElement('code', {
className: `block${title ? ' pd-t-05 border-t mg-t-05' : ''}`,
style: {
fontFamily: 'monospace',
@@ -96,8 +96,8 @@ export default class TooltipProvider extends Module {
if ( ! handler )
return [
- e('strong', null, 'Unhandled Tooltip Type'),
- e('code', {
+ createElement('strong', null, 'Unhandled Tooltip Type'),
+ createElement('code', {
className: 'block pd-t-05 border-t mg-t-05',
style: {
fontFamily: 'monospace',
diff --git a/src/modules/translation_ui/nondex.js b/src/modules/translation_ui/nondex.js
index 741c2f11..4c237e57 100644
--- a/src/modules/translation_ui/nondex.js
+++ b/src/modules/translation_ui/nondex.js
@@ -5,7 +5,7 @@
// ============================================================================
import Module from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
export default class TranslationUI extends Module {
constructor(...args) {
diff --git a/src/sites/base.js b/src/sites/base.js
index 791eb52a..44fe8501 100644
--- a/src/sites/base.js
+++ b/src/sites/base.js
@@ -16,7 +16,7 @@ export default class BaseSite extends Module {
}
populateModules() {
- const ctx = require.context('site/modules', true, /(?:^(?:\.\/)?[^/]+|index)\.js$/);
+ const ctx = require.context('site/modules', true, /(?:^(?:\.\/)?[^/]+|index)\.jsx?$/);
const modules = this.populate(ctx, this.log);
this.log.info(`Loaded descriptions of ${Object.keys(modules).length} modules.`);
}
diff --git a/src/sites/twitch-twilight/index.js b/src/sites/twitch-twilight/index.js
index a1980460..ff199f1a 100644
--- a/src/sites/twitch-twilight/index.js
+++ b/src/sites/twitch-twilight/index.js
@@ -11,7 +11,7 @@ import Fine from 'utilities/compat/fine';
import FineRouter from 'utilities/compat/fine-router';
import Apollo from 'utilities/compat/apollo';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
import MAIN_URL from 'site/styles/main.scss';
@@ -56,7 +56,7 @@ export default class Twilight extends BaseSite {
const current = this.router.current;
this.fine.route(current && current.name);
- document.head.appendChild(e('link', {
+ document.head.appendChild(createElement('link', {
href: MAIN_URL,
rel: 'stylesheet',
type: 'text/css'
diff --git a/src/sites/twitch-twilight/modules/chat/index.js b/src/sites/twitch-twilight/modules/chat/index.js
index 70d22b24..de32cccc 100644
--- a/src/sites/twitch-twilight/modules/chat/index.js
+++ b/src/sites/twitch-twilight/modules/chat/index.js
@@ -376,12 +376,12 @@ export default class ChatHook extends Module {
cls.prototype.render = function() {
if ( this.state.ffz_errors > 0 ) {
const React = t.web_munch.getModule('react'),
- e = React && React.createElement;
+ createElement = React && React.createElement;
- if ( ! e )
+ if ( ! createElement )
return null;
- return e('div', {
+ return createElement('div', {
className: 'tw-border-l tw-c-background-alt-2 tw-c-text tw-full-width tw-full-height tw-align-items-center tw-flex tw-flex-column tw-justify-content-center tw-relative'
}, 'There was an error displaying chat.');
diff --git a/src/sites/twitch-twilight/modules/chat/scroller.js b/src/sites/twitch-twilight/modules/chat/scroller.js
index ec23d3b4..390b5d48 100644
--- a/src/sites/twitch-twilight/modules/chat/scroller.js
+++ b/src/sites/twitch-twilight/modules/chat/scroller.js
@@ -4,7 +4,7 @@
// Chat Scroller
// ============================================================================
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
import Twilight from 'site';
import Module from 'utilities/module';
@@ -95,7 +95,7 @@ export default class Scroller extends Module {
let timer;
const auto = this.state.ffz_total_errors < 10,
React = t.web_munch.getModule('react'),
- e = React && React.createElement,
+ createElement = React && React.createElement,
handler = () => {
clearTimeout(timer);
this.ffzZeroErrors();
@@ -104,17 +104,17 @@ export default class Scroller extends Module {
if ( auto )
timer = setTimeout(handler, 250);
- if ( ! e )
+ if ( ! createElement )
return null;
- return e('div', {
+ return createElement('div', {
className: 'tw-border-l tw-c-background-alt-2 tw-c-text tw-full-width tw-full-height tw-align-items-center tw-flex tw-flex-column tw-justify-content-center tw-relative'
}, [
- e('div', {className: 'tw-mg-b-1'}, 'There was an error displaying chat.'),
- ! auto && e('button', {
+ createElement('div', {className: 'tw-mg-b-1'}, 'There was an error displaying chat.'),
+ ! auto && createElement('button', {
className: 'tw-button',
onClick: handler
- }, e('span', {className: 'tw-button__text'}, 'Try Again'))
+ }, createElement('span', {className: 'tw-button__text'}, 'Try Again'))
]);
} else
@@ -176,9 +176,9 @@ export default class Scroller extends Module {
node.classList.add('tw-full-height');
- el = this._ffz_freeze_indicator = e('div', {
+ el = this._ffz_freeze_indicator = createElement('div', {
className: 'ffz--freeze-indicator chat-list__more-messages-placeholder tw-relative tw-mg-x-2'
- }, e('div', {
+ }, createElement('div', {
className: 'chat-list__more-messages tw-bottom-0 tw-full-width tw-align-items-center tw-flex tw-justify-content-center tw-absolute tw-pd-05'
}));
diff --git a/src/sites/twitch-twilight/modules/chat/settings_menu.js b/src/sites/twitch-twilight/modules/chat/settings_menu.jsx
similarity index 73%
rename from src/sites/twitch-twilight/modules/chat/settings_menu.js
rename to src/sites/twitch-twilight/modules/chat/settings_menu.jsx
index a334a5dd..1145892a 100644
--- a/src/sites/twitch-twilight/modules/chat/settings_menu.js
+++ b/src/sites/twitch-twilight/modules/chat/settings_menu.jsx
@@ -32,27 +32,36 @@ export default class SettingsMenu extends Module {
if ( ! React )
return;
- const e = React.createElement;
+ const createElement = React.createElement;
- this.SettingsMenu.ready(cls => {
+ this.SettingsMenu.ready((cls, instances) => {
const old_universal = cls.prototype.renderUniversalOptions;
cls.prototype.renderUniversalOptions = function() {
const val = old_universal.call(this);
- val.props.children.push(e('div', {
- className: 'tw-mg-t-1'
- }, e('button', {
- onClick: () => t.click(this)
- }, t.i18n.t('site.menu_button', 'FrankerFaceZ Control Center'))
- ));
- window.menu = this;
+ val.props.children.push(
+
+
);
return val;
}
+ for(const inst of instances)
+ inst.ffzSettingsClick = e => t.click(inst, e);
+
this.SettingsMenu.forceUpdate();
});
+
+ this.SettingsMenu.on('mount', inst => {
+ inst.ffzSettingsClick = e => t.click(inst, e)
+ });
+
+ this.SettingsMenu.on('unmount', inst => {
+ inst.ffzSettingsClick = null;
+ });
}
click(inst) {
@@ -68,6 +77,7 @@ export default class SettingsMenu extends Module {
} else {
this.emit('site.menu_button:clicked');
}
+
const parent = this.fine.searchParent(inst, n => n.toggleBalloonId);
parent && parent.handleButtonClick();
}
diff --git a/src/sites/twitch-twilight/modules/directory/following.js b/src/sites/twitch-twilight/modules/directory/following.jsx
similarity index 84%
rename from src/sites/twitch-twilight/modules/directory/following.js
rename to src/sites/twitch-twilight/modules/directory/following.jsx
index b7202c2c..9bba47c8 100644
--- a/src/sites/twitch-twilight/modules/directory/following.js
+++ b/src/sites/twitch-twilight/modules/directory/following.jsx
@@ -5,7 +5,7 @@
// ============================================================================
import {SiteModule} from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
import {get} from 'utilities/object';
import Popper from 'popper.js';
@@ -236,83 +236,61 @@ export default class Following extends SiteModule {
const simplebarContentChildren = [];
// Hosted Channel Header
- simplebarContentChildren.push(
- e('p', {
- className: 'tw-pd-t-05 tw-pd-x-1 tw-c-text-alt-2',
- textContent: this.i18n.t('directory.hosted', 'Hosted Channel')
- })
- );
+ simplebarContentChildren.push(
+ {this.i18n.t('directory.hosted', 'Hosted Channel')}
+
);
// Hosted Channel Content
- simplebarContentChildren.push(
- e('a', {
- className: 'tw-interactable',
- href: `/${hostData.channel}`,
- onclick: event =>
- this.parent.hijackUserClick(
- event,
- hostData.channel,
- this.destroyHostMenu.bind(this)
- )
- }, e('div', 'tw-align-items-center tw-flex tw-flex-row tw-flex-nowrap tw-mg-x-1 tw-mg-y-05',
- [
- e('div', {
- className: 'ffz-channel-avatar',
- }, e('img', {
- src: inst.props.viewerCount.profileImageURL,
- alt: inst.props.channelName
- })),
- e('p', {
- className: 'tw-ellipsis tw-flex-grow-1 tw-mg-l-1 tw-font-size-5',
- textContent: inst.props.channelName
- })
- ]
- ))
- );
+ simplebarContentChildren.push( this.parent.hijackUserClick(e, hostData.channel, this.destroyHostMenu.bind(this))} // eslint-disable-line react/jsx-no-bind
+ >
+
+
+

+
+
+ {inst.props.channelName}
+
+
+ );
// Hosting Channels Header
- simplebarContentChildren.push(
- e('p', {
- className: 'tw-pd-t-05 tw-pd-x-1 tw-c-text-alt-2',
- textContent: this.i18n.t('directory.hosting', 'Hosting Channels')
- })
- );
+ simplebarContentChildren.push(
+ {this.i18n.t('directory.hosting', 'Hosting Channels')}
+
);
// Hosting Channels Content
for (let i = 0; i < hostData.nodes.length; i++) {
const node = hostData.nodes[i];
- simplebarContentChildren.push(
- e('a', {
- className: 'tw-interactable',
- href: `/${node.login}`,
- onclick: event => this.parent.hijackUserClick(event, node.login, this.destroyHostMenu.bind(this))
- }, e('div', 'tw-align-items-center tw-flex tw-flex-row tw-flex-nowrap tw-mg-x-1 tw-mg-y-05',
- [
- e('div', {
- className: 'ffz-channel-avatar',
- }, e('img', {
- src: node.profileImageURL,
- alt: node.displayName
- })),
- e('p', {
- className: 'tw-ellipsis tw-flex-grow-1 tw-mg-l-1 tw-font-size-5',
- textContent: node.displayName
- })
- ]
- ))
- );
+ simplebarContentChildren.push( this.parent.hijackUserClick(e, node.login, this.destroyHostMenu.bind(this))} // eslint-disable-line react/jsx-no-bind
+ >
+
+
+

+
+
+ {node.displayName}
+
+
+ );
}
- this.hostMenu = e('div', 'ffz-host-menu tw-balloon tw-block',
- e('div', 'tw-border tw-elevation-1 tw-border-radius-small tw-c-background',
- e('div', {
- className: 'scrollable-area',
- 'data-simplebar': true,
- }, e('div', 'simplebar-scroll-content',
- e('div', 'simplebar-content', simplebarContentChildren)
- ))
- )
- );
+ this.hostMenu = ();
const root = (document.body.querySelector('.twilight-root') || document.body);
root.appendChild(this.hostMenu);
diff --git a/src/sites/twitch-twilight/modules/directory/game.js b/src/sites/twitch-twilight/modules/directory/game.jsx
similarity index 80%
rename from src/sites/twitch-twilight/modules/directory/game.js
rename to src/sites/twitch-twilight/modules/directory/game.jsx
index 9f29f64f..3f61a227 100644
--- a/src/sites/twitch-twilight/modules/directory/game.js
+++ b/src/sites/twitch-twilight/modules/directory/game.jsx
@@ -5,7 +5,7 @@
// ============================================================================
import {SiteModule} from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
import GAME_QUERY from './game.gql';
@@ -66,10 +66,12 @@ export default class Game extends SiteModule {
}
- block_btn = e('button', {
- className: 'tw-mg-l-1 tw-button ffz-directory-toggle-block',
- onClick: this.generateClickHandler('directory.game.blocked-games', game, update_block)
- }, block_label = e('span', 'tw-button__text'));
+ block_btn = ();
update_block();
@@ -86,17 +88,19 @@ export default class Game extends SiteModule {
this.parent.ChannelCard.forceUpdate();
}
- hidden_btn = e('button', {
- className: 'tw-mg-l-1 tw-button ffz-directory-toggle-thumbnail',
- onClick: this.generateClickHandler('directory.game.hidden-thumbnails', game, update_hidden)
- }, hidden_label = e('span', 'tw-button__text'));
+ hidden_btn = ()
update_hidden();
- buttons.appendChild(e('div', 'ffz-buttons', [
- block_btn,
- hidden_btn
- ]));
+ buttons.appendChild(
+ {block_btn}
+ {hidden_btn}
+
);
}
generateClickHandler(setting, game, update_func) {
diff --git a/src/sites/twitch-twilight/modules/directory/index.js b/src/sites/twitch-twilight/modules/directory/index.jsx
similarity index 87%
rename from src/sites/twitch-twilight/modules/directory/index.js
rename to src/sites/twitch-twilight/modules/directory/index.jsx
index bf9e8d1d..5d4bd1cd 100644
--- a/src/sites/twitch-twilight/modules/directory/index.js
+++ b/src/sites/twitch-twilight/modules/directory/index.jsx
@@ -6,7 +6,7 @@
import {SiteModule} from 'utilities/module';
import {duration_to_string} from 'utilities/time';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
import {get} from 'utilities/object';
import Following from './following';
@@ -244,15 +244,17 @@ export default class Directory extends SiteModule {
const up_text = duration_to_string(uptime, false, false, false, setting === 1);
if ( ! inst.ffz_uptime_el || card.querySelector('.ffz-uptime-element') === undefined ) {
- card.appendChild(inst.ffz_uptime_el = e('div',
- 'video-preview-card__preview-overlay-stat tw-c-background-overlay tw-c-text-overlay tw-font-size-6 tw-top-0 tw-right-0 tw-z-default tw-inline-flex tw-absolute tw-mg-05 ffz-uptime-element',
- e('div', 'tw-tooltip-wrapper tw-inline-flex', [
- e('div', 'tw-stat', [
- e('span', 'tw-c-text-live tw-stat__icon', e('figure', 'ffz-i-clock')),
- inst.ffz_uptime_span = e('span', 'tw-stat__value')
- ]),
- inst.ffz_uptime_tt = e('div', 'tw-tooltip tw-tooltip--down tw-tooltip--align-center')
- ])));
+ card.appendChild(inst.ffz_uptime_el = ());
}
if ( ! inst.ffz_update_timer )
@@ -306,29 +308,29 @@ export default class Directory extends SiteModule {
if ( setting === 1 ) {
const body = card.querySelector('.tw-card-body .tw-flex'),
- avatar = e('a', {
- className: 'ffz-channel-avatar tw-mg-r-05 tw-mg-t-05',
- href: `/${data.login}`,
- title: data.displayName,
- onclick: event => this.hijackUserClick(event, data.login)
- }, e('img', {
- src: data.profileImageURL
- }));
+ avatar = ( this.hijackUserClick(e, data.login)} // eslint-disable-line react/jsx-no-bind
+ >
+
+ );
body.insertBefore(avatar, body.firstElementChild);
} else if ( setting === 2 || setting === 3 ) {
- const avatar_el = e('a', {
- className: 'ffz-channel-avatar',
- href: `/${data.login}`,
- onclick: event => this.hijackUserClick(event, data.login)
- }, e('div', 'live-channel-card__boxart tw-bottom-0 tw-absolute',
- e('figure', 'tw-aspect tw-aspect--align-top',
- e('img', {
- title: data.displayName,
- src: data.profileImageURL
- })))
- );
+ const avatar_el = ( this.hijackUserClick(e, data.login)} // eslint-disable-line react/jsx-no-bind
+ >
+
+
+
+
+
+ );
const cont = card.querySelector('figure.tw-aspect > div');
if ( cont )
diff --git a/src/sites/twitch-twilight/modules/host_button.js b/src/sites/twitch-twilight/modules/host_button.js
index d77200bd..3ccb50d7 100644
--- a/src/sites/twitch-twilight/modules/host_button.js
+++ b/src/sites/twitch-twilight/modules/host_button.js
@@ -5,7 +5,7 @@
// ============================================================================
import Module from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
const HOST_ERRORS = {
COMMAND_EXECUTION: {
@@ -212,7 +212,7 @@ export default class HostButton extends Module {
this.activeTab = this.activeTab || 'auto-host';
this.vueEl = new vue.Vue({
- el: e('div'),
+ el: createElement('div'),
render: h => h('host-options', {
hosts,
autoHostSettings,
diff --git a/src/sites/twitch-twilight/modules/menu_button.js b/src/sites/twitch-twilight/modules/menu_button.jsx
similarity index 65%
rename from src/sites/twitch-twilight/modules/menu_button.js
rename to src/sites/twitch-twilight/modules/menu_button.jsx
index 1bd74c24..66e227e6 100644
--- a/src/sites/twitch-twilight/modules/menu_button.js
+++ b/src/sites/twitch-twilight/modules/menu_button.jsx
@@ -5,7 +5,7 @@
// ============================================================================
import {SiteModule} from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement, setChildren} from 'utilities/dom';
export default class MenuButton extends SiteModule {
constructor(...args) {
@@ -29,7 +29,7 @@ export default class MenuButton extends SiteModule {
set pill(val) {
this._pill_content = val;
if ( this._pill )
- this._pill.innerHTML = this.formatPill();
+ setChildren(this._pill, this.formatPill(), true);
}
formatPill() {
@@ -46,28 +46,26 @@ export default class MenuButton extends SiteModule {
user_menu = container.querySelector('.top-nav__nav-items-container:last-child');
- this._el = e('div',
- 'ffz-top-nav tw-align-self-center tw-flex-grow-0 tw-flex-shrink-0 tw-flex-nowrap tw-pd-r-1 tw-pd-l-05',
- this._btn = e('button',
- {
- className: 'tw-button-icon tw-button-icon--overlay tw-button-icon--large',
- onClick: e => this.emit(':clicked', e)
- },
- e('div', 'tw-tooltip-wrapper', [
- e('span', 'tw-button-icon__icon',
- e('figure', 'ffz-i-zreknarf')
- ),
+ this._el = (
+ {this._btn = (
)}
+
);
- e('div', 'ffz-menu__pill absolute',
- e('div', 'tw-animation tw-animation--animate tw-animation--duration-medium tw-animation--timing-ease-in tw-animation--bounce-in',
- this._pill = e('span', 'tw-pill tw-pill--notification', this.formatPill(), true)
- )
- ),
-
- this._tip = e('div', 'tw-tooltip tw-tooltip--down tw-tooltip--align-center')
- ])
- )
- );
+ setChildren(this._pill, this.formatPill(), true);
this.onTranslate();
diff --git a/src/sites/twitch-twilight/modules/player.js b/src/sites/twitch-twilight/modules/player.jsx
similarity index 96%
rename from src/sites/twitch-twilight/modules/player.js
rename to src/sites/twitch-twilight/modules/player.jsx
index 191c422a..a2bb4fae 100644
--- a/src/sites/twitch-twilight/modules/player.js
+++ b/src/sites/twitch-twilight/modules/player.jsx
@@ -5,7 +5,7 @@
// ============================================================================
import Module from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
export default class Player extends Module {
@@ -348,14 +348,13 @@ export default class Player extends Module {
let tip = container.querySelector('.ffz--player-reset .player-tip');
if ( ! tip )
- container.insertBefore(
- e('button', {
- className: 'player-button player-button--reset ffz--player-reset ffz-i-cancel',
- type: 'button',
- onDblClick: t.resetPlayer.bind(t, inst)
- }, tip = e('span', 'player-tip js-control-tip')),
- el.nextSibling
- );
+ container.insertBefore(, el.nextSibling);
tip.dataset.tip = this.i18n.t('player.reset_button', 'Double-Click to Reset Player');
}
diff --git a/src/sites/twitch-twilight/modules/sub_button.js b/src/sites/twitch-twilight/modules/sub_button.jsx
similarity index 81%
rename from src/sites/twitch-twilight/modules/sub_button.js
rename to src/sites/twitch-twilight/modules/sub_button.jsx
index fbedb3c9..1bc0f2b3 100644
--- a/src/sites/twitch-twilight/modules/sub_button.js
+++ b/src/sites/twitch-twilight/modules/sub_button.jsx
@@ -5,7 +5,7 @@
// ============================================================================
import Module from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
export default class SubButton extends Module {
constructor(...args) {
@@ -59,16 +59,13 @@ export default class SubButton extends Module {
icon = btn.querySelector('.ffz--can-prime');
if ( should_show && ! icon ) {
- btn.insertBefore(
- e('span', 'tw-button__icon tw-button__icon--left ffz--can-prime',
- e('figure', {
- className: 'ffz-i-crown ffz-tooltip',
- 'data-tooltip-type': 'html',
- 'data-title': this.i18n.t('sub-button.prime', 'Your free channel sub with Prime is available.')
- })
- ),
- btn.firstElementChild
- );
+ btn.insertBefore(
+
+ , btn.firstElementChild);
} else if ( ! should_show && icon )
icon.remove();
diff --git a/src/sites/twitch-twilight/modules/theme/index.js b/src/sites/twitch-twilight/modules/theme/index.js
index d99a892e..f7ed233f 100644
--- a/src/sites/twitch-twilight/modules/theme/index.js
+++ b/src/sites/twitch-twilight/modules/theme/index.js
@@ -5,7 +5,7 @@
// ============================================================================
import Module from 'utilities/module';
-import {createElement as e} from 'utilities/dom';
+import {createElement} from 'utilities/dom';
import THEME_CSS_URL from 'site/styles/theme.scss';
@@ -58,7 +58,7 @@ export default class ThemeEngine extends Module {
if ( ! enable )
return;
- this._style = e('link', {
+ this._style = createElement('link', {
rel: 'stylesheet',
type: 'text/css',
href: THEME_CSS_URL
diff --git a/src/utilities/dom.js b/src/utilities/dom.js
index 25de3965..ca30cdf1 100644
--- a/src/utilities/dom.js
+++ b/src/utilities/dom.js
@@ -30,9 +30,12 @@ function camelCase(name) {
}
-export function createElement(tag, props, children, no_sanitize) {
+export function createElement(tag, props, ...children) {
const el = document.createElement(tag);
+ if ( children.length === 1)
+ children = children[0];
+
if ( typeof props === 'string' )
el.className = props;
else if ( props )
@@ -73,7 +76,7 @@ export function createElement(tag, props, children, no_sanitize) {
}
if ( children )
- setChildren(el, children, no_sanitize);
+ setChildren(el, children);
return el;
}
diff --git a/src/utilities/module.js b/src/utilities/module.js
index 440fde54..856fc475 100644
--- a/src/utilities/module.js
+++ b/src/utilities/module.js
@@ -521,7 +521,9 @@ export class Module extends EventEmitter {
for(const raw_path of ctx.keys()) {
const raw_module = ctx(raw_path),
module = raw_module.module || raw_module.default,
- name = raw_path.slice(2, raw_path.length - (raw_path.endsWith('/index.js') ? 9 : 3));
+ name = raw_path.slice(2, raw_path.length - (raw_path.endsWith('/index.jsx') ? 10 : raw_path.endsWith('/index.js') ? 9 : raw_path.endsWith('.jsx') ? 4 : 3));
+
+ // TODO: rewrite the name code to not have like 4 endsWith in it.
try {
added[name] = this.register(name, module);
diff --git a/src/utilities/tooltip.js b/src/utilities/tooltip.js
index d79e7d3d..892b46c8 100644
--- a/src/utilities/tooltip.js
+++ b/src/utilities/tooltip.js
@@ -8,7 +8,7 @@
// Better because they aren't hidden by parents with overflow: hidden;
// ============================================================================
-import {createElement as e, setChildren} from 'utilities/dom';
+import {createElement, setChildren} from 'utilities/dom';
import {maybe_call} from 'utilities/object';
import Popper from 'popper.js';
@@ -208,17 +208,17 @@ export class Tooltip {
return;
// Build the DOM.
- const arrow = e('div', opts.arrowClass),
- inner = tip.element = e('div', opts.innerClass),
+ const arrow = createElement('div', opts.arrowClass),
+ inner = tip.element = createElement('div', opts.innerClass),
- el = tip.outer = e('div', {
+ el = tip.outer = createElement('div', {
className: opts.tooltipClass
}, [inner, arrow]);
arrow.setAttribute('x-arrow', true);
if ( opts.arrowInner )
- arrow.appendChild(e('div', opts.arrowInner));
+ arrow.appendChild(createElement('div', opts.arrowInner));
if ( tip.add_class ) {
inner.classList.add(tip.add_class);
diff --git a/styles/widgets.scss b/styles/widgets.scss
index b238c0a6..1306a0c5 100644
--- a/styles/widgets.scss
+++ b/styles/widgets.scss
@@ -46,7 +46,9 @@
}
+.ffz--chat-actions,
.ffz--profile-manager {
+ .ffz--action,
.ffz--profile {
outline: none;
diff --git a/webpack.common.js b/webpack.common.js
index 2bb89f2c..39fefdd4 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -8,6 +8,7 @@ module.exports = {
avalon: './src/main.js'
},
resolve: {
+ extensions: ['.js', '.jsx'],
alias: {
res: path.resolve(__dirname, 'res/'),
styles: path.resolve(__dirname, 'styles/'),
@@ -45,6 +46,11 @@ module.exports = {
}
}]
},
+ {
+ test: /\.jsx$/,
+ exclude: /node_modules/,
+ loader: 'babel-loader'
+ },
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,