Problems displaying image in Tinymce text editor with JS - tinymce

I have the code, the image is uploaded and returns me the URL of the image that is saved next to my server, the problem that always throws me the following error and I can not understand the error or where is the fall.
var tin = tinymce.init({
selector: 'textarea#description',
height: 350,
menubar: true,
language: 'es',
plugins: ['image', 'advlist', 'autolink', 'lists', 'link', 'charmap', 'preview', 'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen', 'insertdatetime', 'media', 'table', 'help', 'wordcount'],
toolbar: 'undo redo | image | blocks | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table | removeformat',
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
branding: false,
relative_urls: false,
images_upload_handler: function (blobInfo, success, failure){
var xhr, formData;
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST','{{ route("upload_image_no_attach") }}');
var token = '{{ csrf_token() }}';
xhr.setRequestHeader("X-CSRF-TOKEN",token);
xhr.onload = function() {
// var json;
// if (xhr.status != 200) {
// console.log('HTTP Error: ' + xhr.status);
// return;
// }
// json = JSON.parse(xhr.responseText);
// if (!json || typeof json.location != 'string') {
// console.log('Invalid JSON: ' + xhr.responseText);
// return;
// }
// success(json.location);
if (xhr.status === 200) {
// this is callback data: url
const url = JSON.parse(xhr.responseText);//.data;
const location = `${window.location.origin}/opportunity/image/${url}`;
console.log(location);
success(location)
}
};
formData = new FormData();
formData.append('image',blobInfo.blob(), blobInfo.filename());
xhr.send(formData);
}
});
The console does not bring me any error, only the error is presented from a Tinymce alert.
Controller
public function publishImage(Request $request){
$file_data = File::createFiles($request->all(),null,null);
return json_encode(['location' => $file_data->pathURL()]);
}

Given your error message, the issue appears to be in your implementation of the images_upload_handler option. The implementation used is for TinyMCE 4/5, however given the error message it appears that you're using TinyMCE 6. TinyMCE 6 made some modernization changes and this was one of them whereby it now needs to return a Promise instead of using the success and failure callbacks.
The TinyMCE 6 migration guide and this specific point can be found here: https://www.tiny.cloud/docs/tinymce/6/migration-from-5x/#images_upload_handler. A newer working example can be found here: https://www.tiny.cloud/docs/tinymce/6/file-image-upload/#images_upload_handler
As such, to fix that error you should just need to update your TinyMCE configuration to something like this:
var tin = tinymce.init({
selector: 'textarea#description',
height: 350,
menubar: true,
language: 'es',
plugins: ['image', 'advlist', 'autolink', 'lists', 'link', 'charmap', 'preview', 'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen', 'insertdatetime', 'media', 'table', 'help', 'wordcount'],
toolbar: 'undo redo | image | blocks | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table | removeformat',
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
branding: false,
relative_urls: false,
images_upload_handler: (blobInfo) => {
return new Promise((success, failure) => {
var xhr, formData;
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST','{{ route("upload_image_no_attach") }}');
var token = '{{ csrf_token() }}';
xhr.setRequestHeader("X-CSRF-TOKEN",token);
xhr.onload = function() {
// var json;
// if (xhr.status != 200) {
// console.log('HTTP Error: ' + xhr.status);
// return;
// }
// json = JSON.parse(xhr.responseText);
// if (!json || typeof json.location != 'string') {
// console.log('Invalid JSON: ' + xhr.responseText);
// return;
// }
// success(json.location);
if (xhr.status === 200) {
// this is callback data: url
const url = JSON.parse(xhr.responseText);//.data;
const location = `${window.location.origin}/opportunity/image/${url}`;
console.log(location);
success(location)
}
};
formData = new FormData();
formData.append('image', blobInfo.blob(), blobInfo.filename());
xhr.send(formData);
});
}
});

Related

How to stop cursor flickering over mapbox marker icons?

How do I stop cursor flickering over my Mapbox icons? Here is the live site where you can see the problem when hovering over the marker icons: https://rustic-waters-group.thesparksite.com/lakes/
Here is my code:
mapboxgl.accessToken = 'pk.eyJ1IjoiZG1pdHJpbWFydGluIiwiYSI6ImNreHRobHRmcjVqM3cydmt5NWkxdWNibTcifQ.CuN5Dwc963TW-BKRcowxBA';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/dmitrimartin/ckxtj5aur4fvv15mrhmihala8',
center: [-89.2, 44.33],
zoom: 9.2
});
map.on('load', () => {
const popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
map.on('mouseenter', 'lakes', (event) => {
map.getCanvas().style.cursor = 'pointer';
const features = map.queryRenderedFeatures(event.point, {
layers: ['lakes']
});
if (event.features.length === 0) return;
popup.setLngLat(features[0].geometry.coordinates);
popup.setHTML('<h3 class="lake-popup">' + features[0].properties.title + '</h3>');
popup.setMaxWidth("calc(100vw - 40px);");
popup.addTo(map);
});
map.on('mouseleave', 'lakes', () => {
map.getCanvas().style.cursor = '';
popup.remove();
});
map.on('click', 'lakes', (event) => {
const features = map.queryRenderedFeatures(event.point, {
layers: ['lakes']
});
if (event.features.length === 0) return;
window.location.href = ('/lakes/' + features[0].properties.url + '/');
});
document.getElementById("lakes-header").onmouseover = function() {
MyMouseOver();
};
function MyMouseOver() {
document.getElementById("wrapper").style.display = "none";
document.getElementById("lakes-header").style.display = "none";
}
});
The issue stems from the popup box on top of the layer and can be fixed by adding the CSS property 'cursor-events: none' to the popup. That should stop the flickering and shouldn't interfere with the layers.

Code migration from tinymce 4 to tinymce 5 - problem with action function (true / false)

I have a problem with migrating the plugin from tinymce 4 to tinymka 5. The console tells me "Uncaught (in promise) TypeError: btn.active is not a function"
I can not find an equivalent for tinymce 5. Can someone replace it?
Code below:
tinymce.PluginManager.add('phonelink', function(editor, url) {
// Add a button that opens a window
var linkText = "";
var linkTitle = "";
var link = "";
// tinymce.DOM.loadCSS(url + '/css/phonelink.css');
editor.ui.registry.addButton('phonelink2', {
text: 'asddas',
icon: 'image-options',
onSetup: updateOnSelect,
onAction: onClickPhoneButton
});
// Adds a menu item to the tools menu
editor.ui.registry.addMenuItem('phonelink', {
text: 'asddas',
icon: 'image-options',
context: 'tools',
onAction: onClickPhoneButton,
onSetup: updateOnSelect
});
function onClickPhoneButton(){
editor.windowManager.open({
title: '123213123',
body: {
type: 'panel',
items: [
{type: 'input', name: 'phone', label: 'Teléfono', value: link},
{type: 'input', name: 'showtext', label: 'Texto a mostrar', value: linkText},
{type: 'input', name: 'title', label: 'Título', value: linkTitle}
]
},
buttons: [
{
text: 'Close',
type: 'cancel',
onclick: 'close'
},
{
type: 'submit',
name: 'submitButton',
text: 'Stwórz',
primary: true
}
],
onAction: function(e) {
alert('Toggle menu item clicked');
},
onSubmit: function(e) {
var data = e.getData();
var hrefLink = '<a title="' + data .title + '" href="tel:+34' + data .phone + '">' + data .showtext + '</a>';
if(link !== ''){
editor.setContent(hrefLink);
}else{
editor.insertContent(hrefLink);
}
e.close();
}
});
}
function updateOnSelect() {
var btn = this;
const editorEventCallback = function (e) {
var node = editor.selection.getNode();
var isTelLink = node.href !== undefined && node.href.indexOf('tel:+') !== -1
btn.active(isTelLink);
if(isTelLink){
link = node.href;
link = link.replace("tel:+34", "");
linkTitle = node.title;
linkText = node.text;
}
};
editor.on('NodeChange', editorEventCallback);
return function (btn) {
editor.off('NodeChange', editorEventCallback);
}
}
});
I searched the documentation for a replacement, but found nothing.
TinyMCE 5 no longer passes the button and menu instance via this. Instead it passes an API instance as the first parameter, so you'll want to change your updateOnSelect function to something like this:
function updateOnSelect(api) {
const editorEventCallback = function (e) {
var node = editor.selection.getNode();
var isTelLink = node.href !== undefined && node.href.indexOf('tel:+') !== -1
api.setActive(isTelLink);
if(isTelLink){
link = node.href;
link = link.replace("tel:+34", "");
linkTitle = node.title;
linkText = node.text;
}
};
editor.on('NodeChange', editorEventCallback);
return function (btn) {
editor.off('NodeChange', editorEventCallback);
}
}
You'll note var btn = this has been removed and that the API to set an item as active is setActive instead of active. This can be found in the documentation here: https://www.tiny.cloud/docs/ui-components/typesoftoolbarbuttons/#togglebutton and https://www.tiny.cloud/docs/ui-components/menuitems/#togglemenuitems (see the API section in both links).
In the above, you may have noticed both reference "Toggle" items. That's another change in TinyMCE 5, as different types of buttons/menu items have a separate registration API. So you'll also need to swap to using editor.ui.registry.addToggleButton and editor.ui.registry.addToggleMenuItem. More details about that can be found here if needed: https://www.tiny.cloud/docs/migration-from-4x/#customtoolbarbuttons
Here's an example fiddle showing the changes mentioned above: https://fiddle.tiny.cloud/B5haab.
Hopefully that helps!

adding multiple (dynamic number) google charts in page

Am trying to create a test report from the xml file generated from the test suite run. I have to generate the google line chart for each activity in an application node in xml. Its dynamic and we dont know how many activities will be there under application tag.
so far i tried to generate the line charts using the callback method in a for loop but, all the graphs are having the same data. when i debugged the code i found that the code in call back method to create the datatable is always executing for the last activity and generating the same chart for each activity.
here is the code i tried
html
<div id="container">
<div id="report" class="table-responsive">
<select id="app" name="app" aria-placeholder="Select Application">
<option>-- Select Application --</option>
</select>
<select id="activity" name="activity" aria-placeholder="Select Activity">
<option>-- Select Activity --</option>
</select>
<select id="type" name="type" aria-placeholder="Select StartupType">
<option value="coldstart" >Cold</option>
<option value="warmstart" selected>Warm</option>
</select>
<br/>
<div id="chartContainer">
</div>
</div>
<div align="center" class="loader">
<img src="images/loader.gif" id="load" width="400" height="400" align="absmiddle" />
</div>
</div>
javascript
var appXml;
var summaryXml;
$(document).ready(function () {
prepareCharts();
$("#app").change(function () {
var app = $(this).val();
if (app != "") {
$(appXml).find('package').each(function () {
if ($(this).attr('appname') == app) {
var options = '<option value="">-- Select activity --</option>';
$(this).find('activity').each(function () {
options += '<option value="' + $(this).attr('activityname') + '">' + $(this).attr('activityname') + '</option>';
});
$('#activity').html(options);
}
});
}
});
$("#activity").change(function () {
if ($(this).val() != "")
drawActivityChart();
else
drawActivityCharts(appXml, $('#type').val());
});
$('#type').change(function () {
var type = $(this).val();
if ($('#activity').val() == "")
drawActivityCharts(appXml, type);
else
drawActivityChart();
});
});
function prepareCharts() {
$.ajax({
type: "GET",
url: "Startuptime.xml",
dataType: "xml",
success: drawCharts
});
}
function drawCharts(xml) {
console.log('drawing charts');
appXml = xml;
prepareDropdowns(xml);
drawActivityCharts(xml);
}
function prepareDropdowns(xml) {
var options = '<option value="">-- Select application --</option>';
$(xml).find('package').each(function () {
options += '<option value="' + $(this).attr('appname') + '">' + $(this).attr('appname') + '</option>';
});
$('#app').html(options);
$('#app option:nth-child(2)').attr('selected', 'selected').change();
}
function drawActivityCharts(xml, type) {
$('#chartContainer').children().remove();
if (typeof type === 'undefined')
type = 'warmstart';
google.charts.load('current', { 'packages': ['corechart'] });
var app = $('#app').val();
$(xml).find('package').each(function () {
var that = this;
if ($(that).attr('appname') == app) {
var i = 1;
$(that).find('activity').each(function () {
var activityName = $(this).attr('activityname');
console.log(i);
console.log(activityName);
i++;
if ($(this).find(type).length > 0) {
that = this;
$('#chartContainer').append('<div id="' + activityName + '"></div>')
google.charts.setOnLoadCallback(function () {
var data = new google.visualization.DataTable();
data.addColumn('number', 'Occurance');
data.addColumn('number', 'Time');
var row = 1;
$(that).children(type).find('displaytime').each(function () {
data.addRow([row, parseFloat($.trim($(this).find('timetoinitialdisplay').text()))]);
console.log(row + " " + parseFloat($.trim($(this).find('timetoinitialdisplay').text())));
row++;
});
// Set chart options
var options = {
'title': activityName,
'width': 800,
'height': 200
};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.LineChart(document.getElementById(activityName));
chart.draw(data, options);
});
}
});
}
});
}
function drawActivityChart() {
$('#chartContainer').children().remove();
google.charts.load('current', { 'packages': ['corechart'] });
var app = $('#app').val();
var activity = $('#activity').val();
var type = $('#type').val();
$(appXml).find('package').each(function () {
var that = this;
if ($(that).attr('appname') == app) {
$(that).find('activity').each(function () {
var activityName = $(this).attr('activityname');
if (activityName == activity) {
if ($(this).find(type).length > 0) {
that = this;
$('#chartContainer').append('<div id="' + activityName + '"></div>')
google.charts.setOnLoadCallback(function () {
var data = new google.visualization.DataTable();
data.addColumn('number', 'Occurance');
data.addColumn('number', 'Time');
var row = 1;
$(that).find('displaytime').each(function () {
data.addRow([row, parseFloat($.trim($(this).find('timetoinitialdisplay').text()))]);
console.log(row + " " + parseFloat($.trim($(this).find('timetoinitialdisplay').text())));
row++;
});
// Set chart options
var options = {
'title': activityName,
'width': 800,
'height': 200
};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.LineChart(document.getElementById(activityName));
chart.draw(data, options);
});
}
}
});
}
});
}
drawActivityCharts() is the method which has to draw the activity charts
and xml schema will be like below.
<?xml version='1.0' encoding='UTF-8' ?>
<appstartuptime>
<package appname="appname" name="packagename" packageversion="version">
<activity activityname="activityname">
<coldstart numberoftimes="1">
<displaytime>
<timetoinitialdisplay>841</timetoinitialdisplay>
</displaytime>
</coldstart>
</activity>
<activity activityname="activityname">
<warmstart numberoftimes="2">
<displaytime>
<timetoinitialdisplay>454</timetoinitialdisplay>
</displaytime>
<displaytime>
<timetoinitialdisplay>467</timetoinitialdisplay>
</displaytime>
</warmstart>
</activity>
</package>
</appstartuptime>
both google.charts.load and google.charts.setOnLoadCallback only need to be called once per page load.
in addition, google.charts.load will wait on the page to load by default,
as such, it can be used in place of $(document).ready
you can also include google's callback in the load statement.
google.charts.load('current', {
callback: drawChart,
packages:['corechart']
});
or use the promise it returns.
google.charts.load('current', {
packages:['corechart']
}).then(drawChart);
given this, recommend loading google first,
then you can draw as many charts as needed.
i didn't go thru all of the code, but something similar to the following should work...
var appXml;
var summaryXml;
google.charts.load('current', {
packages:['corechart']
}).then(function () {
prepareCharts();
$("#app").change(function () {
var app = $(this).val();
if (app != "") {
$(appXml).find('package').each(function () {
if ($(this).attr('appname') == app) {
var options = '<option value="">-- Select activity --</option>';
$(this).find('activity').each(function () {
options += '<option value="' + $(this).attr('activityname') + '">' + $(this).attr('activityname') + '</option>';
});
$('#activity').html(options);
}
});
}
});
$("#activity").change(function () {
if ($(this).val() != "")
drawActivityChart();
else
drawActivityCharts(appXml, $('#type').val());
});
$('#type').change(function () {
var type = $(this).val();
if ($('#activity').val() == "")
drawActivityCharts(appXml, type);
else
drawActivityChart();
});
});
function prepareCharts() {
$.ajax({
type: "GET",
url: "Startuptime.xml",
dataType: "xml",
success: drawCharts
});
}
function drawCharts(xml) {
console.log('drawing charts');
appXml = xml;
prepareDropdowns(xml);
drawActivityCharts(xml);
}
function prepareDropdowns(xml) {
var options = '<option value="">-- Select application --</option>';
$(xml).find('package').each(function () {
options += '<option value="' + $(this).attr('appname') + '">' + $(this).attr('appname') + '</option>';
});
$('#app').html(options);
$('#app option:nth-child(2)').attr('selected', 'selected').change();
}
function drawActivityCharts(xml, type) {
$('#chartContainer').children().remove();
if (typeof type === 'undefined')
type = 'warmstart';
var app = $('#app').val();
$(xml).find('package').each(function () {
var that = this;
if ($(that).attr('appname') == app) {
var i = 1;
$(that).find('activity').each(function () {
var activityName = $(this).attr('activityname');
console.log(i);
console.log(activityName);
i++;
if ($(this).find(type).length > 0) {
that = this;
$('#chartContainer').append('<div id="' + activityName + '"></div>')
var data = new google.visualization.DataTable();
data.addColumn('number', 'Occurance');
data.addColumn('number', 'Time');
var row = 1;
$(that).children(type).find('displaytime').each(function () {
data.addRow([row, parseFloat($.trim($(this).find('timetoinitialdisplay').text()))]);
console.log(row + " " + parseFloat($.trim($(this).find('timetoinitialdisplay').text())));
row++;
});
// Set chart options
var options = {
'title': activityName,
'width': 800,
'height': 200
};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.LineChart(document.getElementById(activityName));
chart.draw(data, options);
}
});
}
});
}
function drawActivityChart() {
$('#chartContainer').children().remove();
var app = $('#app').val();
var activity = $('#activity').val();
var type = $('#type').val();
$(appXml).find('package').each(function () {
var that = this;
if ($(that).attr('appname') == app) {
$(that).find('activity').each(function () {
var activityName = $(this).attr('activityname');
if (activityName == activity) {
if ($(this).find(type).length > 0) {
that = this;
$('#chartContainer').append('<div id="' + activityName + '"></div>')
var data = new google.visualization.DataTable();
data.addColumn('number', 'Occurance');
data.addColumn('number', 'Time');
var row = 1;
$(that).find('displaytime').each(function () {
data.addRow([row, parseFloat($.trim($(this).find('timetoinitialdisplay').text()))]);
console.log(row + " " + parseFloat($.trim($(this).find('timetoinitialdisplay').text())));
row++;
});
// Set chart options
var options = {
'title': activityName,
'width': 800,
'height': 200
};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.LineChart(document.getElementById(activityName));
chart.draw(data, options);
}
}
});
}
});
}

Masonry filter + infinite scroll issue

I built jQuery Masonry layout with infinite scroll and filtering.
My problem is that when a filter clicked before scrolling, the content loaded with infinite scroll is not filtered.
Is there any way to fix it?
Here is the link to check: http://www.jcvergara.com/working/
Here is the full masonry code:
$(document).ready(function(){
var $container = $('.container');
// initialize
$container.masonry({
columnWidth: 250,
itemSelector: '.item',
isFitWidth: true
});
$container.masonry( 'on', 'layoutComplete', function() {
$('.right-line').css('height', $('.container').height());
});
$('.right-line').css('height', $('.container').height());
// infinite scroll
var $container = $('#content');
$container.infinitescroll({
navSelector : "nav.posts-navigation",
nextSelector : "div.nav-previous a:first",
itemSelector : "#content div.item",
},
// trigger Masonry as a callback
function( newElements ) {
var $newElems = $( newElements );
$container.masonry( 'appended', $newElems );
// open posts in ajax
$('.post-link').click(function(){
$('.openarticle').css('display', 'block');
var post_link = $(this).attr('href');
$('#openthis').html('<div class="title"><h2>Loading..</h2><div class="text"></div>');
$('#openthis').load(post_link);
$('<a></a>', {
text: 'Close',
class: 'close',
id: 'close',
href: '#'
})
.prependTo($('.openarticle .main'))
.click(function() {
$('.openarticle').css('display', 'none');
$('#close').remove();
});
return false;
});
}
);
// filtering
$(".cat-item a").click(function(e) {
e.preventDefault();
var cut_url = "http://www.jcvergara.com/category/";
var group = $(this).attr('href');
group = group.replace(cut_url, '').slice(0,-1);
var group_class = "." + group;
$(".cat-item a.active").removeClass('active');
$(this).addClass('active');
if(group != "all") {
$(".item").hide();
$(group_class).show();
$container.masonry();
} else {
$(".item").show();
$container.masonry();
}
});
});
Try using "on" for your click function:
// filtering
$(".cat-item a").on( "click",function (e) {
e.preventDefault();
var cut_url = "http://www.jcvergara.com/category/";
var group = $(this).attr('href');
group = group.replace(cut_url, '').slice(0,-1);
var group_class = "." + group;
$(".cat-item a.active").removeClass('active');
$(this).addClass('active');
if(group != "all") {
$(".item").hide();
$(group_class).show();
$container.masonry();
} else {
$(".item").show();
$container.masonry();
}
});

upload base64 image facebook graph api how to use this script

Upload Base64 Image Facebook Graph API
i want to use this script that link is attached how i can use this in my wordpress post?
i want to use this for fbcover photo site.
Take a look at this code I hacked together from various examples - you can use this to post a pure base64 string to the Facebook API - no server side processing.
Here's a demo: http://rocky-plains-2911.herokuapp.com/
This javascript handles the converting of a HTML5 Canvas element to base64 and using the Facebook API to post the image string
<script type = "text/javascript">
// Post a BASE64 Encoded PNG Image to facebook
function PostImageToFacebook(authToken) {
var canvas = document.getElementById("c");
var imageData = canvas.toDataURL("image/png");
try {
blob = dataURItoBlob(imageData);
} catch (e) {
console.log(e);
}
var fd = new FormData();
fd.append("access_token", authToken);
fd.append("source", blob);
fd.append("message", "Photo Text");
try {
$.ajax({
url: "https://graph.facebook.com/me/photos?access_token=" + authToken,
type: "POST",
data: fd,
processData: false,
contentType: false,
cache: false,
success: function (data) {
console.log("success " + data);
$("#poster").html("Posted Canvas Successfully");
},
error: function (shr, status, data) {
console.log("error " + data + " Status " + shr.status);
},
complete: function () {
console.log("Posted to facebook");
}
});
} catch (e) {
console.log(e);
}
}
// Convert a data URI to blob
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], {
type: 'image/png'
});
}
</script>
This handles the Facebook Authentication and shows basic HTML setup
<script type="text/javascript">
$(document).ready(function () {
$.ajaxSetup({
cache: true
});
$.getScript('//connect.facebook.net/en_UK/all.js', function () {
// Load the APP / SDK
FB.init({
appId: '288585397909199', // App ID from the App Dashboard
cookie: true, // set sessions cookies to allow your server to access the session?
xfbml: true, // parse XFBML tags on this page?
frictionlessRequests: true,
oauth: true
});
FB.login(function (response) {
if (response.authResponse) {
window.authToken = response.authResponse.accessToken;
} else {
}
}, {
scope: 'publish_actions,publish_stream'
});
});
// Populate the canvas
var c = document.getElementById("c");
var ctx = c.getContext("2d");
ctx.font = "20px Georgia";
ctx.fillText("This will be posted to Facebook as an image", 10, 50);
});
</script>
<div id="fb-root"></div>
<canvas id="c" width="500" height="500"></canvas>
<a id="poster" href="#" onclick="PostImageToFacebook(window.authToken)">Post Canvas Image To Facebook</a>
I needed this too, and was not happy with all the code around it because it is lengthy and usually needs jQuery. Here is my code for uploading from Canvas to Facebook:
const dataURItoBlob = (dataURI) => {
let byteString = atob(dataURI.split(',')[1]);
let ab = new ArrayBuffer(byteString.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {
type: 'image/jpeg'
});
}
const upload = async (response) => {
let canvas = document.getElementById('canvas');
let dataURL = canvas.toDataURL('image/jpeg', 1.0);
let blob = dataURItoBlob(dataURL);
let formData = new FormData();
formData.append('access_token', response.authResponse.accessToken);
formData.append('source', blob);
let responseFB = await fetch(`https://graph.facebook.com/me/photos`, {
body: formData,
method: 'post'
});
responseFB = await responseFB.json();
console.log(responseFB);
};
document.getElementById('upload').addEventListener('click', () => {
FB.login((response) => {
//TODO check if user is logged in and authorized publish_actions
upload(response);
}, {scope: 'publish_actions'})
})
Source: http://www.devils-heaven.com/facebook-javascript-sdk-photo-upload-from-canvas/