With a QR code vcard, the user scans the code with their phone and then the dialog with the "add to contacts" pops up on their phone, such as the code below:
How can I do the same but instead of a QR code scan, I want it to do the same with a button click.
I have tried the following:
var btn = document.getElementById(“clickMe”);
btn.addEventListener(“click”, loadvcard);
function loadvcard(){
url = "BEGIN%3AVCARD%0AVERSION%3A3.0%0AN%3ADoe%3BJohn%0AFN%3AJohn%20Doe%0ATITLE%3A08002221111%0AORG%3AStackflowover%0AEMAIL%3BTYPE%3DINTERNET%3Ajohndoe%40gmail.com%0AEND%3AVCARD";
window.open(url);
}
You can open your vcard in the browser as a data url if you want.
Your code would be:
var btn = document.getElementById(“clickMe”);
btn.addEventListener(“click”, loadvcard);
function loadvcard(){
var data = "BEGIN%3AVCARD%0AVERSION%3A3.0%0AN%3ADoe%3BJohn%0AFN%3AJohn%20Doe%0ATITLE%3A08002221111%0AORG%3AStackflowover%0AEMAIL%3BTYPE%3DINTERNET%3Ajohndoe%40gmail.com%0AEND%3AVCARD";
window.open("data:text/x-vcard;urlencoded," + data);
}
Try to use the web share api, it works.
<html>
<title>
Web Share API
</title>
<body>
<div>
<div>
<button onclick="shareVcard" id="shareFilesButton">Share Files</button>
</div>
</div>
</body>
<script>
document.getElementById('shareFilesButton').addEventListener("click", () => shareVcard())
function shareVcard() {
fetch("sample.vcf")
.then(function(response) {
return response.text()
})
.then(function(text) {
var file = new File([text], "sample.vcf", {type: 'text/vcard'});
var filesArray = [file];
var shareData = { files: filesArray };
if (navigator.canShare && navigator.canShare(shareData)) {
// Adding title afterwards as navigator.canShare just
// takes files as input
shareData.title = "vcard";
navigator.share(shareData)
.then(() => console.log('Share was successful.'))
.catch((error) => console.log('Sharing failed', error));
} else {
console.log("Your system doesn't support sharing files.");
}
});
}
</script>
</html>
Related
What I am trying to do : I have a cloud page where the user can upload CSV file. When user clicks on the “upload” button the a function called getBase64() is called (please refer the below code). The getBase64() function will encode the uploaded file and post it to a second cloud page.The second cloud page then takes the posted data.
Note: I am trying to adapt this solution to my need (csv file) by referring to this article partially https://sfmarketing.cloud/2020/02/29/create-a-cloudpages-form-with-an-image-file-upload-option/
What’s the problem : When I try to click the the “upload” button the page is not taking me to the second CloudPage. Please could anyone let me know what I am doing wrong here ?
Here is the code:
CloudPage 1
<input id="file" type="file" accept=".csv">
<br>
<button id="button">Upload</button>
<script runat="client">
document.getElementById("button")
.addEventListener("click", function() {
var files = document.getElementById("file").files;
if (files.length > 0) {
getBase64(files[0]);
}
});
function getBase64(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {
//prepare data to pass to processing page
var fileEncoded = reader.result;
var base64enc = fileEncoded.split(";base64,")[1];
var fullFileName = document.getElementById("file").files[0].name;
var fileName = fullFileName.split(".")[0];
var assetName = fullFileName.split(".")[1];
fetch("https://cloud.link.example.com/PAGE2", { //provide URL of the processing page
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
base64enc: base64enc,
fileName: fileName,
assetName: assetName
})
})
.then(function(res) {
window.alert("Success!");
})
.catch(function(err) {
window.alert("Error!");
});
};
reader.onerror = function(error) {
console.log('Error: ', error);
};
}
</script>
CloudPage 2
<script runat="server">
var jsonData = Platform.Request.GetPostData();
var obj = Platform.Function.ParseJSON(jsonData);
</script>
I do not see any errors in the code and when I click on the upload button I get a success message but it does not take me to the second page. Please can anyone guide me how to retrieve this posted data in second page as I am not able to get the encoded data in page 2?
I am trying to load multiple files using jQuery's get function. A minimal example looks like this:
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<select name="file-select" id="file-select" multiple>
<option>http://url.to.file/Test1.txt</option>
<option>http://url.to.file/Test2.txt</option>
</select>
<script>
$(document).ready(function() {
$('#file-select').change(function () {
let loaded_files_list = new Array();
let promise = new Promise(function(myResolve,myReject) {
$("#file-select option:selected").each(function () {
file_name = $(this).val();
$.get($(this).val(), function(response) {
data = response;
// Do something with data
console.log("file select length: "+$("#file-select option:selected").length);
loaded_files_list.push(file_name);
console.log('loaded files length: '+loaded_files_list.length)
if(loaded_files_list.length == $("#file-select option:selected").length) {
console.log('Entered if clause');
myResolve(loaded_files_list);
}
else {
myReject('List is not complete yet');
}
});
});
});
promise.then(
function(value) {
console.log('List is complete!');
console.log(value);
},
function(error) {
console.log(error);
}
);
});
});
</script>
</body>
</html>
Now if I select a single file this works just like expected and I can see the list with a single item in the console. However if I I select both files, the if clause is entered after both files are loaded, but it seems myResolve is not called as neither List is complete! is displayed nor the list with two elements. What am I doing incorrect here?
$(document).ready(() => {
// make this a function that "waits" for the await command when
// used on promises
$('#file-select').change(async () => {
// declare an array that we will use to store our HTTP requests
const filesList = [];
// extract the clicked options
const selectedOpts = $('#file-select').children('option:selected')
// loop over the clicked options
selectedOpts.each((index, opt) => {
// extract the text value from the option
const link = opt.text
// push the HTTP request to the filesList array without
// waiting for the response
filesList.push($.get(link))
})
// wait for the HTTP requests to to complete before
// continuing
const responses = await Promise.all(filesList)
// loaded all files
responses.forEach(data => {
console.log(data) // => file contents
})
});
});
I'm messing around w/ Office Dialog for Add-Ins in JS. I've got it so far where I can open a dialog, capture input to console, run a function from a button and close the dialog box, but I can't seem to get my function to interact with Excel. It's lost context I beleive, I tried using this and I get no errors, but it doesn't work --> var context = new Excel.RequestContext().
Here is my open function and my main function and the end function.
main.js
export async function helloworld(event) {
try {
await Excel.run(async (context) => {
//Start Func
console.log("BEFORE OPEN UI");
openDialog("/yo/dist/dialog.html", 30, 20);
console.log("AFTER OPEN UI");
await context.sync()
.then(function () {
console.log("AFTER SYNC UI");
var ws = context.workbook.worksheets.getActiveWorksheet();
var range = ws.getRange("A1:D5");
range.select();
})
//End Func
await context.sync();
});
} catch (error) {
console.error(error);
}
console.log("EVENT COMPLETEED HELLOW");
//event.completed();
}
open.js
function openDialog(RelURLStr, H, W) {
Office.context.ui.displayDialogAsync(window.location.origin + RelURLStr,
{ height: H, width: W }, dialogCallback);
}
run func //this gets ran, but nothing output to worksheet and no errors.
function dobuttonrun(event) {
console.log("ENDING");
var context = new Excel.RequestContext()
var ws = context.workbook.worksheets.getActiveWorksheet();
var fakedatarng = ws.getRange("A1");
fakedatarng.values = "TEST";
return context.sync();
event.completed();
}
function getGlobal() {
return typeof self !== "undefined"
? self
: typeof window !== "undefined"
? window
: typeof global !== "undefined"
? global
: undefined;
}
const g = getGlobal();
// The add-in command functions need to be available in global scope
g.dobuttonrun = dobuttonrun;
Opening a popup has UX issues on web clients. If you need to support excel web, it will ask the user a confirmation for the dialog each time and cannot by bypassed; Excel is asking not the browser!
I strongly suggest you, to use a Popup from your UX framework. If you are using Fluent UI, the microsoft suggested framework that keeps look and feel of office please refer to this page.
In case that you need a dialog solution, keep in mind that you are running a new page /yo/dist/dialog.html you'll loose all the variables and context from your parent webpage:
If you are using any SPA javascript framework like react.js or VUE.js, another clean app will be rendered.
BTW You can achieve your need implementing a simple communication as follows:
// Office.Dialog type
let dialog;
export async function helloworld(event) {
Office.context.ui.displayDialogAsync(
'/yo/dist/dialog',
{ promptBeforeOpen: true, height: 70, width: 50, displayInIframe: false },
(result) => {
if (result.status === Office.AsyncResultStatus.Failed) {
// unable to manage status
throw new Error(result.error.message)
}
else {
dialog = result.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived, messageHandler);
}
}
)
}
/**
* Assign the project to an employee.
* #param {Object} arg - Event handler args
* #param {string} arg.message - Message as string (NOT js OBJECT)
* #param {string} arg.origin - Message Origin
*/
const messageHandler = (arg) => {
// messaging can send only strings.
// If you need to pass complex JSON you need to stringify and parse objects
const message = JSON.parse(arg.message)
if (message.type === "execution-canceled") {
// you have all props of message like message.reason
dialog.close();
return
}
if (message.type === "execution-confirmed") {
// execute than close popup
// if you need to close popup than execute, just run dialog.close()
// before doStuff() and remove finally method call
doStuff()
.then(() => console.log("EVENT COMPLETEED HELLOW"))
.catch(error => console.error(error))
.finally(() => dialog.close())
}
}
const doStuff = async () => Excel.run(async (context) => {
// you don't need context.sync() before interacting excel
var ws = context.workbook.worksheets.getActiveWorksheet();
var range = ws.getRange("A1:D5");
range.select();
await context.sync();
})
On your dialog page you need to wait for Office.initialize in order to use Office.context.ui.messaging apis, than you can simply send your object.
const send = (message) => {
Office.context.ui.messageParent(JSON.stringify(message));
}
Office.initialize = () => {
// render page or activate button
}
const onExecutionConfirmed = () => send({ type: 'execution-confirmed' })
const onExcecutionCanceled = () => send({
type: 'execution-canceled',
reason: "additional infos can be placed in any props of this serialized JSON object"
})
Here is what I finished with after #CLAudio detailed answer.
commands.js
Office.onReady(() => {
// If needed, Office.js is ready to be called
});
export async function helloworld(event) {
try {
await Excel.run(async (context) => {
//Start Func
openDialog("/yo/dist/dialog.html", 30, 20);
//End Func
await context.sync();
});
} catch (error) {
console.error(error);
}
event.completed();
}
const doStuff = async () => Excel.run(async (context) => {
// you don't need context.sync() before interacting excel
var ws = context.workbook.worksheets.getActiveWorksheet();
var range = ws.getRange("A1:D5");
range.select();
await context.sync();
})
//////////////////////////////////////////
let dialog;
function openDialog(HTMLUrl, H, W) {
Office.context.ui.displayDialogAsync(window.location.origin + HTMLUrl, { promptBeforeOpen: true, height: H, width: W, displayInIframe: false },
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
// In addition to general system errors, there are 3 specific errors for
// displayDialogAsync that you can handle individually.
switch (asyncResult.error.code) {
case 12004:
console.log("Domain is not trusted");
break;
case 12005:
console.log("HTTPS is required");
break;
case 12007:
console.log("A dialog is already opened.");
break;
default:
console.log(asyncResult.error.message);
break;
}
} else {
dialog = asyncResult.value;
/*Messages are sent by developers programatically from the dialog using office.context.ui.messageParent(...)*/
dialog.addEventHandler(Office.EventType.DialogMessageReceived, messageHandler);
/*Events are sent by the platform in response to user actions or errors. For example, the dialog is closed via the 'x' button*/
dialog.addEventHandler(Office.EventType.DialogEventReceived, eventHandler);
}
}
)
}
function eventHandler(arg) {
// In addition to general system errors, there are 2 specific errors
// and one event that you can handle individually.
switch (arg.error) {
case 12002:
console.log("Cannot load URL, no such page or bad URL syntax.");
break;
case 12003:
console.log("HTTPS is required.");
break;
case 12006:
// The dialog was closed, typically because the user the pressed X button.
console.log("Dialog closed by user");
break;
default:
console.log("Undefined error in dialog window");
break;
}
}
/**
* Assign the project to an employee.
* #param {Object} arg - Event handler args
* #param {string} arg.message - Message as string (NOT js OBJECT)
* #param {string} arg.origin - Message Origin
*/
const messageHandler = (arg) => {
// messaging can send only strings.
// If you need to pass complex JSON you need to stringify and parse objects
const message = JSON.parse(arg.message)
console.log("message:")
console.log(message)
if (message.type === "execution-canceled") {
// you have all props of message like message.reason
dialog.close();
return
}
if (message.type === "execution-confirmed") {
// execute than close popup
// if you need to close popup than execute, just run dialog.close()
// before doStuff() and remove finally method call
doStuff()
.then(() => console.log("doStuff .then"))
.catch(error => console.error(error))
.finally(() => dialog.close())
}
}
//////////////////////////////////////////
dialog.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Contoso Task Pane Add-in</title>
<script src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
<!-- For the Office UI Fabric, go to http://aka.ms/office-ui-fabric to learn more. -->
<link rel="stylesheet" href="https://appsforoffice.microsoft.com/fabric/2.1.0/fabric.min.css">
<link rel="stylesheet" href="https://appsforoffice.microsoft.com/fabric/2.1.0/fabric.components.min.css">
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.1.min.js"></script>
<script defer="defer" src="dialog.js"></script>
</head>
<body>
<p class="ms-font-xxl ms-fontColor-neutralSecondary ms-fontWeight-semilight">Pick a number</p>
<button class="ms-Button ms-Button--primary" id="run">
<span class="ms-Button-icon"><i class="ms-Icon ms-Icon--plus"></i></span>
<span class="ms-Button-label" id="runbutton-text">RUN</span>
<span class="ms-Button-description" id="runbutton-desc">Run Func</span>
</button>
<button class="ms-Button ms-Button--primary" id="close">
<span class="ms-Button-icon"><i class="ms-Icon ms-Icon--plus"></i></span>
<span class="ms-Button-label" id="closebutton-text">EXIT</span>
<span class="ms-Button-description" id="closebutton-desc">Exit Func</span>
</button>
<form>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname">
</form>
</body>
</html>
dialog.js
Office.initialize = () => {
document.getElementById("run").onclick = onExecutionConfirmed;
document.getElementById("close").onclick = onExcecutionCanceled;
}
const send = (message) => {
Office.context.ui.messageParent(JSON.stringify(message));
}
const onExecutionConfirmed = () => send({ type: 'execution-confirmed' })
const onExcecutionCanceled = () => send({
type: 'execution-canceled',
reason: "additional infos can be placed in any props of this serialized JSON object"
})
Good evening guys,
I program a symfony website by using webpack encore bundle to manage js & css.
I used to work with jquery which is quite simple, but would like to evolve to pure javascript.
I try to translate the following code in javascript :
<html>
<button class="exercice-class" data-id="x">exercice button</button>
</html>
when an user click on the "exercice button", i want to get the value of data-id to generate an URL
<script>
$(function() {
$('.exercice-class').on("click", function(e) {
e.preventDefault();
let id = $(this).data("id");
let url = "../exercice-class/" + id + "/";
$.get(url, function(data){
$(".container-fluid").append(data);
$('#showModal').modal('show');
});
});
});
</script>
Then i get the content of the URL and add it to the modal window
What I want to do first is to open a modal window by using a variable as a parameter.
Second question, I would like to get data from a modal (using a form) and send them to a database. I read things about asynchronous request by it's not really clear for me, i'm looking for something close to ajax request.
Thank you in advance.!
Juuk
here is a small example, you can test it
var classbutton = document.querySelector('.exercice-class');
classbutton.addEventListener('click', (e) => {
e.preventDefault();
let id = element.getAttribute('id');
let url = "../exercice-class/" + id + "/";
let requete = new XMLHttpRequest();
requete.open('GET', url);
requete.send();
requete.onload = function() {
if (requete.readyState === XMLHttpRequest.DONE) {
if (requete.status === 200) {
let reponse = requete.response;
document.querySelector('.container-fluid').append(reponse);
document.querySelector('#showModal').showModal();
}
else {
}
}
}
});
Thanks for your response! i tried your code and work on it...
This is what i did :
let httpRequest = new XMLHttpRequest();
httpRequest.open("GET", url);
httpRequest.send();
httpRequest.onload = function (){
if (httpRequest.readyState === XMLHttpRequest.DONE){
if (httpRequest.status === 200){
let httpResponse = httpRequest.response;
console.log(httpResponse);
}
}
}
It seems that fetch is a newer way to work with data since vanilla.
I did the same thing we tried to do before and i succeeded to get data.
document.addEventListener('click', function (event) {
if (!event.target.closest('.exercice-class')){
return null;
}
else {
event.preventDefault();
let exercice = event.target.closest(".exercice-class");
let dataAttribute = exercice.getAttribute('data-id');
let url = "../exercice-class/" + dataAttribute + "/";
fetch(url)
.then(function (response) {
return response.text();
})
.then(function (data) {
console.log(data)
})
.catch(function (error) {
console.log(error);
});
}
});
In reality i get the same result with the two solutions.. the problem is that i get "data" i can't exploit...
Imagine i use the second example :
if i try to do :
.then(function (data) {
let exerciceData = data.getElementById("#adiv");
document.querySelector('container-fluid').append(exerciceData);
document.querySelector('showModal').show();
"exerciceData" can't be used.
Modal just don't open.
Thank for your help.
I implemented a PRG pattern for form submit.
And on post form submit request. I redirect the page to custom defined rediret Page as given
<!DOCTYPE html>
<html>
<head>
<meta http-equiv = "refresh" content = "2; url =https://script.google.com/macros/s/AKfycbzSSFEHTowTqAqHabGhtipC0fsoSvaQWfV0uF34Igf2/dev?dest=saveClsTchRvwClsDtl &token=44" />
<title>HTML Meta Tag</title>
</head>
<body>
<p>This is demo redirect text.</p>
</body>
</html>
But when page reloads and I refresh the redirect Page again it again submit the post form submitted earlier.
Even when after redirect to when i generated the result page which got generated by GET request on refresh of the result page it submits post request for form submit.
Entire PRG pattern gets failed.
Below is the google App Script Code
var PostRoute = {};
PostRoute.path = function (route, callback) {
PostRoute[route] = callback;
};
PostRoute.path("saveClsStdDtl", saveClsStudentDetails);
function doPost(e) {
try {
Logger.log("POST request");
Logger.log(e);
if (PostRoute[e.parameter.dest]) {
PostRoute[e.parameter.dest](e);
} else {
return homePage(e);
}
return redirect(e);
} catch (err) {
Logger.log(err);
}
}
function doGet(e) {
if (e.parameter.token != null) {
receivedTokenValue(e.parameter.token);
}
var token = generateTokenValue(e);
e.parameter.token = token;
e.parameters.token = [token];
Logger.log(e);
Route.path("clsDtl", tchSubmitClassDetails);
Route.path("saveClsTchRvwClsDtl", homePage);
if (Route[e.parameter.dest]) {
return Route[e.parameter.dest](e);
} else {
return homePage(e);
}
}
function redirect(e) {
Logger.log("redirect : " + e.parameter);
return renderPage("RedirectPage", {});
}
function renderPage(filename, varObjects) {
var tmp = HtmlService.createTemplateFromFile(filename);
var keys = Object.keys(varObjects);
if (varObjects) {
keys.forEach(function (key) {
Logger.log(key);
Logger.log(varObjects[key]);
tmp[key] = varObjects[key];
})
}
return tmp.evaluate();
}
function saveClsTchRvwStudentDetails(e) {
Logger.log("Inside saveClsTchRvwStudentDetails");
}