├── .gitignore ├── _config.yml ├── public ├── img │ ├── ipfs.png │ ├── link.png │ ├── reset.png │ ├── upload.png │ ├── connect.png │ ├── favicon.ico │ ├── preview.png │ ├── connected.png │ └── disconnected.png ├── config.js ├── js │ ├── node-menu.js │ ├── node-config.js │ ├── connect-and-upload.js │ └── buffer.js ├── css │ ├── upload-utilities.css │ ├── node-menu.css │ └── style.css └── index.html ├── package.json ├── LICENSE ├── README.md └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /public/img/ipfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/ipfs.png -------------------------------------------------------------------------------- /public/img/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/link.png -------------------------------------------------------------------------------- /public/img/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/reset.png -------------------------------------------------------------------------------- /public/img/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/upload.png -------------------------------------------------------------------------------- /public/img/connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/connect.png -------------------------------------------------------------------------------- /public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/favicon.ico -------------------------------------------------------------------------------- /public/img/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/preview.png -------------------------------------------------------------------------------- /public/img/connected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/connected.png -------------------------------------------------------------------------------- /public/img/disconnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anarkrypto/upload-files-to-ipfs-from-browser-panel/HEAD/public/img/disconnected.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upload-file-to-ipfs-from-browser-panel", 3 | "version": "0.0.1", 4 | "private": false, 5 | "scripts": { 6 | "start": "node ./app.js" 7 | }, 8 | "dependencies": { 9 | "body-parser": ">=1.15.2", 10 | "cookie-parser": ">=1.4.3", 11 | "debug": ">=2.2.0", 12 | "express": "^4.14.0", 13 | "morgan": ">=1.7.0", 14 | "serve-favicon": ">=2.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /public/config.js: -------------------------------------------------------------------------------- 1 | // Change this file as you need it 2 | 3 | const config = { 4 | port: 5001, 5 | gateway: 8080 6 | } 7 | 8 | const node = { 9 | // default remote Node 10 | default: 'remote', 11 | remote: { 12 | address: 'ipfs.sea.tube', 13 | ...config, 14 | protocol: 'https' 15 | }, 16 | 17 | // default local node 18 | local: { 19 | address: '127.0.0.1', 20 | ...config, 21 | protocol: 'http' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/js/node-menu.js: -------------------------------------------------------------------------------- 1 | const formRemote = document.getElementById("remote") 2 | const formLocal = document.getElementById("local") 3 | const tabGroup = document.getElementById("tab-group") 4 | const tabRemote = document.getElementById("tab-remote") 5 | const tabLocal = document.getElementById("tab-local") 6 | 7 | function changeToRemote() { 8 | formRemote.style.display = "block" 9 | formLocal.style.display = "none" 10 | tabRemote.className = "tab active" 11 | tabLocal.className = "tab" 12 | } 13 | 14 | function changeToLocal() { 15 | formRemote.style.display = "none" 16 | formLocal.style.display = "block" 17 | tabRemote.className = "tab" 18 | tabLocal.className = "tab active" 19 | } 20 | 21 | if (node.default === "remote") { 22 | tabGroup.style.flexDirection = "row-reverse" 23 | changeToRemote() 24 | } 25 | 26 | if (node.default === "local") { 27 | tabGroup.style.flexDirection = "row" 28 | changeToLocal() 29 | } 30 | 31 | tabRemote.addEventListener("click", changeToRemote) 32 | tabLocal.addEventListener("click", changeToLocal) 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Anarkrypto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /public/js/node-config.js: -------------------------------------------------------------------------------- 1 | //set default remote Node 2 | document.getElementById("remote_address").value = node.remote.address; 3 | document.getElementById("remote_apiPort").value = node.remote.port; 4 | document.getElementById("remote_gatewayPort").value = node.remote.gateway; 5 | document.querySelector("#remote").querySelector("#labelAddress").className = "active" 6 | document.querySelector("#remote").querySelector("#labelPort").className = "active" 7 | document.querySelector("#remote").querySelector("#labelGateway").className = "active" 8 | if (node.remote.protocol.toLowerCase() == "https" || node.remote.protocol.toLowerCase() == "http") { 9 | changeProtocol("remote", node.remote.protocol.toLowerCase()) 10 | } else { 11 | alert ("Configurations Invalid: Protocols accepted are HTTP or HTTPS only! Edit your config.js") 12 | throw new Error(); 13 | } 14 | 15 | //set default local node 16 | document.querySelector("#local_address").value = node.local.address; 17 | document.querySelector("#local_apiPort").value = node.local.port; 18 | document.getElementById("local_gatewayPort").value = node.local.gateway; 19 | document.querySelector("#local").querySelector("#labelAddress").className = "active" 20 | document.querySelector("#local").querySelector("#labelPort").className = "active" 21 | document.querySelector("#local").querySelector("#labelGateway").className = "active" 22 | if (node.local.protocol.toLowerCase() == "https" || node.local.protocol.toLowerCase() == "http") { 23 | changeProtocol("local", node.local.protocol.toLowerCase()) 24 | } else { 25 | alert ("Configurations Invalid: Protocols accept are HTTP or HTTPS only! Edit your config.js") 26 | throw new Error(); 27 | } 28 | 29 | 30 | function changeProtocol (selectedNode, protocol) { 31 | if (protocol == "https") { 32 | node[selectedNode].protocol = "https" 33 | document.querySelector("#"+selectedNode).querySelector("#http").className = "tab" 34 | document.querySelector("#"+selectedNode).querySelector("#https").className += " active" 35 | document.querySelector("input#remote_apiPort").value = 443; 36 | document.querySelector("input#remote_apiPort").disabled = true; 37 | } 38 | if (protocol == "http") { 39 | node[selectedNode].protocol = "http" 40 | document.querySelector("#"+selectedNode).querySelector("#https").className = "tab" 41 | document.querySelector("#"+selectedNode).querySelector("#http").className += " active" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Upload Files to IPFS from Browser - Panel 2 | 3 |

4 | Upload files to IPFS with Browser - Panel 5 |

6 | 7 | 8 | ### Introduction 9 | 10 | Upload your files to IPFS directly from the Browser using local or remote IPFS nodes. 11 | 12 | A simple and intuitive web interface for the API [js-ipfs-http-client](https://github.com/ipfs/js-ipfs-http-client) 13 | 14 | [

Demo Online

](https://anarkrypto.github.io/upload-files-to-ipfs-from-browser-panel/public) 15 | 16 | ## Running locally (node js): 17 | 18 | ```bash 19 | // Clone this project 20 | git clone https://github.com/anarkrypto/upload-files-to-ipfs-from-browser-panel.git 21 | 22 | // Go to the project directory 23 | cd upload-files-to-ipfs-from-browser-panel 24 | 25 | // Instal node dependencies 26 | npm install 27 | 28 | // Run the server: 29 | app.js 30 | ``` 31 | 32 | If everything went well, it will return something like: 33 | ``` Server listening on https://localhost:3000 ``` 34 | 35 | So open the address https://localhost:3000 in your browser and that's it! 36 | You can now start uploading your files. 37 | 38 | 39 | ### Sending to a local IPFS node 40 | 41 | 42 | If you haven't installed it yet, follow the steps to install and configure the IPFS node: [IPFS - Getting Started](https://ipfs.io/ipfs/Qme5m1hmmMbjdzcDeUC2LtHZxAABYtdmq5mBpvtBsC8VL5/docs/getting-started/) 43 | 44 | #### Cors 45 | 46 | To use an IPFS node running locally you need to set IPFS CORS policies correctly. 47 | 48 | Otherwise, you will have permission errors in the requests. 49 | 50 | Paste the following commands in your terminal: 51 | 52 | ```bash 53 | ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["*"]' 54 | ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["GET", "POST"]' 55 | ipfs config --json API.HTTPHeaders.Access-Control-Allow-Headers '["Authorization"]' 56 | ipfs config --json API.HTTPHeaders.Access-Control-Expose-Headers '["Location"]' 57 | ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]' 58 | ``` 59 | 60 | Start / Restart IPFS node: 61 | ```bash 62 | ipfs daemon 63 | ``` 64 | 65 | Ready! Your node will be online locally and ready to serve API requests. 66 | 67 | By default, the IPFS node runs the API at localhost:5001 (or 127.0.0.1:5001). And the gateway on port 8080. 68 | -------------------------------------------------------------------------------- /public/css/upload-utilities.css: -------------------------------------------------------------------------------- 1 | #fileDropBox { 2 | line-height: 5em; 3 | text-align: center; 4 | border-radius: 7px; 5 | color: #0f3c4b; 6 | background-color: #e5edf1; 7 | outline: 1px dashed gray; 8 | outline-offset: -6px; 9 | box-shadow: 0 4px 10px 4px rgba(19, 35, 47, 0.3); 10 | padding: 25px 0 0; 11 | width: 100%; 12 | } 13 | 14 | .box_icon { 15 | width: 100%; 16 | height: 60px; 17 | vertical-align: middle; 18 | text-align: center; 19 | fill: #92b0b3; 20 | } 21 | 22 | .box_file { 23 | position: absolute; 24 | } 25 | 26 | #fileDropBox label { 27 | cursor: pointer; 28 | box-sizing: border-box; 29 | -webkit-font-smoothing: antialiased; 30 | -moz-osx-font-smoothing: grayscale; 31 | } 32 | 33 | #fileDropBox label:hover, 34 | #fileDropBox label:focus { 35 | background: #207ab6; 36 | cursor: pointer; 37 | } 38 | 39 | input[type='file'] { 40 | display: none; 41 | } 42 | 43 | #fileDropBox label { 44 | background-color: #3498db; 45 | color: #fff; 46 | margin: 10px; 47 | padding: 6px 20px; 48 | font-size: 1rem; 49 | text-transform: uppercase; 50 | letter-spacing: .1em; 51 | border-radius: .2em; 52 | } 53 | 54 | .button-upload { 55 | width: 9rem; 56 | border: 0; 57 | outline: none; 58 | padding: 10px; 59 | font-size: 1rem; 60 | text-transform: uppercase; 61 | letter-spacing: .1em; 62 | display: flex; 63 | justify-content: center; 64 | background-color: #3498db; 65 | color: #ffffff; 66 | transition: all 0.5s ease; 67 | appearance: none; 68 | -webkit-appearance: none; 69 | box-shadow: 0 2px 5px 2px rgba(19, 35, 47, 0.3); 70 | font-weight: bold; 71 | border-radius: 12px; 72 | gap: 4px; 73 | } 74 | 75 | .button-upload img { 76 | width: 18px; 77 | height: 18px; 78 | } 79 | 80 | .upload-wrapper { 81 | display: flex; 82 | padding: 20px 0; 83 | } 84 | 85 | .button-upload:hover, 86 | .button-upload:focus { 87 | background: #207ab6; 88 | cursor: pointer; 89 | } 90 | 91 | #list { 92 | max-height: 400px; 93 | min-height: 100px; 94 | border: 1px solid #ccc; 95 | margin-top: 30px; 96 | overflow: auto; 97 | width: 100%; 98 | } 99 | 100 | #list ul { 101 | margin: 0; 102 | padding: 10px; 103 | list-style-type: none; 104 | color: #687b9d; 105 | } 106 | 107 | #list li { 108 | margin-top: 3px; 109 | background-color: #f2f3f5; 110 | border: 1px solid #dbdbdb; 111 | padding: 5px 0px 6px 8px; 112 | word-wrap: break-word; 113 | font-size: 14px; 114 | } 115 | 116 | #list .uploaded { 117 | text-decoration: none; 118 | color: #3498db; 119 | } 120 | 121 | #list .uploaded:hover { 122 | color: #207ab6; 123 | cursor: pointer; 124 | } 125 | 126 | .removeItem { 127 | color: red; 128 | font-size: 20px; 129 | text-decoration: none; 130 | } 131 | 132 | .removeItem:hover { 133 | font-weight: bold; 134 | cursor: pointer; 135 | } 136 | 137 | spam#fileProperties { 138 | color: #687b9d; 139 | } 140 | 141 | spam#info { 142 | color: #92b0b3; 143 | } 144 | -------------------------------------------------------------------------------- /public/css/node-menu.css: -------------------------------------------------------------------------------- 1 | .form { 2 | background: rgba(35, 61, 77, 0.81); 3 | padding: 16px; 4 | width: 100%; 5 | border-radius: 4px; 6 | box-shadow: 0 4px 10px 4px rgba(19, 35, 47, 0.3); 7 | } 8 | 9 | .tab-group { 10 | display: flex; 11 | flex-direction: row; 12 | list-style: none; 13 | margin: 0; 14 | padding: 0; 15 | border-radius: 4px; 16 | overflow: hidden; 17 | } 18 | 19 | .tab-group .tab { 20 | width: 50%; 21 | display: flex; 22 | justify-content: center; 23 | align-items: center; 24 | text-decoration: none; 25 | padding: 10px; 26 | background: rgba(160, 179, 176, 0.25); 27 | color: #a0b3b0; 28 | font-size: 16px; 29 | cursor: pointer; 30 | -webkit-transition: .5s ease; 31 | transition: .5s ease; 32 | } 33 | 34 | .tab-group .tab:hover { 35 | background: #207ab6; 36 | color: #ffffff; 37 | cursor: pointer; 38 | } 39 | 40 | .tab-group .tab.active { 41 | background: #3498db; 42 | color: #ffffff; 43 | } 44 | 45 | .tab-group-node { 46 | margin-bottom: 12px; 47 | } 48 | 49 | .tab-group-protocol { 50 | width: 50%; 51 | height: 28px; 52 | } 53 | 54 | .tab-group-protocol .tab { 55 | width: 50%; 56 | padding: 4px; 57 | font-size: 14px; 58 | } 59 | 60 | .tab-content>div:last-child { 61 | display: none; 62 | } 63 | 64 | .form-row { 65 | display: flex; 66 | flex-direction: row; 67 | justify-content: space-between; 68 | align-items: center; 69 | gap: 16px; 70 | margin-bottom: 4px; 71 | } 72 | 73 | .field-group { 74 | margin-bottom: 8px; 75 | } 76 | 77 | .form label { 78 | color: rgba(255, 255, 255, 0.5); 79 | font-size: 13px; 80 | } 81 | 82 | .form label .req { 83 | margin: 0 2px; 84 | color: #3498db; 85 | } 86 | 87 | .form input { 88 | width: 100%; 89 | height: 40px; 90 | font-size: 15px; 91 | margin-top: 2px; 92 | padding: 5px 10px; 93 | background: none; 94 | background-image: none; 95 | border: 1px solid #a0b3b0; 96 | color: #ffffff; 97 | border-radius: 4px; 98 | -webkit-transition: border-color .25s ease, box-shadow .25s ease; 99 | transition: border-color .25s ease, box-shadow .25s ease; 100 | } 101 | 102 | .form input:focus { 103 | outline: 0; 104 | border-color: #207ab6; 105 | } 106 | 107 | .form input:disabled { 108 | opacity: .4; 109 | } 110 | 111 | #address { 112 | float: left; 113 | width: 70%; 114 | } 115 | 116 | #apiPort { 117 | float: right; 118 | width: 26%; 119 | } 120 | 121 | #gatewayPort { 122 | float: right; 123 | width: 26%; 124 | } 125 | 126 | .button { 127 | border: 0; 128 | outline: none; 129 | border-radius: 0; 130 | padding: 6px 0; 131 | font-size: 1rem; 132 | font-weight: 500; 133 | text-transform: uppercase; 134 | letter-spacing: .1em; 135 | background: #3498db; 136 | color: #ffffff; 137 | -webkit-transition: all 0.5s ease; 138 | transition: all 0.5s ease; 139 | appearance: none; 140 | -webkit-appearance: none; 141 | } 142 | 143 | .button:hover, 144 | .button:focus { 145 | background: #207ab6; 146 | cursor: pointer; 147 | } 148 | 149 | .button-connect { 150 | display: flex; 151 | width: 100%; 152 | height: 36px; 153 | flex-direction: column; 154 | justify-content: center; 155 | align-items: center; 156 | border-radius: 4px; 157 | } 158 | 159 | .button-connect>div:first-child { 160 | display: flex; 161 | justify-content: center; 162 | align-items: center; 163 | } 164 | 165 | .button-connect img { 166 | width: 20px; 167 | } 168 | 169 | .min-loading-hidden { 170 | display: none; 171 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const logger = require('morgan') 3 | const path = require('path') 4 | const cookieParser = require('cookie-parser') 5 | const bodyParser = require('body-parser') 6 | const favicon = require('serve-favicon') 7 | const debug = require('debug')('Upload-file-to-ipfs-panel:server') 8 | const http = require('http') 9 | 10 | 11 | const app = express() 12 | 13 | 14 | app.use(favicon(path.join(__dirname, 'public', 'img/favicon.ico'))) 15 | app.use(logger('dev')) 16 | app.use(bodyParser.json()) 17 | app.use(bodyParser.urlencoded({ extended: true })) 18 | app.use(cookieParser()) 19 | app.use(express.static(path.join(__dirname, 'public'))) 20 | 21 | 22 | app.use((req, res, next) => { 23 | res.setHeader('Access-Control-Allow-Origin', '*') 24 | res.setHeader('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE') 25 | res.setHeader( 26 | 'Access-Control-Allow-Headers', 27 | 'Origin, X-Requested-With, Content-Type, Accept' 28 | ) 29 | next() 30 | }) 31 | 32 | // catch 404 and forward to error handler 33 | app.use((req, res, next) => { 34 | const err = new Error('Not Found') 35 | err.status = 404 36 | next(err) 37 | }) 38 | 39 | // error handlers 40 | 41 | // development error handler 42 | // will print stacktrace 43 | if (app.get('env') === 'development') { 44 | app.use((err, req, res, next) => { 45 | res.status(err.status || 500) 46 | res.render('error', { 47 | message: err.message, 48 | error: err 49 | }) 50 | }) 51 | } 52 | 53 | 54 | // production error handler 55 | // no stacktraces leaked to user 56 | app.use((err, req, res, next) => { 57 | res.status(err.status || 500) 58 | res.render('error', { 59 | message: err.message, 60 | error: {} 61 | }) 62 | }) 63 | 64 | 65 | 66 | /** 67 | * Normalize a port into a number, string, or false. 68 | */ 69 | const normalizePort = val => { 70 | const port = parseInt(val, 10) 71 | if (isNaN(port)) { 72 | // named pipe 73 | return val 74 | } 75 | if (port >= 0) { 76 | // port number 77 | return port 78 | } 79 | return false 80 | } 81 | 82 | /** 83 | * Event listener for HTTP server "error" event. 84 | */ 85 | 86 | const onError = error => { 87 | if (error.syscall !== 'listen') { 88 | throw error 89 | } 90 | const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}` 91 | 92 | // handle specific listen errors with friendly messages 93 | switch (error.code) { 94 | case 'EACCES': 95 | console.error(`${bind} requires elevated privileges`) 96 | process.exit(1) 97 | break 98 | case 'EADDRINUSE': 99 | console.error(`${bind} is already in use`) 100 | process.exit(1) 101 | break 102 | default: 103 | throw error 104 | } 105 | } 106 | 107 | /** 108 | * Event listener for HTTP server "listening" event. 109 | */ 110 | 111 | const onListening = () => { 112 | const addr = server.address() 113 | const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}` 114 | debug(`Listening on ${bind}`) 115 | } 116 | 117 | /** 118 | * Get port from environment and store in Express. 119 | */ 120 | 121 | const port = normalizePort(process.env.PORT || '3000') 122 | app.set('port', port) 123 | 124 | /** 125 | * Create HTTP server. 126 | */ 127 | 128 | const server = http.createServer(app) 129 | 130 | /** 131 | * Listen on provided port, on all network interfaces. 132 | */ 133 | 134 | server.listen(port) 135 | server.on('error', onError) 136 | server.on('listening', onListening) 137 | console.log(`Server listening on https://${process.env.IP}:${port}`) 138 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | *, 2 | *:before, 3 | *:after { 4 | box-sizing: border-box; 5 | } 6 | 7 | html, body { 8 | margin: 0px; 9 | width: 100%; 10 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 11 | background-color: #e9ebee; 12 | } 13 | 14 | main { 15 | margin: 25px auto; 16 | width: 100%; 17 | max-width: 1000px; 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | } 22 | 23 | .wrapper { 24 | display: flex; 25 | flex-direction: row-reverse; 26 | flex-wrap: wrap; 27 | justify-content: space-around; 28 | width: 100%; 29 | padding: 0 8px; 30 | } 31 | 32 | #left { 33 | width: 400px; 34 | } 35 | 36 | #right { 37 | width: 400px; 38 | } 39 | 40 | nav { 41 | text-transform: uppercase; 42 | color: #92b0b3; 43 | width: 100%; 44 | display: flex; 45 | flex-direction: row; 46 | justify-content: space-between; 47 | align-items: center; 48 | padding-left: 12px; 49 | padding-right: 12px; 50 | background: rgba(35, 61, 77, 1); 51 | } 52 | 53 | nav ul { 54 | list-style: none; 55 | padding: 0; 56 | margin: 0; 57 | display: flex; 58 | flex-wrap: wrap; 59 | } 60 | 61 | nav img { 62 | width: 40px; 63 | } 64 | 65 | nav li { 66 | position: relative; 67 | padding: 25px 1.2em; 68 | line-height: 1.2em; 69 | white-space: nowrap; 70 | } 71 | 72 | nav a { 73 | display: flex; 74 | justify-content: center; 75 | gap: 5px; 76 | color: #92b0b3; 77 | text-decoration: none; 78 | } 79 | 80 | nav a:hover { 81 | color: white; 82 | } 83 | 84 | nav svg { 85 | fill: #92b0b3; 86 | height: 1.2em; 87 | width: 1.2em; 88 | } 89 | 90 | nav a:hover svg { 91 | fill: white; 92 | } 93 | 94 | nav ul ul { 95 | display: none; 96 | position: absolute; 97 | top: 100%; 98 | /*sets the top edge of the element above/below top edge of nearest ancestor*/ 99 | } 100 | 101 | nav ul li:hover>ul { 102 | display: block; 103 | } 104 | 105 | nav ul ul li { 106 | float: none; 107 | background: rgba(35, 61, 77, 0.81); 108 | padding: 10px; 109 | text-align: center; 110 | } 111 | 112 | nav ul ul li a { 113 | color: #92b0b3; 114 | } 115 | 116 | nav ul ul li a:hover { 117 | color: white; 118 | } 119 | 120 | @media screen and (max-width: 860px) { 121 | 122 | nav { 123 | flex-direction: column; 124 | } 125 | 126 | nav h1 { 127 | margin-top: 16px; 128 | } 129 | 130 | pre#response { 131 | margin: 30px 0; 132 | } 133 | } 134 | 135 | @media screen and (max-width: 477px) { 136 | nav h1 { 137 | font-size: 14px; 138 | } 139 | 140 | nav ul { 141 | font-size: 12px; 142 | } 143 | } 144 | 145 | @media screen and (max-width: 410px) { 146 | nav h1 { 147 | margin: 15px 0; 148 | } 149 | 150 | nav>ul>li { 151 | padding: 0 .5em 15px; 152 | } 153 | } 154 | 155 | @media screen and (max-width: 370px) { 156 | nav h1 { 157 | font-size: 13px; 158 | } 159 | 160 | nav img { 161 | width: 30px; 162 | } 163 | } 164 | 165 | @media screen and (max-width: 355px) { 166 | nav h1 { 167 | font-size: 12px; 168 | } 169 | 170 | nav img { 171 | width: 28px; 172 | } 173 | } 174 | 175 | @media screen and (max-width: 350px) { 176 | nav h1 { 177 | font-size: 11px; 178 | } 179 | 180 | nav img { 181 | width: 25px; 182 | } 183 | 184 | nav>ul>li { 185 | font-size: 11px; 186 | } 187 | } 188 | 189 | h1 { 190 | color: #92b0b3; 191 | font-weight: 500; 192 | font-size: 1.2em; 193 | display: flex; 194 | align-items: center; 195 | margin: 0; 196 | } 197 | 198 | .buttonContent>div:first-child { 199 | display: flex; 200 | justify-content: center; 201 | align-items: center; 202 | } 203 | 204 | .buttonContent>div:first-child img { 205 | margin-left: 5px; 206 | } 207 | 208 | .min-loading { 209 | display: flex; 210 | justify-content: center; 211 | align-items: center; 212 | position: relative; 213 | width: 64px; 214 | } 215 | 216 | .min-loading.blue { 217 | width: 68px; 218 | } 219 | 220 | .min-loading div { 221 | position: absolute; 222 | width: 11px; 223 | height: 11px; 224 | border-radius: 50%; 225 | background: #fff; 226 | animation-timing-function: cubic-bezier(0, 1, 1, 0); 227 | } 228 | 229 | .min-loading.blue div { 230 | background-color: #3498db; 231 | width: 15px; 232 | height: 15px; 233 | } 234 | 235 | .min-loading div:nth-child(1) { 236 | left: 6px; 237 | animation: min-loading1 0.6s infinite; 238 | } 239 | 240 | .min-loading div:nth-child(2) { 241 | left: 6px; 242 | animation: min-loading2 0.6s infinite; 243 | } 244 | 245 | .min-loading div:nth-child(3) { 246 | left: 26px; 247 | animation: min-loading2 0.6s infinite; 248 | } 249 | 250 | .min-loading div:nth-child(4) { 251 | left: 45px; 252 | animation: min-loading3 0.6s infinite; 253 | } 254 | 255 | @keyframes min-loading1 { 256 | 0% { 257 | transform: scale(0); 258 | } 259 | 260 | 100% { 261 | transform: scale(1); 262 | } 263 | } 264 | 265 | @keyframes min-loading3 { 266 | 0% { 267 | transform: scale(1); 268 | } 269 | 270 | 100% { 271 | transform: scale(0); 272 | } 273 | } 274 | 275 | @keyframes min-loading2 { 276 | 0% { 277 | transform: translate(0, 0); 278 | } 279 | 280 | 100% { 281 | transform: translate(19px, 0); 282 | } 283 | } 284 | 285 | .loading-hidden { 286 | display: none; 287 | } 288 | 289 | @keyframes spin { 290 | 0% { 291 | transform: rotate(0deg); 292 | } 293 | 294 | 100% { 295 | transform: rotate(360deg); 296 | } 297 | } 298 | 299 | #divResponse { 300 | width: 100%; 301 | } 302 | 303 | #divResponse pre { 304 | display: flex; 305 | flex-direction: column; 306 | color: #333; 307 | min-height: 80px; 308 | background-color: #f5f5f5; 309 | border: 1px solid #ccc; 310 | padding: 7px; 311 | font-size: 12px; 312 | overflow: auto; 313 | margin: 30px 0; 314 | white-space: pre-wrap; 315 | word-break: break-word; 316 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Upload files to IPFS from Browser 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 69 |
70 |
71 | 172 |
173 |
174 | 175 | 177 | 178 | 179 | Drop files here
or 180 | 181 | 183 |
184 | 185 |
186 |
    187 |
    188 |
    189 |
    190 |
    191 | 193 |
    194 |
    195 |
    196 |
    197 |
    198 |
    199 |
    200 |
    201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /public/js/connect-and-upload.js: -------------------------------------------------------------------------------- 1 | function updateNode (selectedNode) { 2 | if (selectedNode == "remote") { 3 | node.default = "remote"; 4 | node.remote.address = document.querySelector("#remote_address").value 5 | node.remote.port = document.querySelector("#remote_apiPort").value 6 | node.remote.gateway = document.querySelector("#remote").querySelector("#remote_gatewayPort").value 7 | node.remote.protocol = document.querySelector("#remoteProtocol").querySelector("li.active").innerText.toLowerCase() 8 | } 9 | 10 | if (selectedNode == "local") { 11 | node.default = "local"; 12 | node.local.address = document.querySelector("#local_address").value 13 | node.local.port = document.querySelector("#local_apiPort").value 14 | node.local.gateway = document.querySelector("#local").querySelector("#local_gatewayPort").value 15 | node.local.protocol = document.querySelector("#localProtocol").querySelector("li.active").innerText.toLowerCase() 16 | } 17 | 18 | nodeConnect(selectedNode); 19 | } 20 | 21 | 22 | function nodeConnect (selectedNode) { 23 | if (selectedNode == "remote") { 24 | document.querySelector('button#buttonRemote').setAttribute('disabled', '') 25 | document.querySelector('button#buttonRemote').querySelector(".buttonContent div").innerText = "Connecting" 26 | document.querySelector('button#buttonRemote').querySelector(".buttonContent div").classList.add("connecting") 27 | document.querySelector('button#buttonLocal').querySelector(".buttonContent div").innerHTML = 'Connect ' 28 | document.querySelector('button#buttonRemote').querySelector(".buttonContent .min-loading").classList.remove('min-loading-hidden') //loading event 29 | } 30 | 31 | if (selectedNode == "local") { 32 | document.querySelector('button#buttonLocal').setAttribute('disabled', '') 33 | document.querySelector('button#buttonLocal').querySelector(".buttonContent div").innerText = "Connecting" 34 | document.querySelector('button#buttonLocal').querySelector(".buttonContent div").classList.add("connecting") 35 | document.querySelector('button#buttonRemote').querySelector(".buttonContent div").innerHTML = 'Connect ' 36 | document.querySelector('button#buttonLocal').querySelector(".buttonContent .min-loading").classList.remove('min-loading-hidden') //loading event 37 | } 38 | 39 | var status = "wait" 40 | 41 | ipfsRequest ("GatewayCheck.log", buffer.Buffer.from('ABC', 'utf-8')).then((data) => { 42 | if (data[0].hash == "QmNz1UBzpdd4HfZ3qir3aPiRdX5a93XwTuDNyXRc6PKhWW" ) { 43 | online(selectedNode); 44 | status = "online" 45 | } else { 46 | offline(selectedNode); 47 | status = "offline" 48 | } 49 | document.querySelector('button#buttonRemote').removeAttribute("disabled"); 50 | document.querySelector('button#buttonLocal').removeAttribute("disabled"); 51 | document.querySelector('button#buttonRemote').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') //loading event 52 | document.querySelector('button#buttonLocal').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') //loading event 53 | return status 54 | }, function (reason) { 55 | document.querySelector('button#buttonRemote').removeAttribute("disabled"); 56 | document.querySelector('button#buttonLocal').removeAttribute("disabled"); 57 | document.querySelector('button#buttonRemote').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') //loading event 58 | document.querySelector('button#buttonLocal').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') //loading event 59 | offline(selectedNode) 60 | return "offline" 61 | }) 62 | } 63 | 64 | function online (selectedNode) { 65 | connected = 1 66 | if (selectedNode == "remote") { 67 | document.querySelector('button#buttonRemote').querySelector(".buttonContent div").classList.remove("connecting") 68 | document.querySelector('button#buttonRemote').querySelector(".buttonContent div").innerHTML = 'Node Online ' 69 | document.querySelector('button#buttonRemote').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') 70 | } 71 | if (selectedNode == "local") { 72 | document.querySelector('button#buttonLocal').querySelector(".buttonContent div").classList.remove("connecting") 73 | document.querySelector('button#buttonLocal').querySelector(".buttonContent div").innerHTML = 'Node Online ' 74 | document.querySelector('button#buttonLocal').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') 75 | } 76 | } 77 | 78 | function offline (selectedNode) { 79 | connected = 0 80 | if (selectedNode == "remote") { 81 | document.querySelector('button#buttonRemote').querySelector(".buttonContent div").innerHTML = 'Node Offline! ' 82 | } 83 | if (selectedNode == "local") { 84 | document.querySelector('button#buttonLocal').querySelector(".buttonContent div").innerHTML = 'Node Offline! ' 85 | } 86 | } 87 | 88 | function upload() { 89 | if (!connected) { 90 | alert ("Connect to node First!") 91 | return 92 | } 93 | if (filesOk.length < 1) { 94 | alert("At least one selected file is invalid - do not select any folders."); 95 | return 96 | } 97 | document.querySelector('.min-loading.blue').classList.remove('loading-hidden') //loading event 98 | document.querySelector('button#buttonUpload').setAttribute('disabled', '') 99 | document.querySelector('button#buttonRemote').setAttribute('disabled', '') 100 | document.querySelector('button#buttonLocal').setAttribute('disabled', '') 101 | 102 | filesOk.forEach(function(file){ 103 | let reader = new FileReader(); 104 | reader.readAsArrayBuffer(file); 105 | reader.onloadend = function() { 106 | ipfsRequest (file.name, buffer.Buffer(reader.result)).then((data) => { 107 | response.push(data[0]) 108 | document.querySelector("#response").innerText = JSON.stringify(response, null, 2) 109 | updateList(fileChecksum(file), data[0].hash) 110 | uploadCount++ 111 | if (uploadCount == filesOk.length) { 112 | document.querySelector('.min-loading.blue').classList.add('loading-hidden'); //stop loading event 113 | document.querySelector('button#buttonRemote').removeAttribute('disabled', '') 114 | document.querySelector('button#buttonLocal').removeAttribute('disabled', '') 115 | document.querySelector('button#buttonUpload').onclick=function(){resetFiles()} 116 | document.querySelector('button#buttonUpload').innerHTML = 'Clean Up' 117 | document.querySelector('button#buttonUpload').removeAttribute("disabled"); 118 | } 119 | }) 120 | } 121 | }) 122 | } 123 | 124 | function ipfsRequest (file_name, data) { 125 | var ipfs = window.IpfsHttpClient(node[node.default].address, node[node.default].port, {protocol: node[node.default].protocol}) //router to the IPFS network without any local node 126 | var file_send = 127 | [ 128 | { 129 | path: file_name, 130 | content: data 131 | } 132 | ] 133 | return new Promise((resolve, reject) => { 134 | ipfs.add(file_send, function (err, json) { 135 | if (err) { 136 | //alert(err); 137 | reject (0) 138 | } else { 139 | resolve (json) 140 | } 141 | }) 142 | }) 143 | } 144 | 145 | 146 | function removeItem(checksum) { 147 | var item = files_checksum.indexOf(checksum) 148 | filesOk.splice(item, 1) 149 | files_checksum.splice(item, 1) 150 | 151 | document.getElementById(checksum).remove(); 152 | } 153 | 154 | function handleDragOver(evt) { 155 | evt.stopPropagation(); // Do not allow the dragover event to bubble. 156 | evt.preventDefault(); // Prevent default dragover event behavior. 157 | } // handleDragOver 158 | 159 | 160 | function handleFileSelect(evt) { 161 | evt.stopPropagation(); // Do not allow the drop event to bubble. 162 | evt.preventDefault(); // Prevent default drop event behavior. 163 | 164 | if (evt.dataTransfer != null){ 165 | var files = evt.dataTransfer.files; // Grab the list of files dragged to the drop box. 166 | } else { 167 | var files = evt.target.files; // FileList object from input 168 | } 169 | 170 | 171 | if (!files) { 172 | alert("

    At least one selected file is invalid - do not select any folders.

    Please reselect and try again.

    "); 173 | return; 174 | } 175 | for (var i = 0; i < files.length; i++) { 176 | if (!files[i]) { 177 | alert("Unable to access " + file.name); 178 | continue; // Immediately move to the next file object. 179 | } 180 | if (files[i].size == 0) { 181 | alert("Skipping " + files[i].name.toUpperCase() + " because it is empty."); 182 | continue; 183 | } 184 | if (files_checksum.includes(fileChecksum(files[i]))) { 185 | alert("This files is already listed"); 186 | continue 187 | } else { 188 | files_checksum[filesOk.length] = fileChecksum(files[i]) 189 | document.querySelector("#list").querySelector("ul").innerHTML += '
  • ' + 190 | files[i].name + ' ×' + 191 | '
    (' + (files[i].type || 'n/a' ) +') - ' + 192 | files[i].size + ' bytes, last modified: ' + new Date(files[i].lastModified).toLocaleDateString() +'
  • '; 193 | 194 | filesOk[filesOk.length] = files[i]; //push valid files for filesOk array 195 | } 196 | 197 | } 198 | 199 | //reset filesList input 200 | document.getElementById("files").value = '' 201 | if(!/safari/i.test(navigator.userAgent)){ 202 | document.getElementById("files").type = '' 203 | document.getElementById("files").type = 'file' 204 | } 205 | } 206 | 207 | function updateList (checksum, ipfsHash) { 208 | var i = files_checksum.indexOf(checksum) // equal filesOk[i] 209 | if (node[node.default].gateway == 80 || node[node.default].protocol == "https"){ 210 | var gatewayPort = '' 211 | } else { 212 | var gatewayPort = node[node.default].gateway 213 | } 214 | document.getElementById(checksum).innerHTML = '' + filesOk[i].name + ' ' + 215 | '
    (' + (filesOk[i].type || 'n/a' ) +') - ' + filesOk[i].size + ' bytes, last modified: ' + new Date(filesOk[i].lastModified).toLocaleDateString() +'' 216 | } 217 | 218 | function fileChecksum(file) { 219 | var MD5 = function(d){result = M(V(Y(X(d),8*d.length)));return result.toLowerCase()};function M(d){for(var _,m="0123456789ABCDEF",f="",r=0;r>>4&15)+m.charAt(15&_);return f}function X(d){for(var _=Array(d.length>>2),m=0;m<_.length;m++)_[m]=0;for(m=0;m<8*d.length;m+=8)_[m>>5]|=(255&d.charCodeAt(m/8))<>5]>>>m%32&255);return _}function Y(d,_){d[_>>5]|=128<<_%32,d[14+(_+64>>>9<<4)]=_;for(var m=1732584193,f=-271733879,r=-1732584194,i=271733878,n=0;n>16)+(_>>16)+(m>>16)<<16|65535&m}function bit_rol(d,_){return d<<_|d>>>32-_} 220 | 221 | var checksum = MD5(file.name + file.size + file.lastModified) 222 | return checksum 223 | } 224 | 225 | function resetFiles() { 226 | filesOk = [] 227 | files_checksum = [] 228 | response = [] 229 | uploadCount = 0 230 | document.querySelector("#list").querySelector("ul").innerHTML = "" 231 | document.querySelector("pre#response").innerHTML = 'Response IPFS API:' 232 | document.querySelector('button#buttonUpload').onclick = function(){upload()} 233 | document.querySelector('button#buttonUpload').innerHTML = 'Upload' 234 | } 235 | 236 | 237 | var filesOk = [] 238 | var response = [] 239 | var message = [] 240 | var files_checksum = [] 241 | var selectedNode = "" 242 | var uploadCount = 0 243 | var connected = 0 244 | if (!window.FileReader) { 245 | message = '

    The ' + 246 | 'File APIs ' + 247 | 'are not fully supported by this browser.

    ' + 248 | '

    Upgrade your browser to the latest version.

    '; 249 | document.querySelector('body').innerHTML = message; 250 | } else { 251 | // Set up the file drag and drop listeners: 252 | document.getElementById('fileDropBox').addEventListener('dragover', handleDragOver, false); 253 | document.getElementById('fileDropBox').addEventListener('drop', handleFileSelect, false); 254 | document.getElementById('files').addEventListener('change', handleFileSelect, false); 255 | } 256 | updateNode(node.default) 257 | -------------------------------------------------------------------------------- /public/js/buffer.js: -------------------------------------------------------------------------------- 1 | //You can download this script at https://bundle.run/buffer@5.2.1 2 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).buffer=t()}}(function(){return function(){return function t(r,e,n){function i(f,u){if(!e[f]){if(!r[f]){var s="function"==typeof require&&require;if(!u&&s)return s(f,!0);if(o)return o(f,!0);var h=new Error("Cannot find module '"+f+"'");throw h.code="MODULE_NOT_FOUND",h}var a=e[f]={exports:{}};r[f][0].call(a.exports,function(t){return i(r[f][1][t]||t)},a,a.exports,t,r,e,n)}return e[f].exports}for(var o="function"==typeof require&&require,f=0;fo)throw new RangeError('The value "'+t+'" is invalid for option "size"');var e=new Uint8Array(t);return e.__proto__=r.prototype,e}function r(t,r,e){if("number"==typeof t){if("string"==typeof r)throw new TypeError('The "string" argument must be of type string. Received type number');return h(t)}return u(t,r,e)}function u(t,e,n){if("string"==typeof t)return function(t,e){"string"==typeof e&&""!==e||(e="utf8");if(!r.isEncoding(e))throw new TypeError("Unknown encoding: "+e);var n=0|c(t,e),i=f(n),o=i.write(t,e);o!==n&&(i=i.slice(0,o));return i}(t,e);if(ArrayBuffer.isView(t))return a(t);if(null==t)throw TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);if(z(t,ArrayBuffer)||t&&z(t.buffer,ArrayBuffer))return function(t,e,n){if(e<0||t.byteLength=o)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+o.toString(16)+" bytes");return 0|t}function c(t,e){if(r.isBuffer(t))return t.length;if(ArrayBuffer.isView(t)||z(t,ArrayBuffer))return t.byteLength;if("string"!=typeof t)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof t);var n=t.length,i=arguments.length>2&&!0===arguments[2];if(!i&&0===n)return 0;for(var o=!1;;)switch(e){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":return N(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return P(t).length;default:if(o)return i?-1:N(t).length;e=(""+e).toLowerCase(),o=!0}}function l(t,r,e){var n=t[r];t[r]=t[e],t[e]=n}function y(t,e,n,i,o){if(0===t.length)return-1;if("string"==typeof n?(i=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),D(n=+n)&&(n=o?0:t.length-1),n<0&&(n=t.length+n),n>=t.length){if(o)return-1;n=t.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof e&&(e=r.from(e,i)),r.isBuffer(e))return 0===e.length?-1:g(t,e,n,i,o);if("number"==typeof e)return e&=255,"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(t,e,n):Uint8Array.prototype.lastIndexOf.call(t,e,n):g(t,[e],n,i,o);throw new TypeError("val must be string, number or Buffer")}function g(t,r,e,n,i){var o,f=1,u=t.length,s=r.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(t.length<2||r.length<2)return-1;f=2,u/=2,s/=2,e/=2}function h(t,r){return 1===f?t[r]:t.readUInt16BE(r*f)}if(i){var a=-1;for(o=e;ou&&(e=u-s),o=e;o>=0;o--){for(var p=!0,c=0;ci&&(n=i):n=i;var o=r.length;n>o/2&&(n=o/2);for(var f=0;f>8,i=e%256,o.push(i),o.push(n);return o}(r,t.length-e),t,e,n)}function A(t,r,e){return 0===r&&e===t.length?n.fromByteArray(t):n.fromByteArray(t.slice(r,e))}function B(t,r,e){e=Math.min(t.length,e);for(var n=[],i=r;i239?4:h>223?3:h>191?2:1;if(i+p<=e)switch(p){case 1:h<128&&(a=h);break;case 2:128==(192&(o=t[i+1]))&&(s=(31&h)<<6|63&o)>127&&(a=s);break;case 3:o=t[i+1],f=t[i+2],128==(192&o)&&128==(192&f)&&(s=(15&h)<<12|(63&o)<<6|63&f)>2047&&(s<55296||s>57343)&&(a=s);break;case 4:o=t[i+1],f=t[i+2],u=t[i+3],128==(192&o)&&128==(192&f)&&128==(192&u)&&(s=(15&h)<<18|(63&o)<<12|(63&f)<<6|63&u)>65535&&s<1114112&&(a=s)}null===a?(a=65533,p=1):a>65535&&(a-=65536,n.push(a>>>10&1023|55296),a=56320|1023&a),n.push(a),i+=p}return function(t){var r=t.length;if(r<=U)return String.fromCharCode.apply(String,t);var e="",n=0;for(;nthis.length)return"";if((void 0===e||e>this.length)&&(e=this.length),e<=0)return"";if((e>>>=0)<=(r>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return I(this,r,e);case"utf8":case"utf-8":return B(this,r,e);case"ascii":return _(this,r,e);case"latin1":case"binary":return T(this,r,e);case"base64":return A(this,r,e);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,r,e);default:if(n)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),n=!0}}.apply(this,arguments)},r.prototype.toLocaleString=r.prototype.toString,r.prototype.equals=function(t){if(!r.isBuffer(t))throw new TypeError("Argument must be a Buffer");return this===t||0===r.compare(this,t)},r.prototype.inspect=function(){var t="",r=e.INSPECT_MAX_BYTES;return t=this.toString("hex",0,r).replace(/(.{2})/g,"$1 ").trim(),this.length>r&&(t+=" ... "),""},r.prototype.compare=function(t,e,n,i,o){if(z(t,Uint8Array)&&(t=r.from(t,t.offset,t.byteLength)),!r.isBuffer(t))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof t);if(void 0===e&&(e=0),void 0===n&&(n=t?t.length:0),void 0===i&&(i=0),void 0===o&&(o=this.length),e<0||n>t.length||i<0||o>this.length)throw new RangeError("out of range index");if(i>=o&&e>=n)return 0;if(i>=o)return-1;if(e>=n)return 1;if(this===t)return 0;for(var f=(o>>>=0)-(i>>>=0),u=(n>>>=0)-(e>>>=0),s=Math.min(f,u),h=this.slice(i,o),a=t.slice(e,n),p=0;p>>=0,isFinite(e)?(e>>>=0,void 0===n&&(n="utf8")):(n=e,e=void 0)}var i=this.length-r;if((void 0===e||e>i)&&(e=i),t.length>0&&(e<0||r<0)||r>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var o=!1;;)switch(n){case"hex":return w(this,t,r,e);case"utf8":case"utf-8":return d(this,t,r,e);case"ascii":return v(this,t,r,e);case"latin1":case"binary":return b(this,t,r,e);case"base64":return m(this,t,r,e);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return E(this,t,r,e);default:if(o)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),o=!0}},r.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var U=4096;function _(t,r,e){var n="";e=Math.min(t.length,e);for(var i=r;in)&&(e=n);for(var i="",o=r;oe)throw new RangeError("Trying to access beyond buffer length")}function L(t,e,n,i,o,f){if(!r.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>o||et.length)throw new RangeError("Index out of range")}function R(t,r,e,n,i,o){if(e+n>t.length)throw new RangeError("Index out of range");if(e<0)throw new RangeError("Index out of range")}function x(t,r,e,n,o){return r=+r,e>>>=0,o||R(t,0,e,4),i.write(t,r,e,n,23,4),e+4}function M(t,r,e,n,o){return r=+r,e>>>=0,o||R(t,0,e,8),i.write(t,r,e,n,52,8),e+8}r.prototype.slice=function(t,e){var n=this.length;(t=~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),(e=void 0===e?n:~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),e>>=0,r>>>=0,e||C(t,r,this.length);for(var n=this[t],i=1,o=0;++o>>=0,r>>>=0,e||C(t,r,this.length);for(var n=this[t+--r],i=1;r>0&&(i*=256);)n+=this[t+--r]*i;return n},r.prototype.readUInt8=function(t,r){return t>>>=0,r||C(t,1,this.length),this[t]},r.prototype.readUInt16LE=function(t,r){return t>>>=0,r||C(t,2,this.length),this[t]|this[t+1]<<8},r.prototype.readUInt16BE=function(t,r){return t>>>=0,r||C(t,2,this.length),this[t]<<8|this[t+1]},r.prototype.readUInt32LE=function(t,r){return t>>>=0,r||C(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},r.prototype.readUInt32BE=function(t,r){return t>>>=0,r||C(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},r.prototype.readIntLE=function(t,r,e){t>>>=0,r>>>=0,e||C(t,r,this.length);for(var n=this[t],i=1,o=0;++o=(i*=128)&&(n-=Math.pow(2,8*r)),n},r.prototype.readIntBE=function(t,r,e){t>>>=0,r>>>=0,e||C(t,r,this.length);for(var n=r,i=1,o=this[t+--n];n>0&&(i*=256);)o+=this[t+--n]*i;return o>=(i*=128)&&(o-=Math.pow(2,8*r)),o},r.prototype.readInt8=function(t,r){return t>>>=0,r||C(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},r.prototype.readInt16LE=function(t,r){t>>>=0,r||C(t,2,this.length);var e=this[t]|this[t+1]<<8;return 32768&e?4294901760|e:e},r.prototype.readInt16BE=function(t,r){t>>>=0,r||C(t,2,this.length);var e=this[t+1]|this[t]<<8;return 32768&e?4294901760|e:e},r.prototype.readInt32LE=function(t,r){return t>>>=0,r||C(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},r.prototype.readInt32BE=function(t,r){return t>>>=0,r||C(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},r.prototype.readFloatLE=function(t,r){return t>>>=0,r||C(t,4,this.length),i.read(this,t,!0,23,4)},r.prototype.readFloatBE=function(t,r){return t>>>=0,r||C(t,4,this.length),i.read(this,t,!1,23,4)},r.prototype.readDoubleLE=function(t,r){return t>>>=0,r||C(t,8,this.length),i.read(this,t,!0,52,8)},r.prototype.readDoubleBE=function(t,r){return t>>>=0,r||C(t,8,this.length),i.read(this,t,!1,52,8)},r.prototype.writeUIntLE=function(t,r,e,n){(t=+t,r>>>=0,e>>>=0,n)||L(this,t,r,e,Math.pow(2,8*e)-1,0);var i=1,o=0;for(this[r]=255&t;++o>>=0,e>>>=0,n)||L(this,t,r,e,Math.pow(2,8*e)-1,0);var i=e-1,o=1;for(this[r+i]=255&t;--i>=0&&(o*=256);)this[r+i]=t/o&255;return r+e},r.prototype.writeUInt8=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,1,255,0),this[r]=255&t,r+1},r.prototype.writeUInt16LE=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,2,65535,0),this[r]=255&t,this[r+1]=t>>>8,r+2},r.prototype.writeUInt16BE=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,2,65535,0),this[r]=t>>>8,this[r+1]=255&t,r+2},r.prototype.writeUInt32LE=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,4,4294967295,0),this[r+3]=t>>>24,this[r+2]=t>>>16,this[r+1]=t>>>8,this[r]=255&t,r+4},r.prototype.writeUInt32BE=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,4,4294967295,0),this[r]=t>>>24,this[r+1]=t>>>16,this[r+2]=t>>>8,this[r+3]=255&t,r+4},r.prototype.writeIntLE=function(t,r,e,n){if(t=+t,r>>>=0,!n){var i=Math.pow(2,8*e-1);L(this,t,r,e,i-1,-i)}var o=0,f=1,u=0;for(this[r]=255&t;++o>0)-u&255;return r+e},r.prototype.writeIntBE=function(t,r,e,n){if(t=+t,r>>>=0,!n){var i=Math.pow(2,8*e-1);L(this,t,r,e,i-1,-i)}var o=e-1,f=1,u=0;for(this[r+o]=255&t;--o>=0&&(f*=256);)t<0&&0===u&&0!==this[r+o+1]&&(u=1),this[r+o]=(t/f>>0)-u&255;return r+e},r.prototype.writeInt8=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,1,127,-128),t<0&&(t=255+t+1),this[r]=255&t,r+1},r.prototype.writeInt16LE=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,2,32767,-32768),this[r]=255&t,this[r+1]=t>>>8,r+2},r.prototype.writeInt16BE=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,2,32767,-32768),this[r]=t>>>8,this[r+1]=255&t,r+2},r.prototype.writeInt32LE=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,4,2147483647,-2147483648),this[r]=255&t,this[r+1]=t>>>8,this[r+2]=t>>>16,this[r+3]=t>>>24,r+4},r.prototype.writeInt32BE=function(t,r,e){return t=+t,r>>>=0,e||L(this,t,r,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),this[r]=t>>>24,this[r+1]=t>>>16,this[r+2]=t>>>8,this[r+3]=255&t,r+4},r.prototype.writeFloatLE=function(t,r,e){return x(this,t,r,!0,e)},r.prototype.writeFloatBE=function(t,r,e){return x(this,t,r,!1,e)},r.prototype.writeDoubleLE=function(t,r,e){return M(this,t,r,!0,e)},r.prototype.writeDoubleBE=function(t,r,e){return M(this,t,r,!1,e)},r.prototype.copy=function(t,e,n,i){if(!r.isBuffer(t))throw new TypeError("argument should be a Buffer");if(n||(n=0),i||0===i||(i=this.length),e>=t.length&&(e=t.length),e||(e=0),i>0&&i=this.length)throw new RangeError("Index out of range");if(i<0)throw new RangeError("sourceEnd out of bounds");i>this.length&&(i=this.length),t.length-e=0;--f)t[f+e]=this[f+n];else Uint8Array.prototype.set.call(t,this.subarray(n,i),e);return o},r.prototype.fill=function(t,e,n,i){if("string"==typeof t){if("string"==typeof e?(i=e,e=0,n=this.length):"string"==typeof n&&(i=n,n=this.length),void 0!==i&&"string"!=typeof i)throw new TypeError("encoding must be a string");if("string"==typeof i&&!r.isEncoding(i))throw new TypeError("Unknown encoding: "+i);if(1===t.length){var o=t.charCodeAt(0);("utf8"===i&&o<128||"latin1"===i)&&(t=o)}}else"number"==typeof t&&(t&=255);if(e<0||this.length>>=0,n=void 0===n?this.length:n>>>0,t||(t=0),"number"==typeof t)for(f=e;f55295&&e<57344){if(!i){if(e>56319){(r-=3)>-1&&o.push(239,191,189);continue}if(f+1===n){(r-=3)>-1&&o.push(239,191,189);continue}i=e;continue}if(e<56320){(r-=3)>-1&&o.push(239,191,189),i=e;continue}e=65536+(i-55296<<10|e-56320)}else i&&(r-=3)>-1&&o.push(239,191,189);if(i=null,e<128){if((r-=1)<0)break;o.push(e)}else if(e<2048){if((r-=2)<0)break;o.push(e>>6|192,63&e|128)}else if(e<65536){if((r-=3)<0)break;o.push(e>>12|224,e>>6&63|128,63&e|128)}else{if(!(e<1114112))throw new Error("Invalid code point");if((r-=4)<0)break;o.push(e>>18|240,e>>12&63|128,e>>6&63|128,63&e|128)}}return o}function P(t){return n.toByteArray(function(t){if((t=(t=t.split("=")[0]).trim().replace(O,"")).length<2)return"";for(;t.length%4!=0;)t+="=";return t}(t))}function j(t,r,e,n){for(var i=0;i=r.length||i>=t.length);++i)r[i+e]=t[i];return i}function z(t,r){return t instanceof r||null!=t&&null!=t.constructor&&null!=t.constructor.name&&t.constructor.name===r.name}function D(t){return t!=t}}).call(this,t("buffer").Buffer)},{"base64-js":2,buffer:5,ieee754:3}],2:[function(t,r,e){"use strict";e.byteLength=function(t){var r=h(t),e=r[0],n=r[1];return 3*(e+n)/4-n},e.toByteArray=function(t){for(var r,e=h(t),n=e[0],f=e[1],u=new o(function(t,r,e){return 3*(r+e)/4-e}(0,n,f)),s=0,a=f>0?n-4:n,p=0;p>16&255,u[s++]=r>>8&255,u[s++]=255&r;2===f&&(r=i[t.charCodeAt(p)]<<2|i[t.charCodeAt(p+1)]>>4,u[s++]=255&r);1===f&&(r=i[t.charCodeAt(p)]<<10|i[t.charCodeAt(p+1)]<<4|i[t.charCodeAt(p+2)]>>2,u[s++]=r>>8&255,u[s++]=255&r);return u},e.fromByteArray=function(t){for(var r,e=t.length,i=e%3,o=[],f=0,u=e-i;fu?u:f+16383));1===i?(r=t[e-1],o.push(n[r>>2]+n[r<<4&63]+"==")):2===i&&(r=(t[e-2]<<8)+t[e-1],o.push(n[r>>10]+n[r>>4&63]+n[r<<2&63]+"="));return o.join("")};for(var n=[],i=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",u=0,s=f.length;u0)throw new Error("Invalid string. Length must be a multiple of 4");var e=t.indexOf("=");return-1===e&&(e=r),[e,e===r?0:4-e%4]}function a(t,r,e){for(var i,o,f=[],u=r;u>18&63]+n[o>>12&63]+n[o>>6&63]+n[63&o]);return f.join("")}i["-".charCodeAt(0)]=62,i["_".charCodeAt(0)]=63},{}],3:[function(t,r,e){e.read=function(t,r,e,n,i){var o,f,u=8*i-n-1,s=(1<>1,a=-7,p=e?i-1:0,c=e?-1:1,l=t[r+p];for(p+=c,o=l&(1<<-a)-1,l>>=-a,a+=u;a>0;o=256*o+t[r+p],p+=c,a-=8);for(f=o&(1<<-a)-1,o>>=-a,a+=n;a>0;f=256*f+t[r+p],p+=c,a-=8);if(0===o)o=1-h;else{if(o===s)return f?NaN:1/0*(l?-1:1);f+=Math.pow(2,n),o-=h}return(l?-1:1)*f*Math.pow(2,o-n)},e.write=function(t,r,e,n,i,o){var f,u,s,h=8*o-i-1,a=(1<>1,c=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,l=n?0:o-1,y=n?1:-1,g=r<0||0===r&&1/r<0?1:0;for(r=Math.abs(r),isNaN(r)||r===1/0?(u=isNaN(r)?1:0,f=a):(f=Math.floor(Math.log(r)/Math.LN2),r*(s=Math.pow(2,-f))<1&&(f--,s*=2),(r+=f+p>=1?c/s:c*Math.pow(2,1-p))*s>=2&&(f++,s/=2),f+p>=a?(u=0,f=a):f+p>=1?(u=(r*s-1)*Math.pow(2,i),f+=p):(u=r*Math.pow(2,p-1)*Math.pow(2,i),f=0));i>=8;t[e+l]=255&u,l+=y,u/=256,i-=8);for(f=f<0;t[e+l]=255&f,l+=y,f/=256,h-=8);t[e+l-y]|=128*g}},{}],4:[function(t,r,e){arguments[4][2][0].apply(e,arguments)},{dup:2}],5:[function(t,r,e){arguments[4][1][0].apply(e,arguments)},{"base64-js":4,buffer:5,dup:1,ieee754:6}],6:[function(t,r,e){arguments[4][3][0].apply(e,arguments)},{dup:3}]},{},[1])(1)}); 3 | --------------------------------------------------------------------------------