// ==UserScript== // @name Slack Files Tools // @version 1.0.0 // @description Extends Slack files webpage functionalities // @author Mauro // @license MIT // @namespace https://github.com/gusuraman // @match https://*.slack.com/files* // @grant none // @run-at document-end // @icon https://github.com/fluidicon.png // @updateURL https://raw.githubusercontent.com/gusuraman/slack-files-tools/master/dist/slack.files_tools.js // @downloadURL https://raw.githubusercontent.com/gusuraman/slack-files-tools/master/dist/slack.files_tools.js // ==/UserScript== (function () { "use strict"; /******/ (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 = 1); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var GS = exports.GS = {}; /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { __webpack_require__(2); module.exports = __webpack_require__(11); /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var _globals = __webpack_require__(0); var _config = __webpack_require__(3); var _config2 = _interopRequireDefault(_config); var _data = __webpack_require__(4); var _data2 = _interopRequireDefault(_data); var _utilities = __webpack_require__(5); var __utils = _interopRequireWildcard(_utilities); var _files = __webpack_require__(6); var _files2 = _interopRequireDefault(_files); var _events = __webpack_require__(8); var _events2 = _interopRequireDefault(_events); var _templates = __webpack_require__(9); var _templates2 = _interopRequireDefault(_templates); var _ui = __webpack_require__(10); var _ui2 = _interopRequireDefault(_ui); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } boot_data.files = []; _globals.GS.cfg = _config2.default; loadUtils(); parseUrl(); initGlobals(); loadRes(); // GS.loadStyle(GS.cfg.styles); _globals.GS.debug.info(_globals.GS.cfg); $('.pagination-centered').addClass('hidden'); var viewSummary = $('.span_1_of_3>p.subtle_silver').addClass('hidden'); if ($('#files_div').length) { $(".loading_hash_animation").on("remove", function () { $('#files_list').hide().before(_globals.GS.templates.loadingAnim()); _globals.GS.data.loadData().then(finalizeBoot); }); } else { $('#files_owner').bind("DOMSubtreeModified", function () { $(this).unbind("DOMSubtreeModified"); _globals.GS.data.loadData().then(_globals.GS.ui.initTools); }); } function finalizeBoot() { _globals.GS.debug.info('Boot complete'); var $filesList = $('#files_list'); $filesList.removeClass('loaded'); viewSummary.find('strong:nth-child(1)').attr('id', 'view_files_from'); viewSummary.find('strong:nth-child(2)').attr('id', 'view_files_to'); viewSummary.find('strong:nth-child(3)').attr('id', 'view_files_total'); TS.web.files.renderFiles(boot_data.files); _globals.GS.debug.info('Files rendered'); _globals.GS.ui.initTools(); $filesList.before(_globals.GS.templates.selectAllCb('files_select_all_top')); $filesList.after(_globals.GS.templates.selectAllCb('files_select_all_bottom')); $('.files_select_all_cb').click(_globals.GS.files.batch.selectAllCb); _globals.GS.files.batch.updateFileTools(); $('.span_1_of_3>p.subtle_silver').removeClass('hidden'); if (_globals.GS.paging.pages > 1) { _globals.GS.ui.initPager(); } _globals.GS.debug.info('Initializing files items'); var listItems = $('.file_list_item'); listItems.each(function () { _globals.GS.ui.initFileItem(_globals.GS.ui.getItemFileId(this), $(this)); }); $('#loading').remove(); $filesList.slideDown(1000); } function loadRes() { _globals.GS.templates = _templates2.default; _globals.GS.files = _files2.default; _globals.GS.events = _events2.default; _globals.GS.ui = _ui2.default; } function initGlobals() { _globals.GS.user = { id: boot_data.user_id, username: $('#user_menu_name').text(), team: $('script:contains(TS.clog.setTeam)').text().replace(/(?:.|[\r\n])*?TS\.clog\.setTeam\('(T[A-Z0-9]+)(?:.|[\r\n])*/, '$1'), admin: !!$('#admin_nav').length }; _globals.GS.paging = { from: 0, to: 0, total: 0, page: 0, pages: 0, per_page: 0 }; _globals.GS.data = _data2.default; } function loadUtils() { _globals.GS.debug = __utils.debug; _globals.GS.sizeOf = __utils.sizeOf; _globals.GS.loadStyle = __utils.loadStyle; } function parseUrl() { var urlHash = location.hash.slice(1); var urlPath = location.pathname.split('/'); _globals.GS.url = { protocol: location.protocol, team: location.protocol + '//' + location.host, path: location.pathname, user: urlPath[2] ? urlPath[2] : 'all', page: location.search ? parseInt(location.search.replace(/.*?page=([0-9]+).*/, '$1'), 10) : 1, filter: 'all' }; if (urlPath[3]) { location.hash = ''; _globals.GS.url.filter = urlPath[3]; _globals.GS.url.search = false; } /*slack search works differently than expected else { switch (urlHash) { case 'videos': GS.url.filter = '3g2 3gp asf avi dif flv m4u m4v mkv mov mp4 mpg mpe mpeg mxu qt swf vob wmv'; GS.url.search = true; break; case 'audios': GS.url.filter = 'au snd kar mid midi m4a m4b m4p mp2 mp3 mpga aif aifc aiff m3u ra ram wav'; GS.url.search = true; break; default: GS.url.filter = 'all'; } }*/ } /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); //import style from '../css/slack.files_extension.scss' exports.default = { env: "production", debug: "production" === 'development', simulation: "production" === 'development', promptDelete: true, anim: { delay: 40, time: 200 }, maxSelect: 200 //styles: [style] }; /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _globals = __webpack_require__(0); function Queue() { this.items = {}; this.pushed = 0; this.popped = 0; this.accepted = 0; this.rejected = 0; this.push = function (file) { if (!this.items[file.id]) { this.items[file.id] = file; this.pushed++; } return this.pushed; }; this.pop = function (id) { if (!id) { id = Object.keys(this.items)[0]; } var item = this.items[id]; if (this.items[id]) { delete this.items[id]; this.accepted++; this.popped++; } return item; }; this.reject = function (id) { if (!id) { id = Object.keys(this.items)[0]; } var item = this.items[id]; if (this.items[id]) { delete this.items[id]; this.rejected++; this.popped++; } return item; }; this.reset = function () { this.items = {}; this.pushed = this.popped = this.rejected = this.accepted = 0; }; } exports.default = { filesCache: {}, selectedFiles: {}, selectedFilesView: {}, viewFiles: {}, downloadFiles: {}, deleteFiles: {}, delQueue: new Queue(), downloadQueue: new Queue(), getFileInfo: function getFileInfo(id) { var file = _globals.GS.data.filesCache[id]; if (file) { return Promise.resolve(file); } return _globals.GS.files.info(id, function (id, file) { if (file) { _globals.GS.data.filesCache[id] = file; } }); }, cacheFile: function cacheFile(file) { if (_globals.GS.data.filesCache[file.id]) { return; } _globals.GS.data.filesCache[id] = file; }, loadFile: function loadFile(file) { if (!file) { return; } _globals.GS.data.filesCache[file.id] = file; _globals.GS.data.viewFiles[file.id] = file; }, selectFile: function selectFile(id) { var file = _globals.GS.data.filesCache[id]; if (!file) { return; } _globals.GS.data.selectedFiles[id] = file; if (_globals.GS.data.viewFiles[id]) { _globals.GS.data.selectedFilesView[id] = file; } if (file.can_download) { _globals.GS.data.downloadFiles[id] = file; } if (file.can_delete) { _globals.GS.data.deleteFiles[id] = file; } return file; }, deselectFile: function deselectFile(id) { delete _globals.GS.data.selectedFiles[id]; delete _globals.GS.data.selectedFilesView[id]; delete _globals.GS.data.downloadFiles[id]; delete _globals.GS.data.deleteFiles[id]; }, deleteFile: function deleteFile(id) { _globals.GS.data.deselectFile(id); delete _globals.GS.data.viewFiles[id]; delete _globals.GS.data.filesCache[id]; _globals.GS.paging.total--; _globals.GS.paging.from = _globals.GS.sizeOf(_globals.GS.data.viewFiles) > 0 ? _globals.GS.paging.from : 0; _globals.GS.paging.to = _globals.GS.sizeOf(_globals.GS.data.viewFiles) > 0 ? _globals.GS.paging.from + _globals.GS.sizeOf(_globals.GS.data.viewFiles) - 1 : 0; }, saveData: function saveData() { localStorage.filesCache = JSON.stringify(_globals.GS.data.filesCache); localStorage.selectedFiles = JSON.stringify(Object.keys(_globals.GS.data.selectedFiles)); }, loadData: function loadData() { if (localStorage.filesCache) { _globals.GS.data.filesCache = JSON.parse(localStorage.filesCache); } var args = { types: _globals.GS.url.filter, count: 50, page: _globals.GS.url.page }; if (_globals.GS.url.user !== 'all') { args.user = TS.members.getMemberByName(_globals.GS.url.user).id; } return _globals.GS.files.list(args, function (ok, data, args) { //GS.files.search({ query: 'png', count: 50 }, function (ok, data, args) { data.files.forEach(function (file) { boot_data.files.push(file); _globals.GS.data.loadFile(file); }); _globals.GS.paging.total = data.paging.total; _globals.GS.paging.pages = data.paging.pages; _globals.GS.paging.page = data.paging.page; _globals.GS.paging.per_page = Math.ceil(data.paging.total / data.paging.pages); _globals.GS.paging.from = (data.paging.page - 1) * _globals.GS.paging.per_page + 1; _globals.GS.paging.to = _globals.GS.paging.from + _globals.GS.sizeOf(_globals.GS.data.viewFiles) - 1; _globals.GS.debug.info('Files loaded'); }).then(function () { if (localStorage.selectedFiles) { var selection = JSON.parse(localStorage.selectedFiles); var notCached = []; selection.forEach(function (id) { if (!_globals.GS.data.selectFile(id)) { notCached.push(id); } }); if (notCached.length) { return _globals.GS.files.infoArray(notCached, function (ok, results) { results.forEach(function (result) { if (result.ok) { var file = result.data.file; _globals.GS.data.cacheFile(file); _globals.GS.data.selectFile(file.id); } }); _globals.GS.debug.info('Selected file loaded'); }); } } return Promise.resolve(); }).then(function () { _globals.GS.data.saveData(); }); } }; /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.debug = undefined; exports.sizeOf = sizeOf; exports.loadStyle = loadStyle; var _globals = __webpack_require__(0); var debug = exports.debug = { logMessage: function logMessage() { if (!_globals.GS.cfg.debug || arguments.length < 2) return; var type = arguments[0]; var newArgs = Array.prototype.slice.call(arguments, 1); switch (type) { case 'error': TS.error.apply(this, newArgs); break; case 'info': TS.info.apply(this, newArgs); break; case 'warn': TS.warn.apply(this, newArgs); break; case 'log': console.log.apply(this, newArgs); break; default: return; } }, info: function info() { if (arguments.length > 0) { _globals.GS.debug.logMessage.apply(this, ['info'].concat(Array.prototype.slice.call(arguments))); } }, error: function error() { if (arguments.length > 0) { _globals.GS.debug.logMessage.apply(this, ['error'].concat(Array.prototype.slice.call(arguments))); } }, warn: function warn() { if (arguments.length > 0) { _globals.GS.debug.logMessage.apply(this, ['warn'].concat(Array.prototype.slice.call(arguments))); } }, log: function log() { if (arguments.length > 0) { _globals.GS.debug.logMessage.apply(this, ['log'].concat(Array.prototype.slice.call(arguments))); } } }; function sizeOf(obj) { if ($.isFunction(Object.keys)) { return Object.keys(obj).length; } var size = 0; for (var key in obj) { size++; } return size; } function loadStyle(styles) { if (Array.isArray(styles)) { styles.forEach(function (stylesheet) { if (stylesheet.search('http') === 0) { $('head').append(''); } else { $('head').append(''); } }); return; } if (styles.search('http') === 0) { $('head').append(''); } else { $('head').append(''); } } /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _globals = __webpack_require__(0); var _filesBatch = __webpack_require__(7); var _filesBatch2 = _interopRequireDefault(_filesBatch); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.default = { upsertFile: function upsertFile(file) { TS.files.upsertFile(file); file.can_download = !file.is_external; file.can_delete = _globals.GS.user.admin || _globals.GS.user.id === file.user; }, list: function list(args, callback) { return TS.api.call('files.list', args).then(function (resp) { resp.data.files.forEach(function (file) { _globals.GS.files.upsertFile(file); }); if (typeof callback === "function") { callback(resp.data.ok, { files: resp.data.files, paging: resp.data.paging, ok: resp.data.ok }, resp.args); } return resp; }).catch(function (resp) { if (typeof callback === "function") { callback(false, resp.data, resp.args); } return resp; }); }, search: function search(args, callback) { return TS.api.call('search.files', args).then(function (resp) { resp.data.files.matches.forEach(function (file) { _globals.GS.files.upsertFile(file); }); if (typeof callback === "function") { callback(resp.data.ok, { files: resp.data.files.matches, paging: resp.data.files.paging, ok: resp.data.ok, query: resp.data.query }, resp.args); } return resp; }).catch(function (resp) { if (typeof callback === "function") { callback(false, resp.data, resp.args); } return resp; }); }, infoArray: function infoArray(files, callback) { var result = { ok: false, results: [], array: files }; _globals.GS.debug.info('Files: ', files); if (Array.isArray(files)) { return Promise.all(files.map(function (item, i, arr) { _globals.GS.debug.info('File: ', item); if (item === undefined) { return Promise.resolve('undefined'); } return _globals.GS.files.info(item).then(function (resp) { result.results.push({ ok: true, index: i, item: item, data: resp.data }); return resp; }).catch(function (resp) { result.results.push({ ok: false, index: i, item: item, data: resp }); return resp; }); }, Promise.resolve())).then(function () { if (result.error.length > 0) { result.ok = false; return Promise.reject(result); } result.ok = true; if (typeof callback === "function") { callback(result.ok, result.results); return result; } return Promise.resolve(result); }).catch(function () { result.ok = false; if (typeof callback === "function") { callback(result.ok, result.results); } return result; }); } throw 'files must be an array'; }, info: function info(id, callback) { _globals.GS.debug.info('Fetching "' + id + '"'); return TS.api.call('files.info', { file: id }).then(function (resp) { var file = resp.data.file; _globals.GS.debug.info('File "' + id + '" fetched'); _globals.GS.files.upsertFile(file); if (typeof callback === "function") { callback(id, file); } return resp; }).catch(function (resp) { TS.error('Error "' + resp.data.error + '" retrieving info for file "' + id + '"'); if (typeof callback === "function") { callback(id); } return resp; }); }, extractFileInfo: function extractFileInfo(files, props) { var extracted = []; var _loop = function _loop(id) { var file = files[id]; var item = [id]; props.forEach(function (key) { if (file[key] !== undefined) { item.push(typeof file[key] === 'boolean' ? file[key] ? 1 : 0 : file[key]); } }); extracted.push(item); }; for (var id in files) { _loop(id); } return extracted; }, deleteFile: function deleteFile(id, callback) { if (_globals.GS.cfg.simulation) { return setTimeout(function () { _globals.GS.debug.info('File "' + id + '" deleted'); if (typeof callback === 'function') { var error = void 0; if (!Math.round(Math.random() * 10)) { error = 'test-error'; } callback(id, error); } }, Math.floor(Math.random() * 5000 + 1)); } else { return TS.api.call('files.delete', { file: id }).then(function (resp) { callback(id); }).catch(function (resp) { callback(id, resp.data.error); }); } }, selectFile: function selectFile(id, selection) { switch (selection) { case 'select': if (_globals.GS.data.selectedFiles[id] !== undefined) return; if (_globals.GS.sizeOf(_globals.GS.data.selectedFiles) >= _globals.GS.cfg.maxSelect) { _globals.GS.ui.showToast({ type: 'warning', message: 'Max selection ' + _globals.GS.cfg.maxSelect + ' files' }); return 'full'; } _globals.GS.data.selectFile(id); break; case 'deselect': if (_globals.GS.data.selectedFiles[id] === undefined) return; _globals.GS.data.deselectFile(id); break; default: if (_globals.GS.data.selectedFiles[id] === undefined) { return _globals.GS.files.selectFile(id, 'select'); } else { return _globals.GS.files.selectFile(id, 'deselect'); } } _globals.GS.files.batch.updateFileTools(); return selection; }, batch: _filesBatch2.default }; /***/ }), /* 7 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _globals = __webpack_require__(0); exports.default = { /*generic_dialog _DEFAULT_SETTING = { type: "default", graphic: false, title: "", body: "BODY", body_template: null, $body: null, show_go_button: true, show_secondary_go_button: false, show_cancel_button: true, go_button_text: _OKAY_TEXT, go_button_class: "", secondary_go_button_text: _OKAY_TEXT, secondary_go_button_class: "", cancel_button_text: _CANCEL_TEXT, onGo: null, onSecondaryGo: null, onCancel: null, onEnd: null, show_throbber: false, esc_for_ok: false, onShow: null, force_small: false, enter_always_gos: false, fullscreen: false, dialog_class: null, hide_footer: false, backdrop_click_to_dismiss: false } */ deleteFiles: function deleteFiles() { TS.generic_dialog.start({ title: 'Delete selected files', body: _globals.GS.templates.batchDeleteBody(), show_cancel_button: true, show_go_button: true, go_button_text: "Yes, delete these files", go_button_class: "btn_danger", cancel_button_text: "Cancel", onGo: function onGo() { for (var id in _globals.GS.data.selectedFiles) { if (_globals.GS.data.selectedFiles[id].can_delete) { _globals.GS.data.delQueue.push(_globals.GS.data.selectedFiles[id]); if (_globals.GS.data.viewFiles[id]) { $(_globals.GS.ui.getFileItem(id)).trigger('file:delete', [function (id, fileTitle, error) { if (error) { _globals.GS.data.delQueue.reject(id); } else { _globals.GS.data.delQueue.pop(id); } $('#delete_progress').trigger('progress:update'); }]); } else { _globals.GS.files.deleteFile(id, function (id, error) { if (error) { _globals.GS.data.delQueue.reject(id); } else { _globals.GS.data.delQueue.pop(id); _globals.GS.data.deleteFile(id); } _globals.GS.files.batch.updateFileTools(); $('#delete_progress').trigger('progress:update'); _globals.GS.data.saveData(); }); } } } _globals.GS.files.batch.deleteProgress(); } }); }, deleteProgress: function deleteProgress() { var title = 'Deleting Files'; var txt = _globals.GS.templates.progresBar({ id: 'delete_progress', text: _globals.GS.data.delQueue.popped + '/' + _globals.GS.data.delQueue.pushed + ' files deleted', type: 'success', val: 0 }); _globals.GS.ui.showPageAlert({ body: txt, dismissable: false, type: 'info', id: 'delete_progress_alert' }); $('#delete_progress').on('progress:update', function (event) { var percent = _globals.GS.data.delQueue.popped / _globals.GS.data.delQueue.pushed * 100; if (_globals.GS.data.delQueue.rejected > 0) { $(this).addClass('progress-bar-warning'); } $(this).width(percent + '%').next().html(_globals.GS.data.delQueue.popped + '/' + _globals.GS.data.delQueue.pushed + ' files deleted'); if (_globals.GS.data.delQueue.popped >= _globals.GS.data.delQueue.pushed) { _globals.GS.ui.dismissPageAlert($('#delete_progress_alert')); var toats = { type: 'success', message: _globals.GS.data.delQueue.popped + ' files deleted' }; var alert = { body: toats.message, dismissable: true, type: 'success', id: 'delete_complete_alert' }; if (_globals.GS.data.delQueue.rejected) { toats = { type: 'warning', message: _globals.GS.data.delQueue.accepted + ' files deleted - ' + _globals.GS.data.delQueue.rejected + ' files failed' }; alert.body = toats.message; alert.type = toats.type; } _globals.GS.ui.showPageAlert(alert); _globals.GS.ui.showToast(toats); _globals.GS.data.delQueue.reset(); } }); }, downloadFiles: function downloadFiles() { var totalBytes = 0; var totalSize = ''; for (var id in _globals.GS.data.downloadFiles) { totalBytes += _globals.GS.data.downloadFiles[id].size; } if (totalBytes > 1000000000) { totalSize = (totalBytes / 1000000000).toFixed(2) + ' Gb'; } else if (totalBytes > 1000000) { totalSize = (totalBytes / 1000000).toFixed(2) + ' Mb'; } else if (totalBytes > 1000) { totalSize = (totalBytes / 1000).toFixed(2) + ' Kb'; } else { totalSize = totalBytes + ' Bytes'; } TS.generic_dialog.start({ title: 'Download selected files', body: _globals.GS.templates.batchDownloadBody(totalSize), show_cancel_button: true, show_go_button: true, go_button_text: "Yes, download these files", cancel_button_text: "Cancel", onGo: function onGo() { $('body').append('