ESPAsyncWebServer request->send_P problem - esp32

I am doing a simple example using ESPAsyncWebServer on ESP32. In this context I wrote a html file (there are a slider and a button) and tested it on a browser until the content look well. Then I integrate it in the C++ source code for ESP32. It works and look as expected until I don't add a callback to read the value of the slider.
This is the complete source code:
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
// Replace with your network credentials
const char ssid[] = "Vodafone-A40881218";
const char pswd[] = "rJbFMktHCcqN67Ye";
const int output = 2;
String sliderValue = "0";
// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;
const char* PARAM_INPUT = "value";
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
#if 0
const char old_index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP Web Server</title>
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 2.3rem;}
p {font-size: 1.9rem;}
body {max-width: 400px; margin:0px auto; padding-bottom: 25px;}
.slider { -webkit-appearance: none; margin: 14px; width: 360px; height: 25px; background: #FFD65C; outline: none; -webkit-transition: .2s; transition: opacity .2s;}
.slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background: #003249; cursor: pointer;}
.slider::-moz-range-thumb { width: 35px; height: 35px; background: #003249; cursor: pointer; }
button { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:20%;color:white;font-size:130%; }
.buttons { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:15%;color:white;font-size:80%; }
.buttonsm { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:9%; color:white;font-size:70%; }
.buttonm { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:15%;color:white;font-size:70%; }
.buttonw { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:40%;color:white;font-size:70%; }
.buttong { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:40%;color:white;font-size:130%; }
</style>
</head>
<body>
<h2>ESP Web Server</h2>
<p><span id="textSliderValue">%SLIDERVALUE%</span></p>
<p><input type="range" onchange="updateSliderPWM(this)" id="pwmSlider" min="0" max="21" value="%SLIDERVALUE%" step="1" class="slider"></p>
<a href='/setup'><button class='button'>SETUP</button></a>
<script>
function updateSliderPWM(element) {
var sliderValue = document.getElementById("pwmSlider").value;
document.getElementById("textSliderValue").innerHTML = sliderValue;
console.log(sliderValue);
var xhr = new XMLHttpRequest();
xhr.open("GET", "/slider?value="+sliderValue, true);
xhr.send();
}
</script>
</body>
</html>
)rawliteral";
#endif
const char index_html[] = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP Web Server</title>
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 2.3rem;}
p {font-size: 1.9rem;}
body {max-width: 400px; margin:0px auto; padding-bottom: 25px;}
.slider { width: 360px; }
.slider::-webkit-slider-thumb { width: 50px; height: 50px; }
.slider::-moz-range-thumb { width: 50px; height: 50px; }
button { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:40%;color:white;font-size:130%; }
.buttons { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:15%;color:white;font-size:80%; }
.buttonx { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:9%; color:white;font-size:70%; }
.buttonm { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:15%;color:white;font-size:70%; }
.buttonw { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:40%;color:white;font-size:70%; }
.buttong { border-radius:0.5em;background:#C20000;padding:0.3em 0.3em;width:40%;color:white;font-size:130%; }
</style>
</head>
<body>
<h2>ESP Web Server</h2>
<p><span id="textSliderValue">%SLIDERVALUE%</span></p>
<p><input type="range" onchange="updateSliderPWM(this)" id="pwmSlider" min="0" max="21" value="%SLIDERVALUE%" step="1" class="slider"></p>
<a href='/setup'><button class='button'>SETUP</button></a>
<script>
function updateSliderPWM(element) {
var sliderValue = document.getElementById("pwmSlider").value;
document.getElementById("textSliderValue").innerHTML = sliderValue;
console.log(sliderValue);
var xhr = new XMLHttpRequest();
xhr.open("GET", "/slider?value="+sliderValue, true);
xhr.send();
}
</script>
</body>
</html>
)rawliteral";
// Replaces placeholder with button section in your web page
String processor(const String& var)
{
//Serial.println(var);
if (var == "SLIDERVALUE"){
return sliderValue;
}
return String();
}
#include <Preferences.h>
Preferences Pref;
int32_t g_iVolume = 0;
void setup()
{
// Serial port for debugging purposes
Serial.begin(115200);
Pref.begin("datasetup", false);
g_iVolume = Pref.getInt("volume", 5);
Serial.print("volume="); Serial.println(g_iVolume);
sliderValue = String(g_iVolume);
Pref.end();
// Connect to Wi-Fi
WiFi.begin(ssid, pswd);
Serial.println("Connecting ...");
while (WiFi.status() != WL_CONNECTED)
{ // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above
delay(250);
Serial.print('.');
}
// Print ESP Local IP Address
Serial.println(WiFi.localIP());
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
// Send a GET request to <ESP_IP>/slider?value=<inputMessage>
server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
// GET input1 value on <ESP_IP>/slider?value=<inputMessage>
if (request->hasParam(PARAM_INPUT)) {
inputMessage = request->getParam(PARAM_INPUT)->value();
sliderValue = inputMessage;
int ival = sliderValue.toInt();
Serial.print("ival="); Serial.println(ival);
Pref.begin("datasetup", false);
size_t st = Pref.putInt("volume", ival);
Pref.end();
Serial.print("st="); Serial.println(st);
Pref.begin("datasetup", false);
int vol = Pref.getInt("volume", -1);
Serial.print("volume="); Serial.println(vol);
Pref.end();
}
else {
inputMessage = "No message sent";
}
Serial.println(inputMessage);
request->send(200, "text/plain", "OK");
});
// Start server
server.begin();
}
//------------------------------------------------------------------------
void loop()
{
// put your main code here, to run repeatedly:
}
//------------------------------------------------------------------------
the 1st image is with nullptr instead of processor and the 2nd with processor callback.

I found the problem. The author of ESPAsyncWebServer decided to use the % character as the delimiter for placeholders for the template processor. Unfortunately, the % is quite common in CSS and JavaScript so, writing CSS and JavaScript in HTML file(s) or Strings does not work as expected because the wrong interpretation of % as delimiters of chuncks of text that are not placeholders but CSS or JavaScript code. At the moment there is not a workaround, just do not use % in CSS and JavaScript.

I had the same problem and I fixed it by changing the delimiter character (%) to a ($).
To do this you have to modify the definition of "TEMPLATE_PLACEHOLDER" which is in the file "ESPAsyncWebServer\src\WebResponseImpl.h"
#ifndef TEMPLATE_PLACEHOLDER
#define TEMPLATE_PLACEHOLDER '$' <<<<
#endif

Related

How to send data to multiple clients at once with ESP8266 on AP mode

So i have an ESP8266 (ESP-01) on AP mode and is working fairly good enough. I can receive data from the client and have used AJAX to handle the data. When connected to a single client, if i send data from the serial it shows up on the client webpage and is working as expected. But when i connect multiple clients, only the last connected client gets the data and the older ones remain static. Is there a way to send the data to multiple clients at once?
Here's the code.
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
const byte DNS_PORT = 53;
IPAddress apIP(172, 217, 28, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(80);
void handleRoot() {
String html ="<!DOCTYPE html> <html style=\"text-align: center; background-color: #000000; border-style: solid; border-width: 5px; border-color: #FFFFFF;\"> <head> <title>EDNCS</title> <meta name=\"viewport\" content=\"width=device-width, minimumscale=1.0, maximum-scale=1.0, initial-scale=1\" /> </head> <body> <h1 style=\"color: #ff6600;\">Emergency Distributed Network Communication Service</h1> <p style=\"text-align: right; color: #ffff00;\">-Owned and maintained by Wolf Lusana.</p> <div style=\"color: #339966\"> <div> <strong><span style=\"color: #ff0000;\">Name: </span><input type=\"text\" id=\"name\"> <h2 span style=\"color: #ff6600;\">IMPORTANT BROADCASTS</h2> <div id=\"chatRoomDiv\" style=\"border:2px solid #ccc; width:300px; height: 300px; overflow-y: scroll; overscroll-behavior-x:unset; color:#FFFFFF; margin: 0 auto; display: flex; flex-direction: column-reverse;\"> <ul id=\"chatRoomList\" style=\"list-style-type: none; text-align: left;font-size: 14px; padding-left: 3px;\"></ul> </div> <br> <strong><span style=\"color: #ff0000;\">Message: </span> <input type=\"text\" id=\"message\"> <br><br> <button type=\"button\" onclick=\"sendData()\">CHAT</button> <button type=\"button\" >BROADCAST</button> <br> <script> function sendData() { var inputDataCH = document.getElementById(\"message\").value; var nameDataCH = document.getElementById(\"name\").value; var showDataCH = nameDataCH+\": \"+inputDataCH; if(nameDataCH==null||nameDataCH==\" \"||nameDataCH==\"\"||inputDataCH==null||inputDataCH==\" \"||inputDataCH==\"\"){ alert(\"Please enter your name and message. Thank you.\");} else{ var xhttpCH = new XMLHttpRequest(); xhttpCH.open(\"GET\", \"catchData?data=!\"+showDataCH, true); xhttpCH.send(); document.getElementById(\"message\").value = ''} } setInterval(function() { getData(); }, 500); function getData() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { updateChatRoom(this.responseText); } }; xhttp.open(\"GET\", \"throwData\", true); xhttp.send(); } function updateChatRoom(needData){ var ulCH = document.getElementById(\"chatRoomList\"); var liCH = document.createElement(\"li\"); var objDivCH = document.getElementById(\"chatRoomDiv\"); objDivCH.scrollTop = objDivCH.scrollHeight; liCH.appendChild(document.createTextNode(needData)); ulCH.appendChild(liCH); } </script> </html>";
webServer.send(200, "text/html", html);
}
void handleData() {
String dataChat = webServer.arg("data");
if(dataChat[0]=='!'){
dataChat[0] = '%';
Serial.println(dataChat);
}
}
void handleThrow() {
String throwData;
if (Serial.available() > 0) {
throwData = Serial.readString();
}
webServer.send(200, "text/plane", throwData);
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("Emergency Network Service");
dnsServer.start(DNS_PORT, "*", apIP);
webServer.onNotFound([]() {
handleRoot();
});
webServer.on("/",handleRoot);
webServer.on("/catchData", handleData);
webServer.on("/throwData", handleThrow);
webServer.begin();
}
void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}
And here is the HTML code
<!DOCTYPE html>
<html style="text-align: center; background-color: #000000; border-style: solid; border-width: 5px; border-color: #FFFFFF;">
<head>
<title>EDNCS</title>
<meta name="viewport" content="width=device-width, minimumscale=1.0, maximum-scale=1.0, initial-scale=1" />
</head>
<body>
<h1 style="color: #ff6600;">Emergency Distributed Network Communication Service</h1>
<p style="text-align: right; color: #ffff00;">-Owned and maintained by Wolf Lusana.</p>
<div style="color: #339966">
<p>Please enter your name before sending your message on the network.</p>
<div>
<strong><span style="color: #ff0000;">Name: </span><input type="text" id="name">
<h2 span style="color: #ff6600;">IMPORTANT BROADCASTS</h2>
<div id="chatRoomDiv" style="border:2px solid #ccc; width:300px; height: 300px; overflow-y: scroll; overscroll-behavior-x:unset; color:#FFFFFF; margin: 0 auto; display: flex;
flex-direction: column-reverse;">
<ul id="chatRoomList" style="list-style-type: none; text-align: left;font-size: 14px; padding-left: 3px;"></ul>
</div>
<br>
<strong><span style="color: #ff0000;">Message: </span> <input type="text" id="message">
<br><br>
<button type="button" onclick="sendData()">CHAT</button> <button type="button" >BROADCAST</button>
<br>
<script>
function sendData() {
var inputDataCH = document.getElementById("message").value;
var nameDataCH = document.getElementById("name").value;
var showDataCH = nameDataCH+": "+inputDataCH;
if(nameDataCH==null||nameDataCH==" "||nameDataCH==""||inputDataCH==null||inputDataCH==" "||inputDataCH==""){
alert("Please enter your name and message. Thank you.");}
else{
var xhttpCH = new XMLHttpRequest();
xhttpCH.open("GET", "catchData?data=!"+showDataCH, true);
xhttpCH.send();
document.getElementById("message").value = ''}
}
setInterval(function() {
getData();
}, 500);
function getData() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
updateChatRoom(this.responseText);
}
};
xhttp.open("GET", "throwData", true);
xhttp.send();
}
function updateChatRoom(needData){
var ulCH = document.getElementById("chatRoomList");
var liCH = document.createElement("li");
var objDivCH = document.getElementById("chatRoomDiv");
objDivCH.scrollTop = objDivCH.scrollHeight;
liCH.appendChild(document.createTextNode(needData));
ulCH.appendChild(liCH);
}
</script>
</html>
Thanks.

Creating a pdf with multiple pages per sheet

Is there a simple way to create pdfs with multiple pages per sheet using PuppeteerSharp (as per the option available when you print a pdf from Chrome) e.g. 1 / 2 / 4 / 6 / 9 / 16 pages per sheet?
There is a trick that I created where the viewport is set to a certain width and height, and then the container div of the page is set to the "contained" height after trial and error.
pdf generation function
public async Task<byte[]> GeneratePDF(string html)
{
var browser = await GetBrowserAsync();
var page = await browser.NewPageAsync();
// Here, we sit the viewport height to match a div height in html
await page.SetViewportAsync(new ViewPortOptions{Width = 720, Height = 1280 });
await page.SetContentAsync(html);
var data = await page.PdfDataAsync(new PdfOptions
{
PrintBackground = true,
Format = PaperFormat.A4,
DisplayHeaderFooter = false,
MarginOptions = new MarginOptions
{
Top = "60px",
Right = "40px",
Bottom = "60px",
Left = "40px"
}
});
return data;
}
private async Task<IBrowser> GetBrowserAsync()
{
var browserOptions = new BrowserFetcherOptions{ Path = AppContext.BaseDirectory };
using var browserFetcher = new BrowserFetcher(browserOptions);
await browserFetcher.DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
string[] args = {"--no-sandbox"};
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = args
});
return browser;
}
Razor page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Test Page</title>
<style>
:root {
--primary-color: #ffffff;
--accent-color: #f2f2f2;
--secondary-color: #5f7cb3;
--text-color: #000;
}
body {
font-family: Helvetica, sans-serif;
}
h1, h2, h3, h4, h5, h6 {
color: var(--secondary-color);
}
.page-height {
min-height: 870px; /*trial and error*/
height: 870px;
max-height: 870px;
padding: 30px 0;
}
</style>
</head>
<body>
<div class="page-height" style="display: flex; flex-direction: column; justify-content: center; align-items: center;">
<img src="data:image/png;base64,BASE64_HERE" alt="logo.png" style="width: auto; height: 80px; padding: 20px 0;"/>
<h2 style="color: var(--text-color);">Title Here/h2>
<h3>Subtitle Here</h3>
</div>
<div class="page-height">
<p>Content of the first page.</p>
</div>
<div class="page-height">
<p>Content of the second page. Etc...</p>
</div>
</body>
</html>
This should do the trick.

Socket.io submitting button

Hey guys im learning socket io on their website, i just dont understand one thing about the event. Where do they say that clicking on the button "send" is a submit action ? I mean i dont see a onclick event or something like this. Thanks ! Here are the codes:
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
$(function () {
var socket = io();
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
});
</script>
</body>
</html>
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket){
socket.on('chat message', function(msg){
console.log('message: ' + msg);
});
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
When the submit event happens the script sends the data with Socket.IO, but not submits the form (return false;). But of course you can use onclick event too.
Socket.IO send:
socket.emit('chat message', $('#m').val());
For example if you have a button with id "testButton":
$('#testButton').click(function() {
socket.emit('chat message', $('#m').val());
});

How to upload socket io chatroom code on cpanel

i have used the code as following
index.html
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var socket = io();
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
</script>
</body>
</html>
index.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket){
socket.on('chat message', function(msg){
io.emit('chat message', msg);
});
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
and then i have to write node index on command prompt and then i can access the chatroom using localhost:3000
but when i upload it to cpanel and open my url the chatroom is not running do i have to do something like putting a command or have to do something with Cpanel?
Thanks
Regards

Magnific Popup with upload image preview

I'm using Magnific popup to display an upload form where the user can select multiple images to upload and preview them before submitting the form, I let the user preview the images plus add a from inputs underneath the image when he clicks on it to enter the caption and alt for it, here's the code that I have ...
(function() {
if ($("a.uploadMediaImageForm").length) {
$("a.uploadMediaImageForm").magnificPopup({
type: 'inline',
preloader: false,
closeOnBgClick: false,
enableEscapeKey: false,
focus: '#name',
removalDelay: 500, //delay removal by X to allow out-animation
// When elemened is focused, some mobile browsers in some cases zoom in
// It looks not nice, so we disable it:
callbacks: {
beforeOpen: function() {
if ($(window).width() < 700) {
this.st.focus = false;
} else {
this.st.focus = '#name';
}
this.st.mainClass = this.st.el.attr('data-effect');
},
open: function() {
if ($("input#imageUpload").length) {
$("input#imageUpload").on('change', function() {
//Get count of selected files
var countFiles = $(this)[0].files.length;
var imgPath = $(this)[0].value;
var extension = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
var previewHolder = $("ul.imagePreview");
previewHolder.empty();
if (extension == "gif" || extension == "png" || extension == "jpg" || extension == "jpeg") {
if (typeof(FileReader) != "undefined") {
//loop for each file selected for uploaded.
for (var i = 0; i < countFiles; i++) {
var reader = new FileReader();
reader.onload = function(e) {
$("<li><img src='" + e.target.result +"'></li>").appendTo(previewHolder);
}
previewHolder.show();
reader.readAsDataURL($(this)[0].files[i]);
}
} else {
alert("This browser does not support FileReader.");
}
} else {
alert("Please select only images");
}
});
} //Image upload preview
if($("ul.imagePreview").length) {
$("ul.imagePreview").on("click", "li", function(event) {
if($(this).hasClass("selected")) {
$(this).removeClass("selected");
$(this).find("div").remove();
} else {
$(this).addClass("selected");
$(this).append("<div><label><span>Image Alt</span><input type='text'></label><label><span>Image Caption</span><input type='text'></label></div>");
}
});
$("ul.imagePreview").on("click", "div", function(event) {
event.stopPropagation();
});
}//add form when clicked on an image
},
beforeClose: function() {
// $("ul.imagePreview").empty();
var countFiles = "";
var imgPath = "";
var extension = "";
var previewHolder = $("ul.imagePreview");
previewHolder.empty();
}
},
midClick: true // allow opening popup on middle mouse click. Always set
});
}
})(); //popup Forms and Uploads
div.uploadPopup {
width: 80%;
margin: auto;
background: white;
position: relative;
padding: 40px;
}
label {
width: 100%;
margin-bottom: 20px;
clear: both;
}
ul.imagePreview {
width: 100%;
clear: both;
display: block;
}
ul.imagePreview li {
width: 100%;
display: block;
margin-bottom: 20px;
max-height: 150px;
cursor: pointer;
}
ul.imagePreview li.selected {
max-height: auto;
}
ul.imagePreview li img {
max-height: 150px;
display: block;
margin: auto;
}
<link href="https://cdn.jsdelivr.net/jquery.magnific-popup/1.0.0/magnific-popup.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/1.0.1/jquery.magnific-popup.min.js"></script>
Upload Media
<div id="uploadMediaImageForm" class="uploadPopup mfp-with-anim mfp-hide">
<form action="#">
<label class="upload">
<span>Upload Images</span>
<input id="imageUpload" type="file" multiple>
</label>
<ul class="imagePreview">
</ul>
</form>
</div>
Now everything works fine the first time, but when I close the popup and re-open it again, something wrong happens in the image previewer, it duplicates the images I choose and sometimes doesn't even show the image if it were the last image I choose before closing.
I tried to set all the variables before closing the popup and clear the image preview ul element, but that didn't help.
I need your help guys, what am I doing wrong here?
EDIT
I gave the form itself an id of "fileForm" and tried to reset the whole form and empty the ul.imagePreview before closing the popup with this code ...
$("#fileForm")[0].reset();
$("ul.imagePreview").empty();
But still no luck, it still duplicated any image I upload the second time after closing the popup and opening it again !!
need help here.
You are binding more and more listeners to the same event:
Your form always exists in your document even when the popup is closed, you just hide it most of the time (using the class mfp-hide).
Each time you open the popup, the callback "open" is called, which bind a function to the change event of your input, and this callback do the preview stuff.
But if you open the popup twice, it will bind again the same function to the same event on the same input. That's why you have duplicate: the code is called twice.
Move all your binding outside your callback so that they will be done once:
(function() {
if ($("input#imageUpload").length) {
$("input#imageUpload").on('change', function() {
//Get count of selected files
var countFiles = $(this)[0].files.length;
var imgPath = $(this)[0].value;
var extension = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
var previewHolder = $("ul.imagePreview");
previewHolder.empty();
if (extension == "gif" || extension == "png" || extension == "jpg" || extension == "jpeg") {
if (typeof(FileReader) != "undefined") {
//loop for each file selected for uploaded.
for (var i = 0; i < countFiles; i++) {
var reader = new FileReader();
reader.onload = function(e) {
$("<li><img src='" + e.target.result +"'></li>").appendTo(previewHolder);
}
previewHolder.show();
reader.readAsDataURL($(this)[0].files[i]);
}
} else {
alert("This browser does not support FileReader.");
}
} else {
alert("Please select only images");
}
});
} //Image upload preview
if($("ul.imagePreview").length) {
$("ul.imagePreview").on("click", "li", function(event) {
if($(this).hasClass("selected")) {
$(this).removeClass("selected");
$(this).find("div").remove();
} else {
$(this).addClass("selected");
$(this).append("<div><label><span>Image Alt</span><input type='text'></label><label><span>Image Caption</span><input type='text'></label></div>");
}
});
$("ul.imagePreview").on("click", "div", function(event) {
event.stopPropagation();
});
}//add form when clicked on an image
if ($("a.uploadMediaImageForm").length) {
$("a.uploadMediaImageForm").magnificPopup({
type: 'inline',
preloader: false,
closeOnBgClick: false,
enableEscapeKey: false,
focus: '#name',
removalDelay: 500, //delay removal by X to allow out-animation
// When elemened is focused, some mobile browsers in some cases zoom in
// It looks not nice, so we disable it:
callbacks: {
beforeOpen: function() {
if ($(window).width() < 700) {
this.st.focus = false;
} else {
this.st.focus = '#name';
}
this.st.mainClass = this.st.el.attr('data-effect');
},
beforeClose: function() {
///$("ul.imagePreview").empty();
var countFiles = "";
var imgPath = "";
var extension = "";
var previewHolder = $("ul.imagePreview");
previewHolder.empty();
$("#fileForm")[0].reset();
}
},
midClick: true // allow opening popup on middle mouse click. Always set
});
}
})(); //popup Forms and Uploads
div.uploadPopup {
width: 80%;
margin: auto;
background: white;
position: relative;
padding: 40px;
}
label {
width: 100%;
margin-bottom: 20px;
clear: both;
}
ul.imagePreview {
width: 100%;
clear: both;
display: block;
}
ul.imagePreview li {
width: 100%;
display: block;
margin-bottom: 20px;
max-height: 150px;
cursor: pointer;
}
ul.imagePreview li.selected {
max-height: auto;
}
ul.imagePreview li img {
max-height: 150px;
display: block;
margin: auto;
}
<link href="https://cdn.jsdelivr.net/jquery.magnific-popup/1.0.0/magnific-popup.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/1.0.1/jquery.magnific-popup.min.js"></script>
Upload Media
<div id="uploadMediaImageForm" class="uploadPopup mfp-with-anim mfp-hide">
<form action="#" id="fileForm">
<label class="upload">
<span>Upload Images</span>
<input id="imageUpload" type="file" multiple>
</label>
<ul class="imagePreview">
</ul>
</form>
</div>

Resources