From 06abbf07134ab0be0e6a3b7620f9fa97feb12c30 Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 21 Sep 2020 16:05:04 +0200 Subject: [PATCH] inputosaurus jQuery to native so we can drop jquery-ui --- README.md | 20 +- dev/External/User/ko.js | 23 +- dev/Styles/Layout.less | 4 - dev/Styles/SettingsAccounts.less | 26 - dev/Styles/SettingsFilters.less | 13 - dev/Styles/SettingsTemplates.less | 13 - dev/Styles/_InputosaurusFix.less | 8 - tasks/config.js | 2 - vendors/inputosaurus/inputosaurus.js | 951 ++- vendors/jquery-ui/AUTHORS.txt | 245 - vendors/jquery-ui/MIT-LICENSE.txt | 26 - vendors/jquery-ui/README.md | 99 - .../css/no-theme/jquery-ui-1.12.1.custom.css | 90 - .../no-theme/jquery-ui-1.12.1.custom.min.css | 6 - .../smoothness/images/animated-overlay.gif | Bin 1738 -> 0 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 212 -> 0 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 208 -> 0 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 335 -> 0 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 207 -> 0 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 262 -> 0 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 262 -> 0 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 332 -> 0 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 280 -> 0 bytes .../images/ui-icons_222222_256x240.png | Bin 6922 -> 0 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 4549 -> 0 bytes .../images/ui-icons_454545_256x240.png | Bin 6992 -> 0 bytes .../images/ui-icons_888888_256x240.png | Bin 6999 -> 0 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 4549 -> 0 bytes .../smoothness/jquery-ui-1.12.1.custom.css | 576 -- .../jquery-ui-1.12.1.custom.min.css | 5 - .../jquery-ui/js/jquery-ui-1.12.1.custom.js | 5823 ----------------- .../js/jquery-ui-1.12.1.custom.min.js | 6 - vendors/jquery-ui/package.json | 70 - 33 files changed, 422 insertions(+), 7584 deletions(-) delete mode 100644 vendors/jquery-ui/AUTHORS.txt delete mode 100644 vendors/jquery-ui/MIT-LICENSE.txt delete mode 100644 vendors/jquery-ui/README.md delete mode 100644 vendors/jquery-ui/css/no-theme/jquery-ui-1.12.1.custom.css delete mode 100644 vendors/jquery-ui/css/no-theme/jquery-ui-1.12.1.custom.min.css delete mode 100644 vendors/jquery-ui/css/smoothness/images/animated-overlay.gif delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-icons_222222_256x240.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-icons_2e83ff_256x240.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-icons_454545_256x240.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-icons_888888_256x240.png delete mode 100644 vendors/jquery-ui/css/smoothness/images/ui-icons_cd0a0a_256x240.png delete mode 100644 vendors/jquery-ui/css/smoothness/jquery-ui-1.12.1.custom.css delete mode 100644 vendors/jquery-ui/css/smoothness/jquery-ui-1.12.1.custom.min.css delete mode 100644 vendors/jquery-ui/js/jquery-ui-1.12.1.custom.js delete mode 100644 vendors/jquery-ui/js/jquery-ui-1.12.1.custom.min.js delete mode 100644 vendors/jquery-ui/package.json diff --git a/README.md b/README.md index bcdd222e9..3b7d2e0d6 100644 --- a/README.md +++ b/README.md @@ -79,11 +79,13 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill * Replaced knockoutjs 3.4 with a modified 3.5.1 * Replaced knockout-sortable with native HTML5 drag&drop * Replaced simplestatemanager with @media +* Replaced inputosaurus to native * Removed pikaday * Removed underscore * Removed polyfills * Removed Modernizr * Removed nanoscroll +* Removed jquery-ui * Removed jquery-scrollstop * Removed jquery-mousewheel * Removed matchmedia-polyfill @@ -94,22 +96,22 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill |js/* |1.14.0 |native | |----------- |--------: |--------: | |admin.js |2.130.942 | 851.365 | -|app.js |4.184.455 |2.496.337 | +|app.js |4.184.455 |2.494.604 | |boot.js | 671.522 | 5.777 | -|libs.js | 647.614 | 311.853 | +|libs.js | 647.614 | 227.246 | |polyfills.js | 325.834 | 0 | -|TOTAL |7.960.367 |3.665.332 | +|TOTAL |7.960.367 |3.578.992 | |js/min/* |1.14.0 |native |gzip 1.14 |gzip |brotli | |--------------- |--------: |--------: |--------: |--------: |--------: | |admin.min.js | 252.147 | 116.656 | 73.657 | 33.346 | 28.783 | -|app.min.js | 511.202 | 340.291 |140.462 | 89.644 | 72.618 | +|app.min.js | 511.202 | 340.071 |140.462 | 89.602 | 72.526 | |boot.min.js | 66.007 | 3.117 | 22.567 | 1.563 | 1.333 | -|libs.min.js | 572.545 | 288.859 |176.720 | 90.396 | 79.821 | +|libs.min.js | 572.545 | 206.304 |176.720 | 69.988 | 62.272 | |polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 | -|TOTAL |1.434.353 | 748.923 |424.718 |214.949 |182.555 | +|TOTAL |1.434.353 | 666.148 |424.718 |194.499 |164.914 | -685.430 bytes (209.769 gzip) is around 48% smaller and faster. +768.205 bytes (230.219 gzip) is around 54% smaller and faster. ### CSS changes @@ -134,8 +136,8 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill |css/* |1.14.0 |native |gzip 1.14 |gzip |brotli | |-------------- |-------: |-------: |------: |------: |------: | -|app.css | 340.334 | 254.026 | 46,959 | 36.763 | 30.920 | -|app.min.css | 274.791 | 207.208 | 39.618 | 32.074 | 27.219 | +|app.css | 340.334 | 233.750 | 46,959 | 33.604 | 28.245 | +|app.min.css | 274.791 | 200.511 | 39.618 | 30.790 | 26.080 | |boot.css | | 2.534 | | 837 | 668 | |boot.min.css | | 2.055 | | 732 | 560 | diff --git a/dev/External/User/ko.js b/dev/External/User/ko.js index a72866655..75975d935 100644 --- a/dev/External/User/ko.js +++ b/dev/External/User/ko.js @@ -1,5 +1,4 @@ const ko = window.ko, - $ = jQuery, rlContentType = 'application/x-rainloop-action', validTransfer = data => ['move','copy'].includes(data.dropEffect) // effectAllowed && data.getData('application/x-rainloop-messages'); @@ -39,24 +38,18 @@ ko.bindingHandlers.moment = { ko.bindingHandlers.emailsTags = { init: (element, fValueAccessor, fAllBindingsAccessor) => { const EmailModel = require('Model/Email').default, - $el = $(element), fValue = fValueAccessor(), fAllBindings = fAllBindingsAccessor(), inputDelimiters = [',', ';', '\n']; - $el.inputosaurus({ - focusCallback: value => { - if (fValue && fValue.focused) { - fValue.focused(!!value); - } - }, + element.inputosaurus = new window.Inputosaurus(element, { + focusCallback: value => fValue && fValue.focused && fValue.focused(!!value), autoCompleteSource: fAllBindings.autoCompleteSource || null, splitHook: value => { const v = value.trim(); - if (v && inputDelimiters.includes(v.substr(-1))) { - return EmailModel.splitEmailLine(value); - } - return null; + return (v && inputDelimiters.includes(v.substr(-1))) + ? EmailModel.splitEmailLine(value) + : null; }, parseHook: input => input.map(inputValue => { @@ -72,8 +65,8 @@ ko.bindingHandlers.emailsTags = { }); if (fValue && fValue.focused && fValue.focused.subscribe) { - fValue.focused.subscribe((value) => { - $el.inputosaurus(value ? 'focus' : 'blur'); + fValue.focused.subscribe(value => { + element.inputosaurus[value ? 'focus' : 'blur'](); }); } }, @@ -83,7 +76,7 @@ ko.bindingHandlers.emailsTags = { if (element.EmailsTagsValue !== value) { element.value = value; element.EmailsTagsValue = value; - $(element).inputosaurus('refresh'); + element.inputosaurus.refresh(); } } }; diff --git a/dev/Styles/Layout.less b/dev/Styles/Layout.less index 68f4487fa..6be23aa14 100644 --- a/dev/Styles/Layout.less +++ b/dev/Styles/Layout.less @@ -304,10 +304,6 @@ html.rl-left-panel-disabled { .hide-on-panel-disabled { display: none; } - - &.ui-state-disabled { - opacity: 1; - } } #rl-right { diff --git a/dev/Styles/SettingsAccounts.less b/dev/Styles/SettingsAccounts.less index 2bfc7277b..d8282aa92 100644 --- a/dev/Styles/SettingsAccounts.less +++ b/dev/Styles/SettingsAccounts.less @@ -92,19 +92,6 @@ cursor: pointer; opacity: 0.5; } - - &.ui-sortable-helper { - background: #F5F5F5; - border: 1px solid #DDD; - - .button-delete, .delete-account { - display: none; - } - - td { - border-width: 0; - } - } } .identity-item { @@ -134,18 +121,5 @@ cursor: pointer; opacity: 0.5; } - - &.ui-sortable-helper { - background: #F5F5F5; - border: 1px solid #DDD; - - .button-delete, .delete-identity { - display: none; - } - - td { - border-width: 0; - } - } } } diff --git a/dev/Styles/SettingsFilters.less b/dev/Styles/SettingsFilters.less index 86ef0e1ed..1d38dbbac 100644 --- a/dev/Styles/SettingsFilters.less +++ b/dev/Styles/SettingsFilters.less @@ -88,18 +88,5 @@ cursor: pointer; opacity: 0.5; } - - &.ui-sortable-helper { - background: #F5F5F5; - border: 1px solid #DDD; - - .button-delete, .delete-filter { - display: none; - } - - td { - border-width: 0; - } - } } } diff --git a/dev/Styles/SettingsTemplates.less b/dev/Styles/SettingsTemplates.less index f8c824206..742861aa9 100644 --- a/dev/Styles/SettingsTemplates.less +++ b/dev/Styles/SettingsTemplates.less @@ -77,18 +77,5 @@ cursor: pointer; opacity: 0.5; } - - &.ui-sortable-helper { - background: #F5F5F5; - border: 1px solid #DDD; - - .button-delete, .delete-template { - display: none; - } - - td { - border-width: 0; - } - } } } diff --git a/dev/Styles/_InputosaurusFix.less b/dev/Styles/_InputosaurusFix.less index 8a75ba92e..7bd238c38 100644 --- a/dev/Styles/_InputosaurusFix.less +++ b/dev/Styles/_InputosaurusFix.less @@ -26,10 +26,6 @@ color: #555; z-index: 100; - &.ui-draggable.ui-draggable-dragging { - z-index: 101; - } - position: relative; a { @@ -74,10 +70,6 @@ } } -.ui-autocomplete { - z-index: 2000; -} - .inputosaurus-fake-span { position: absolute; top: 0; diff --git a/tasks/config.js b/tasks/config.js index 60a3731f1..e6b916874 100644 --- a/tasks/config.js +++ b/tasks/config.js @@ -51,7 +51,6 @@ config.paths.css = { name: 'app.css', src: [ 'vendors/normalize.css/normalize.css', - 'vendors/jquery-ui/css/smoothness/jquery-ui-1.12.1.custom.css', 'vendors/fontastic/styles.css', 'vendors/inputosaurus/inputosaurus.css', 'vendors/flags/flags-fixed.css', @@ -78,7 +77,6 @@ config.paths.js = { 'dev/polyfill.js', 'dev/prototype.js', 'node_modules/jquery/dist/jquery.slim.min.js', - 'vendors/jquery-ui/js/jquery-ui-1.12.1.custom.min.js', // custom 'vendors/inputosaurus/inputosaurus.js', // custom (modified) 'vendors/routes/signals.min.js', // fixed 'vendors/routes/hasher.min.js', // fixed diff --git a/vendors/inputosaurus/inputosaurus.js b/vendors/inputosaurus/inputosaurus.js index 2a572f91c..0bbeddabc 100644 --- a/vendors/inputosaurus/inputosaurus.js +++ b/vendors/inputosaurus/inputosaurus.js @@ -4,11 +4,6 @@ * Must be instantiated on an element * Allows multiple input items. Each item is represented with a removable tag that appears to be inside the input area. * - * @requires: - * - * jQuery 3.5+ - * jQueryUI 1.12+ Core - * * @version 0.1.6 * @author Dan Kielp * @created October 3,2012 @@ -17,15 +12,36 @@ * @modified by DJMaze */ +(() => { -($ => { +const doc = document, + createEl = (name, attr) => { + let el = doc.createElement(name); + attr && Object.entries(attr).forEach(([k,v]) => el.setAttribute(k,v)); + return el; + }, + datalist = createEl('datalist',{id:"inputosaurus-datalist"}), + fakeSpan = createEl('span',{class:"inputosaurus-fake-span"}), - const fakeSpan = $(''); + contentType = 'application/x-inputosaurus-item', + getTransferData = data => 'move' === data.dropEffect && data.getData(contentType); - $('body').append(fakeSpan); - $.widget("ui.inputosaurus", { +doc.body.append(fakeSpan, datalist); - options: { +let removeDragged = null; + +window.Inputosaurus = class { + + constructor(element, options) { + + var self = this, + els = {}; + + self.element = element; + + self._focusTriggerTimer = 0; + + self.options = Object.assign({ // bindable events // @@ -34,607 +50,446 @@ // while typing, the user can separate values using these delimiters // the value tags are created on the fly when an inputDelimiter is detected - inputDelimiters : [',', ';'], - - allowDragAndDrop : true, + inputDelimiters : [',', ';', '\n'], focusCallback : null, - parseOnBlur : false, - // simply passing an autoComplete source (array, string or function) will instantiate autocomplete functionality autoCompleteSource : '', - // When forcing users to select from the autocomplete list, allow them to press 'Enter' to select an item if it's the only option left. - activateFinalResult : false, - // manipulate and return the input value after parseInput() parsing // the array of tag names is passed and expected to be returned as an array after manipulation parseHook : null, splitHook : null - }, + }, options); - _create: function() { - var widget = this, - els = {}, - o = widget.options, - placeholder = this.element.attr('placeholder') || null; + self._chosenValues = []; - this._chosenValues = []; + // Create the elements + els.ul = createEl('ul',{class:"inputosaurus-container"}); - // Create the elements - els.ul = $(''); + els.ul.addEventListener("dragover", e => getTransferData(e.dataTransfer) && e.preventDefault()); + els.ul.addEventListener("drop", e => { + let value = getTransferData(e.dataTransfer); + if (value) { + els.input.value = value; + removeDragged(); + self.parseInput(); + } + }); - if (this.options.allowDragAndDrop) - { - els.ul.droppable({ - 'drop': (event, ui) => { + els.input = createEl('input',{type:"text", list:datalist.id, + autocomplete:"off", autocorrect:"off", autocapitalize:"off", spellcheck:"false"}); - ui.draggable.addClass('inputosaurus-dropped'); - els.input.val(ui.draggable.data('inputosaurus-value')); + els.lastEdit = ''; - if (ui.draggable.__widget) - { - ui.draggable.__widget._removeDraggedTag(ui.draggable); + els.input.addEventListener('focus', () => self._focusTrigger(true)); + els.input.addEventListener('blur', () => self._focusTrigger(false)); + + // define starting placeholder + if (element.placeholder) { + els.input.placeholder = element.placeholder; + } + + element.replaceWith(els.ul); + element.hidden = true; + + els.inputCont = createEl('li',{class:"inputosaurus-input"}); + els.inputCont.append(els.input); + els.ul.append(els.inputCont); + + self.elements = els; + + els.input.addEventListener('keyup', e => self._inputKeypress(e)); + els.input.addEventListener('keydown', e => self._inputKeypress(e)); + els.input.addEventListener('change', e => self._inputKeypress(e)); + els.input.addEventListener('input', e => self._inputKeypress(e)); + els.input.addEventListener('focus', () => els.input.value || self._resetAutocomplete()); + els.input.addEventListener('blur', e => self.parseInput(e)); + + els.ul.addEventListener('click', e => self._focus(e)); + els.ul.addEventListener('dblclick', e => e.currentTarget.closest('li') && self._editTag(e)); + + // if instantiated input already contains a value, parse that junk + if (element.value.trim()) { + els.input.value = element.value; + self.parseInput(); + } + + self._updateAutocomplete = self.options.autoCompleteSource + ? (() => { + let value = self.elements.input.value.trim(); + if (datalist.inputValue !== value) { + datalist.inputValue = value; + value.length && self.options.autoCompleteSource( + {term:value}, + items => { + self._resetAutocomplete(); + items && items.forEach(item => datalist.append(new Option(item))); } + ) + } + }).throttle(500) + : () => {}; + } - widget.parseInput(); - } - }); - } + _focusTrigger(bValue) { + var self = this; + clearTimeout(self._focusTriggerTimer); + self._focusTriggerTimer = setTimeout(() => { + self.elements.ul.classList.toggle('inputosaurus-focused', bValue); + self.options.focusCallback(bValue); + }, 10); + } - els.input = $(''); -// els.input = $(''); - els.inputCont = $('
  • '); - els.origInputCont = $('
  • '); - els.lastEdit = ''; + _resetAutocomplete() { + datalist.textContent = ''; + } - els.input.on('focus', () => { - widget._focusTrigger(true); - }).on('blur', () => { - widget._focusTrigger(false); - }); + parseInput(ev) { + var self = this, + val, + hook, + delimiterFound = false, + values = []; - // define starting placeholder - if (placeholder) { - o.placeholder = placeholder; - els.input.attr('placeholder', o.placeholder); - } + val = self.elements.input.value; - this.element.replaceWith(els.ul); - els.origInputCont.append(this.element).hide(); + if (val) { + hook = self.options.splitHook(val); + } - els.inputCont.append(els.input); - els.ul.append(els.inputCont); - els.ul.append(els.origInputCont); + if (hook) { + values = hook; + } else if (delimiterFound !== false) { + values = val.split(delimiterFound); + } else if (!ev || ev.key == 'Enter') { + values.push(val); + ev && ev.preventDefault(); - this.elements = els; + // prevent autoComplete menu click from causing a false 'blur' + } else if(ev.type === 'blur'){ + values.push(val); + } - widget._attachEvents(); + values = self.options.parseHook(values); - // if instantiated input already contains a value, parse that junk - if($.trim(this.element.val())){ - els.input.val( this.element.val() ); - this.parseInput(); - } + if (values.length) { + self._setChosen(values); + self.elements.input.value = ''; + self.resizeInput(); + } + } - this._instAutocomplete(); - }, + _inputKeypress(ev) { + let self = this; - _focusTriggerTimer : 0, + switch (ev.key) { + case 'Backspace': + case 'ArrowLeft': + // if our input contains no value and backspace has been pressed, select the last tag + if (ev.type === 'keydown') { + var lastTag = self.elements.inputCont.previousElementSibling, + target = ev.currentTarget; - _focusTrigger : function (bValue) { - var widget = this; - clearTimeout(this._focusTriggerTimer); - this._focusTriggerTimer = setTimeout(() => { - widget.elements.ul[!bValue ? 'removeClass' : 'addClass']('inputosaurus-focused'); - widget.options.focusCallback(bValue); - }, 10); - }, + // IE goes back in history if the event isn't stopped + ev.stopPropagation(); - _instAutocomplete : function() { - if(this.options.autoCompleteSource){ - var widget = this; - - this.elements.input.autocomplete({ - position : { - of : this.elements.ul - }, - source : this.options.autoCompleteSource, - minLength : 1, - autoFocus : true, - select : (ev, ui) => { + if (lastTag && (!target.value + || (('selectionStart' in target) && target.selectionStart === 0 && target.selectionEnd === 0)) + ) { ev.preventDefault(); - widget.elements.input.val(ui.item.value); - widget.parseInput(); - }, - open : function() { - var menu = $(this).data('ui-autocomplete').menu, - $menuItems; + lastTag.querySelector('a').focus(); + } - menu.element.width(widget.elements.ul.outerWidth() - 6); - - // auto-activate the result if it's the only one - if(widget.options.activateFinalResult){ - $menuItems = menu.element.find('li'); - - // activate single item to allow selection upon pressing 'Enter' - if($menuItems.length === 1){ - menu[menu.activate ? 'activate' : 'focus']($.Event('click'), $menuItems); - } - } - }, - focus: () => false - }); - } - }, - - _autoCompleteMenuPosition : function() { - var widget; - if(this.options.autoCompleteSource){ - widget = this.elements.input.data('ui-autocomplete'); - widget && widget.menu.element.position({ - of: this.elements.ul, - my: 'left top', - at: 'left bottom', - collision: 'none' - }); - } - }, - - parseInput : function(ev) { - var widget = (ev && ev.data.widget) || this, - val, - hook, - delimiterFound = false, - values = []; - - val = widget.elements.input.val(); - - if (val) { - hook = widget.options.splitHook(val); - } - - if (hook) { - values = hook; - } else if(delimiterFound !== false){ - values = val.split(delimiterFound); - } else if(!ev || ev.which === $.ui.keyCode.ENTER && !$('.ui-menu-item .ui-state-focus').length && !$('#ui-active-menuitem').length){ - values.push(val); - ev && ev.preventDefault(); - - // prevent autoComplete menu click from causing a false 'blur' - } else if(ev.type === 'blur' && !$('#ui-active-menuitem').length){ - values.push(val); - } - - values = widget.options.parseHook(values); - - if(values.length){ - widget._setChosen(values); - widget.elements.input.val(''); - widget._resizeInput(); - } - }, - - _inputFocus : function(ev) { - var widget = ev.data.widget || this; - - widget.elements.input.value || (widget.options.autoCompleteSource.length && widget.elements.input.autocomplete('search', '')); - }, - - _inputKeypress : function(ev) { - var widget = ev.data.widget || this; - - ev.type === 'keyup' && widget._trigger('keyup', ev, widget); - - switch(ev.which){ - case $.ui.keyCode.BACKSPACE: - ev.type === 'keydown' && widget._inputBackspace(ev); - break; - - case $.ui.keyCode.LEFT: - ev.type === 'keydown' && widget._inputBackspace(ev); - break; - - default : - widget.parseInput(ev); - widget._resizeInput(ev); - } - - // reposition autoComplete menu as