/*
* Project: PicEdit
* Description: Creates an image upload box with tools to edit the image on the front-end before uploading it to the server
* Author: Andy V.
* License: MIT
*/
// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
;
(function ($, window, document, undefined) {
"use strict";
// undefined is used here as the undefined global variable in ECMAScript 3 is
// mutable (ie. it can be changed by someone else). undefined isn't really being
// passed in so we can ensure the value of it is truly undefined. In ES5, undefined
// can no longer be modified.
// window is passed through as local variable rather than global
// as this (slightly) quickens the resolution process and can be more efficiently
// minified (especially when both are regularly referenced in your plugin).
// Create the default params object
var pluginName = 'picEdit',
defaults = {
imageUpdated: function (img) {}, // Image updated callback function
formSubmitted: function (res) {}, // After form was submitted callback function
fileNameChanged: function (filename) {}, // After content is loaded into the canvas from either URL or file
fileLoaded: function (file) {}, // After a file is loaded into the canvas
redirectUrl: false, // Page url for redirect on form submit
maxWidth: 'auto', // Max width parameter
maxHeight: 'auto', // Max height parameter
aspectRatio: true, // Preserve aspect ratio
defaultImage: false, // Default image to be used with the plugin
defaultMessageTimeout: 3000 // Default timeout to autohide messages (in milliseconds)
};
// The actual plugin constructor
function Plugin(element, options) {
this.inputelement = element;
this.element = element;
// jQuery has an extend method which merges the contents of two or
// more objects, storing the result in the first object. The first object
// is generally empty as we don't want to alter the default options for
// future instances of the plugin
this.options = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
// Reference to the loaded image
this._image = false;
// Reference to the filename of the loaded image
this._filename = "";
// Interface variables (data synced from the user interface)
this._variables = {};
/* Prepare the template */
/*unhide_in_prod*/
/*this._template();*/
/*unhide_in_prod*/
/*hide_in_prod*/
this.init();
/*hide_in_prod*/
}
Plugin.prototype = {
init: function () {
// Place initialization logic here
// You already have access to the DOM element and
// the options via the instance, e.g. this.element
// and this.settings
// Save instance of this for inline functions
var _this = this;
// Get type of element to be used (type="file" and type="picedit" are supported)
var type = $(this.inputelement).prop("type");
if (type == "file")
this._fileinput = $(this.inputelement);
else {
// Create a reference to the file input box
$(this.inputelement).after('');
this._fileinput = $(this.inputelement).next("input");
}
// Show regular file upload on old browsers
if (!this.check_browser_capabilities()) {
if (type != "file") {
$(this.inputelement).prop("type", "file");
$(this._fileinput).remove();
}
$(this.inputelement).show();
$(this.element).remove();
return;
}
// Get reference to the main canvas element
this._canvas = $(this.element).find(".picedit_canvas > canvas")[0];
// Create and set the 2d context for the canvas
this._ctx = this._canvas.getContext("2d");
// Reference to the painter element
this._painter = $(this.element).find(".picedit_painter");
this._painter_canvas = this._painter.children("canvas")[0];
this._painter_ctx = this._painter_canvas.getContext("2d");
this._painter_painting = false;
this._text = $(this.element).find(".picedit_textbox");
this._text_canvas = this._text.children("canvas")[0];
this._text_ctx = this._text_canvas.getContext("2d");
this._square = $(this.element).find(".picedit_squarebox");
this._square_canvas = this._square.children("canvas")[0];
this._square_ctx = this._square_canvas.getContext("2d");
this._square_painting = false;
this._circle = $(this.element).find(".picedit_squarebox");
this._circle_canvas = this._circle.children("canvas")[0];
this._circle_ctx = this._circle_canvas.getContext("2d");
this._circle_painting = false;
//
// Save the reference to the messaging box
this._messagebox = $(this.element).find(".picedit_message");
this._messagetimeout = false;
// Reference to the main/top nav buttons holder
this._mainbuttons = $(this.element).find(".picedit_action_btns");
// Size of the viewport to display image (a resized image will be displayed)
this._viewport = {
"width": 0,
"height": 0
};
// All variables responsible for cropping functionality
this._cropping = {
is_dragging: false,
is_resizing: false,
left: 0,
top: 0,
width: 0,
height: 0,
cropbox: $(this.element).find(".picedit_drag_resize"),
cropframe: $(this.element).find(".picedit_drag_resize_box")
};
function build_img_from_file(files) {
if (!files && !files.length)
return;
var file = files[0];
if (!_this._filename) {
_this._filename = file.name;
}
var reader = new FileReader();
reader.onload = function (e) {
_this._create_image_with_datasrc(e.target.result, false, file);
_this.options.fileLoaded(file);
if (file.name != _this._filename) {
_this._filename = file.name;
_this.options.fileNameChanged(file.name);
}
};
reader.readAsDataURL(file);
}
// Bind file drag-n-drop behavior
$(this.element).find(".picedit_canvas_box").on("drop", function (event) {
event.preventDefault();
$(this).removeClass('dragging');
var files = (event.dataTransfer || event.originalEvent.dataTransfer).files;
build_img_from_file(files);
}).on("dragover", function (event) {
event.preventDefault();
$(this).addClass('dragging');
}).on("dragleave", function (event) {
event.preventDefault();
$(this).removeClass('dragging');
});
// Bind onchange event to the fileinput to pre-process the image selected
$(this._fileinput).on("change", function () {
build_img_from_file(this.files);
});
// If Firefox (doesn't support clipboard object), create DIV to catch pasted image
if (!window.Clipboard) { // Firefox
var pasteCatcher = $(document.createElement("div"));
pasteCatcher.prop("contenteditable", "true").css({
"position": "absolute",
"left": -999,
"width": 0,
"height": 0,
"overflow": "hidden",
"outline": 0,
"opacity": 0
});
$(document.body).prepend(pasteCatcher);
}
// Bind onpaste event to capture images from the the clipboard
$(document).on("paste", function (event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
var blob;
if (!items) {
pasteCatcher.get(0).focus();
pasteCatcher.on('DOMSubtreeModified', function () {
var child = pasteCatcher.children().last().get(0);
pasteCatcher.html("");
if (child) {
if (child.tagName === "IMG" && child.src.substr(0, 5) == 'data:') {
_this._create_image_with_datasrc(child.src);
} else if (child.tagName === "IMG" && child.src.substr(0, 4) == 'http') {
_this._create_image_with_datasrc(child.src, false, false, true);
}
}
});
} else {
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") === 0) {
blob = items[i].getAsFile();
}
}
if (blob) {
var reader = new FileReader();
reader.onload = function (e) {
_this._create_image_with_datasrc(e.target.result);
};
reader.readAsDataURL(blob);
}
}
});
// Define formdata element
this._theformdata = false;
this._theform = $(this.inputelement).parents("form");
// Bind form submit event
if (this._theform.length) {
this._theform.on("submit", function () {
return _this._formsubmit();
});
}
// Call helper functions
this._bindControlButtons();
this._bindInputVariables();
this._bindSelectionDrag();
// Set Default interface variable values
this._variables.pen_color = "black";
this._variables.pen_size = false;
this._variables.prev_pos = false;
this._variables.text_color = "black";
// Load default image if one is set
if (this.options.defaultImage)
_this.set_default_image(this.options.defaultImage);
},
// Check Browser Capabilities (determine if the picedit should run, or leave the default file-input field)
check_browser_capabilities: function () {
if (!!window.CanvasRenderingContext2D == false)
return false; //check canvas support
if (!window.FileReader)
return false; //check file reader support
return true; //otherwise return true
},
// Set the default Image
set_default_image: function (path) {
this._create_image_with_datasrc(path, false, false, true);
var m = path.match(/.*\/(.+?)[\?#]/);
if (m && m.length > 1) {
this._filename = m[1];
} else {
this._filename = path;
}
this.options.fileNameChanged(this._filename);
},
// Remove all notification copy and hide message box
hide_messagebox: function () {
var msgbox = this._messagebox;
msgbox.removeClass("active no_close_button");
setTimeout(function () {
msgbox.children("div").html("")
}, 200);
},
// Open a loading spinner message box or working... message box
set_loading: function (message) {
if (message && message == 1) {
return this.set_messagebox("Working...", false, false);
} else
return this.set_messagebox("Please Wait...", false, false);
},
// Open message box alert with defined text autohide after number of milliseconds, display loading spinner
set_messagebox: function (text, autohide, closebutton) {
autohide = typeof autohide !== 'undefined' ? autohide : this.options.defaultMessageTimeout;
closebutton = typeof closebutton !== 'undefined' ? closebutton : true;
this._messagebox.addClass("active");
if (closebutton) {
this._messagebox.removeClass("no_close_button");
} else {
this._messagebox.addClass("no_close_button");
}
if (autohide) {
clearTimeout(this._messagetimeout);
var _this = this;
this._messagetimeout = setTimeout(function () {
_this.hide_messagebox();
}, autohide);
}
return this._messagebox.children("div").html(text);
},
// Toggle button and update variables
toggle_button: function (elem) {
if ($(elem).hasClass("active")) {
var value = false;
$(elem).removeClass("active");
} else {
var value = true;
$(elem).siblings().removeClass("active");
$(elem).addClass("active");
}
var variable = $(elem).data("variable");
if (variable) {
var optional_value = $(elem).data("value");
if (!optional_value)
optional_value = $(elem).val();
if (optional_value && value)
value = optional_value;
this._setVariable(variable, value);
}
if (this._variables.pen_color && this._variables.pen_size)
this.pen_tool_open();
else
this.pen_tool_close();
},
text_button: function (elem) {
if ($(elem).hasClass("active")) {
var value = false;
$(elem).removeClass("active");
} else {
var value = true;
$(elem).siblings().removeClass("active");
$(elem).addClass("active");
}
var variable = $(elem).data("variable");
if (variable) {
var optional_value = $(elem).data("value");
if (!optional_value)
optional_value = $(elem).val();
if (optional_value && value)
value = optional_value;
this._setVariable(variable, value);
}
if (this._variables.text_color)
;
else
this.text_close();
},
// Perform image load when user clicks on image button
load_image: function () {
this._fileinput.click();
},
//
text_open: function () {
if (!this._image)
return this._hideAllNav(1);
this.text_set();
this._text.addClass("active");
this._hideAllNav();
},
text_set: function () {
var textbox = document.getElementById("textbox").value;
var text_size = document.getElementById("text_size").value;
var font = "Tahoma";
this._text_canvas.width = 0;
this._text_canvas.width = this._canvas.width;
this._text_canvas.height = this._canvas.height;
this._text_ctx.font = text_size + "px " + font;
this._text_ctx.fillStyle = this._variables.text_color;
this._text_ctx.fillText(textbox, 100, 150);
},
text_close: function () {
this._text.removeClass("active");
},
square_open: function () {
if (!this._image)
return this._hideAllNav(1);
this.square_set();
this._square.addClass("active");
this._hideAllNav();
},
square_set: function () {
this._square_canvas.width = 0;
this._square_canvas.width = this._canvas.width;
this._square_canvas.height = this._canvas.height;
this._square_ctx.save();
this._square_ctx.beginPath();
this._square_ctx.strokeRect(this._square_canvas.x, this._square_canvas.y, this._square_canvas.w, this._square_canvas.h);
this._square_ctx.restore();
this._square_ctx.strokeStyle = 'red';
this._square_ctx.lineWidth = 3;
// this._square_ctx.stroke();
this._square_ctx.closePath();
$('.picedit_squarebox').append('