Greasy Fork

Greasy Fork is available in English.

Twitch Tab Completion (compatible with BTTV)

Tab completion for emotes (and users) also add chat history. BetterTTV

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Twitch Tab Completion (compatible with BTTV)
// @namespace    https://openuserjs.org/users/daybreakz
// @version      1.0.9
// @description  Tab completion for emotes (and users) also add chat history. BetterTTV
// @author       Daybr3akz
// @license      MIT
// @copyright 2017, daybreakz (https://openuserjs.org/users/daybreakz)
// @match        https://www.twitch.tv/*
// @require      http://code.jquery.com/jquery-3.2.1.slim.min.js
// @grant        none
// ==/UserScript==

// // ==OpenUserJS==
// @author daybreakz
// ==/OpenUserJS==




    /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.getConnectRoot = getConnectRoot;
exports.getChatController = getChatController;
exports.getChatInputController = getChatInputController;
exports.getCurrentChat = getCurrentChat;
exports.setCurrentUser = setCurrentUser;
exports.getCurrentUser = getCurrentUser;
exports.getCurrentChannel = getCurrentChannel;
exports.updateCurrentChannel = updateCurrentChannel;
var REACT_ROOT = '#root div[data-reactroot]';
var CHAT_CONTAINER = '.chat-room__container';
var CHAT_INPUT = '.chat-input';
// const $ = s => document.querySelectorAll(s);
var $ = window.$;

function getReactInstance(element) {
    for (var key in element) {
        if (key.startsWith('__reactInternalInstance$')) {
            return element[key];
        }
    }
    return null;
}

function getReactElement(element) {
    var instance = getReactInstance(element);
    if (!instance) return null;
    return instance._currentElement;
}

function getParentNode(reactElement) {
    try {
        return reactElement._owner._currentElement._owner;
    } catch (_) {
        return null;
    }
}

function searchReactChildren(node, predicate) {
    var maxDepth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 15;
    var depth = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;

    try {
        if (predicate(node)) {
            return node;
        }
    } catch (_) {}

    if (!node || depth > maxDepth) {
        return null;
    }

    var children = node._renderedChildren,
        component = node._renderedComponent;


    if (children) {
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
            for (var _iterator = Object.keys(children)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
                var key = _step.value;

                var childResult = searchReactChildren(children[key], predicate, maxDepth, depth + 1);
                if (childResult) {
                    return childResult;
                }
            }
        } catch (err) {
            _didIteratorError = true;
            _iteratorError = err;
        } finally {
            try {
                if (!_iteratorNormalCompletion && _iterator.return) {
                    _iterator.return();
                }
            } finally {
                if (_didIteratorError) {
                    throw _iteratorError;
                }
            }
        }
    }

    if (component) {
        return searchReactChildren(component, predicate, maxDepth, depth + 1);
    }

    return null;
}

function getConnectRoot() {
    var root = void 0;
    try {
        root = getParentNode(getReactElement($(REACT_ROOT)[0]));
    } catch (_) {}
    return root;
}

function getChatController() {
    var container = $(CHAT_CONTAINER).parent()[0];
    if (!container) return null;

    var controller = searchReactChildren(getReactInstance(container), function (node) {
        return node._instance && node._instance.chatBuffer;
    });

    if (controller) {
        controller = controller._instance;
    }

    return controller;
}

function getChatInputController() {
    var container = $(CHAT_INPUT)[0];
    if (!container) return null;

    var controller = void 0;
    try {
        controller = getParentNode(getReactElement(container))._instance;
    } catch (_) {}

    return controller;
}

function getCurrentChat() {
    var container = $(CHAT_CONTAINER)[0];
    if (!container) return null;
    var controller = void 0;
    try {
        controller = getParentNode(getReactElement(container))._instance;
    } catch (_) {}
    return controller;
}

var currentUser = null;
function setCurrentUser(accessToken, id, name, displayName) {
    // twitchAPI.setAccessToken(accessToken);
    currentUser = {
        id: id.toString(),
        name: name,
        displayName: displayName
    };
}

function getCurrentUser() {
    return currentUser;
}

var currentChannel = void 0;
function getCurrentChannel() {
    return currentChannel;
}

function updateCurrentChannel() {
    var rv = void 0;
    var currentChat = getCurrentChat();
    if (currentChat && currentChat.props && currentChat.props.channelID) {
        var _currentChat$props = currentChat.props,
            channelID = _currentChat$props.channelID,
            channelLogin = _currentChat$props.channelLogin,
            channelDisplayName = _currentChat$props.channelDisplayName;

        rv = {
            id: channelID.toString(),
            name: channelLogin,
            displayName: channelDisplayName
        };
    }
    currentChannel = rv;
    return rv;
}

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});
window.customLog = localStorage.getItem('customLog') === 'true';

var console = window.console;
try {
    console = $('iframe')[0].contentWindow.console;
} catch (e) {}

function log(type) {
    if (!console || !window.customLog) return;

    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        args[_key - 1] = arguments[_key];
    }

    console[type].apply(console, ['SCRIPT:'].concat(args));
}

var debug = {
    log: log.bind(undefined, 'log'),
    error: log.bind(undefined, 'error'),
    warn: log.bind(undefined, 'warn'),
    info: log.bind(undefined, 'info')
};

exports.default = debug;

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _debug = __webpack_require__(1);

var _debug2 = _interopRequireDefault(_debug);

var _twitch = __webpack_require__(0);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var API_ENDPOINT = 'https://api.betterttv.net/2/';
var fetchJson = function fetchJson(path) {
    return fetch('' + API_ENDPOINT + path).then(function (r) {
        return r.json();
    });
};

var channel = {};

var EmotesProvider = function () {
    function EmotesProvider() {
        _classCallCheck(this, EmotesProvider);

        this.channelEmotes = new Set();
        this.globalEmotes = new Set();
        this.emojis = new Set();
        this.loadBTTVGlobalEmotes();
        // this.loadEmojis();
    }

    _createClass(EmotesProvider, [{
        key: 'updateChannel',
        value: function () {
            var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
                var currentChannel;
                return regeneratorRuntime.wrap(function _callee$(_context) {
                    while (1) {
                        switch (_context.prev = _context.next) {
                            case 0:
                                currentChannel = (0, _twitch.getCurrentChannel)();

                                if (currentChannel) {
                                    _context.next = 3;
                                    break;
                                }

                                return _context.abrupt('return');

                            case 3:
                                if (!(currentChannel.id === channel.id)) {
                                    _context.next = 5;
                                    break;
                                }

                                return _context.abrupt('return');

                            case 5:
                                channel = currentChannel;
                                _context.next = 8;
                                return fetchJson('channels/' + channel.name);

                            case 8:
                                return _context.abrupt('return', _context.sent);

                            case 9:
                            case 'end':
                                return _context.stop();
                        }
                    }
                }, _callee, this);
            }));

            function updateChannel() {
                return _ref.apply(this, arguments);
            }

            return updateChannel;
        }()
    }, {
        key: 'load',
        value: function () {
            var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
                var channelData;
                return regeneratorRuntime.wrap(function _callee2$(_context2) {
                    while (1) {
                        switch (_context2.prev = _context2.next) {
                            case 0:
                                _context2.next = 2;
                                return this.updateChannel();

                            case 2:
                                channelData = _context2.sent;

                                if (channelData && channelData.emotes) {
                                    this.loadBTTVChannelEmotes(channelData.emotes);
                                }

                            case 4:
                            case 'end':
                                return _context2.stop();
                        }
                    }
                }, _callee2, this);
            }));

            function load() {
                return _ref2.apply(this, arguments);
            }

            return load;
        }()
    }, {
        key: 'loadBTTVGlobalEmotes',
        value: function () {
            var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {
                var _this = this;

                var x, emotesJson;
                return regeneratorRuntime.wrap(function _callee3$(_context3) {
                    while (1) {
                        switch (_context3.prev = _context3.next) {
                            case 0:
                                _context3.next = 2;
                                return fetchJson('emotes');

                            case 2:
                                x = _context3.sent;
                                emotesJson = x.emotes;

                                emotesJson.forEach(function (em) {
                                    if (!em || !em.code) return;
                                    _this.globalEmotes.add(em.code);
                                });
                                _debug2.default.log('Got globalEmotes');

                            case 6:
                            case 'end':
                                return _context3.stop();
                        }
                    }
                }, _callee3, this);
            }));

            function loadBTTVGlobalEmotes() {
                return _ref3.apply(this, arguments);
            }

            return loadBTTVGlobalEmotes;
        }()
    }, {
        key: 'loadBTTVChannelEmotes',
        value: function loadBTTVChannelEmotes(emotesJson) {
            var _this2 = this;

            this.channelEmotes.clear();
            emotesJson.forEach(function (em) {
                if (!em || !em.code) return;
                _this2.channelEmotes.add(em.code);
            });
            _debug2.default.log('Got channel emotes');
        }
    }, {
        key: 'getEmotes',
        value: function getEmotes() {
            if (!window.BetterTTV) {
                return [];
            }
            var user = (0, _twitch.getCurrentUser)();user;

            var emotes = [].concat(_toConsumableArray(this.globalEmotes))
            // .concat([...this.emojis])
            .concat([].concat(_toConsumableArray(this.channelEmotes)));
            return emotes;
        }
    }]);

    return EmotesProvider;
}();

// let provider = new NopeProvider();
// if (window.BetterTTV) {


var provider = new EmotesProvider();
exports.default = provider;

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _routeKeysToPaths;

var waitForChat = function () {
    var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
        var abort, currentIsWaiting;
        return regeneratorRuntime.wrap(function _callee$(_context) {
            while (1) {
                switch (_context.prev = _context.next) {
                    case 0:
                        abort = false;

                        wait(15000).then(function () {
                            return abort = true;
                        });
                        isWaiting = Symbol('waitingForChat');
                        currentIsWaiting = isWaiting;

                    case 4:
                        if (abort) {
                            _context.next = 14;
                            break;
                        }

                        if (!checkChat()) {
                            _context.next = 7;
                            break;
                        }

                        return _context.abrupt('return', true);

                    case 7:
                        if (!(isWaiting !== currentIsWaiting)) {
                            _context.next = 10;
                            break;
                        }

                        _debug2.default.log('waitForChat was cancelled');
                        return _context.abrupt('return', false);

                    case 10:
                        _context.next = 12;
                        return wait(25);

                    case 12:
                        _context.next = 4;
                        break;

                    case 14:
                        return _context.abrupt('return', false);

                    case 15:
                    case 'end':
                        return _context.stop();
                }
            }
        }, _callee, this);
    }));

    return function waitForChat() {
        return _ref.apply(this, arguments);
    };
}();

var main = function () {
    var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
        var router;
        return regeneratorRuntime.wrap(function _callee2$(_context2) {
            while (1) {
                switch (_context2.prev = _context2.next) {
                    case 0:
                        _context2.next = 2;
                        return getRouter();

                    case 2:
                        router = _context2.sent;

                        router.history.listen(function (location) {
                            return onRouteChange(location);
                        });
                        onRouteChange(router.history.location);

                    case 5:
                    case 'end':
                        return _context2.stop();
                }
            }
        }, _callee2, this);
    }));

    return function main() {
        return _ref2.apply(this, arguments);
    };
}();

var _debug = __webpack_require__(1);

var _debug2 = _interopRequireDefault(_debug);

var _tab_completion = __webpack_require__(4);

var _tab_completion2 = _interopRequireDefault(_tab_completion);

var _emotes = __webpack_require__(2);

var _emotes2 = _interopRequireDefault(_emotes);

var _twitch = __webpack_require__(0);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var currentPath = void 0;

var routes = {
    DIRECTORY_FOLLOWING_LIVE: 'DIRECTORY_FOLLOWING_LIVE',
    DIRECTORY_FOLLOWING: 'DIRECTORY_FOLLOWING',
    DIRECTORY: 'DIRECTORY',
    CHAT: 'CHAT',
    CHANNEL: 'CHANNEL',
    VOD: 'VOD'
};

var routeKeysToPaths = (_routeKeysToPaths = {}, _defineProperty(_routeKeysToPaths, routes.DIRECTORY_FOLLOWING_LIVE, /^\/directory\/following\/live$/i), _defineProperty(_routeKeysToPaths, routes.DIRECTORY_FOLLOWING, /^\/directory\/following$/i), _defineProperty(_routeKeysToPaths, routes.DIRECTORY, /^\/directory/i), _defineProperty(_routeKeysToPaths, routes.CHAT, /^(\/popout)?\/[a-z0-9-_]+\/chat$/i), _defineProperty(_routeKeysToPaths, routes.VOD, /^\/videos\/[0-9]+$/i), _defineProperty(_routeKeysToPaths, routes.CHANNEL, /^\/[a-z0-9-_]+/i), _routeKeysToPaths);

function getRouteFromPath(path) {
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;

    try {
        for (var _iterator = Object.keys(routeKeysToPaths)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var name = _step.value;

            var regex = routeKeysToPaths[name];
            if (!regex.test(path)) continue;
            return name;
        }
    } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
    } finally {
        try {
            if (!_iteratorNormalCompletion && _iterator.return) {
                _iterator.return();
            }
        } finally {
            if (_didIteratorError) {
                throw _iteratorError;
            }
        }
    }

    return null;
}

function getRouter() {
    return new Promise(function (resolve) {
        var loadInterval = setInterval(function () {
            var user = void 0,
                router = void 0;
            try {
                var connectRoot = (0, _twitch.getConnectRoot)();
                if (!connectRoot) return;
                var context = connectRoot._context;
                router = context.router;
                user = context.store.getState().session.user;
            } catch (_) {
                return;
            }

            if (!router || !user) return;
            clearInterval(loadInterval);

            (0, _twitch.setCurrentUser)(user.authToken, user.id, user.login, user.displayName);
            resolve(router);
        }, 25);
    });
}

function makeCheckChat() {
    var currentChatReference = null;
    return function () {
        if (!(0, _twitch.updateCurrentChannel)()) return false;
        var lastReference = currentChatReference;
        var currentChat = (0, _twitch.getCurrentChat)();

        if (currentChat && currentChat === lastReference) {
            return false;
        }
        if (lastReference && currentChat.props.channelID.toString() === lastReference.props.channelID.toString()) {
            return false;
        }

        currentChatReference = currentChat;

        return true;
    };
}
var checkChat = makeCheckChat();

var wait = function wait(t) {
    return new Promise(function (r) {
        return setTimeout(r, t);
    });
};
var isWaiting = false;


function triggerRouteChanged() {}

function triggerChatLoaded() {
    _debug2.default.log('CHAT WAS LOADED');
    _tab_completion2.default.load(true);
    _emotes2.default.load();
}

function onRouteChange(location) {
    var lastPath = currentPath;
    var path = location.pathname;
    var route = getRouteFromPath(path);
    _debug2.default.log('New route: ' + location.pathname + ' as ' + route);

    // emit load
    triggerRouteChanged();
    currentPath = path;
    if (currentPath === lastPath) return;
    if (route === routes.CHAT || route === routes.CHANNEL) {
        waitForChat().then(function (loaded) {
            return loaded && triggerChatLoaded();
        });
    }
}

main();

/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
// const InputPatcherModule = require('./input-patcher-module');


var _customInputModule = __webpack_require__(5);

var _customInputModule2 = _interopRequireDefault(_customInputModule);

var _chatHistoryModule = __webpack_require__(6);

var _chatHistoryModule2 = _interopRequireDefault(_chatHistoryModule);

var _twitch = __webpack_require__(0);

var _debug = __webpack_require__(1);

var _debug2 = _interopRequireDefault(_debug);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// window.getChatController = getChatController;

var CHAT_INPUT = '.chat-input';
var $ = window.$;

function patchSendMessage(callback) {
    var chatBuffer = (0, _twitch.getChatController)().chatBuffer;
    _debug2.default.log(chatBuffer);

    if (chatBuffer._consumeChatEventPatched === true) {
        return;
    }
    chatBuffer._consumeChatEventPatched = true;
    var twitchConsumeChatEvent = chatBuffer.consumeChatEvent;

    function myConsumeChatEvent(event) {
        if (event && event.type === 0) {
            try {
                callback(event);
            } catch (error) {
                _debug2.default.error(error);
            }
        }
        return twitchConsumeChatEvent.apply(this, arguments);
    }
    chatBuffer.consumeChatEvent = myConsumeChatEvent;
}

var ChatTabCompletionModule = function () {
    function ChatTabCompletionModule() {
        var _this = this;

        _classCallCheck(this, ChatTabCompletionModule);

        this.customInput = new _customInputModule2.default(this);
        this.chatHistory = new _chatHistoryModule2.default(this);
        this.currentInput = null;

        // watcher.on('load.chat', () => this.load());
        // settings.on('changed.tabAutocomplete', () => this.load(false));

        $('body').off('click.tabComplete focus.tabComplete keydown.tabComplete').on('click.tabComplete focus.tabComplete', CHAT_INPUT, function () {
            return _this.onFocus();
        }).on('keydown.tabComplete', CHAT_INPUT, function (e) {
            return _this.onKeydown(e);
        });
    }

    _createClass(ChatTabCompletionModule, [{
        key: 'load',
        value: function load() {
            var _this2 = this;

            var chatLoad = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;

            this.customInput.load(chatLoad);
            this.chatHistory.load(chatLoad);
            // if (settings.get('tabAutocomplete')) {
            this.customInput.enable();
            this.currentInput = this.customInput;
            // } else {
            //     this.customInput.disable();
            //     this.currentInput = this.patchedInput;
            // }
            patchSendMessage(function (event) {
                _this2.currentInput.storeUser(event.user);
            });
        }
    }, {
        key: 'onKeydown',
        value: function onKeydown(e) {
            if (this.currentInput) {
                this.currentInput.onKeydown(e);
            }
            this.chatHistory.onKeydown(e);
        }
    }, {
        key: 'onFocus',
        value: function onFocus() {
            if (this.currentInput) {
                this.currentInput.onFocus();
            }
            this.chatHistory.onFocus();
        }
    }, {
        key: 'onSendMessage',
        value: function onSendMessage(message) {
            this.chatHistory.onSendMessage(message);
        }
    }]);

    return ChatTabCompletionModule;
}();

var mod = new ChatTabCompletionModule();
exports.default = mod;

/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _emotes = __webpack_require__(2);

var _emotes2 = _interopRequireDefault(_emotes);

var _twitch = __webpack_require__(0);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var $ = window.$;


var ORIGINAL_TEXTAREA = '.chat-input textarea';

function setReactTextareaValue(txt, msg) {
    txt.value = msg;
    var ev = new Event('input', { target: txt, bubbles: true });
    txt.dispatchEvent(ev);
}

function newTextArea() {
    var $oldText = $(ORIGINAL_TEXTAREA);
    var $text = $oldText.clone().insertBefore(ORIGINAL_TEXTAREA);
    $text.attr('id', 'bttv-chat-input');
    $oldText.attr('id', 'twitch-chat-input');
    $oldText.hide();
    $text.focus();

    $text[0].customSetValue = function (value) {
        $text.val(value);
    };

    $oldText[0].customSetValue = function (value) {
        setReactTextareaValue($oldText[0], value);
    };

    return { $text: $text, $oldText: $oldText };
}

var CustomInputModule = function () {
    function CustomInputModule(parentModule) {
        _classCallCheck(this, CustomInputModule);

        this.parentModule = parentModule;
        this.init();
        // watcher.on('input.onSendMessage', () => this.sendMessage());
        // watcher.on('chat.message', ($el, msg) => this.storeUser($el, msg));
    }

    _createClass(CustomInputModule, [{
        key: 'init',
        value: function init() {
            this.userList = new Set();
            this.tabTries = -1;
            this.suggestions = null;
            this.textSplit = ['', '', ''];
        }
    }, {
        key: 'storeUser',
        value: function storeUser(user) {
            this.userList.add(user.userDisplayName || user.userLogin);
        }
    }, {
        key: 'sendMessage',
        value: function sendMessage() {
            var message = this.$text.val();
            if (message.trim().length === 0) {
                return;
            }
            this.chatInputCtrl.props.onSendMessage(message);
            this.parentModule.onSendMessage(message);
            this.$text.val('');
        }
    }, {
        key: 'load',
        value: function load() {
            var createTextarea = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;

            this.chatInputCtrl = (0, _twitch.getChatInputController)();
            if (createTextarea) {
                var _newTextArea = newTextArea(),
                    $text = _newTextArea.$text,
                    $oldText = _newTextArea.$oldText;

                this.$text = $text;
                this.$oldText = $oldText;
                this.userList = new Set();
            }
        }
    }, {
        key: 'enable',
        value: function enable() {
            this.$text.show();
            this.$oldText.hide();
        }
    }, {
        key: 'disable',
        value: function disable() {
            this.$text.hide();
            this.$oldText.show();
        }
    }, {
        key: 'getSuggestions',
        value: function getSuggestions(prefix) {
            var includeUsers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
            var includeEmotes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;

            var userList = [];
            var emoteList = [];

            if (includeEmotes) {
                var _emoteList;

                emoteList = _emotes2.default.getEmotes(); // .map(emote => emote.code);
                (_emoteList = emoteList).push.apply(_emoteList, _toConsumableArray(this.getTwitchEmotes()));
                emoteList = emoteList.filter(function (word) {
                    return word.toLowerCase().indexOf(prefix.toLowerCase()) === 0;
                });
                emoteList = Array.from(new Set(emoteList).values());
                emoteList.sort();
            }

            if (includeUsers) {
                userList = this.getChatMembers().filter(function (word) {
                    return word.toLowerCase().indexOf(prefix.toLowerCase()) === 0;
                });
                userList.sort();
            }

            return [].concat(_toConsumableArray(emoteList), _toConsumableArray(userList));
        }
    }, {
        key: 'onKeydown',
        value: function onKeydown(e, includeUsers) {
            var keyCode = e.key;
            if (e.ctrlKey) {
                return;
            }
            var $inputField = this.$text;

            if (keyCode === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                this.sendMessage();
            } else if (keyCode === 'Tab') {
                e.preventDefault();
                this.onAutoComplete(includeUsers, e.shiftKey);
            } else if (keyCode === 'Escape' && this.tabTries >= 0) {
                $inputField.val(this.textSplit.join(''));
            } else if (keyCode !== 'Shift') {
                this.tabTries = -1;
            }
        }
    }, {
        key: 'onFocus',
        value: function onFocus() {
            this.tabTries = -1;
        }
    }, {
        key: 'onAutoComplete',
        value: function onAutoComplete(includeUsers, shiftKey) {
            var $inputField = this.$text;

            // First time pressing tab, split before and after the word
            if (this.tabTries === -1) {
                var caretPos = $inputField[0].selectionStart;
                var text = $inputField.val();

                var start = (/[\:\(\)\w]+$/.exec(text.substr(0, caretPos)) || { index: caretPos }).index;
                var end = caretPos + (/^\w+/.exec(text.substr(caretPos)) || [''])[0].length;
                this.textSplit = [text.substring(0, start), text.substring(start, end), text.substring(end + 1)];

                // If there are no words in front of the caret, exit
                if (this.textSplit[1] === '') return;

                // Get all matching completions
                var includeEmotes = this.textSplit[0].slice(-1) !== '@';
                this.suggestions = this.getSuggestions(this.textSplit[1], includeUsers, includeEmotes);
            }

            if (this.suggestions.length > 0) {
                this.tabTries += shiftKey ? -1 : 1; // shift key iterates backwards
                if (this.tabTries >= this.suggestions.length) this.tabTries = 0;
                if (this.tabTries < 0) this.tabTries = this.suggestions.length - 1;
                if (!this.suggestions[this.tabTries]) return;

                var cursorOffset = 0;
                if (this.textSplit[2].trim() === '') {
                    this.textSplit[2] = ' ';
                    cursorOffset = 1;
                }

                var cursorPos = this.textSplit[0].length + this.suggestions[this.tabTries].length + cursorOffset;
                $inputField.val(this.textSplit[0] + this.suggestions[this.tabTries] + this.textSplit[2]);
                $inputField[0].setSelectionRange(cursorPos, cursorPos);
            }
        }
    }, {
        key: 'getTwitchEmotes',
        value: function getTwitchEmotes() {
            var twEmotes = this.chatInputCtrl.props.emotes;
            if (!twEmotes) {
                return [];
            }
            return twEmotes.reduce(function (accum, v) {
                return accum.concat(v.emotes);
            }, []).map(function (emote) {
                return emote.displayName;
            });
        }
    }, {
        key: 'getChatMembers',
        value: function getChatMembers() {
            var broadcasterName = this.chatInputCtrl.props.channelDisplayName;
            this.userList.add(broadcasterName);
            return Array.from(this.userList.values());
        }
    }]);

    return CustomInputModule;
}();

exports.default = CustomInputModule;

/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var $ = window.$;

function isSuggestionsShowing() {
    return !!$('[data-a-target="autocomplete-balloon"]')[0];
}

var ChatHistoryModule = function () {
    function ChatHistoryModule(parentModule) {
        _classCallCheck(this, ChatHistoryModule);

        this.parentModule = parentModule;
    }

    _createClass(ChatHistoryModule, [{
        key: 'load',
        value: function load(resetHistory) {
            if (resetHistory) {
                this.messageHistory = [];
            }
            this.historyPos = -1;
        }
    }, {
        key: 'onKeydown',
        value: function onKeydown(e) {
            var keyCode = e.key;
            if (e.ctrlKey) {
                return;
            }
            var $inputField = $(e.target);
            var setInputValue = function setInputValue(value) {
                e.target.customSetValue(value);
            };

            if (keyCode === 'ArrowUp') {
                if (isSuggestionsShowing()) return;
                if ($inputField[0].selectionStart > 0) return;
                if (this.historyPos + 1 === this.messageHistory.length) return;
                var prevMsg = this.messageHistory[++this.historyPos];
                setInputValue(prevMsg);
                $inputField[0].setSelectionRange(0, 0);
            } else if (keyCode === 'ArrowDown') {
                if (isSuggestionsShowing()) return;
                if ($inputField[0].selectionStart < $inputField.val().length) return;
                if (this.historyPos > 0) {
                    var _prevMsg = this.messageHistory[--this.historyPos];
                    setInputValue(_prevMsg);
                    $inputField[0].setSelectionRange(_prevMsg.length, _prevMsg.length);
                } else {
                    var draft = $inputField.val().trim();
                    if (this.historyPos < 0 && draft.length > 0) {
                        this.messageHistory.unshift(draft);
                    }
                    this.historyPos = -1;
                    $inputField.val('');
                    setInputValue('');
                }
            } else if (this.historyPos >= 0) {
                this.messageHistory[this.historyPos] = $inputField.val();
            }
        }
    }, {
        key: 'onSendMessage',
        value: function onSendMessage(message) {
            if (message.trim().length === 0) return;
            this.messageHistory.unshift(message);
            this.historyPos = -1;
            // watcher.emit('input.onSendMessage', message);
        }
    }, {
        key: 'onFocus',
        value: function onFocus() {
            this.historyPos = -1;
        }
    }]);

    return ChatHistoryModule;
}();

exports.default = ChatHistoryModule;

/***/ })
/******/ ]);
//# sourceMappingURL=main.bundle.js.map