refactor: emoji blots to not clobber native quill format/image blot properties

This commit is contained in:
Julian Lam 2021-06-02 15:06:02 -04:00
parent 99eeb39442
commit 4cc350ca77
3 changed files with 148 additions and 141 deletions

191
.eslintrc
View file

@ -5,136 +5,111 @@
},

"rules": {
// Customized
// === Configure rules for our style ===
// imports must be resolvable
"import/no-unresolved": "error",
// use single quotes,
// unless a different style allows avoiding escapes
"quotes": ["error", "single", {
"avoidEscape": true,
"allowTemplateLiterals": true
}],
// allow else-if return
"no-else-return": [ "error", { "allowElseIf": true } ],
// expressions split over multiple lines
// should break after the operator
"operator-linebreak": [ "error", "after" ],
// require arrow parens only when needed
// and whenever the body is a block
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
// what variables are errors in callbacks
"handle-callback-err": [ "error","^(e$|(e|(.*(_e|E)))rr)" ],
// allow dangling commas in functions
// require them everywhere else
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
"functions": "only-multiline"
}],
// we actually encourage `return await`
"no-return-await": "off",
"no-constant-condition": "off",
// allow `while (true)`
"no-constant-condition": ["error", { "checkLoops": false }],
// allow ignoring an error with `catch`
"no-empty": ["error", { "allowEmptyCatch": true }],
"no-underscore-dangle": "off",
"no-console": "off",
// allow `3 + 5 - 1`, but not `3 * 5 - 1`
"no-mixed-operators": ["error", { "allowSamePrecedence": true }],
// require `'use strict';`
"strict": ["error", "global"],
"consistent-return": "off",
"func-names": "off",
"no-tabs": "off",
// we actually use tabs for indentation
"indent": ["error", "tab", { "SwitchCase": 1 }],
"no-tabs": "off",
// we want `== null` to also handle undefined
"no-eq-null": "off",
"camelcase": "off",
"no-new": "off",
"no-shadow": "off",
"no-use-before-define": ["error", "nofunc"],
"no-prototype-builtins": "off",
"new-cap": "off",
// allow `for (..; i++)`
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
"object-curly-newline": "off",
"no-restricted-globals": "off",
"function-paren-newline": "off",
"import/no-unresolved": "error",
"quotes": ["error", "single", {
"avoidEscape": true,
"allowTemplateLiterals": true
// allow using functions defined later
"no-use-before-define": ["error", "nofunc"],
// require consistent newlines before and after braces
// if contents are multiline
"object-curly-newline": ["error", { "consistent": true, "multiline": true }],
// require consistent linebreaks inline function parenthesis (arguments or params)
"function-paren-newline": ["error", "consistent"],
// only require const if all parts of destructuring can be const
"prefer-const": ["error", { "destructuring": "all" }],
// don't require destructuring for arrays or assignment
"prefer-destructuring": ["error", {
"VariableDeclarator": { "array": false, "object": true },
"AssignmentExpression": { "array": false, "object": false }
}],
"no-else-return": [ "error", { "allowElseIf": true } ],
"operator-linebreak": [ "error", "after" ],
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
// identical to airbnb rule, except for allowing for..of, because we want to use it
"no-restricted-syntax": [
"error",
{
"selector": "ForInStatement",
"message": "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array."
},
{
"selector": "LabeledStatement",
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
},
{
"selector": "WithStatement",
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
}
],
// allow lines of up to 120 characters
"max-len": ["error", { "code": 120, "tabWidth": 2, "ignoreUrls": true, "ignoreStrings": true, "ignoreTemplateLiterals": true, "ignoreRegExpLiterals": true }],

// ES6
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-arrow-callback": "off",
"prefer-template": "off",
"no-var": "off",
// === Disable rules ===
// more liberal naming
"camelcase": "off",
"no-underscore-dangle": "off",
// don't require anonymous function names
"func-names": "off",
// allow console
"no-console": "off",
// allow new for side effects
// allow new with non-capitalized
"no-new": "off",
"new-cap": "off",
// allow shadowing variables (usually callbacks)
"no-shadow": "off",
// allow multiple empty lines in a row
"no-multiple-empty-lines": "off",
// allow not using object shorthand
"object-shorthand": "off",
"vars-on-top": "off",
"prefer-destructuring": "off",

// TODO
"consistent-return": "off",
"no-restricted-globals": "off",
"no-prototype-builtins": "off",
"import/no-extraneous-dependencies": "off",
"import/no-dynamic-require": "off",
"import/newline-after-import": "off",
"no-bitwise": "off",
"global-require": "off",
"max-len": "off",
"no-param-reassign": "off",
"no-restricted-syntax": "off",
"no-script-url": "off",
"default-case": "off",
"linebreak-style": "off",

// "no-multi-assign": "off",
// "one-var": "off",
// "no-undef": "off",
// "max-nested-callbacks": "off",
// "no-mixed-requires": "off",
// "brace-style": "off",
// "max-statements-per-line": "off",
// "no-unused-vars": "off",
// "no-mixed-spaces-and-tabs": "off",
// "no-useless-concat": "off",
// "require-jsdoc": "off",
// "eqeqeq": "off",
// "no-negated-condition": "off",
// "one-var-declaration-per-line": "off",
// "no-lonely-if": "off",
// "radix": "off",
// "no-else-return": "off",
// "no-useless-escape": "off",
// "block-scoped-var": "off",
// "operator-assignment": "off",
// "yoda": "off",
// "no-loop-func": "off",
// "no-void": "off",
// "valid-jsdoc": "off",
// "no-cond-assign": "off",
// "no-redeclare": "off",
// "no-unreachable": "off",
// "no-nested-ternary": "off",
// "operator-linebreak": "off",
// "guard-for-in": "off",
// "no-unneeded-ternary": "off",
// "no-sequences": "off",
// "no-extend-native": "off",
// "no-shadow-restricted-names": "off",
// "no-extra-boolean-cast": "off",
// "no-path-concat": "off",
// "no-unused-expressions": "off",
// "no-return-assign": "off",
// "no-restricted-modules": "off",
// "object-curly-spacing": "off",
// "indent": "off",
// "padded-blocks": "off",
// "eol-last": "off",
// "lines-around-directive": "off",
// "strict": "off",
// "comma-dangle": "off",
// "no-multi-spaces": "off",
// "quotes": "off",
// "keyword-spacing": "off",
// "no-mixed-operators": "off",
// "comma-spacing": "off",
// "no-trailing-spaces": "off",
// "key-spacing": "off",
// "no-multiple-empty-lines": "off",
// "spaced-comment": "off",
// "space-in-parens": "off",
// "block-spacing": "off",
// "quote-props": "off",
// "space-unary-ops": "off",
// "no-empty": "off",
// "dot-notation": "off",
// "func-call-spacing": "off",
// "array-bracket-spacing": "off",
// "object-property-newline": "off",
// "no-continue": "off",
// "no-extra-semi": "off",
// "no-spaced-func": "off",
// "no-useless-return": "off"
"default-case": "off"
}
}

View file

@ -1,3 +1,26 @@
'use strict';

module.exports = { extends: ['@commitlint/config-angular'] };
module.exports = {
extends: ['@commitlint/config-angular'],
rules: {
'header-max-length': [1, 'always', 72],
'type-enum': [
2,
'always',
[
'breaking',
'build',
'chore',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test',
],
],
},
};

View file

@ -1,6 +1,6 @@
'use strict';

/* globals $, define, socket, app, config */
/* globals define, socket, app, config */

/**
* DEVELOPER NOTE
@ -17,42 +17,49 @@
* for a hook to be fired by autocomplete, of which there is none right now.
*/

define('quill-emoji', ['quill'], function (quill) {
var Emoji = {
define('quill-emoji', ['quill'], (quill) => {
const Emoji = {
table: {},
blots: {},
};

// Emoji Blot
var imageBlot = quill.import('formats/image');
var emojiAttributes = ['alt', 'class'];
Emoji.blots.emoji = $.extend(true, imageBlot, {
blotName: 'emoji',
formats: function (domNode) {
const imageBlot = quill.import('formats/image');
const emojiAttributes = ['alt', 'class'];

class EmojiBlot extends imageBlot {
static create(value) {
const node = super.create(value.src);
node.setAttribute('class', value.class);
return node;
}

static formats(domNode) {
return emojiAttributes.reduce((formats, attribute) => {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
},
});
Emoji.blots.emoji.prototype.format = function (name, value) {
if (emojiAttributes.includes(name)) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
imageBlot.format(name, value);
}
};
quill.register(Emoji.blots.emoji);

static format(name, value) {
if (emojiAttributes.includes(name)) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
}
EmojiBlot.blotName = 'emoji';
quill.register(EmojiBlot);

Emoji.enable = function (quill) {
if (!Object.keys(Emoji.table).length) {
socket.emit('plugins.composer-quill.getEmojiTable', {}, function (err, table) {
socket.emit('plugins.composer-quill.getEmojiTable', {}, (err, table) => {
if (err) {
app.alertError(err.message);
}
@ -66,27 +73,29 @@ define('quill-emoji', ['quill'], function (quill) {
};

Emoji.convert = function (delta) {
var quill = this;
var contents = quill.getContents();
var emojiRegex = /:([\w+-]+):/g;
const quill = this;
const contents = quill.getContents();
const emojiRegex = /:([\w+-]+):/g;

// Special handling for emoji plugin
if (!delta || delta.ops.some(command => command.insert && (command.insert === ':' || String(command.insert).endsWith(':') || String(command.insert).endsWith(': \n')))) {
// Check all nodes for emoji shorthand and replace with image
contents.reduce(function (retain, cur) {
var match = emojiRegex.exec(cur.insert);
var contents;
var emojiObj;
contents.reduce((retain, cur) => {
let match = emojiRegex.exec(cur.insert);
let contents;
let emojiObj;
while (match !== null) {
emojiObj = Emoji.table[match[1]];
if (emojiObj) {
contents = [{
insert: {
emoji: config.relative_path + '/plugins/nodebb-plugin-emoji/emoji/' + emojiObj.pack + '/' + emojiObj.image + '?' + app.cacheBuster,
emoji: {
url: `${config.relative_path}/plugins/nodebb-plugin-emoji/emoji/${emojiObj.pack}/${emojiObj.image}?${app.cacheBuster}`,
class: `not-responsive emoji emoji-${emojiObj.pack} emoji--${emojiObj.name}`,
},
},
attributes: {
alt: emojiObj.character,
class: 'not-responsive emoji emoji-' + emojiObj.pack + ' emoji--' + emojiObj.name,
},
}];
if (match[0].length) {