From 79d6f4fed12682c639a34e6fab2ef35c146e7eb2 Mon Sep 17 00:00:00 2001 From: Tor Hveem Date: Thu, 16 Feb 2023 19:23:52 +0100 Subject: [PATCH] Remove underscore.js and reimplement all usage using native code --- package-lock.json | 12 +++------ package.json | 1 - src/js/filters.js | 4 +-- src/js/glowingbear.js | 38 ++++++++++++++++++++--------- src/js/handlers.js | 19 ++++++++------- src/js/inputbar.js | 10 ++++---- src/js/irc-utils.js | 11 +++------ src/js/models.js | 42 +++++++++++++++----------------- src/js/plugins.js | 4 +-- src/js/utils.js | 6 ++--- src/js/websockets.js | 4 +-- src/js/whenscrolled-directive.js | 4 +-- 12 files changed, 79 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cca230..273c502 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,6 @@ "emojione": "^4.5.0", "favico.js": "^0.3.10", "jquery-linkify": "^2.2.1", - "underscore": "^1.13.2", "zlibjs": "^0.3.1" }, "devDependencies": { @@ -3238,9 +3237,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001332", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", - "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", + "version": "1.0.30001452", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz", + "integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==", "dev": true, "funding": [ { @@ -9450,11 +9449,6 @@ "node": "*" } }, - "node_modules/underscore": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz", - "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==" - }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index 98759be..a575d22 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "emojione": "^4.5.0", "favico.js": "^0.3.10", "jquery-linkify": "^2.2.1", - "underscore": "^1.13.2", "zlibjs": "^0.3.1" } } diff --git a/src/js/filters.js b/src/js/filters.js index a994918..6274c78 100644 --- a/src/js/filters.js +++ b/src/js/filters.js @@ -1,6 +1,6 @@ 'use strict'; -import * as _ from "underscore"; + var weechat = angular.module('weechat'); @@ -169,7 +169,7 @@ weechat.filter('getBufferQuickKeys', function () { buf.$quickKey = idx < 10 ? (idx + 1) % 10 : ''; }); } else { - _.map(obj, function(buffer, idx) { + obj.map(function(buffer, idx) { return [buffer.number, buffer.$idx, idx]; }).sort(function(left, right) { // By default, Array.prototype.sort() sorts alphabetically. diff --git a/src/js/glowingbear.js b/src/js/glowingbear.js index 65cd0bf..779522d 100644 --- a/src/js/glowingbear.js +++ b/src/js/glowingbear.js @@ -1,10 +1,28 @@ 'use strict'; import * as Favico from "favico.js"; -import * as _ from "underscore"; + import { connectionFactory } from './connection'; +/* debounce helper so we dont have to use underscore.js */ +const debounce = function (func, wait, immediate) { + var timeout; + return function () { + var context = this, args = arguments; + clearTimeout(timeout); + timeout = setTimeout(function () { + timeout = null; + if (!immediate) func.apply(context, args); + }, wait); + if (immediate && !timeout) func.apply(context, args); + }; +}; + +const sortBy = (key) => { + return (a, b) => (a[key] > b[key]) ? 1 : ((b[key] > a[key]) ? -1 : 0); +}; + var weechat = angular.module('weechat', ['ngRoute', 'localStorage', 'weechatModels', 'bufferResume', 'plugins', 'IrcUtils', 'ngSanitize', 'ngWebsockets', 'ngTouch'], ['$compileProvider', function($compileProvider) { // hacky way to be able to find out if we're in debug mode weechat.compileProvider = $compileProvider; @@ -209,7 +227,7 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', }; $rootScope.updateBufferBottom(true); $rootScope.scrollWithBuffer(true); - bl.onscroll = _.debounce(function() { + bl.onscroll = debounce(function() { $rootScope.updateBufferBottom(); }, 80); setTimeout(scrollHeightObserver, 500); @@ -370,7 +388,8 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', document.getElementById('content').setAttribute('sidebar-state', 'visible'); if (utils.isMobileUi()) { // de-focus the input bar when opening the sidebar on mobile, so that the keyboard goes down - _.each(document.getElementsByTagName('textarea'), function(elem) { + // TODO: this should be using get element by id, since there is other texareas + Object.entries(document.getElementsByTagName('textarea')).forEach(function([key, elem]) { $timeout(function(){elem.blur();}); }); } @@ -558,7 +577,7 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame; // Recalculate number of lines on resize - window.addEventListener("resize", _.debounce(function() { + window.addEventListener("resize", debounce(function() { // Recalculation fails when not connected if ($rootScope.connected) { // Show the sidebar if switching away from mobile view, hide it when switching to mobile @@ -884,19 +903,16 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', //XXX not sure whether this belongs here $rootScope.switchToActivityBuffer = function() { // Find next buffer with activity and switch to it - var sortedBuffers = _.sortBy($scope.getBuffers(), 'number'); - var i, buffer; + var sortedBuffers = Object.entries($scope.getBuffers()).sort(sortBy('number')); // Try to find buffer with notification - for (i in sortedBuffers) { - buffer = sortedBuffers[i]; + for (const [bufferid, buffer] of sortedBuffers) { if (buffer.notification > 0) { $scope.setActiveBuffer(buffer.id); return; // return instead of break so that the second for loop isn't executed } } // No notifications, find first buffer with unread lines instead - for (i in sortedBuffers) { - buffer = sortedBuffers[i]; + for (const [bufferid, buffer] of sortedBuffers) { if (buffer.unread > 0 && !buffer.hidden) { $scope.setActiveBuffer(buffer.id); return; @@ -910,7 +926,7 @@ weechat.controller('WeechatCtrl', ['$rootScope', '$scope', '$store', '$timeout', $rootScope.switchToAdjacentBuffer = function(direction) { // direction is +1 for next buffer, -1 for previous buffer - var sortedBuffers = _.sortBy($scope.getBuffers(), $rootScope.predicate); + var sortedBuffers = Object.values($scope.getBuffers()).sort(sortBy($rootScope.predicate)); var activeBuffer = models.getActiveBuffer(); var index = sortedBuffers.indexOf(activeBuffer) + direction; var newBuffer; diff --git a/src/js/handlers.js b/src/js/handlers.js index 656d6cc..ad8b4ca 100644 --- a/src/js/handlers.js +++ b/src/js/handlers.js @@ -1,7 +1,7 @@ 'use strict'; -import * as _ from "underscore"; + var weechat = angular.module('weechat'); @@ -164,13 +164,14 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific if (!manually && (!buffer.active || !$rootScope.isWindowFocused())) { var server = models.getServerForBuffer(buffer); - if (buffer.notify > 1 && _.contains(message.tags, 'notify_message') && !_.contains(message.tags, 'notify_none')) { + + if (buffer.notify > 1 && message.tags.includes('notify_message') && !message.tags.includes('notify_none')) { buffer.unread++; server.unread++; $rootScope.$emit('notificationChanged'); } - if ((buffer.notify !== 0) && (message.highlight || _.contains(message.tags, 'notify_private'))) { + if ((buffer.notify !== 0) && (message.highlight || message.tags.includes('notify_private'))) { buffer.notification++; server.unread++; notifications.createHighlight(buffer, message); @@ -304,7 +305,7 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific var old_number = old.number; var new_number = obj.number; - _.each(models.getBuffers(), function(buffer) { + Object.entries(models.getBuffers()).forEach(function([key, buffer]) { if (buffer.number > old_number && buffer.number <= new_number) { buffer.number -= 1; } @@ -389,11 +390,11 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific var handleHotlistInfo = function(message) { // Hotlist includes only buffers with unread counts so first we // iterate all our buffers and resets the counts. - _.each(models.getBuffers(), function(buffer) { + Object.entries(models.getBuffers()).forEach(function([key, buffer]) { buffer.unread = 0; buffer.notification = 0; }); - _.each(models.getServers(), function(server) { + Object.entries(models.getServers()).forEach(function([key, server]) { server.unread = 0; }); if (message.objects.length > 0) { @@ -417,7 +418,7 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific /* Since there is unread messages, we can guess * what the last read line is and update it accordingly */ - var unreadSum = _.reduce(l.count, function(memo, num) { return memo + num; }, 0); + var unreadSum = l.count.reduce(function(memo, num) { return memo + num; }, 0); buffer.lastSeen = buffer.lines.length - 1 - unreadSum; // update server buffer. Don't incude index 0 -> not unreadSum @@ -517,7 +518,7 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific }; $rootScope.$on('onMessage', function(event, message) { - if (_.has(eventHandlers, message.id)) { + if (message.id in eventHandlers) { eventHandlers[message.id](message); } else { $log.debug('Unhandled event received: ' + message.id); @@ -525,7 +526,7 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific }); var handleEvent = function(event) { - if (_.has(eventHandlers, event.id)) { + if (event.id in eventHandlers) { eventHandlers[event.id](event); } }; diff --git a/src/js/inputbar.js b/src/js/inputbar.js index dea251f..29f43d4 100644 --- a/src/js/inputbar.js +++ b/src/js/inputbar.js @@ -1,6 +1,6 @@ 'use strict'; -import * as _ from "underscore"; + var weechat = angular.module('weechat'); @@ -314,7 +314,7 @@ weechat.directive('inputBar', function() { ab.addToHistory($scope.command); // Split the command into multiple commands based on line breaks - _.each($scope.command.split(/\r?\n/), function(line) { + $scope.command.split(/\r?\n/).forEach(function(line) { // Ask before a /quit if (line === '/quit' || line.indexOf('/quit ') === 0) { if (!window.confirm("Are you sure you want to quit WeeChat? This will prevent you from connecting with Glowing Bear until you restart WeeChat on the command line!")) { @@ -500,7 +500,7 @@ weechat.directive('inputBar', function() { // Map the buffers to only their numbers and IDs so we don't have to // copy the entire (possibly very large) buffer object, and then sort // the buffers according to their WeeChat number - sortedBuffers = _.map(models.getBuffers(), function(buffer) { + sortedBuffers = Object.entries(models.getBuffers()).map(function([key, buffer], index) { return [buffer.number, buffer.id]; }).sort(function(left, right) { // By default, Array.prototype.sort() sorts alphabetically. @@ -610,12 +610,12 @@ weechat.directive('inputBar', function() { // Alt-h -> Toggle all as read if ($event.altKey && !$event.ctrlKey && code === 72) { var buffers = models.getBuffers(); - _.each(buffers, function(buffer) { + Object.entries(buffers).forEach(function([key, buffer], index) { buffer.unread = 0; buffer.notification = 0; }); var servers = models.getServers(); - _.each(servers, function(server) { + Object.entries(servers).forEach(function([key, server], index) { server.unread = 0; }); connection.sendHotlistClearAll(); diff --git a/src/js/irc-utils.js b/src/js/irc-utils.js index 33c2667..d3e0ae6 100644 --- a/src/js/irc-utils.js +++ b/src/js/irc-utils.js @@ -3,8 +3,6 @@ */ 'use strict'; -import {sortBy, pluck} from "underscore"; - var IrcUtils = angular.module('IrcUtils', []); @@ -19,17 +17,14 @@ IrcUtils.service('IrcUtils', [function() { }; /** - * Get a new version of a nick list, sorted by last speaker + * Get a new version of a nick list * * @param nickList Original nick list - * @return Sorted nick list + * @return list of nick names */ var _ciNickList = function(nickList) { - var newList = sortBy(nickList, function(nickObj) { - return -nickObj.spokeAt; - }); - newList = pluck(newList, 'name'); + let newList = nickList.map((el) => el.name); return newList; }; diff --git a/src/js/models.js b/src/js/models.js index 0f2316b..6327dbc 100644 --- a/src/js/models.js +++ b/src/js/models.js @@ -4,7 +4,7 @@ */ 'use strict'; -import * as _ from "underscore"; + import * as weeChat from './weechat'; @@ -156,15 +156,12 @@ models.service('models', ['$rootScope', '$filter', 'bufferResume', function($roo if (group === undefined) { return; } - group.nicks = _.filter(group.nicks, function(n) { return n.name !== nick.name;}); - /* for (i in group.nicks) { if (group.nicks[i].name == nick.name) { delete group.nicks[i]; break; } } - */ }; /* * Clear the nicklist @@ -219,15 +216,14 @@ models.service('models', ['$rootScope', '$filter', 'bufferResume', function($roo else if (nick === "" || nick === "=!=") { return; } - _.each(nicklist, function(nickGroup) { - _.each(nickGroup.nicks, function(nickObj) { - if (nickObj.name === nick) { - // Use the order the line arrive in for simplicity - // instead of using weechat's own timestamp - nickObj.spokeAt = Date.now(); + for (let groupIdx in nicklist) { + let nicks = nicklist[groupIdx].nicks; + for (let nickIdx in nicks) { + if (nicks[nickIdx].name === nick) { + nicks[nickIdx].spokeAt = Date.now(); } - }); - }); + } + } }; /* @@ -237,11 +233,12 @@ models.service('models', ['$rootScope', '$filter', 'bufferResume', function($roo */ var getNicklistByTime = function() { var newlist = []; - _.each(nicklist, function(nickGroup) { - _.each(nickGroup.nicks, function(nickObj) { - newlist.push(nickObj); - }); - }); + for (let groupIdx in nicklist) { + let nicks = nicklist[groupIdx].nicks; + for (let nickIdx in nicks) { + newlist.push(nicks[nickIdx]); + } + } newlist.sort(function(a, b) { return a.spokeAt < b.spokeAt; @@ -601,11 +598,12 @@ models.service('models', ['$rootScope', '$filter', 'bufferResume', function($roo activeBuffer = this.model.buffers[bufferId]; } else { - activeBuffer = _.find(this.model.buffers, function(buffer) { - if (buffer[key] === bufferId) { - return buffer; - } + activeBuffer = Object.entries(this.model.buffers).find(([id, buffer]) => { + return buffer[key] === bufferId; }); + if (activeBuffer !== undefined) { + activeBuffer = activeBuffer[1]; // value not key + } } if (activeBuffer === undefined) { @@ -703,7 +701,7 @@ models.service('models', ['$rootScope', '$filter', 'bufferResume', function($roo return; } if (buffer.active) { - var firstBuffer = _.keys(this.model.buffers)[0]; + var firstBuffer = Object.keys(this.model.buffers)[0]; this.setActiveBuffer(firstBuffer); } // Can't use `buffer` here, needs to be deleted from the list diff --git a/src/js/plugins.js b/src/js/plugins.js index 106fedc..ffd4ab2 100644 --- a/src/js/plugins.js +++ b/src/js/plugins.js @@ -4,7 +4,7 @@ 'use strict'; -import * as _ from "underscore"; + var plugins = angular.module('plugins', []); @@ -34,7 +34,7 @@ var urlRegexp = /(?:(?:https?|ftp):\/\/|www\.|ftp\.)\S*[^\s.;,(){}<>[\]]/g; var UrlPlugin = function(name, urlCallback) { return { contentForMessage: function(message) { - var urls = _.uniq(message.match(urlRegexp)); + var urls = [... new Set(message.match(urlRegexp))]; var content = []; for (var i = 0; urls && i < urls.length; i++) { diff --git a/src/js/utils.js b/src/js/utils.js index 756f624..ac5dde0 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -1,18 +1,18 @@ -import * as _ from "underscore"; + var weechat = angular.module('weechat'); weechat.factory('utils', function() { // Helper to change style of a class var changeClassStyle = function(classSelector, attr, value) { - _.each(document.getElementsByClassName(classSelector), function(e) { + Array.from(document.getElementsByClassName(classSelector)).forEach(function(e) { e.style[attr] = value; }); }; // Helper to get style from a class var getClassStyle = function(classSelector, attr) { - _.each(document.getElementsByClassName(classSelector), function(e) { + Array.from(document.getElementsByClassName(classSelector)).forEach(function(e) { return e.style[attr]; }); }; diff --git a/src/js/websockets.js b/src/js/websockets.js index be1704e..1ce9457 100644 --- a/src/js/websockets.js +++ b/src/js/websockets.js @@ -1,7 +1,7 @@ 'use strict'; -import * as _ from "underscore"; + var websockets = angular.module('ngWebsockets', []); @@ -102,7 +102,7 @@ function($rootScope, $q) { * Receives a message on the websocket */ var message = protocol.parse(evt.data); - if (_.has(callbacks, message.id)) { + if (message.id in callbacks) { // see if it's bound to one of the callbacks var promise = callbacks[message.id]; promise.cb.resolve(message); diff --git a/src/js/whenscrolled-directive.js b/src/js/whenscrolled-directive.js index bf37410..a21c447 100644 --- a/src/js/whenscrolled-directive.js +++ b/src/js/whenscrolled-directive.js @@ -1,7 +1,7 @@ 'use strict'; -import * as _ from "underscore"; + var weechat = angular.module('weechat'); @@ -16,7 +16,7 @@ weechat.directive('whenScrolled', function() { }; elm.bind('scroll', function() { - _.debounce(fun, 200)(); + setTimeout(fun, 200); }); }; });