TinyMCE and Image Cropping - image-processing

I am writing a plugin for TinyMCE to crop images. This code works on firefox but doesn’t seem to work in other browsers.
Basically, I’m using JCrop to get the co-ordinates of the image and selected area and passing it to a server-side
method which does the cropping and returns the updated width, height and image src.
After, getting back the results. I update the image dimensions and src as follows.
tinyMCE.activeEditor.selection.getNode().src = croppedImageSource;
tinyMCE.activeEditor.selection.getNode().width = croppedImageWidth;
tinyMCE.activeEditor.selection.getNode().height = croppedImageHeight;
The Server-side method and crop co-ordinates are working as expected. While, the above code is not working well. Works on firefox but not on other browsers.
I was wondering If was updating the image selected in TinyMCE correctly?
Here is my full javascript function
function cropAndSave()
{
var imgSrc = document.getElementById('jcrop_target').src;
if(checkJcropCoords())
{
$.ajax({
async: false,
url: "/DocViewImageCrop.page",
type: 'POST',
data:
{
imgData: imgSrc,
cW: $("#w").val(),
cH: $("#h").val(),
cX: $("#x").val(),
cY: $("#y").val()
},
dataType: 'json',
complete: function(xmlRequestObject, successString)
{
var fileExists = xmlRequestObject.responseXML.getElementsByTagName("fileExists")[0].firstChild.nodeValue;
if(fileExists == undefined || fileExists == "false")
{
alert('Image not found on server. Try uploading the image, before attempting to resize');
}
else
{
tinyMCE.activeEditor.selection.getNode().src = xmlRequestObject.responseXML.getElementsByTagName("imgsrc")[0].firstChild.nodeValue;
tinyMCE.activeEditor.selection.getNode().width = xmlRequestObject.responseXML.getElementsByTagName("width")[0].firstChild.nodeValue;
tinyMCE.activeEditor.selection.getNode().height = xmlRequestObject.responseXML.getElementsByTagName("height")[0].firstChild.nodeValue;
}
}
});
}
}

You may try
tinyMCE.activeEditor.selection.getNode().setAttribute('scr', croppedImageSource);
tinyMCE.activeEditor.selection.getNode().setAttribute('width', croppedImageWidth);
tinyMCE.activeEditor.selection.getNode().setAttribute('height', croppedImageHeight);
or jQuery
var $node = $( tinyMCE.activeEditor.selection.getNode() );
$node.attr('scr', croppedImageSource);
$node.attr('width', croppedImageWidth);
$node.attr('height', croppedImageHeight);

Related

Ruby Shrine - crop & direct upload Safari issue

I am implementing direct upload with Shrine, jquery.fileupload and cropper.js
in the add portion I am loading the image from the file upload to modal, define the cropper and show the modal
if (data.files && data.files[0]) {
var reader = new FileReader();
var $preview = $('#preview_avatar');
reader.onload = function(e) {
$preview.attr('src', e.target.result); // insert preview image
$preview.cropper({
dragMode: 'move',
aspectRatio: 1.0 / 1.0,
autoCropArea: 0.65,
data: {width: 270, height: 270}
})
};
reader.readAsDataURL(data.files[0]);
$('#crop_modal').modal('show', {
backdrop: 'static',
keyboard: false
});
}
Then on the modal button click I get the cropped canvas call on it toBlob and submit to S3
$('#crop_button').on('click', function(){
var options = {
extension: data.files[0].name.match(/(\.\w+)?$/)[0], // set extension
_: Date.now() // prevent caching
};
var canvas = $preview.cropper('getCroppedCanvas');
$.getJSON('/images/cache/presign', options).
then(function (result) {
data.formData = result['fields'];
data.url = result['url'];
data.paramName = 'file';
if (canvas.toBlob) {
canvas.toBlob(
function (blob) {
var file = new File([blob], 'cropped_file.jpeg');
console.log('file', file);
data.files[0] = file;
data.originalFiles[0] = data.files[0];
data.submit();
},
'image/jpeg'
);
}
});
});
After the upload to S3 is done I am writing to image attributes to hidden field, closing the modal and destroying the cropper
done: function (e, data) {
var image = {
id: data.formData.key.match(/cache\/(.+)/)[1], // we have to remove the prefix part
storage: 'cache',
metadata: {
size: data.files[0].size,
filename: data.files[0].name.match(/[^\/\\]*$/)[0], // IE returns full path
// mime_type: data.files[0].type
mime_type: 'image/jpeg'
}
};
console.log('image', image);
$('.cached-avatar').val(JSON.stringify(image));
$('#crop_modal').modal('hide');
$('#preview_avatar').cropper('destroy');
}
An chrome everything worked fine from the very beginning, but then I figured out the safari has no toBlob functionality.
I found this one:
https://github.com/blueimp/JavaScript-Canvas-to-Blob
And toBlob is not a function error was gone..
Now I can not save the image due to some mime type related issue.
I was able to find out the exact location where it fails on safari but not chrome.
determine_mime_type.rb line 142
on line 139 in the options = {stdin_data: io.read(MAGIC_NUMBER), binmode: true}
the stdin_data is empty after the io.read
Any ideas?
Thank you!
UPDATE
I was able to figure out that the url to the cached image returned by the
$.getJSON('/images/cache/presign', options)
returns empty file when cropped and uploaded from safari.
So as I mentioned in the question safari uploaded empty file once it was cropped by cropper.js.
The problem clearly originated from this block:
if (canvas.toBlob) {
canvas.toBlob(
function (blob) {
var file = new File([blob], 'cropped_file.jpeg');
console.log('file', file);
data.files[0] = file;
data.originalFiles[0] = data.files[0];
data.submit();
},
'image/jpeg'
);
}
I found in some comment on one of the articles I read that safari does some thing like "file.toString" which in my case resulted in empty file upload.
I appended the blob directly without creating a file from it first and everything worked fine.
if (canvas.toBlob) {
canvas.toBlob(
function (blob) {
data.files[0] = blob;
data.files[0].name = 'cropped_file.jpeg';
data.files[0].type = 'image/jpeg';
data.originalFiles[0] = data.files[0];
data.submit();
},
'image/jpeg'
);
}

select2 (remote data) throws exception because of typing to fast

I'm using select2 for loading remote data. I declared the minimumInputLength to 3 letters, so after that it will start searching.
Whenever I hit the fourth letter while typing fast I get an Javascript exception saying :
Sorry. An error occured while communicating with the server. Please try again later.
How can I avoid this? I already changed the quietMillis (waitTimesMs) to lower or higher (does this even have something to do with it?).
Every help is appreciate.
My code is like:
$(function () {
$("#Search").select2({
minimumInputLength: 3,
ajax: {
url: site,
dataType: "json",
quietMillis: waitTimeMs,
data: function (params) {
var page = (params.page || 1) - 1;
return {
searchText: params.term,
pageCount: 10,
page: page
};
},
processResults: function (data) {
var select2Data = $.map(data.Items, function (obj) {
obj.id = obj.ID;
obj.text = obj.Name;
return obj;
});
return {
results: select2Data,
pagination: { more: (data.PageNo * 10) < data.TotalCount }
};
}
Finally it works!
select2 changed "quietMillis" to "delay" so I could change quietMillis as big as I wanted and nothing changed at all...

iOS callback not triggered after file upload from $cordovaCamera

I'm building a hybrid app in ionic, which runs over cordova. I use the $cordovaCamera plugin to capture images from the phone, either by selecting from the phone's gallery or by using the camera to take a picture.
I then send that image using Restangular to my server, and when that action finishes, I want to display a status message on the screen.
My problem: All of the above works perfectly on Android. On iOS, it is working only when the image is selected from the gallery, but not when the image is directly captured from the phone. In that case, the image is correctly transferred to the server, the request returns a 201 Created just as it should - but the then() callback function is never entered.
If anyone can explain this behavior, that would be awesome...my second best would be to capture the image on the iPhone, save to gallery, and then attempt to retrieve the last saved image, but I haven't been able to figure out how to yet and I'd rather just get this working.
Update: I've narrowed it down to the Restangular part - if instead of calling the Restangular upload function, I use $http, the callback is triggered as expected and all is good...so that's what I'm going to do, but if anyone can tell me what the problem was I'd be grateful.
Relevant code:
/** cameraService functions **/
//when the user chooses to snap a picture from the camera
takePicture: function(){
var options = {
quality: 50,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
encodingType: Camera.EncodingType.JPEG,
popoverOptions: CameraPopoverOptions
};
return $cordovaCamera.getPicture(options).then(
function(imageData) {
return imageData;
},
function(err) {
console.log("error", err);
});
},
//when the user chooses to select image from the gallery
choosePicture: function(){
var options = {
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY
};
return $cordovaCamera.getPicture(options).then(
function(imageData) {
return imageData;
},
function(err) {
console.log("error", err);
});
},
uploadPicture: function(imageSource, caption, is_logo){
if (typeof is_logo == 'undefined') is_logo = false;
var upload_object = {
caption: caption,
source: imageSource,
is_logo: is_logo
};
//apiService is my wrapper for Restangular
return apiService.uploadFile(loadingService.getClientUrl('images'), upload_object);
},
/**apiService uploadFile - apparently the problem is here ***/
uploadFile: function(baseElem, object, route, path){
var headers = apiFunctions.setHeaders({'Content-Type': undefined});
//this DOES NOT WORK (on iPhone with image from camera) - request completes but callback not triggered
return Restangular.all(baseElem).customPOST(object, path, route, headers);
//this works fine:
return $http.post('https://api.mysite.dev/v1/clients/'+localStorageService.getValue('client_id')+'/images', JSON.stringify(object), {headers:headers}
);
},
/** controller functions **/
$scope.takePicture = function () {
cameraService.takePicture().then(function (imageData) {
$scope.data.imageSource = imageData;
});
};
$scope.choosePicture = function () {
cameraService.choosePicture().then(function (imageData) {
$scope.data.imageSource = imageData;
});
};
$scope.uploadPicture = function () {
cameraService.uploadPicture($scope.data.imageSource, $scope.data.caption)
.then(function (response) { //this is never entered if image is captured from camera on iPhone
$ionicScrollDelegate.scrollTop();
$scope.data.caption = '';
$scope.data.imageSource = '';
if (response.data.response.is_success.data_value == true) {
$scope.messages.success.push("Photo uploaded successfully");
} else {
$scope.messages.failure.push("Error uploading photo.");
}
});
}

turbolinks change url manually

I use endless scrolling in my rails 4 application.
The code automatically updates the url when I scroll a list:
var loading = false;
$(function() {
function nearBottomOfPage() {
return $(window).scrollTop() > $(document).height() - $(window).height() - 200;
}
$(window).scroll(function(){
if (loading || $(".infinite-scroll").length == 0) {
return;
}
if(nearBottomOfPage()) {
loading=true;
var qstring = parseInt(getParameterByName('page'));
if(isNaN(qstring)) {qstring = 1;}
qstring++
updateQueryString('page',qstring);
var url = $(".infinite-scroll").data("update-url")
var q_string = window.location.search
if(url.indexOf("?") != -1) {
q_string = q_string.replace("?","&")
}
url = url + q_string
$.ajax({
url: url,
type: 'get',
dataType: 'script',
success: function() {
$(window).sausage('draw');
loading=false;
}
});
}
});
$(window).sausage();
});
The updateQuerystring function replaces the state like so:
window.history.replaceState({turbolinks: true, position: Date.now()}, document.title, new_url);
This is a hack to try to get something that worked in rails 3 to work in rails 4...
It seems to work but it is not clean and has some issues.
The main issue is the fact that the scroll position is not reminded. When clicking the back button, I come back to the top of the page.
Is there a way to do this cleaner. Ideally, instead to do window.history.replaceState I'd like to do Turbolinks.replaceState(newUrl) or something like that.
But if I could find a way to remember the scroll position, It would be great already.
Thank you!
Triggering a Turbolinks visit manually
You can use Turbolinks.visit(path) to go to a URL through Turbolinks.
https://github.com/rails/turbolinks#triggering-a-turbolinks-visit-manually

Phonegap 1.3 captureImage to Photo Library and display image in HTML/App interface

I have been using the following code to grab a photo and display it in html works great.
function takePicture() {
navigator.camera.getPicture(
function(uri) {
var img = document.getElementById('camera_image1');
img.style.visibility = "visible";
img.style.display = "block";
img.src = uri;
document.getElementById('camera_status').innerHTML = "Success";
},
{ quality: 50, allowEdit: true, destinationType: navigator.camera.DestinationType.FILE_URI});
};
html later
<img style="width:144px;height:144px;" id="camera_image1" src="nophoto.jpg"/>
However I would like to save the image to the users Library at the same time, any pointer much appreciated.
I have tried using captureImage but this gives me less options like editing and did not place image inline in html.
Thanks again
PhoneGap 1.3
With Phonegap 2.2 you can save the image to the local device.
add "saveToPhotoAlbum : true" to the cameraOptions
function takePicture() {
navigator.camera.getPicture(
function(uri) {
var img = document.getElementById('camera_image1');
img.style.visibility = "visible";
img.style.display = "block";
img.src = uri;
document.getElementById('camera_status').innerHTML = "Success";
}, {
quality: 50,
allowEdit: true,
destinationType: navigator.camera.DestinationType.FILE_URI,
saveToPhotoAlbum : true
});
};
you'll have to change the phonegap code a bit. it wont save the image in the implementation thats there currently.
tell me if you are working on phonegap android. may be able to help u with that.

Resources