add ui to upload to IPFS node on box

This commit is contained in:
John Hess 2023-05-24 17:30:54 -05:00
parent 99089126c0
commit 54b3da996c
27 changed files with 78892 additions and 4 deletions

19
LICENSE Normal file
View file

@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,6 +1,7 @@
# Deployment-specific variables to specify what features to surface.
has_internet: false
show_chat: true
show_ipfs: true
# The standard dnsmasq config includes butterbox.lan even when a non-EN language
# is selected so this should work even when the user-visible domain is different.
chat_server: "REPLACEME.lan"

View file

@ -1,6 +1,7 @@
# Deployment-specific variables to specify what features to surface.
has_internet: false
show_chat: true
show_ipfs: true
# The standard dnsmasq config includes butterbox.lan even when a non-EN language
# is selected so this should work even when the user-visible domain is different.
chat_server: "REPLACEME.lan"

View file

@ -1,3 +1,4 @@
# Deployment-specific variables to specify what features to surface.
has_internet: true
show_chat: false
show_ipfs: false

View file

@ -29,6 +29,14 @@ chat_subtitle: 'See what people are saying.'
chat_text: 'Check the chat for messages, videos, or files from nearby peers.'
chat_button_text: 'View Chat'
chat_secondary_cta: 'Open my own chat room'
chat_image_alt: 'Various forms of media that can be shared through chat'
ipfs_subtitle: 'Upload to IPFS'
ipfs_text: 'Upload a file from your device to the Interplanetary File System. This box will pin your upload and share it with the global IPFS network whenever the Butter Box has an upstream connection.'
ipfs_button_text: 'Upload'
ipfs_secondary_cta: 'See what others have uploaded'
ipfs_image_alt: 'Interplanetary File System logo'
upload_ui_source_ack: "Upload UI upstream source"
captive:
welcome_details: |

1
_layouts/empty.html Normal file
View file

@ -0,0 +1 @@
{{ content }}

View file

@ -121,12 +121,21 @@ a:active {
.left-content {
width: 65%;
}
.right-content {
width: 65%;
}
.right-bleed {
width: 35%;
img {
width: calc(100% + 40px);
}
}
.left-img {
width: 35%;
img {
width: 80%;
}
}
.content-text {
text-align: left;
}

View file

@ -0,0 +1,213 @@
.form {
background: rgba(35, 61, 77, 0.81);
padding: 15px;
width: 100%;
border-radius: 4px;
box-shadow: 0 4px 10px 4px rgba(19, 35, 47, 0.3);
}
.tab-group {
list-style: none;
padding: 0;
margin: 0 0 20px 0;
}
.tab-group:after {
content: "";
display: table;
clear: both;
}
.tab-group li a {
display: block;
text-decoration: none;
padding: 10px;
background: rgba(160, 179, 176, 0.25);
color: #a0b3b0;
font-size: 16px;
float: left;
width: 50%;
text-align: center;
cursor: pointer;
-webkit-transition: .5s ease;
transition: .5s ease;
}
#localProtocol li,
#remoteProtocol li {
margin-left: 25%;
}
#localProtocol li a,
#remoteProtocol li a {
width: 28%;
padding: 4px;
font-size: 14px;
}
.tab-group li a:hover {
background: #207ab6;
color: #ffffff;
cursor: pointer;
}
.tab-group .active a {
background: #3498db;
color: #ffffff;
}
.tab-content > div:last-child {
display: none;
}
.form h1 {
text-align: center;
color: #ffffff;
font-weight: 300;
margin-bottom: 10px;
padding: 0px 0px;
font-size: 1em;
}
.form label {
position: absolute;
left: 10px;
color: rgba(255, 255, 255, 0.5);
transition: all 0.25s ease;
pointer-events: none;
font-size: 13px;
}
label .req {
margin: 2px;
color: #3498db;
}
label.active {
-webkit-transform: translateY(30px);
transform: translateY(30px);
left: 2px;
font-size: 14px;
}
label.active .req {
opacity: 0;
}
label.highlight {
color: #ffffff;
}
input,
textarea {
font-size: 15px;
width: 100%;
height: 100%;
padding: 5px 10px;
background: none;
background-image: none;
border: 1px solid #a0b3b0;
color: #ffffff;
border-radius: 0;
-webkit-transition: border-color .25s ease, box-shadow .25s ease;
transition: border-color .25s ease, box-shadow .25s ease;
}
input:focus,
textarea:focus {
outline: 0;
border-color: #207ab6;
}
input:disabled {
opacity: .4;
}
textarea {
border: 2px solid #a0b3b0;
resize: vertical;
}
.field-wrap {
position: relative;
margin-bottom: 20px;
}
.top-row:after {
content: "";
display: table;
clear: both;
}
.top-row > div {
margin-right: 4%;
}
#address {
float: left;
width: 70%;
}
#apiPort {
float: right;
width: 26%;
}
#gatewayPort {
float: right;
width: 26%;
}
.top-row > div:last-child {
margin: 0;
}
.button {
border: 0;
outline: none;
border-radius: 0;
padding: 6px 0;
font-size: 1rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: .1em;
background: #3498db;
color: #ffffff;
-webkit-transition: all 0.5s ease;
transition: all 0.5s ease;
-webkit-appearance: none;
}
.button:hover,
.button:focus {
background: #207ab6;
cursor: pointer;
}
.button-block {
display: flex;
width: 100%;
flex-direction: column;
justify-content: center;
align-items: center;
}
.button-block > div:first-child {
display: flex;
justify-content: center;
align-items: center;
}
.button-block img {
width: 20px;
}
.min-line {
display: inline-block;
width: 12px;
height: 10px;
border-radius: 5px;
background-color: #0c89ba;
}
.min-loading-hidden {
display: none;
}

333
assets/css/upload/style.css Normal file
View file

@ -0,0 +1,333 @@
html, body {
margin: 0px;
padding: 0px;
width: 100%;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
background-color: #e9ebee;
}
#content {
margin: 15px auto;
width: 100%;
max-width: 1000px;
display: flex;
flex-direction: column;
background-color: white;
align-items: center;
padding-bottom: 50px;
}
#wrapper {
background-color: white;
padding: 50px 0px 50px 0px;
display: flex;
flex-direction: row-reverse;
flex-wrap: wrap;
justify-content: space-around;
width: 100%;
}
#hidden-menu {
display: none;
}
#top {
width: 100%;
margin: 25px 50px;
}
#left {
width: 400px;
}
#right {
width: 400px;
}
nav {
text-transform: uppercase;
color: #555555;
width: 100%;
max-width: 1400px;
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
}
nav ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-wrap: wrap;
}
nav img {
width: 40px;
}
nav > ul > li {
display: inline-block;
padding: 25px 1.2em;
white-space: nowrap;
position: relative;
}
nav a {
color: #92b0b3;
text-decoration: none;
}
nav a:hover {
color: white;
}
nav a:hover > * {
fill: white;
}
nav .svg {
fill: #92b0b3;
height: 1.2em;
width: 1.2em;
display: block;
float: right;
}
nav ul ul {
display: none;
position: absolute;
top: 100%; /*sets the top edge of the element above/below top edge of nearest ancestor*/
}
nav ul li:hover > ul {
display: block;
}
nav ul ul li {
float: none;
background: rgba(35, 61, 77, 0.81);
padding: 10px;
text-align: center;
}
nav ul ul li a {
color: #92b0b3;
}
nav ul ul li a:hover {
color: white;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
@media screen and (max-width: 860px) {
nav h1 {
margin-top: 25px;
}
pre#response {
margin: 30px 0;
}
}
@media screen and (max-width: 477px) {
nav h1 {
font-size: 14px;
}
nav ul {
font-size: 12px;
}
}
@media screen and (max-width: 410px) {
nav h1 {
margin: 15px 0;
}
nav > ul > li {
padding: 0 .5em 15px;
}
}
@media screen and (max-width: 390px) {
#wrapper {
padding: 50px 0;
}
}
@media screen and (max-width: 370px) {
nav h1 {
font-size: 13px;
}
nav img {
width: 30px;
}
}
@media screen and (max-width: 355px) {
nav h1 {
font-size: 12px;
}
nav img {
width: 28px;
}
}
@media screen and (max-width: 350px) {
nav h1 {
font-size: 11px;
}
nav img {
width: 25px;
}
nav > ul > li {
font-size: 11px;
}
}
h1 {
color: #92b0b3;
font-weight: 500;
font-size: 1.2em;
display: flex;
align-items: center;
margin: 0;
}
.buttonContent > div:first-child {
display: flex;
justify-content: center;
align-items: center;
}
.buttonContent > div:first-child img {
margin-left: 5px;
}
.min-loading {
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 64px;
}
.min-loading.blue {
width: 68px;
}
.min-loading div {
position: absolute;
width: 11px;
height: 11px;
border-radius: 50%;
background: #fff;
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
.min-loading.blue div {
background-color: #3498db;
width: 15px;
height: 15px;
}
.min-loading div:nth-child(1) {
left: 6px;
animation: min-loading1 0.6s infinite;
}
.min-loading div:nth-child(2) {
left: 6px;
animation: min-loading2 0.6s infinite;
}
.min-loading div:nth-child(3) {
left: 26px;
animation: min-loading2 0.6s infinite;
}
.min-loading div:nth-child(4) {
left: 45px;
animation: min-loading3 0.6s infinite;
}
@keyframes min-loading1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes min-loading3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes min-loading2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(19px, 0);
}
}
.loading-hidden {
display: none;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#divResponse {
width: 100%;
}
#divResponse pre {
display: flex;
flex-direction: column;
color: #333;
min-height: 80px;
background-color: #f5f5f5;
border: 1px solid #ccc;
padding: 7px;
font-size: 12px;
overflow: auto;
margin: 30px 0;
white-space: pre-wrap;
word-break: break-word;
}
.b-nav {
margin: 0px auto;
width: 100%;
display: flex;
align-items: center;
background: rgba(35, 61, 77, 0.81);
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
min-height: 60px;
}
#source-ack
{
text-align: center;
margin-bottom: 10px;
}

View file

@ -0,0 +1,138 @@
#fileDropBox {
line-height: 5em;
text-align: center;
border-radius: 7px;
color: #0f3c4b;
background-color: #e5edf1;
outline: 1px dashed gray;
outline-offset: -6px;
box-shadow: 0 4px 10px 4px rgba(19, 35, 47, 0.3);
padding: 25px 0 0;
width: 100%;
}
.box_icon {
width: 100%;
height: 60px;
vertical-align: middle;
text-align: center;
fill: #92b0b3;
}
.box_file {
position: absolute;
}
#fileDropBox label {
cursor: pointer;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#fileDropBox label:hover,
#fileDropBox label:focus {
background: #207ab6;
cursor: pointer;
}
input[type='file'] {
display: none;
}
#fileDropBox label {
background-color: #3498db;
color: #fff;
margin: 10px;
padding: 6px 20px;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: .1em;
border-radius: .2em;
}
.box_button {
border: 0;
outline: none;
padding: 10px;
font-size: 1rem;
width: 9rem;
text-transform: uppercase;
letter-spacing: .1em;
background-color: #3498db;
color: #ffffff;
transition: all 0.5s ease;
-webkit-appearance: none;
box-shadow: 0 2px 5px 2px rgba(19, 35, 47, 0.3);
font-weight: bold;
}
.box_button img {
margin-bottom: -2px;
margin-left: 2px;
width: 18px;
}
#upload_conf {
display: flex;
}
.box_button:hover,
.box_button:focus {
background: #207ab6;
cursor: pointer;
}
#list {
max-height: 400px;
min-height: 100px;
border: 1px solid #ccc;
margin-top: 30px;
overflow: auto;
width: 100%;
}
#list ul {
margin: 0;
padding: 10px;
list-style-type: none;
color: #687b9d;
}
#list li {
margin-top: 3px;
background-color: #f2f3f5;
border: 1px solid #dbdbdb;
padding: 5px 0px 6px 8px;
word-wrap: break-word;
font-size: 14px;
}
#list .uploaded {
text-decoration: none;
color: #3498db;
}
#list .uploaded:hover {
color: #207ab6;
cursor: pointer;
}
.removeItem {
color: red;
font-size: 20px;
text-decoration: none;
}
.removeItem:hover {
font-weight: bold;
cursor: pointer;
}
spam#fileProperties {
color: #687b9d;
}
spam#info {
color: #92b0b3;
}

BIN
assets/images/btr-ipfs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,24 @@
// Change this file as you need it
const config = {
port: 5001,
gateway: 8080
}
const node = {
default: 'local',
remote: {
address: 'ipfs.sea.tube',
...config,
protocol: 'https'
},
// default local node
// $butter_name is replaced with the butterbox's address during provisioning
// see https://gitlab.com/likebutter/butterbox-rpi
local: {
address: window.location.hostname,
...config,
protocol: 'http'
}
}

View file

@ -0,0 +1,256 @@
function updateNode (selectedNode) {
if (selectedNode == "remote") {
node.default = "remote";
node.remote.address = document.querySelector("#remote_address").value
node.remote.port = document.querySelector("#remote_apiPort").value
node.remote.gateway = document.querySelector("#remote").querySelector("#remote_gatewayPort").value
node.remote.protocol = document.querySelector("#remoteProtocol").querySelector("li.active").innerText.toLowerCase()
}
if (selectedNode == "local") {
node.default = "local";
node.local.address = document.querySelector("#local_address").value
node.local.port = document.querySelector("#local_apiPort").value
node.local.gateway = document.querySelector("#local").querySelector("#local_gatewayPort").value
node.local.protocol = document.querySelector("#localProtocol").querySelector("li.active").innerText.toLowerCase()
}
nodeConnect(selectedNode);
}
function nodeConnect (selectedNode) {
if (selectedNode == "remote") {
document.querySelector('button#buttonRemote').setAttribute('disabled', '')
document.querySelector('button#buttonRemote').querySelector(".buttonContent div").innerText = "Connecting"
document.querySelector('button#buttonRemote').querySelector(".buttonContent div").classList.add("connecting")
document.querySelector('button#buttonLocal').querySelector(".buttonContent div").innerHTML = 'Connect <img src="/assets/images/upload/connect.png"/>'
document.querySelector('button#buttonRemote').querySelector(".buttonContent .min-loading").classList.remove('min-loading-hidden') //loading event
}
if (selectedNode == "local") {
document.querySelector('button#buttonLocal').setAttribute('disabled', '')
document.querySelector('button#buttonLocal').querySelector(".buttonContent div").innerText = "Connecting"
document.querySelector('button#buttonLocal').querySelector(".buttonContent div").classList.add("connecting")
document.querySelector('button#buttonRemote').querySelector(".buttonContent div").innerHTML = 'Connect <img src="/assets/images/upload/connect.png"/>'
document.querySelector('button#buttonLocal').querySelector(".buttonContent .min-loading").classList.remove('min-loading-hidden') //loading event
}
var status = "wait"
ipfsRequest ("GatewayCheck.log", buffer.Buffer.from('ABC', 'utf-8')).then((data) => {
if (data[0].hash == "QmNz1UBzpdd4HfZ3qir3aPiRdX5a93XwTuDNyXRc6PKhWW" ) {
online(selectedNode);
status = "online"
} else {
offline(selectedNode);
status = "offline"
}
document.querySelector('button#buttonRemote').removeAttribute("disabled");
document.querySelector('button#buttonLocal').removeAttribute("disabled");
document.querySelector('button#buttonRemote').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') //loading event
document.querySelector('button#buttonLocal').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') //loading event
return status
}, function (reason) {
document.querySelector('button#buttonRemote').removeAttribute("disabled");
document.querySelector('button#buttonLocal').removeAttribute("disabled");
document.querySelector('button#buttonRemote').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') //loading event
document.querySelector('button#buttonLocal').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden') //loading event
offline(selectedNode)
return "offline"
})
}
function online (selectedNode) {
connected = 1
if (selectedNode == "remote") {
document.querySelector('button#buttonRemote').querySelector(".buttonContent div").classList.remove("connecting")
document.querySelector('button#buttonRemote').querySelector(".buttonContent div").innerHTML = 'Node Online <img src="/assets/images/upload/connected.png"/>'
document.querySelector('button#buttonRemote').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden')
}
if (selectedNode == "local") {
document.querySelector('button#buttonLocal').querySelector(".buttonContent div").classList.remove("connecting")
document.querySelector('button#buttonLocal').querySelector(".buttonContent div").innerHTML = 'Node Online <img src="/assets/images/upload/connected.png"/>'
document.querySelector('button#buttonLocal').querySelector('.buttonContent .min-loading').classList.add('min-loading-hidden')
}
}
function offline (selectedNode) {
connected = 0
if (selectedNode == "remote") {
document.querySelector('button#buttonRemote').querySelector(".buttonContent div").innerHTML = 'Node Offline! <img src="/assets/images/upload/disconnected.png"/>'
}
if (selectedNode == "local") {
document.querySelector('button#buttonLocal').querySelector(".buttonContent div").innerHTML = 'Node Offline! <img src="/assets/images/upload/disconnected.png"/>'
}
}
function upload() {
if (!connected) {
alert ("Unable to connect to Butter Box's IPFS Node.")
return
}
if (filesOk.length < 1) {
alert("<p>At least one selected file is invalid - do not select any folders.</p><p>Please reselect and try again.</p>");
return
}
document.querySelector('.min-loading.blue').classList.remove('loading-hidden') //loading event
document.querySelector('button#buttonUpload').setAttribute('disabled', '')
document.querySelector('button#buttonRemote').setAttribute('disabled', '')
document.querySelector('button#buttonLocal').setAttribute('disabled', '')
filesOk.forEach(function(file){
let reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onloadend = function() {
ipfsRequest (file.name, buffer.Buffer(reader.result)).then((data) => {
response.push(data[0])
document.querySelector("#response").innerText = JSON.stringify(response, null, 2)
updateList(fileChecksum(file), data[0].hash)
uploadCount++
if (uploadCount == filesOk.length) {
document.querySelector('.min-loading.blue').classList.add('loading-hidden'); //stop loading event
document.querySelector('button#buttonRemote').removeAttribute('disabled', '')
document.querySelector('button#buttonLocal').removeAttribute('disabled', '')
document.querySelector('button#buttonUpload').onclick=function(){resetFiles()}
document.querySelector('button#buttonUpload').innerHTML = 'Clean Up<img src="/assets/images/upload/reset.png" />'
document.querySelector('button#buttonUpload').removeAttribute("disabled");
}
})
}
})
}
function ipfsRequest (file_name, data) {
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
var file_send =
[
{
path: file_name,
content: data
}
]
return new Promise((resolve, reject) => {
ipfs.add(file_send, function (err, json) {
if (err) {
alert(err);
reject (0)
} else {
resolve (json)
}
})
})
}
function removeItem(checksum) {
var item = files_checksum.indexOf(checksum)
filesOk.splice(item, 1)
files_checksum.splice(item, 1)
document.getElementById(checksum).remove();
}
function handleDragOver(evt) {
evt.stopPropagation(); // Do not allow the dragover event to bubble.
evt.preventDefault(); // Prevent default dragover event behavior.
} // handleDragOver
function handleFileSelect(evt) {
evt.stopPropagation(); // Do not allow the drop event to bubble.
evt.preventDefault(); // Prevent default drop event behavior.
if (evt.dataTransfer != null){
var files = evt.dataTransfer.files; // Grab the list of files dragged to the drop box.
} else {
var files = evt.target.files; // FileList object from input
}
if (!files) {
alert("<p>At least one selected file is invalid - do not select any folders.</p><p>Please reselect and try again.</p>");
return;
}
for (var i = 0; i < files.length; i++) {
if (!files[i]) {
alert("Unable to access " + file.name);
continue; // Immediately move to the next file object.
}
if (files[i].size == 0) {
alert("Skipping " + files[i].name.toUpperCase() + " because it is empty.");
continue;
}
if (files_checksum.includes(fileChecksum(files[i]))) {
alert("This files is already listed");
continue
} else {
files_checksum[filesOk.length] = fileChecksum(files[i])
document.querySelector("#list").querySelector("ul").innerHTML += '<li id="' + fileChecksum(files[i]) + '"><strong class="fileName">' +
files[i].name + '</strong> <spam class="itemClose"><a class="removeItem" href="#" onclick="removeItem(\''+fileChecksum(files[i])+'\')">&times;</a></spam>' +
'</a></spam><br> <spam id="fileProperties"> (' + (files[i].type || 'n/a' ) +') - ' +
files[i].size + ' bytes, last modified: ' + new Date(files[i].lastModified).toLocaleDateString() +'</spam></li>';
filesOk[filesOk.length] = files[i]; //push valid files for filesOk array
}
}
//reset filesList input
document.getElementById("files").value = ''
if(!/safari/i.test(navigator.userAgent)){
document.getElementById("files").type = ''
document.getElementById("files").type = 'file'
}
}
function updateList (checksum, ipfsHash) {
var i = files_checksum.indexOf(checksum) // equal filesOk[i]
if (node[node.default].gateway == 80 || node[node.default].protocol == "https"){
var gatewayPort = ''
} else {
var gatewayPort = node[node.default].gateway
}
document.getElementById(checksum).innerHTML = '<strong class="fileName"><a href="' + node[node.default].protocol + '://' + node[node.default].address + ':' + gatewayPort + '/ipfs/' + ipfsHash + '" class="uploaded" target="_blank">' + filesOk[i].name + ' <img src="/assets/images/upload/link.png" width="12px"/></a></strong>' +
'</a></spam><br> <spam id="fileProperties"> (' + (filesOk[i].type || 'n/a' ) +') - ' + filesOk[i].size + ' bytes, last modified: ' + new Date(filesOk[i].lastModified).toLocaleDateString() +'</spam>'
}
function fileChecksum(file) {
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<d.length;r++)_=d.charCodeAt(r),f+=m.charAt(_>>>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))<<m%32;return _}function V(d){for(var _="",m=0;m<32*d.length;m+=8)_+=String.fromCharCode(d[m>>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<d.length;n+=16){var h=m,t=f,g=r,e=i;f=md5_ii(f=md5_ii(f=md5_ii(f=md5_ii(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_ff(f=md5_ff(f=md5_ff(f=md5_ff(f,r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+0],7,-680876936),f,r,d[n+1],12,-389564586),m,f,d[n+2],17,606105819),i,m,d[n+3],22,-1044525330),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+4],7,-176418897),f,r,d[n+5],12,1200080426),m,f,d[n+6],17,-1473231341),i,m,d[n+7],22,-45705983),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+8],7,1770035416),f,r,d[n+9],12,-1958414417),m,f,d[n+10],17,-42063),i,m,d[n+11],22,-1990404162),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+12],7,1804603682),f,r,d[n+13],12,-40341101),m,f,d[n+14],17,-1502002290),i,m,d[n+15],22,1236535329),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+1],5,-165796510),f,r,d[n+6],9,-1069501632),m,f,d[n+11],14,643717713),i,m,d[n+0],20,-373897302),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+5],5,-701558691),f,r,d[n+10],9,38016083),m,f,d[n+15],14,-660478335),i,m,d[n+4],20,-405537848),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+9],5,568446438),f,r,d[n+14],9,-1019803690),m,f,d[n+3],14,-187363961),i,m,d[n+8],20,1163531501),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+13],5,-1444681467),f,r,d[n+2],9,-51403784),m,f,d[n+7],14,1735328473),i,m,d[n+12],20,-1926607734),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+5],4,-378558),f,r,d[n+8],11,-2022574463),m,f,d[n+11],16,1839030562),i,m,d[n+14],23,-35309556),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+1],4,-1530992060),f,r,d[n+4],11,1272893353),m,f,d[n+7],16,-155497632),i,m,d[n+10],23,-1094730640),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+13],4,681279174),f,r,d[n+0],11,-358537222),m,f,d[n+3],16,-722521979),i,m,d[n+6],23,76029189),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+9],4,-640364487),f,r,d[n+12],11,-421815835),m,f,d[n+15],16,530742520),i,m,d[n+2],23,-995338651),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+0],6,-198630844),f,r,d[n+7],10,1126891415),m,f,d[n+14],15,-1416354905),i,m,d[n+5],21,-57434055),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+12],6,1700485571),f,r,d[n+3],10,-1894986606),m,f,d[n+10],15,-1051523),i,m,d[n+1],21,-2054922799),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+8],6,1873313359),f,r,d[n+15],10,-30611744),m,f,d[n+6],15,-1560198380),i,m,d[n+13],21,1309151649),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+4],6,-145523070),f,r,d[n+11],10,-1120210379),m,f,d[n+2],15,718787259),i,m,d[n+9],21,-343485551),m=safe_add(m,h),f=safe_add(f,t),r=safe_add(r,g),i=safe_add(i,e)}return Array(m,f,r,i)}function md5_cmn(d,_,m,f,r,i){return safe_add(bit_rol(safe_add(safe_add(_,d),safe_add(f,i)),r),m)}function md5_ff(d,_,m,f,r,i,n){return md5_cmn(_&m|~_&f,d,_,r,i,n)}function md5_gg(d,_,m,f,r,i,n){return md5_cmn(_&f|m&~f,d,_,r,i,n)}function md5_hh(d,_,m,f,r,i,n){return md5_cmn(_^m^f,d,_,r,i,n)}function md5_ii(d,_,m,f,r,i,n){return md5_cmn(m^(_|~f),d,_,r,i,n)}function safe_add(d,_){var m=(65535&d)+(65535&_);return(d>>16)+(_>>16)+(m>>16)<<16|65535&m}function bit_rol(d,_){return d<<_|d>>>32-_}
var checksum = MD5(file.name + file.size + file.lastModified)
return checksum
}
function resetFiles() {
filesOk = []
files_checksum = []
response = []
uploadCount = 0
document.querySelector("#list").querySelector("ul").innerHTML = ""
document.querySelector("pre#response").innerHTML = '<spam id="info">Response IPFS API:</spam>'
document.querySelector('button#buttonUpload').onclick = function(){upload()}
document.querySelector('button#buttonUpload').innerHTML = 'Upload<img src="/assets/images/upload/upload.png">'
}
var filesOk = []
var response = []
var message = []
var files_checksum = []
var selectedNode = ""
var uploadCount = 0
var connected = 0
if (!window.FileReader) {
message = '<p>The ' +
'<a href="http://dev.w3.org/2006/webapi/FileAPI/" target="_blank">File API</a>s ' +
'are not fully supported by this browser.</p>' +
'<p>Upgrade your browser to the latest version.</p>';
document.querySelector('body').innerHTML = message;
} else {
// Set up the file drag and drop listeners:
document.getElementById('fileDropBox').addEventListener('dragover', handleDragOver, false);
document.getElementById('fileDropBox').addEventListener('drop', handleFileSelect, false);
document.getElementById('files').addEventListener('change', handleFileSelect, false);
}
updateNode(node.default)

File diff suppressed because one or more lines are too long

5
assets/js/upload/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,43 @@
//set default remote Node
document.getElementById("remote_address").value = node.remote.address;
document.getElementById("remote_apiPort").value = node.remote.port;
document.getElementById("remote_gatewayPort").value = node.remote.gateway;
document.querySelector("#remote").querySelector("#labelAddress").className = "active"
document.querySelector("#remote").querySelector("#labelPort").className = "active"
document.querySelector("#remote").querySelector("#labelGateway").className = "active"
if (node.remote.protocol.toLowerCase() == "https" || node.remote.protocol.toLowerCase() == "http") {
changeProtocol("remote", node.remote.protocol.toLowerCase())
} else {
alert ("Configurations Invalid: Protocols accepted are HTTP or HTTPS only! Edit your config.js")
throw new Error();
}
//set default local node
document.querySelector("#local_address").value = node.local.address;
document.querySelector("#local_apiPort").value = node.local.port;
document.getElementById("local_gatewayPort").value = node.local.gateway;
document.querySelector("#local").querySelector("#labelAddress").className = "active"
document.querySelector("#local").querySelector("#labelPort").className = "active"
document.querySelector("#local").querySelector("#labelGateway").className = "active"
if (node.local.protocol.toLowerCase() == "https" || node.local.protocol.toLowerCase() == "http") {
changeProtocol("local", node.local.protocol.toLowerCase())
} else {
alert ("Configurations Invalid: Protocols accept are HTTP or HTTPS only! Edit your config.js")
throw new Error();
}
function changeProtocol (selectedNode, protocol) {
if (protocol == "https") {
node[selectedNode].protocol = "https"
document.querySelector("#"+selectedNode).querySelector("#http").className = "tab"
document.querySelector("#"+selectedNode).querySelector("#https").className += " active"
document.querySelector("input#remote_apiPort").value = 443;
document.querySelector("input#remote_apiPort").disabled = true;
}
if (protocol == "http") {
node[selectedNode].protocol = "http"
document.querySelector("#"+selectedNode).querySelector("#https").className = "tab"
document.querySelector("#"+selectedNode).querySelector("#http").className += " active"
}
}

View file

@ -0,0 +1,43 @@
$('.form').find('input, textarea').on('keyup blur focus', function (e) {
var $this = $(this),
label = $this.prev('label');
if (e.type === 'keyup') {
if ($this.val() === '') {
label.removeClass('active highlight');
} else {
label.addClass('active highlight');
}
} else if (e.type === 'blur') {
if( $this.val() === '' ) {
label.removeClass('active highlight');
} else {
label.removeClass('highlight');
}
} else if (e.type === 'focus') {
if( $this.val() === '' ) {
label.removeClass('highlight');
}
else if( $this.val() !== '' ) {
label.addClass('highlight');
}
}
});
$('.tab a').on('click', function (e) {
e.preventDefault();
$(this).parent().addClass('active');
$(this).parent().siblings().removeClass('active');
target = $(this).attr('href');
$('.tab-content > div').not(target).hide();
$(target).fadeIn(600);
});

View file

@ -44,11 +44,32 @@ layout: default
<a class="btn-main uppercase" href="/chat/#/room/%23public%3A{{site.data.deployment.chat_server}}">{% t chat_button_text %}</a>
</div>
<div class="right-bleed">
<img src="/assets/images/btr-convene.png" alt="{% t page_logo_alt %}" />
<img src="/assets/images/btr-convene.png" alt="{% t chat_image_alt %}" />
</div>
</div>
<a href="/chat/#/createroom"><div class="outlink content-expander">
<span>{% t chat_secondary_cta %}</span>
<a href="/chat/#/createroom">
<div class="outlink content-expander">
<span>{% t chat_secondary_cta %}</span>
</div>
</a>
</div>
{% endif %}
{% if site.data.deployment.show_ipfs %}
<div class="content-area-wrapper">
<div class="split-content">
<div class="left-img">
<img src="/assets/images/btr-ipfs.png" alt="{% t ipfs_image_alt %}" />
</div>
<div class="right-content">
<p class="content-subtitle text-black ls-40 mb-2">{% t ipfs_subtitle %}</p>
<p class="content-text text-black ls-40 mb-2">{% t ipfs_text %}</p>
<a class="btn-main uppercase" href="/upload">{% t ipfs_button_text %}</a>
</div>
</div>
<a href="/view_uploads">
<div class="outlink content-expander">
<span>{% t ipfs_secondary_cta %}</span>
</div>
</a>
</div>
{% endif %}

201
upload.html Normal file
View file

@ -0,0 +1,201 @@
---
layout: empty
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="pragma" content="no-cache">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Upload files to IPFS from Browser</title>
<link rel="stylesheet" href="{{ site.baseurl }}/assets/css/upload/style.css?v0.1" type='text/css'>
<link rel="stylesheet" href="{{ site.baseurl }}/assets/css/upload/upload-utilities.css?v0.1" type='text/css'>
<link rel="stylesheet" href="{{ site.baseurl }}/assets/css/upload/node-menu.css?v0.1" type='text/css'>
<script src='{{ site.baseurl }}/assets/js/upload/jquery.min.js?2.1.3'></script>
<script src="{{ site.baseurl }}/assets/js/upload/ipfs-http-client.js"></script>
<script src="{{ site.baseurl }}/assets/js/upload/buffer.js?5.2.1"></script>
<script src="{{ site.baseurl }}/assets/js/upload/config.js?v0.1"></script> <!-- read config.js before upload.js -->
</head>
<body>
<div class="b-nav">
<nav>
<a href="#" title="home">
<h1>Upload files to <img src="{{ site.baseurl }}/assets/images/upload/ipfs.png" style="margin: 0 2px"></h1>
</a>
</nav>
</div>
<div id="content">
<div id="wrapper">
<div id="top">
<p>This Butter Box is part of the Interplanetary File System (IPFS). You can always upload files to this box and see the files others have uploaded. Whenever the Butter Box is connected to the Internet, it'll make those files available to people everywhere.</p>
<p>Remember, anyone will be able to see the files you upload here, so don't upload private things. </p>
</div>
<div id="hidden-menu">
<div class="form">
<script>
var formRemote = `<div id="remote">
<h1>Configure your IPFS Gateway Node as Remote</h1>
<form action="submit" method="post">
<div class="top-row">
<div class="field-wrap" id="address">
<label id="labelAddress">
Node Address<span class="req">*</span>
</label>
<input type="text" name="addressNode" id="remote_address" required autocomplete="off" />
</div>
<div class="field-wrap" id="apiPort">
<label id="labelPort">
API Port<span class="req">*</span>
</label>
<input type="text" name="apiPort" id="remote_apiPort" required autocomplete="off" />
</div>
</div>
<div class="top-row">
<div class="field-wrap" id="gatewayPort">
<label id="labelGateway">
Gateway Port\
</label>
<input type="text" name="gatewayPort" id="remote_gatewayPort" required autocomplete="off" />
</div>
</div>
<ul class="tab-group" id="remoteProtocol">
<li class="tab" id="https" onclick="changeProtocol('remote', 'https')"><a href="#remote">HTTPS</a></li>
<li class="tab" id="http" onclick="changeProtocol('remote', 'http')"><a href="#remote">HTTP</a></li>
</ul>
<button type="button" class="button button-block" id="buttonRemote" onclick="updateNode('remote')">
<div class="buttonContent">
<div>Connect <img src="{{ site.base_url }}/assets/images/upload/connect.png" /></div>
<div class="min-loading min-loading-hidden">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
</button>
</form>
</div>`;
var formLocal = `<div id="local">
<h1>Configure your IPFS Gateway Node as Local</h1>
<form action="submit" method="post">
<div class="top-row">
<div class="field-wrap" id="address">
<label id="labelAddress">
Node Address<span class="req">*</span>
</label>
<input type="text" name="first_name" id="local_address" required autocomplete="off" />
</div>
<div class="field-wrap" id="apiPort">
<label id="labelPort">
API Port<span class="req">*</span>
</label>
<input type="text" name="last_name" id="local_apiPort" required autocomplete="off" />
</div>
</div>
<div class="top-row">
<div class="field-wrap" id="gatewayPort">
<label id="labelGateway">
Gateway Port\
</label>
<input type="text" name="gatewayPort" id="local_gatewayPort" required autocomplete="off" />
</div>
</div>
<ul class="tab-group" id="localProtocol">
<li class="tab" id="https" onclick="changeProtocol('local', 'https')"><a href="#local">HTTPS</a></li>
<li class="tab" id="http" onclick="changeProtocol('local', 'http')"><a href="#local">HTTP</a></li>
</ul>
<button type="button" class="button button-block" id="buttonLocal" onclick="updateNode('local')">
<div class="buttonContent">
<div>Connect <img src="{{ site.base_url }}/assets/images/upload/connect.png" /></div>
<div class="min-loading min-loading-hidden">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
</button>
</form>
</div>`;
if (node.default == "remote") {
document.querySelector("div.form").innerHTML = `<ul class="tab-group">
<li class="tab active">
<a href="#remote">Remote Node</a>
</li>
<li class="tab">
<a href="#local">Local Node</a>
</li>
</ul>
<div class="tab-content">
${formRemote}\n${formLocal}
</div><!-- tab-content -->`
}
if (node.default == "local") {
document.querySelector("div.form").innerHTML = `<ul class="tab-group">
<li class="tab active">
<a href="#local">Local Node</a>
</li>
<li class="tab">
<a href="#remote">Remote Node</a>
</li>
</ul>
<div class="tab-content">
${formLocal}\n${formRemote}
</div><!-- tab-content -->`
}
</script>
</div> <!-- /form -->
</div>
<div id="top">
<div id="fileDropBox">
<svg class="box_icon" xmlns="http://www.w3.org/2000/svg" width="50" height="43" viewBox="0 0 50 43">
<path
d="M48.4 26.5c-.9 0-1.7.7-1.7 1.7v11.6h-43.3v-11.6c0-.9-.7-1.7-1.7-1.7s-1.7.7-1.7 1.7v13.2c0 .9.7 1.7 1.7 1.7h46.7c.9 0 1.7-.7 1.7-1.7v-13.2c0-1-.7-1.7-1.7-1.7zm-24.5 6.1c.3.3.8.5 1.2.5.4 0 .9-.2 1.2-.5l10-11.6c.7-.7.7-1.7 0-2.4s-1.7-.7-2.4 0l-7.1 8.3v-25.3c0-.9-.7-1.7-1.7-1.7s-1.7.7-1.7 1.7v25.3l-7.1-8.3c-.7-.7-1.7-.7-2.4 0s-.7 1.7 0 2.4l10 11.6z">
</path>
</svg>
Drop files here <br> or
<label for='files'>Choose Files &#187;</label>
<input type="file" name="files[]" id="files" class="box_file"
data-multiple-caption="{count} files selected" multiple="">
</div>
<!-- Provides the alert() method. -->
<div id="list">
<ul></ul>
</div>
<div id="divResponse">
<pre id="response">
<spam id="info">Result:</spam>
</pre>
</div>
</div>
</div>
<div id="upload_conf">
<button type="button" onclick="upload()" class="box_button" id="buttonUpload">Upload<img
src="{{ site.baseurl }}/assets/images/upload/upload.png" /></button>
<div class="min-loading blue loading-hidden">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
</div>
<div id="source-ack">
<small><a href="https://github.com/anarkrypto/upload-files-to-ipfs-from-browser-panel">{% t upload_ui_source_ack %}</a></small>
</div>
</body>
</html>
<script src="{{ site.baseurl }}/assets/js/upload/node-config.js?v0.1"></script>
<script src="{{ site.baseurl }}/assets/js/upload/connect-and-upload.js?v0.1"></script>
<script src="{{ site.baseurl }}/assets/js/upload/node-menu.js"></script>