Quellcode durchsuchen

Move the autocomplete code into ./shared

And remove it as a plugin.
JC Brand vor 4 Jahren
Ursprung
Commit
bb3f52d2f2

+ 1 - 1
src/components/adhoc-commands.js

@@ -1,4 +1,4 @@
-import "../plugins/autocomplete.js"
+import 'shared/autocomplete/index.js';
 import log from "@converse/headless/log";
 import sizzle from "sizzle";
 import { CustomElement } from './element.js';

+ 1 - 1
src/components/muc-sidebar.js

@@ -1,4 +1,4 @@
-import "../plugins/autocomplete.js"
+import 'shared/autocomplete/index.js';
 import tpl_muc_sidebar from "templates/muc_sidebar.js";
 import { CustomElement } from './element.js';
 import { api, converse } from "@converse/headless/core";

+ 2 - 4
src/converse.js

@@ -14,15 +14,14 @@ import "shared/registry.js";
  * --------------------
  * Any of the following components may be removed if they're not needed.
  */
-import "./plugins/autocomplete.js";
 import "./plugins/bookmark-views.js";       // Views for XEP-0048 Bookmarks
-import "./plugins/chatview/index.js";             // Renders standalone chat boxes for single user chat
+import "./plugins/chatview/index.js";       // Renders standalone chat boxes for single user chat
 import "./plugins/controlbox/index.js";     // The control box
 import "./plugins/dragresize.js";           // Allows chat boxes to be resized by dragging them
 import "./plugins/fullscreen.js";
 import "./plugins/mam-views.js";
 import "./plugins/minimize.js";             // Allows chat boxes to be minimized
-import "./plugins/muc-views/index.js";            // Views related to MUC
+import "./plugins/muc-views/index.js";      // Views related to MUC
 import "./plugins/headlines-view/index.js";
 import "./plugins/notifications.js";
 import "./plugins/omemo.js";
@@ -40,7 +39,6 @@ import "../sass/converse.scss";
 import { converse } from "@converse/headless/core";
 
 const WHITELISTED_PLUGINS = [
-    'converse-autocomplete',
     'converse-bookmark-views',
     'converse-chatboxviews',
     'converse-chatview',

+ 1 - 0
src/modals/add-contact.js

@@ -1,3 +1,4 @@
+import 'shared/autocomplete/index.js';
 import BootstrapModal from "./base.js";
 import tpl_add_contact_modal from "./templates/add-contact.js";
 import { __ } from '../i18n';

+ 1 - 0
src/modals/muc-invite.js

@@ -1,3 +1,4 @@
+import 'shared/autocomplete/index.js';
 import BootstrapModal from "./base.js";
 import tpl_muc_invite_modal from "./templates/muc-invite.js";
 import { _converse, converse } from "@converse/headless/core";

+ 1 - 0
src/plugins/muc-views/muc.js

@@ -1,5 +1,6 @@
 import './config-form.js';
 import './password-form.js';
+import 'shared/autocomplete/index.js';
 import MUCInviteModal from 'modals/muc-invite.js';
 import ModeratorToolsModal from 'modals/moderator-tools.js';
 import OccupantModal from 'modals/occupant.js';

+ 5 - 136
src/plugins/autocomplete.js → src/shared/autocomplete/autocomplete.js

@@ -1,141 +1,19 @@
 /**
- * @module converse-autocomplete
  * @copyright Lea Verou and the Converse.js contributors
  * @description
- *  Converse.js plugin which started as a fork of Lea Verou's Awesomplete
+ *  Started as a fork of Lea Verou's "Awesomplete"
  *  https://leaverou.github.io/awesomplete/
  * @license Mozilla Public License (MPLv2)
  */
+
 import { Events } from '@converse/skeletor/src/events.js';
+import { helpers, FILTER_CONTAINS, ITEM, SORT_BY_QUERY_POSITION } from './utils.js';
+import Suggestion from './suggestion.js';
 import { converse } from "@converse/headless/core";
 
 const u = converse.env.utils;
 
 
-export const FILTER_CONTAINS = function (text, input) {
-    return RegExp(helpers.regExpEscape(input.trim()), "i").test(text);
-};
-
-
-export const FILTER_STARTSWITH = function (text, input) {
-    return RegExp("^" + helpers.regExpEscape(input.trim()), "i").test(text);
-};
-
-
-const SORT_BY_LENGTH = function (a, b) {
-    if (a.length !== b.length) {
-        return a.length - b.length;
-    }
-    return a < b? -1 : 1;
-};
-
-const SORT_BY_QUERY_POSITION = function (a, b) {
-    const query = a.query.toLowerCase();
-    const x = a.label.toLowerCase().indexOf(query);
-    const y = b.label.toLowerCase().indexOf(query);
-
-    if (x === y) {
-        return SORT_BY_LENGTH(a, b);
-    }
-    return (x === -1 ? Infinity : x) < (y === -1 ? Infinity : y) ? -1 : 1
-}
-
-
-const ITEM = (text, input) => {
-    input = input.trim();
-    const element = document.createElement("li");
-    element.setAttribute("aria-selected", "false");
-
-    const regex = new RegExp("("+input+")", "ig");
-    const parts = input ? text.split(regex) : [text];
-    parts.forEach((txt) => {
-        if (input && txt.match(regex)) {
-            const match = document.createElement("mark");
-            match.textContent = txt;
-            element.appendChild(match);
-        } else {
-            element.appendChild(document.createTextNode(txt));
-        }
-    });
-    return element;
-};
-
-
-const helpers = {
-
-    getElement (expr, el) {
-        return typeof expr === "string"? (el || document).querySelector(expr) : expr || null;
-    },
-
-    bind (element, o) {
-        if (element) {
-            for (var event in o) {
-                if (!Object.prototype.hasOwnProperty.call(o, event)) {
-                    continue;
-                }
-                const callback = o[event];
-                event.split(/\s+/).forEach(event => element.addEventListener(event, callback));
-            }
-        }
-    },
-
-    unbind (element, o) {
-        if (element) {
-            for (var event in o) {
-                if (!Object.prototype.hasOwnProperty.call(o, event)) {
-                    continue;
-                }
-                const callback = o[event];
-                event.split(/\s+/).forEach(event => element.removeEventListener(event, callback));
-            }
-        }
-    },
-
-    regExpEscape (s) {
-        return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
-    },
-
-    isMention (word, ac_triggers) {
-        return (ac_triggers.includes(word[0]) || u.isMentionBoundary(word[0]) && ac_triggers.includes(word[1]));
-    }
-}
-
-
-/**
- * An autocomplete suggestion
- */
-class Suggestion extends String {
-
-    /**
-     * @param { Any } data - The auto-complete data. Ideally an object e.g. { label, value },
-     *      which specifies the value and human-presentable label of the suggestion.
-     * @param { string } query - The query string being auto-completed
-     */
-    constructor (data, query) {
-        super();
-        const o = Array.isArray(data)
-            ? { label: data[0], value: data[1] }
-            : typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data };
-
-        this.label = o.label || o.value;
-        this.value = o.value;
-        this.query = query;
-    }
-
-    get lenth () {
-        return this.label.length;
-    }
-
-    toString () {
-        return "" + this.label;
-    }
-
-    valueOf () {
-        return this.toString();
-    }
-}
-
-
 export class AutoComplete {
 
     constructor (el, config={}) {
@@ -433,13 +311,4 @@ export class AutoComplete {
 // Make it an event emitter
 Object.assign(AutoComplete.prototype, Events);
 
-
-converse.plugins.add("converse-autocomplete", {
-
-    initialize () {
-        const _converse = this._converse;
-        _converse.FILTER_CONTAINS = FILTER_CONTAINS;
-        _converse.FILTER_STARTSWITH = FILTER_STARTSWITH;
-        _converse.AutoComplete = AutoComplete;
-    }
-});
+export default AutoComplete;

+ 26 - 15
src/components/autocomplete.js → src/shared/autocomplete/component.js

@@ -1,10 +1,10 @@
-import { AutoComplete, FILTER_CONTAINS, FILTER_STARTSWITH } from "../converse-autocomplete.js";
-import { CustomElement } from './element.js';
+import AutoComplete from './autocomplete.js';
+import { CustomElement } from 'components/element.js';
+import { FILTER_CONTAINS, FILTER_STARTSWITH } from './utils.js';
+import { api } from '@converse/headless/core';
 import { html } from 'lit-element';
-import { api } from "@converse/headless/core";
 
 export default class AutoCompleteComponent extends CustomElement {
-
     static get properties () {
         return {
             'getAutoCompleteList': { type: Function },
@@ -15,8 +15,8 @@ export default class AutoCompleteComponent extends CustomElement {
             'min_chars': { type: Number },
             'name': { type: String },
             'placeholder': { type: String },
-            'triggers': { type: String },
-        }
+            'triggers': { type: String }
+        };
     }
 
     constructor () {
@@ -35,13 +35,21 @@ export default class AutoCompleteComponent extends CustomElement {
         return html`
             <div class="suggestion-box suggestion-box__name">
                 <ul class="suggestion-box__results suggestion-box__results--above" hidden=""></ul>
-                <input type="text" name="${this.name}"
-                       autocomplete="off"
-                       @keydown=${this.onKeyDown}
-                       @keyup=${this.onKeyUp}
-                       class="form-control suggestion-box__input"
-                       placeholder="${this.placeholder}"/>
-                <span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
+                <input
+                    type="text"
+                    name="${this.name}"
+                    autocomplete="off"
+                    @keydown=${this.onKeyDown}
+                    @keyup=${this.onKeyUp}
+                    class="form-control suggestion-box__input"
+                    placeholder="${this.placeholder}"
+                />
+                <span
+                    class="suggestion-box__additions visually-hidden"
+                    role="status"
+                    aria-live="assertive"
+                    aria-relevant="additions"
+                ></span>
             </div>
         `;
     }
@@ -56,9 +64,12 @@ export default class AutoCompleteComponent extends CustomElement {
             'list': () => this.getAutoCompleteList(),
             'match_current_word': true,
             'max_items': this.max_items,
-            'min_chars': this.min_chars,
+            'min_chars': this.min_chars
         });
-        this.auto_complete.on('suggestion-box-selectcomplete', () => (this.auto_completing = false));
+        this.auto_complete.on(
+            'suggestion-box-selectcomplete',
+            () => (this.auto_completing = false)
+        );
     }
 
     onKeyDown (ev) {

+ 8 - 0
src/shared/autocomplete/index.js

@@ -0,0 +1,8 @@
+import './component.js';
+import AutoComplete from './autocomplete.js';
+import { FILTER_CONTAINS, FILTER_STARTSWITH } from './utils.js';
+import { _converse } from '@converse/headless/core';
+
+_converse.FILTER_CONTAINS = FILTER_CONTAINS;
+_converse.FILTER_STARTSWITH = FILTER_STARTSWITH;
+_converse.AutoComplete = AutoComplete;

+ 36 - 0
src/shared/autocomplete/suggestion.js

@@ -0,0 +1,36 @@
+/**
+ * An autocomplete suggestion
+ */
+class Suggestion extends String {
+    /**
+     * @param { Any } data - The auto-complete data. Ideally an object e.g. { label, value },
+     *      which specifies the value and human-presentable label of the suggestion.
+     * @param { string } query - The query string being auto-completed
+     */
+    constructor (data, query) {
+        super();
+        const o = Array.isArray(data)
+            ? { label: data[0], value: data[1] }
+            : typeof data === 'object' && 'label' in data && 'value' in data
+            ? data
+            : { label: data, value: data };
+
+        this.label = o.label || o.value;
+        this.value = o.value;
+        this.query = query;
+    }
+
+    get lenth () {
+        return this.label.length;
+    }
+
+    toString () {
+        return '' + this.label;
+    }
+
+    valueOf () {
+        return this.toString();
+    }
+}
+
+export default Suggestion;

+ 89 - 0
src/shared/autocomplete/utils.js

@@ -0,0 +1,89 @@
+import { converse } from '@converse/headless/core';
+
+const u = converse.env.utils;
+
+export const helpers = {
+    getElement (expr, el) {
+        return typeof expr === 'string' ? (el || document).querySelector(expr) : expr || null;
+    },
+
+    bind (element, o) {
+        if (element) {
+            for (var event in o) {
+                if (!Object.prototype.hasOwnProperty.call(o, event)) {
+                    continue;
+                }
+                const callback = o[event];
+                event.split(/\s+/).forEach(event => element.addEventListener(event, callback));
+            }
+        }
+    },
+
+    unbind (element, o) {
+        if (element) {
+            for (var event in o) {
+                if (!Object.prototype.hasOwnProperty.call(o, event)) {
+                    continue;
+                }
+                const callback = o[event];
+                event.split(/\s+/).forEach(event => element.removeEventListener(event, callback));
+            }
+        }
+    },
+
+    regExpEscape (s) {
+        return s.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
+    },
+
+    isMention (word, ac_triggers) {
+        return (
+            ac_triggers.includes(word[0]) ||
+            (u.isMentionBoundary(word[0]) && ac_triggers.includes(word[1]))
+        );
+    }
+};
+
+export const FILTER_CONTAINS = function (text, input) {
+    return RegExp(helpers.regExpEscape(input.trim()), 'i').test(text);
+};
+
+export const FILTER_STARTSWITH = function (text, input) {
+    return RegExp('^' + helpers.regExpEscape(input.trim()), 'i').test(text);
+};
+
+const SORT_BY_LENGTH = function (a, b) {
+    if (a.length !== b.length) {
+        return a.length - b.length;
+    }
+    return a < b ? -1 : 1;
+};
+
+export const SORT_BY_QUERY_POSITION = function (a, b) {
+    const query = a.query.toLowerCase();
+    const x = a.label.toLowerCase().indexOf(query);
+    const y = b.label.toLowerCase().indexOf(query);
+
+    if (x === y) {
+        return SORT_BY_LENGTH(a, b);
+    }
+    return (x === -1 ? Infinity : x) < (y === -1 ? Infinity : y) ? -1 : 1;
+};
+
+export const ITEM = (text, input) => {
+    input = input.trim();
+    const element = document.createElement('li');
+    element.setAttribute('aria-selected', 'false');
+
+    const regex = new RegExp('(' + input + ')', 'ig');
+    const parts = input ? text.split(regex) : [text];
+    parts.forEach(txt => {
+        if (input && txt.match(regex)) {
+            const match = document.createElement('mark');
+            match.textContent = txt;
+            element.appendChild(match);
+        } else {
+            element.appendChild(document.createTextNode(txt));
+        }
+    });
+    return element;
+};