From eb732bd68e16a51a0cd76932cc00bba35f33adfe Mon Sep 17 00:00:00 2001 From: Leo Herzog Date: Mon, 22 Jun 2020 21:22:03 -0400 Subject: [PATCH] Add Magnet and Torrent File Export --- bundle.js | 1155 ++++++++++++++++++++++++++++++++++++++++++++++---- index.html | 17 +- package.json | 3 +- parse.js | 55 ++- 4 files changed, 1137 insertions(+), 93 deletions(-) diff --git a/bundle.js b/bundle.js index 1bdbc6b..cb66ebb 100644 --- a/bundle.js +++ b/bundle.js @@ -100,7 +100,7 @@ if ('undefined' === typeof Buffer) { }()); }).call(this,require("buffer").Buffer) -},{"buffer":27}],2:[function(require,module,exports){ +},{"buffer":28}],2:[function(require,module,exports){ var Buffer = require('safe-buffer').Buffer const INTEGER_START = 0x69 // 'i' @@ -272,7 +272,7 @@ decode.buffer = function () { module.exports = decode -},{"safe-buffer":14}],3:[function(require,module,exports){ +},{"safe-buffer":15}],3:[function(require,module,exports){ var Buffer = require('safe-buffer').Buffer /** @@ -389,7 +389,7 @@ encode.list = function (buffers, data) { module.exports = encode -},{"safe-buffer":14}],4:[function(require,module,exports){ +},{"safe-buffer":15}],4:[function(require,module,exports){ var bencode = module.exports bencode.encode = require('./encode') @@ -430,7 +430,7 @@ module.exports = function blobToBuffer (blob, cb) { } }).call(this,require("buffer").Buffer) -},{"buffer":27}],6:[function(require,module,exports){ +},{"buffer":28}],6:[function(require,module,exports){ /*! * bytes * Copyright(c) 2012-2014 TJ Holowaychuk @@ -595,6 +595,980 @@ function parse(val) { } },{}],7:[function(require,module,exports){ +/*! + * clipboard.js v2.0.6 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["ClipboardJS"] = factory(); + else + root["ClipboardJS"] = factory(); +})(this, function() { +return /******/ (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, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // 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 = 6); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +function select(element) { + var selectedText; + + if (element.nodeName === 'SELECT') { + element.focus(); + + selectedText = element.value; + } + else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { + var isReadOnly = element.hasAttribute('readonly'); + + if (!isReadOnly) { + element.setAttribute('readonly', ''); + } + + element.select(); + element.setSelectionRange(0, element.value.length); + + if (!isReadOnly) { + element.removeAttribute('readonly'); + } + + selectedText = element.value; + } + else { + if (element.hasAttribute('contenteditable')) { + element.focus(); + } + + var selection = window.getSelection(); + var range = document.createRange(); + + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + + selectedText = selection.toString(); + } + + return selectedText; +} + +module.exports = select; + + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + +function E () { + // Keep this empty so it's easier to inherit from + // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) +} + +E.prototype = { + on: function (name, callback, ctx) { + var e = this.e || (this.e = {}); + + (e[name] || (e[name] = [])).push({ + fn: callback, + ctx: ctx + }); + + return this; + }, + + once: function (name, callback, ctx) { + var self = this; + function listener () { + self.off(name, listener); + callback.apply(ctx, arguments); + }; + + listener._ = callback + return this.on(name, listener, ctx); + }, + + emit: function (name) { + var data = [].slice.call(arguments, 1); + var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); + var i = 0; + var len = evtArr.length; + + for (i; i < len; i++) { + evtArr[i].fn.apply(evtArr[i].ctx, data); + } + + return this; + }, + + off: function (name, callback) { + var e = this.e || (this.e = {}); + var evts = e[name]; + var liveEvents = []; + + if (evts && callback) { + for (var i = 0, len = evts.length; i < len; i++) { + if (evts[i].fn !== callback && evts[i].fn._ !== callback) + liveEvents.push(evts[i]); + } + } + + // Remove event from queue to prevent memory leak + // Suggested by https://github.com/lazd + // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 + + (liveEvents.length) + ? e[name] = liveEvents + : delete e[name]; + + return this; + } +}; + +module.exports = E; +module.exports.TinyEmitter = E; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +var is = __webpack_require__(3); +var delegate = __webpack_require__(4); + +/** + * Validates all params and calls the right + * listener function based on its target type. + * + * @param {String|HTMLElement|HTMLCollection|NodeList} target + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listen(target, type, callback) { + if (!target && !type && !callback) { + throw new Error('Missing required arguments'); + } + + if (!is.string(type)) { + throw new TypeError('Second argument must be a String'); + } + + if (!is.fn(callback)) { + throw new TypeError('Third argument must be a Function'); + } + + if (is.node(target)) { + return listenNode(target, type, callback); + } + else if (is.nodeList(target)) { + return listenNodeList(target, type, callback); + } + else if (is.string(target)) { + return listenSelector(target, type, callback); + } + else { + throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList'); + } +} + +/** + * Adds an event listener to a HTML element + * and returns a remove listener function. + * + * @param {HTMLElement} node + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenNode(node, type, callback) { + node.addEventListener(type, callback); + + return { + destroy: function() { + node.removeEventListener(type, callback); + } + } +} + +/** + * Add an event listener to a list of HTML elements + * and returns a remove listener function. + * + * @param {NodeList|HTMLCollection} nodeList + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenNodeList(nodeList, type, callback) { + Array.prototype.forEach.call(nodeList, function(node) { + node.addEventListener(type, callback); + }); + + return { + destroy: function() { + Array.prototype.forEach.call(nodeList, function(node) { + node.removeEventListener(type, callback); + }); + } + } +} + +/** + * Add an event listener to a selector + * and returns a remove listener function. + * + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenSelector(selector, type, callback) { + return delegate(document.body, selector, type, callback); +} + +module.exports = listen; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +/** + * Check if argument is a HTML element. + * + * @param {Object} value + * @return {Boolean} + */ +exports.node = function(value) { + return value !== undefined + && value instanceof HTMLElement + && value.nodeType === 1; +}; + +/** + * Check if argument is a list of HTML elements. + * + * @param {Object} value + * @return {Boolean} + */ +exports.nodeList = function(value) { + var type = Object.prototype.toString.call(value); + + return value !== undefined + && (type === '[object NodeList]' || type === '[object HTMLCollection]') + && ('length' in value) + && (value.length === 0 || exports.node(value[0])); +}; + +/** + * Check if argument is a string. + * + * @param {Object} value + * @return {Boolean} + */ +exports.string = function(value) { + return typeof value === 'string' + || value instanceof String; +}; + +/** + * Check if argument is a function. + * + * @param {Object} value + * @return {Boolean} + */ +exports.fn = function(value) { + var type = Object.prototype.toString.call(value); + + return type === '[object Function]'; +}; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +var closest = __webpack_require__(5); + +/** + * Delegates event to a selector. + * + * @param {Element} element + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @param {Boolean} useCapture + * @return {Object} + */ +function _delegate(element, selector, type, callback, useCapture) { + var listenerFn = listener.apply(this, arguments); + + element.addEventListener(type, listenerFn, useCapture); + + return { + destroy: function() { + element.removeEventListener(type, listenerFn, useCapture); + } + } +} + +/** + * Delegates event to a selector. + * + * @param {Element|String|Array} [elements] + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @param {Boolean} useCapture + * @return {Object} + */ +function delegate(elements, selector, type, callback, useCapture) { + // Handle the regular Element usage + if (typeof elements.addEventListener === 'function') { + return _delegate.apply(null, arguments); + } + + // Handle Element-less usage, it defaults to global delegation + if (typeof type === 'function') { + // Use `document` as the first parameter, then apply arguments + // This is a short way to .unshift `arguments` without running into deoptimizations + return _delegate.bind(null, document).apply(null, arguments); + } + + // Handle Selector-based usage + if (typeof elements === 'string') { + elements = document.querySelectorAll(elements); + } + + // Handle Array-like based usage + return Array.prototype.map.call(elements, function (element) { + return _delegate(element, selector, type, callback, useCapture); + }); +} + +/** + * Finds closest match and invokes callback. + * + * @param {Element} element + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @return {Function} + */ +function listener(element, selector, type, callback) { + return function(e) { + e.delegateTarget = closest(e.target, selector); + + if (e.delegateTarget) { + callback.call(element, e); + } + } +} + +module.exports = delegate; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + +var DOCUMENT_NODE_TYPE = 9; + +/** + * A polyfill for Element.matches() + */ +if (typeof Element !== 'undefined' && !Element.prototype.matches) { + var proto = Element.prototype; + + proto.matches = proto.matchesSelector || + proto.mozMatchesSelector || + proto.msMatchesSelector || + proto.oMatchesSelector || + proto.webkitMatchesSelector; +} + +/** + * Finds the closest parent that matches a selector. + * + * @param {Element} element + * @param {String} selector + * @return {Function} + */ +function closest (element, selector) { + while (element && element.nodeType !== DOCUMENT_NODE_TYPE) { + if (typeof element.matches === 'function' && + element.matches(selector)) { + return element; + } + element = element.parentNode; + } +} + +module.exports = closest; + + +/***/ }), +/* 6 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); + +// EXTERNAL MODULE: ./node_modules/select/src/select.js +var src_select = __webpack_require__(0); +var select_default = /*#__PURE__*/__webpack_require__.n(src_select); + +// CONCATENATED MODULE: ./src/clipboard-action.js +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +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"); } } + + + +/** + * Inner class which performs selection from either `text` or `target` + * properties and then executes copy or cut operations. + */ + +var clipboard_action_ClipboardAction = function () { + /** + * @param {Object} options + */ + function ClipboardAction(options) { + _classCallCheck(this, ClipboardAction); + + this.resolveOptions(options); + this.initSelection(); + } + + /** + * Defines base properties passed from constructor. + * @param {Object} options + */ + + + _createClass(ClipboardAction, [{ + key: 'resolveOptions', + value: function resolveOptions() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + this.action = options.action; + this.container = options.container; + this.emitter = options.emitter; + this.target = options.target; + this.text = options.text; + this.trigger = options.trigger; + + this.selectedText = ''; + } + + /** + * Decides which selection strategy is going to be applied based + * on the existence of `text` and `target` properties. + */ + + }, { + key: 'initSelection', + value: function initSelection() { + if (this.text) { + this.selectFake(); + } else if (this.target) { + this.selectTarget(); + } + } + + /** + * Creates a fake textarea element, sets its value from `text` property, + * and makes a selection on it. + */ + + }, { + key: 'selectFake', + value: function selectFake() { + var _this = this; + + var isRTL = document.documentElement.getAttribute('dir') == 'rtl'; + + this.removeFake(); + + this.fakeHandlerCallback = function () { + return _this.removeFake(); + }; + this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true; + + this.fakeElem = document.createElement('textarea'); + // Prevent zooming on iOS + this.fakeElem.style.fontSize = '12pt'; + // Reset box model + this.fakeElem.style.border = '0'; + this.fakeElem.style.padding = '0'; + this.fakeElem.style.margin = '0'; + // Move element out of screen horizontally + this.fakeElem.style.position = 'absolute'; + this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px'; + // Move element to the same position vertically + var yPosition = window.pageYOffset || document.documentElement.scrollTop; + this.fakeElem.style.top = yPosition + 'px'; + + this.fakeElem.setAttribute('readonly', ''); + this.fakeElem.value = this.text; + + this.container.appendChild(this.fakeElem); + + this.selectedText = select_default()(this.fakeElem); + this.copyText(); + } + + /** + * Only removes the fake element after another click event, that way + * a user can hit `Ctrl+C` to copy because selection still exists. + */ + + }, { + key: 'removeFake', + value: function removeFake() { + if (this.fakeHandler) { + this.container.removeEventListener('click', this.fakeHandlerCallback); + this.fakeHandler = null; + this.fakeHandlerCallback = null; + } + + if (this.fakeElem) { + this.container.removeChild(this.fakeElem); + this.fakeElem = null; + } + } + + /** + * Selects the content from element passed on `target` property. + */ + + }, { + key: 'selectTarget', + value: function selectTarget() { + this.selectedText = select_default()(this.target); + this.copyText(); + } + + /** + * Executes the copy operation based on the current selection. + */ + + }, { + key: 'copyText', + value: function copyText() { + var succeeded = void 0; + + try { + succeeded = document.execCommand(this.action); + } catch (err) { + succeeded = false; + } + + this.handleResult(succeeded); + } + + /** + * Fires an event based on the copy operation result. + * @param {Boolean} succeeded + */ + + }, { + key: 'handleResult', + value: function handleResult(succeeded) { + this.emitter.emit(succeeded ? 'success' : 'error', { + action: this.action, + text: this.selectedText, + trigger: this.trigger, + clearSelection: this.clearSelection.bind(this) + }); + } + + /** + * Moves focus away from `target` and back to the trigger, removes current selection. + */ + + }, { + key: 'clearSelection', + value: function clearSelection() { + if (this.trigger) { + this.trigger.focus(); + } + document.activeElement.blur(); + window.getSelection().removeAllRanges(); + } + + /** + * Sets the `action` to be performed which can be either 'copy' or 'cut'. + * @param {String} action + */ + + }, { + key: 'destroy', + + + /** + * Destroy lifecycle. + */ + value: function destroy() { + this.removeFake(); + } + }, { + key: 'action', + set: function set() { + var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy'; + + this._action = action; + + if (this._action !== 'copy' && this._action !== 'cut') { + throw new Error('Invalid "action" value, use either "copy" or "cut"'); + } + } + + /** + * Gets the `action` property. + * @return {String} + */ + , + get: function get() { + return this._action; + } + + /** + * Sets the `target` property using an element + * that will be have its content copied. + * @param {Element} target + */ + + }, { + key: 'target', + set: function set(target) { + if (target !== undefined) { + if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) { + if (this.action === 'copy' && target.hasAttribute('disabled')) { + throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); + } + + if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) { + throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); + } + + this._target = target; + } else { + throw new Error('Invalid "target" value, use a valid Element'); + } + } + } + + /** + * Gets the `target` property. + * @return {String|HTMLElement} + */ + , + get: function get() { + return this._target; + } + }]); + + return ClipboardAction; +}(); + +/* harmony default export */ var clipboard_action = (clipboard_action_ClipboardAction); +// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js +var tiny_emitter = __webpack_require__(1); +var tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter); + +// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js +var listen = __webpack_require__(2); +var listen_default = /*#__PURE__*/__webpack_require__.n(listen); + +// CONCATENATED MODULE: ./src/clipboard.js +var clipboard_typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var clipboard_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 clipboard_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + + + + +/** + * Base class which takes one or more elements, adds event listeners to them, + * and instantiates a new `ClipboardAction` on each click. + */ + +var clipboard_Clipboard = function (_Emitter) { + _inherits(Clipboard, _Emitter); + + /** + * @param {String|HTMLElement|HTMLCollection|NodeList} trigger + * @param {Object} options + */ + function Clipboard(trigger, options) { + clipboard_classCallCheck(this, Clipboard); + + var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this)); + + _this.resolveOptions(options); + _this.listenClick(trigger); + return _this; + } + + /** + * Defines if attributes would be resolved using internal setter functions + * or custom functions that were passed in the constructor. + * @param {Object} options + */ + + + clipboard_createClass(Clipboard, [{ + key: 'resolveOptions', + value: function resolveOptions() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + this.action = typeof options.action === 'function' ? options.action : this.defaultAction; + this.target = typeof options.target === 'function' ? options.target : this.defaultTarget; + this.text = typeof options.text === 'function' ? options.text : this.defaultText; + this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body; + } + + /** + * Adds a click event listener to the passed trigger. + * @param {String|HTMLElement|HTMLCollection|NodeList} trigger + */ + + }, { + key: 'listenClick', + value: function listenClick(trigger) { + var _this2 = this; + + this.listener = listen_default()(trigger, 'click', function (e) { + return _this2.onClick(e); + }); + } + + /** + * Defines a new `ClipboardAction` on each click event. + * @param {Event} e + */ + + }, { + key: 'onClick', + value: function onClick(e) { + var trigger = e.delegateTarget || e.currentTarget; + + if (this.clipboardAction) { + this.clipboardAction = null; + } + + this.clipboardAction = new clipboard_action({ + action: this.action(trigger), + target: this.target(trigger), + text: this.text(trigger), + container: this.container, + trigger: trigger, + emitter: this + }); + } + + /** + * Default `action` lookup function. + * @param {Element} trigger + */ + + }, { + key: 'defaultAction', + value: function defaultAction(trigger) { + return getAttributeValue('action', trigger); + } + + /** + * Default `target` lookup function. + * @param {Element} trigger + */ + + }, { + key: 'defaultTarget', + value: function defaultTarget(trigger) { + var selector = getAttributeValue('target', trigger); + + if (selector) { + return document.querySelector(selector); + } + } + + /** + * Returns the support of the given action, or all actions if no action is + * given. + * @param {String} [action] + */ + + }, { + key: 'defaultText', + + + /** + * Default `text` lookup function. + * @param {Element} trigger + */ + value: function defaultText(trigger) { + return getAttributeValue('text', trigger); + } + + /** + * Destroy lifecycle. + */ + + }, { + key: 'destroy', + value: function destroy() { + this.listener.destroy(); + + if (this.clipboardAction) { + this.clipboardAction.destroy(); + this.clipboardAction = null; + } + } + }], [{ + key: 'isSupported', + value: function isSupported() { + var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut']; + + var actions = typeof action === 'string' ? [action] : action; + var support = !!document.queryCommandSupported; + + actions.forEach(function (action) { + support = support && !!document.queryCommandSupported(action); + }); + + return support; + } + }]); + + return Clipboard; +}(tiny_emitter_default.a); + +/** + * Helper function to retrieve attribute value. + * @param {String} suffix + * @param {Element} element + */ + + +function getAttributeValue(suffix, element) { + var attribute = 'data-clipboard-' + suffix; + + if (!element.hasAttribute(attribute)) { + return; + } + + return element.getAttribute(attribute); +} + +/* harmony default export */ var clipboard = __webpack_exports__["default"] = (clipboard_Clipboard); + +/***/ }) +/******/ ])["default"]; +}); +},{}],8:[function(require,module,exports){ (function (Buffer){ module.exports = magnetURIDecode module.exports.decode = magnetURIDecode @@ -730,7 +1704,7 @@ function magnetURIEncode (obj) { } }).call(this,require("buffer").Buffer) -},{"buffer":27,"thirty-two":19,"uniq":21}],8:[function(require,module,exports){ +},{"buffer":28,"thirty-two":20,"uniq":22}],9:[function(require,module,exports){ module.exports={ "application/1d-interleaved-parityfec": { "source": "iana" @@ -8908,7 +9882,7 @@ module.exports={ } } -},{}],9:[function(require,module,exports){ +},{}],10:[function(require,module,exports){ /*! * mime-db * Copyright(c) 2014 Jonathan Ong @@ -8921,7 +9895,7 @@ module.exports={ module.exports = require('./db.json') -},{"./db.json":8}],10:[function(require,module,exports){ +},{"./db.json":9}],11:[function(require,module,exports){ /*! * mime-types * Copyright(c) 2014 Jonathan Ong @@ -9111,7 +10085,7 @@ function populateMaps (extensions, types) { }) } -},{"mime-db":9,"path":33}],11:[function(require,module,exports){ +},{"mime-db":10,"path":34}],12:[function(require,module,exports){ var wrappy = require('wrappy') module.exports = wrappy(once) module.exports.strict = wrappy(onceStrict) @@ -9155,7 +10129,7 @@ function onceStrict (fn) { return f } -},{"wrappy":22}],12:[function(require,module,exports){ +},{"wrappy":23}],13:[function(require,module,exports){ (function (process,Buffer){ /*! parse-torrent. MIT License. WebTorrent LLC */ /* global Blob */ @@ -9425,7 +10399,7 @@ function ensure (bool, fieldName) { ;(() => { Buffer.alloc(0) })() }).call(this,require('_process'),require("buffer").Buffer) -},{"_process":34,"bencode":4,"blob-to-buffer":5,"buffer":27,"fs":26,"magnet-uri":7,"path":33,"simple-get":16,"simple-sha1":17}],13:[function(require,module,exports){ +},{"_process":35,"bencode":4,"blob-to-buffer":5,"buffer":28,"fs":27,"magnet-uri":8,"path":34,"simple-get":17,"simple-sha1":18}],14:[function(require,module,exports){ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); @@ -10354,7 +11328,7 @@ module.exports = function () { /***/ }) /******/ ]); }); -},{}],14:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ /*! safe-buffer. MIT License. Feross Aboukhadijeh */ /* eslint-disable node/no-deprecated-api */ var buffer = require('buffer') @@ -10421,7 +11395,7 @@ SafeBuffer.allocUnsafeSlow = function (size) { return buffer.SlowBuffer(size) } -},{"buffer":27}],15:[function(require,module,exports){ +},{"buffer":28}],16:[function(require,module,exports){ (function (Buffer){ module.exports = function (stream, cb) { var chunks = [] @@ -10439,7 +11413,7 @@ module.exports = function (stream, cb) { } }).call(this,require("buffer").Buffer) -},{"buffer":27}],16:[function(require,module,exports){ +},{"buffer":28}],17:[function(require,module,exports){ (function (Buffer){ module.exports = simpleGet @@ -10542,7 +11516,7 @@ simpleGet.concat = (opts, cb) => { }) }).call(this,require("buffer").Buffer) -},{"buffer":27,"decompress-response":25,"http":40,"https":30,"once":11,"querystring":38,"simple-concat":15,"url":60}],17:[function(require,module,exports){ +},{"buffer":28,"decompress-response":26,"http":41,"https":31,"once":12,"querystring":39,"simple-concat":16,"url":61}],18:[function(require,module,exports){ /* global self */ var Rusha = require('rusha') @@ -10620,7 +11594,7 @@ function hex (buf) { module.exports = sha1 module.exports.sync = sha1sync -},{"./rusha-worker-sha1":18,"rusha":13}],18:[function(require,module,exports){ +},{"./rusha-worker-sha1":19,"rusha":14}],19:[function(require,module,exports){ var Rusha = require('rusha') var worker @@ -10655,7 +11629,7 @@ function sha1 (buf, cb) { module.exports = sha1 -},{"rusha":13}],19:[function(require,module,exports){ +},{"rusha":14}],20:[function(require,module,exports){ /* Copyright (c) 2011, Chris Umbel @@ -10683,7 +11657,7 @@ var base32 = require('./thirty-two'); exports.encode = base32.encode; exports.decode = base32.decode; -},{"./thirty-two":20}],20:[function(require,module,exports){ +},{"./thirty-two":21}],21:[function(require,module,exports){ (function (Buffer){ /* Copyright (c) 2011, Chris Umbel @@ -10815,7 +11789,7 @@ exports.decode = function(encoded) { }; }).call(this,require("buffer").Buffer) -},{"buffer":27}],21:[function(require,module,exports){ +},{"buffer":28}],22:[function(require,module,exports){ "use strict" function unique_pred(list, compare) { @@ -10874,7 +11848,7 @@ function unique(list, compare, sorted) { module.exports = unique -},{}],22:[function(require,module,exports){ +},{}],23:[function(require,module,exports){ // Returns a wrapper function that returns a wrapped callback // The wrapper function should do some stuff, and return a // presumably different callback function. @@ -10909,11 +11883,12 @@ function wrappy (fn, cb) { } } -},{}],23:[function(require,module,exports){ +},{}],24:[function(require,module,exports){ const parser = require('parse-torrent'); const Buffer = require('Buffer'); const bytes = require('bytes'); const mime = require('mime-types'); +const clipboard = require('clipboard'); var name = document.getElementById('name'); var creationDate = document.getElementById('creationDate'); @@ -10923,7 +11898,8 @@ var hash = document.getElementById('hash'); var trackers = document.getElementById('trackers'); var webseeds = document.getElementById('webseeds'); var files = document.getElementById('filesBody'); -var size = document.getElementById('torrentSize'); +var copyMagnet = document.getElementById('copyMagnet'); +var downloadTorrent = document.getElementById('downloadTorrent'); var parsed; document.addEventListener('DOMContentLoaded', start); @@ -10942,6 +11918,9 @@ function start() { event.target.files[0].arrayBuffer().then(arrayBuffer => parse(Buffer.from(arrayBuffer))); }); + new clipboard('#copyMagnet'); // TODO: Alert user to success + downloadTorrent.addEventListener('click', saveTorrent); + } function parse(toLoad) { @@ -11005,27 +11984,33 @@ function display() { webseeds.innerHTML = "No webseed URLs in the URL/File provided"; } - size.innerHTML = ""; - if (parsed.length) size.innerText = "(" + bytes.format(parsed.length, {"decimalPlaces": 1, "unitSeparator": " "}) + ")"; files.innerHTML = ""; if (parsed.files && parsed.files.length) { for (let file of parsed.files) { - let row = document.createElement('tr'); - let iconcell = document.createElement('td'); - iconcell.innerHTML = ''; - row.appendChild(iconcell); - let namecell = document.createElement('td'); - namecell.innerHTML = file.path; - row.appendChild(namecell); - let sizecell = document.createElement('td'); - sizecell.innerHTML = bytes.format(file.length, {"unitSeparator": " "}); - row.appendChild(sizecell); - files.appendChild(row); + let icon = getFontAwesomeIconForMimetype(mime.lookup(file.name)); + files.appendChild(createFileRow(icon, file.name, file.length)); } + files.appendChild(createFileRow('folder-tree', '', parsed.length)); } else { files.innerHTML = "Files information isn't included in the URL/File provided"; } + copyMagnet.setAttribute('data-clipboard-text', parser.toMagnetURI(parsed)); + +} + +function createFileRow(icon, name, size) { + let row = document.createElement('tr'); + let iconcell = document.createElement('td'); + iconcell.innerHTML = ''; + row.appendChild(iconcell); + let namecell = document.createElement('td'); + namecell.innerHTML = name; + row.appendChild(namecell); + let totalcell = document.createElement('td'); + totalcell.innerHTML = bytes.format(size, {"decimalPlaces": 1, "unitSeparator": " "}); + row.appendChild(totalcell); + return row; } function getFontAwesomeIconForMimetype(mimetype) { @@ -11055,7 +12040,23 @@ function getFontAwesomeIconForMimetype(mimetype) { return 'file'; } } -},{"Buffer":1,"bytes":6,"mime-types":10,"parse-torrent":12}],24:[function(require,module,exports){ + +// https://stackoverflow.com/a/36899900/2700296 +function saveTorrent() { + let data = parser.toTorrentFile(parsed); + if (data !== null && navigator.msSaveBlob) + return navigator.msSaveBlob(new Blob([data], { "type": "application/x-bittorrent" }), parsed.name + '.torrent'); + let a = document.createElement('a'); + a.style.display = 'none'; + let url = window.URL.createObjectURL(new Blob([data], { "type": "application/x-bittorrent" })); + a.setAttribute("href", url); + a.setAttribute("download", parsed.name + '.torrent'); + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + a.remove(); +} +},{"Buffer":1,"bytes":6,"clipboard":7,"mime-types":11,"parse-torrent":13}],25:[function(require,module,exports){ 'use strict' exports.byteLength = byteLength @@ -11209,11 +12210,11 @@ function fromByteArray (uint8) { return parts.join('') } -},{}],25:[function(require,module,exports){ - },{}],26:[function(require,module,exports){ -arguments[4][25][0].apply(exports,arguments) -},{"dup":25}],27:[function(require,module,exports){ + +},{}],27:[function(require,module,exports){ +arguments[4][26][0].apply(exports,arguments) +},{"dup":26}],28:[function(require,module,exports){ (function (Buffer){ /*! * The buffer module from node.js, for the browser. @@ -12994,7 +13995,7 @@ function numberIsNaN (obj) { } }).call(this,require("buffer").Buffer) -},{"base64-js":24,"buffer":27,"ieee754":31}],28:[function(require,module,exports){ +},{"base64-js":25,"buffer":28,"ieee754":32}],29:[function(require,module,exports){ module.exports = { "100": "Continue", "101": "Switching Protocols", @@ -13060,7 +14061,7 @@ module.exports = { "511": "Network Authentication Required" } -},{}],29:[function(require,module,exports){ +},{}],30:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -13585,7 +14586,7 @@ function functionBindPolyfill(context) { }; } -},{}],30:[function(require,module,exports){ +},{}],31:[function(require,module,exports){ var http = require('http') var url = require('url') @@ -13618,7 +14619,7 @@ function validateParams (params) { return params } -},{"http":40,"url":60}],31:[function(require,module,exports){ +},{"http":41,"url":61}],32:[function(require,module,exports){ exports.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m var eLen = (nBytes * 8) - mLen - 1 @@ -13704,7 +14705,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { buffer[offset + i - d] |= s * 128 } -},{}],32:[function(require,module,exports){ +},{}],33:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { @@ -13733,7 +14734,7 @@ if (typeof Object.create === 'function') { } } -},{}],33:[function(require,module,exports){ +},{}],34:[function(require,module,exports){ (function (process){ // .dirname, .basename, and .extname methods are extracted from Node.js v8.11.1, // backported and transplited with Babel, with backwards-compat fixes @@ -14039,7 +15040,7 @@ var substr = 'ab'.substr(-1) === 'b' ; }).call(this,require('_process')) -},{"_process":34}],34:[function(require,module,exports){ +},{"_process":35}],35:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; @@ -14225,7 +15226,7 @@ process.chdir = function (dir) { }; process.umask = function() { return 0; }; -},{}],35:[function(require,module,exports){ +},{}],36:[function(require,module,exports){ (function (global){ /*! https://mths.be/punycode v1.4.1 by @mathias */ ;(function(root) { @@ -14762,7 +15763,7 @@ process.umask = function() { return 0; }; }(this)); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],36:[function(require,module,exports){ +},{}],37:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -14848,7 +15849,7 @@ var isArray = Array.isArray || function (xs) { return Object.prototype.toString.call(xs) === '[object Array]'; }; -},{}],37:[function(require,module,exports){ +},{}],38:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -14935,15 +15936,15 @@ var objectKeys = Object.keys || function (obj) { return res; }; -},{}],38:[function(require,module,exports){ +},{}],39:[function(require,module,exports){ 'use strict'; exports.decode = exports.parse = require('./decode'); exports.encode = exports.stringify = require('./encode'); -},{"./decode":36,"./encode":37}],39:[function(require,module,exports){ -arguments[4][14][0].apply(exports,arguments) -},{"buffer":27,"dup":14}],40:[function(require,module,exports){ +},{"./decode":37,"./encode":38}],40:[function(require,module,exports){ +arguments[4][15][0].apply(exports,arguments) +},{"buffer":28,"dup":15}],41:[function(require,module,exports){ (function (global){ var ClientRequest = require('./lib/request') var response = require('./lib/response') @@ -15031,7 +16032,7 @@ http.METHODS = [ 'UNSUBSCRIBE' ] }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./lib/request":42,"./lib/response":43,"builtin-status-codes":28,"url":60,"xtend":63}],41:[function(require,module,exports){ +},{"./lib/request":43,"./lib/response":44,"builtin-status-codes":29,"url":61,"xtend":64}],42:[function(require,module,exports){ (function (global){ exports.fetch = isFunction(global.fetch) && isFunction(global.ReadableStream) @@ -15094,7 +16095,7 @@ function isFunction (value) { xhr = null // Help gc }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],42:[function(require,module,exports){ +},{}],43:[function(require,module,exports){ (function (process,global,Buffer){ var capability = require('./capability') var inherits = require('inherits') @@ -15412,7 +16413,7 @@ var unsafeHeaders = [ ] }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) -},{"./capability":41,"./response":43,"_process":34,"buffer":27,"inherits":32,"readable-stream":58}],43:[function(require,module,exports){ +},{"./capability":42,"./response":44,"_process":35,"buffer":28,"inherits":33,"readable-stream":59}],44:[function(require,module,exports){ (function (process,global,Buffer){ var capability = require('./capability') var inherits = require('inherits') @@ -15623,7 +16624,7 @@ IncomingMessage.prototype._onXHRProgress = function () { } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) -},{"./capability":41,"_process":34,"buffer":27,"inherits":32,"readable-stream":58}],44:[function(require,module,exports){ +},{"./capability":42,"_process":35,"buffer":28,"inherits":33,"readable-stream":59}],45:[function(require,module,exports){ 'use strict'; function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } @@ -15752,7 +16753,7 @@ createErrorType('ERR_UNKNOWN_ENCODING', function (arg) { createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event'); module.exports.codes = codes; -},{}],45:[function(require,module,exports){ +},{}],46:[function(require,module,exports){ (function (process){ // Copyright Joyent, Inc. and other Node contributors. // @@ -15894,7 +16895,7 @@ Object.defineProperty(Duplex.prototype, 'destroyed', { } }); }).call(this,require('_process')) -},{"./_stream_readable":47,"./_stream_writable":49,"_process":34,"inherits":32}],46:[function(require,module,exports){ +},{"./_stream_readable":48,"./_stream_writable":50,"_process":35,"inherits":33}],47:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -15934,7 +16935,7 @@ function PassThrough(options) { PassThrough.prototype._transform = function (chunk, encoding, cb) { cb(null, chunk); }; -},{"./_stream_transform":48,"inherits":32}],47:[function(require,module,exports){ +},{"./_stream_transform":49,"inherits":33}],48:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // @@ -17061,7 +18062,7 @@ function indexOf(xs, x) { return -1; } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../errors":44,"./_stream_duplex":45,"./internal/streams/async_iterator":50,"./internal/streams/buffer_list":51,"./internal/streams/destroy":52,"./internal/streams/from":54,"./internal/streams/state":56,"./internal/streams/stream":57,"_process":34,"buffer":27,"events":29,"inherits":32,"string_decoder/":59,"util":25}],48:[function(require,module,exports){ +},{"../errors":45,"./_stream_duplex":46,"./internal/streams/async_iterator":51,"./internal/streams/buffer_list":52,"./internal/streams/destroy":53,"./internal/streams/from":55,"./internal/streams/state":57,"./internal/streams/stream":58,"_process":35,"buffer":28,"events":30,"inherits":33,"string_decoder/":60,"util":26}],49:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -17263,7 +18264,7 @@ function done(stream, er, data) { if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING(); return stream.push(null); } -},{"../errors":44,"./_stream_duplex":45,"inherits":32}],49:[function(require,module,exports){ +},{"../errors":45,"./_stream_duplex":46,"inherits":33}],50:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // @@ -17963,7 +18964,7 @@ Writable.prototype._destroy = function (err, cb) { cb(err); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../errors":44,"./_stream_duplex":45,"./internal/streams/destroy":52,"./internal/streams/state":56,"./internal/streams/stream":57,"_process":34,"buffer":27,"inherits":32,"util-deprecate":62}],50:[function(require,module,exports){ +},{"../errors":45,"./_stream_duplex":46,"./internal/streams/destroy":53,"./internal/streams/state":57,"./internal/streams/stream":58,"_process":35,"buffer":28,"inherits":33,"util-deprecate":63}],51:[function(require,module,exports){ (function (process){ 'use strict'; @@ -18173,7 +19174,7 @@ var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterat module.exports = createReadableStreamAsyncIterator; }).call(this,require('_process')) -},{"./end-of-stream":53,"_process":34}],51:[function(require,module,exports){ +},{"./end-of-stream":54,"_process":35}],52:[function(require,module,exports){ 'use strict'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } @@ -18384,7 +19385,7 @@ function () { return BufferList; }(); -},{"buffer":27,"util":25}],52:[function(require,module,exports){ +},{"buffer":28,"util":26}],53:[function(require,module,exports){ (function (process){ 'use strict'; // undocumented cb() API, needed for core, not for public API @@ -18492,7 +19493,7 @@ module.exports = { errorOrDestroy: errorOrDestroy }; }).call(this,require('_process')) -},{"_process":34}],53:[function(require,module,exports){ +},{"_process":35}],54:[function(require,module,exports){ // Ported from https://github.com/mafintosh/end-of-stream with // permission from the author, Mathias Buus (@mafintosh). 'use strict'; @@ -18597,12 +19598,12 @@ function eos(stream, opts, callback) { } module.exports = eos; -},{"../../../errors":44}],54:[function(require,module,exports){ +},{"../../../errors":45}],55:[function(require,module,exports){ module.exports = function () { throw new Error('Readable.from is not available in the browser') }; -},{}],55:[function(require,module,exports){ +},{}],56:[function(require,module,exports){ // Ported from https://github.com/mafintosh/pump with // permission from the author, Mathias Buus (@mafintosh). 'use strict'; @@ -18700,7 +19701,7 @@ function pipeline() { } module.exports = pipeline; -},{"../../../errors":44,"./end-of-stream":53}],56:[function(require,module,exports){ +},{"../../../errors":45,"./end-of-stream":54}],57:[function(require,module,exports){ 'use strict'; var ERR_INVALID_OPT_VALUE = require('../../../errors').codes.ERR_INVALID_OPT_VALUE; @@ -18728,10 +19729,10 @@ function getHighWaterMark(state, options, duplexKey, isDuplex) { module.exports = { getHighWaterMark: getHighWaterMark }; -},{"../../../errors":44}],57:[function(require,module,exports){ +},{"../../../errors":45}],58:[function(require,module,exports){ module.exports = require('events').EventEmitter; -},{"events":29}],58:[function(require,module,exports){ +},{"events":30}],59:[function(require,module,exports){ exports = module.exports = require('./lib/_stream_readable.js'); exports.Stream = exports; exports.Readable = exports; @@ -18742,7 +19743,7 @@ exports.PassThrough = require('./lib/_stream_passthrough.js'); exports.finished = require('./lib/internal/streams/end-of-stream.js'); exports.pipeline = require('./lib/internal/streams/pipeline.js'); -},{"./lib/_stream_duplex.js":45,"./lib/_stream_passthrough.js":46,"./lib/_stream_readable.js":47,"./lib/_stream_transform.js":48,"./lib/_stream_writable.js":49,"./lib/internal/streams/end-of-stream.js":53,"./lib/internal/streams/pipeline.js":55}],59:[function(require,module,exports){ +},{"./lib/_stream_duplex.js":46,"./lib/_stream_passthrough.js":47,"./lib/_stream_readable.js":48,"./lib/_stream_transform.js":49,"./lib/_stream_writable.js":50,"./lib/internal/streams/end-of-stream.js":54,"./lib/internal/streams/pipeline.js":56}],60:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -19039,7 +20040,7 @@ function simpleWrite(buf) { function simpleEnd(buf) { return buf && buf.length ? this.write(buf) : ''; } -},{"safe-buffer":39}],60:[function(require,module,exports){ +},{"safe-buffer":40}],61:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -19773,7 +20774,7 @@ Url.prototype.parseHost = function() { if (host) this.hostname = host; }; -},{"./util":61,"punycode":35,"querystring":38}],61:[function(require,module,exports){ +},{"./util":62,"punycode":36,"querystring":39}],62:[function(require,module,exports){ 'use strict'; module.exports = { @@ -19791,7 +20792,7 @@ module.exports = { } }; -},{}],62:[function(require,module,exports){ +},{}],63:[function(require,module,exports){ (function (global){ /** @@ -19862,7 +20863,7 @@ function config (name) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],63:[function(require,module,exports){ +},{}],64:[function(require,module,exports){ module.exports = extend var hasOwnProperty = Object.prototype.hasOwnProperty; @@ -19883,4 +20884,4 @@ function extend() { return target } -},{}]},{},[23]); +},{}]},{},[24]); diff --git a/index.html b/index.html index 3fa5028..78dfcfd 100644 --- a/index.html +++ b/index.html @@ -47,12 +47,27 @@
- +
+
+
+ + + + +
+
+ + + + +
+
+ diff --git a/package.json b/package.json index 55d41ed..eb2036d 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,9 @@ "Buffer": "latest", "browserify": "latest", "bytes": "latest", + "clipboard": "latest", "dropzone": "latest", - "mime-types": "^2.1.27", + "mime-types": "latest", "parse-torrent": "latest" }, "devDependencies": { diff --git a/parse.js b/parse.js index 1d602fa..df64325 100644 --- a/parse.js +++ b/parse.js @@ -2,6 +2,7 @@ const parser = require('parse-torrent'); const Buffer = require('Buffer'); const bytes = require('bytes'); const mime = require('mime-types'); +const clipboard = require('clipboard'); var name = document.getElementById('name'); var creationDate = document.getElementById('creationDate'); @@ -11,7 +12,8 @@ var hash = document.getElementById('hash'); var trackers = document.getElementById('trackers'); var webseeds = document.getElementById('webseeds'); var files = document.getElementById('filesBody'); -var size = document.getElementById('torrentSize'); +var copyMagnet = document.getElementById('copyMagnet'); +var downloadTorrent = document.getElementById('downloadTorrent'); var parsed; document.addEventListener('DOMContentLoaded', start); @@ -30,6 +32,9 @@ function start() { event.target.files[0].arrayBuffer().then(arrayBuffer => parse(Buffer.from(arrayBuffer))); }); + new clipboard('#copyMagnet'); // TODO: Alert user to success + downloadTorrent.addEventListener('click', saveTorrent); + } function parse(toLoad) { @@ -93,27 +98,33 @@ function display() { webseeds.innerHTML = "No webseed URLs in the URL/File provided"; } - size.innerHTML = ""; - if (parsed.length) size.innerText = "(" + bytes.format(parsed.length, {"decimalPlaces": 1, "unitSeparator": " "}) + ")"; files.innerHTML = ""; if (parsed.files && parsed.files.length) { for (let file of parsed.files) { - let row = document.createElement('tr'); - let iconcell = document.createElement('td'); - iconcell.innerHTML = ''; - row.appendChild(iconcell); - let namecell = document.createElement('td'); - namecell.innerHTML = file.path; - row.appendChild(namecell); - let sizecell = document.createElement('td'); - sizecell.innerHTML = bytes.format(file.length, {"unitSeparator": " "}); - row.appendChild(sizecell); - files.appendChild(row); + let icon = getFontAwesomeIconForMimetype(mime.lookup(file.name)); + files.appendChild(createFileRow(icon, file.name, file.length)); } + files.appendChild(createFileRow('folder-tree', '', parsed.length)); } else { files.innerHTML = "Files information isn't included in the URL/File provided"; } + copyMagnet.setAttribute('data-clipboard-text', parser.toMagnetURI(parsed)); + +} + +function createFileRow(icon, name, size) { + let row = document.createElement('tr'); + let iconcell = document.createElement('td'); + iconcell.innerHTML = ''; + row.appendChild(iconcell); + let namecell = document.createElement('td'); + namecell.innerHTML = name; + row.appendChild(namecell); + let totalcell = document.createElement('td'); + totalcell.innerHTML = bytes.format(size, {"decimalPlaces": 1, "unitSeparator": " "}); + row.appendChild(totalcell); + return row; } function getFontAwesomeIconForMimetype(mimetype) { @@ -142,4 +153,20 @@ function getFontAwesomeIconForMimetype(mimetype) { default: return 'file'; } +} + +// https://stackoverflow.com/a/36899900/2700296 +function saveTorrent() { + let data = parser.toTorrentFile(parsed); + if (data !== null && navigator.msSaveBlob) + return navigator.msSaveBlob(new Blob([data], { "type": "application/x-bittorrent" }), parsed.name + '.torrent'); + let a = document.createElement('a'); + a.style.display = 'none'; + let url = window.URL.createObjectURL(new Blob([data], { "type": "application/x-bittorrent" })); + a.setAttribute("href", url); + a.setAttribute("download", parsed.name + '.torrent'); + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + a.remove(); } \ No newline at end of file