Client side input #122

Merged
bones7242 merged 3 commits from client-side-input into master 2017-07-29 00:39:17 +02:00
7 changed files with 165 additions and 110 deletions
Showing only changes of commit 7c1a0f2e66 - Show all commits

View file

@ -123,6 +123,7 @@ table {
.input-error { .input-error {
font-weight: bold; font-weight: bold;
color: red; color: red;
font-size: small;
} }
@media (max-width: 1250px) { @media (max-width: 1250px) {

View file

@ -61,6 +61,12 @@ function showError(elementId, errorMsg) {
errorDisplay.innerText = errorMsg; errorDisplay.innerText = errorMsg;
} }
function clearError(elementId,) {
var errorDisplay = document.getElementById(elementId);
errorDisplay.hidden = true;
errorDisplay.innerText = '';
}
// Create new error objects, that prototypically inherit from the Error constructor // Create new error objects, that prototypically inherit from the Error constructor
function FileError(message) { function FileError(message) {
this.name = 'FileError'; this.name = 'FileError';

View file

@ -2,96 +2,6 @@
function updatePublishStatus(msg){ function updatePublishStatus(msg){
document.getElementById('publish-status').innerHTML = msg; document.getElementById('publish-status').innerHTML = msg;
} }
// validation function which checks the proposed file's type, size, and name
function validateFile(file) {
if (!file) {
throw new Error('no file provided');
}
// validate size and type
switch (file.type) {
case 'image/jpeg':
case 'image/jpg':
case 'image/png':
case 'image/gif':
if (file.size > 10000000){
throw new Error('Sorry, images are limited to 10 megabytes.');
}
break;
case 'video/mp4':
if (file.size > 50000000){
throw new Error('Sorry, videos are limited to 50 megabytes.');
}
break;
default:
throw new Error(file.type + ' is not supported a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.')
}
// validate the file name (note: different from the lbry claim name)
var invalidCharacter = /[^\w.-\s()]/g.exec(file.name);
if (invalidCharacter) {
throw new Error('Special characters, such as "' + invalidCharacter + '", are not allowed in the file name.');
};
}
// validation function that checks to make sure the claim name is not already claimed
function validateClaimName (name) {
var deferred = new Promise(function(resolve, reject) {
// validate the characters in the 'name' field
if (name.length < 1) {
reject(new NameError("You must enter a name for your claim"));
return;
}
var invalidCharacters = /[^A-Za-z0-9,-]/g.exec(name);
if (invalidCharacters) {
reject(new NameError('"' + invalidCharacters + '" is not allowed. Use only the following characters: A-Z, a-z, 0-9, and "-"'));
return;
}
// make sure the claim name is still available
var xhttp;
xhttp = new XMLHttpRequest();
xhttp.open('GET', '/api/isClaimAvailable/' + name, true);
xhttp.responseType = 'json';
xhttp.onreadystatechange = function() {
if (this.readyState == 4 ) {
if ( this.status == 200) {
if (this.response == true) {
resolve();
} else {
reject( new NameError("That name has already been claimed by spee.ch. Please choose a different name."));
}
} else {
reject("request to check claim name failed with status:" + this.status);
};
}
};
xhttp.send();
});
return deferred;
}
// validation function which checks all aspects of the publish submission
function validateSubmission(stagedFiles, name){
var deferred = new Promise(function (resolve, reject) {
// make sure only 1 file was selected
if (!stagedFiles) {
reject(new FileError("Please select a file"));
} else if (stagedFiles.length > 1) {
reject(new FileError("Only one file is allowed at a time"));
}
// validate the file's name, type, and size
try {
validateFile(stagedFiles[0]);
} catch (error) {
reject(error);
}
// make sure the claim name has not already been used
validateClaimName(name)
.then(function() {
resolve();
})
.catch(function(error) {
reject(error);
})
});
return deferred;
}
/* publish helper functions */ /* publish helper functions */
@ -115,13 +25,14 @@ function previewAndStageFile(selectedFile){
previewReader.onloadend = function () { previewReader.onloadend = function () {
dropzone.style.display = 'none'; dropzone.style.display = 'none';
previewHolder.style.display = 'block'; previewHolder.style.display = 'block';
previewHolder.innerHTML = '<img width="100%" src="' + previewReader.result + '" alt="image preview"/>'; previewHolder.innerHTML = '<img width="100%" src="' + previewReader.result + '" alt="image preview"/>';
}; };
} }
// set the name input value to the image name if none is set yet // set the name input value to the image name if none is set yet
if (nameInput.value === "") { if (nameInput.value === "") {
var filename = selectedFile.name.substring(0, selectedFile.name.indexOf('.')) var filename = selectedFile.name.substring(0, selectedFile.name.indexOf('.'))
nameInput.value = filename.replace(/\s+/g, '-');; nameInput.value = cleanseClaimName(filename);
checkClaimName(nameInput.value);
} }
// store the selected file for upload // store the selected file for upload
stagedFiles = [selectedFile]; stagedFiles = [selectedFile];

View file

@ -0,0 +1,120 @@
// validation function which checks the proposed file's type, size, and name
function validateFile(file) {
if (!file) {
throw new Error('no file provided');
}
// validate size and type
switch (file.type) {
case 'image/jpeg':
case 'image/jpg':
case 'image/png':
case 'image/gif':
if (file.size > 10000000){
throw new Error('Sorry, images are limited to 10 megabytes.');
}
break;
case 'video/mp4':
if (file.size > 50000000){
throw new Error('Sorry, videos are limited to 50 megabytes.');
}
break;
default:
throw new Error(file.type + ' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.')
}
}
// validation function that checks to make sure the claim name is not already claimed
function isNameAvailable (name) {
var deferred = new Promise(function(resolve, reject) {
// make sure the claim name is still available
var xhttp;
xhttp = new XMLHttpRequest();
xhttp.open('GET', '/api/isClaimAvailable/' + name, true);
xhttp.responseType = 'json';
xhttp.onreadystatechange = function() {
if (this.readyState == 4 ) {
if ( this.status == 200) {
if (this.response == true) {
resolve();
} else {
reject( new NameError("That name has already been claimed by another user. Please choose a different name."));
}
} else {
reject("request to check claim name failed with status:" + this.status);
};
}
};
xhttp.send();
});
return deferred;
}
// validation function that checks to make sure the claim name is valid
function validateClaimName (name) {
// ensure a name was entered
if (name.length < 1) {
throw new NameError("You must enter a name for your claim");
}
// validate the characters in the 'name' field
const invalidCharacters = /[^A-Za-z0-9,-]/g.exec(name);
if (invalidCharacters) {
throw new NameError('"' + invalidCharacters + '" characters are not allowed in the title.');
}
}
function cleanseClaimName(name) {
name = name.replace(/\s+/g, '-'); // replace spaces with dashes
name = name.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-'
return name;
}
// validaiton function to check claim name as the input changes
function checkClaimName(name){
try {
// check to make sure the characters are valid
validateClaimName(name);
clearError('input-error-claim-name');
// check to make sure it is availabe
isNameAvailable(name)
.then(function() {
document.getElementById('claim-name-available').hidden = false;
})
.catch(function(error) {
document.getElementById('claim-name-available').hidden = true;
showError('input-error-claim-name', error.message);
});
} catch (error) {
showError('input-error-claim-name', error.message);
document.getElementById('claim-name-available').hidden = true;
}
}
// validation function which checks all aspects of the publish submission
function validateSubmission(stagedFiles, name){
var deferred = new Promise(function (resolve, reject) {
// make sure only 1 file was selected
if (!stagedFiles) {
reject(new FileError("Please select a file"));
} else if (stagedFiles.length > 1) {
reject(new FileError("Only one file is allowed at a time"));
}
// validate the file's name, type, and size
try {
validateFile(stagedFiles[0]);
} catch (error) {
reject(error);
}
// make sure the claim name has not already been used
try {
validateClaimName(name);
} catch (error) {
reject(error);
}
isNameAvailable(name)
.then(function() {
resolve();
})
.catch(function(error) {
reject(error);
});
});
return deferred;
}

View file

@ -11,6 +11,7 @@
<script src="/socket.io/socket.io.js"></script> <script src="/socket.io/socket.io.js"></script>
<script src="/siofu/client.js"></script> <script src="/siofu/client.js"></script>
<script src="/assets/js/generalFunctions.js"></script> <script src="/assets/js/generalFunctions.js"></script>
<script src="/assets/js/validationFunctions.js"></script>
<script src="/assets/js/publishFunctions.js"></script> <script src="/assets/js/publishFunctions.js"></script>
<script typ="text/javascript"> <script typ="text/javascript">
// define variables // define variables
@ -58,10 +59,5 @@
document.getElementById('publish-active-area').innerHTML = publishResults; document.getElementById('publish-active-area').innerHTML = publishResults;
window.location.href = showUrl; window.location.href = showUrl;
}); });
//event listener to filter claim name inputs
document.getElementById('claim-name-input').addEventListener('input', function() {
var name = this.value;
this.value = name.replace(/\s+/g, '-');
})
</script> </script>

View file

@ -1,21 +1,19 @@
<div class="panel"> <div class="panel">
<h2>Publish</h2> <h2>Publish</h2>
<div class="col-left"> <div class="col-left" id="file-selection-area">
<div id="drop-zone" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)"> <div id="drop-zone" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)">
<p>Drag and drop your file here, or choose your file below.</p> <p>Drag and drop your file here, or choose your file below.</p>
<div class="input-error" id="input-error-file-selection" hidden="true"></div> <div class="input-error" id="input-error-file-selection" hidden="true"></div>
<input type="file" id="siofu_input" name="file" accept="video/*,image/*" onchange="previewAndStageFile(event.target.files[0])" enctype="multipart/form-data"/> <input type="file" id="siofu_input" name="file" accept="video/*,image/*" onchange="previewAndStageFile(event.target.files[0])" enctype="multipart/form-data"/>
</div> </div>
<div id="asset-preview-holder"> <div id="asset-preview-holder"></div>
</div>
</div> </div>
<div class="col-right"> <div class="col-right">
<textarea id="direct-link-holder" hidden="true">No URL yet</textarea>
<div id="publish-active-area"> <div id="publish-active-area">
<div class="input-error" id="input-error-claim-name" hidden="true"></div> <div class="input-error" id="input-error-claim-name" hidden="true"></div>
<div class="claim-name-input-area"> <div class="claim-name-input-area">
Spee.ch/<input type="text" id="claim-name-input" placeholder="your-name-here" class="form-control"> Spee.ch/<input type="text" id="claim-name-input" placeholder="your-title-here" oninput="checkClaimName(event.target.value)">
<span id="claim-name-available" hidden="true" style="color: green">&#x2714</span>
</div> </div>
<p class="stop-float"> <p class="stop-float">
<label for="publish-license">License:</label> <label for="publish-license">License:</label>
@ -29,10 +27,34 @@
</p> </p>
<p> <p>
<div class="input-error" id="input-error-publish-submit" hidden="true"></div> <div class="input-error" id="input-error-publish-submit" hidden="true"></div>
<button id="publish-submit" onclick="publishSelectedImage(event)">Publish</button> <button id="publish-submit" onclick="publishSelectedImage(event.target.value)">Publish</button>
<a href="/"><button id="publish-reset">Reset</button></a> <button onclick="resetPublishArea()">Reset</button>
</p> </p>
<p><i>By clicking 'Publish' I attest that I have read and agree to the <a href="https://lbry.io/termsofservice" target="_blank">LBRY terms of service</a>.</i></p> <p><i>By clicking 'Publish' I attest that I have read and agree to the <a href="https://lbry.io/termsofservice" target="_blank">LBRY terms of service</a>.</i></p>
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript" >
function resetPublishArea (){
// reset file selection area
document.getElementById('file-selection-area').innerHTML = `<div id="drop-zone" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)">
<p>Drag and drop your file here, or choose your file below.</p>
<div class="input-error" id="input-error-file-selection" hidden="true"></div>
<input type="file" id="siofu_input" name="file" accept="video/*,image/*" onchange="previewAndStageFile(event.target.files[0])" enctype="multipart/form-data"/>
</div>
<div id="asset-preview-holder"></div>`;
// reset
document.getElementById('claim-name-input').value = '';
// remove staged files
stagedFiles = null;
// clear any errors
document.getElementById('input-error-file-selection').innerHTML = '';
document.getElementById('input-error-claim-name').innerHTML = '';
document.getElementById('input-error-publish-submit').innerHTML = '';
// remove nsfw check
document.getElementById('claim-name-available').hidden = true;
// remove nsfw check
document.getElementById('publish-nsfw').checked = false;
}
</script>

View file

@ -7,13 +7,13 @@
{{#unless this.nsfw}} {{#unless this.nsfw}}
<a href="/{{this.name}}/{{this.claimId}}"> <a href="/{{this.name}}/{{this.claimId}}">
{{#ifConditional this.fileType '===' 'video/mp4'}} {{#ifConditional this.fileType '===' 'video/mp4'}}
<video class="grid-item trending-video" controls onloadeddata="resetLayout()"> <video class="grid-item trending-video" controls onloadeddata="resetTrendingLayout()">
<source src="/media/{{this.fileName}}" > <source src="/media/{{this.fileName}}" >
{{!--fallback--}} {{!--fallback--}}
Your browser does not support the <code>video</code> element. Your browser does not support the <code>video</code> element.
</video> </video>
{{else}} {{else}}
<img class="grid-item trending-image" src="/media/{{this.fileName}}" onload="resetLayout()"/> <img class="grid-item trending-image" src="/media/{{this.fileName}}" onload="resetTrendingLayout()"/>
{{/ifConditional}} {{/ifConditional}}
</a> </a>
{{/unless}} {{/unless}}
@ -29,8 +29,7 @@
itemSelector: '.grid-item' itemSelector: '.grid-item'
}); });
function resetLayout() { function resetTrendingLayout() {
console.log('resetting layout');
msnry.layout(); msnry.layout();
} }
</script> </script>