text: /*
   *  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: 400,					// 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('<input type="file" style="display:none" accept="image/*">');
  				    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 video elemment holder element
  				this._videobox = $(this.element).find(".picedit_video");
  				// 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;
  				// 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;
                  // 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();
  		},
  		// Perform image load when user clicks on image button
  		load_image: function () {
  			this._fileinput.click();
  		},
  		// Open pen tool and start drawing
  		pen_tool_open: function () {
  			if(!this._image) return this._hideAllNav(1);
  			this.pen_tool_params_set();
  			this._painter.addClass("active");
  			this._hideAllNav();
  		},
  		// Set pen tool parameters
  		pen_tool_params_set: function () {
  			this._painter_canvas.width = 0;
  			this._painter_canvas.width = this._canvas.width;
  			this._painter_canvas.height = this._canvas.height;
  			this._painter_ctx.lineJoin = "round";
  			this._painter_ctx.lineCap = "round";
  			this._painter_ctx.strokeStyle = this._variables.pen_color;
        		this._painter_ctx.lineWidth = this._variables.pen_size;
  		},
  		// Close pen tool
  		pen_tool_close: function () {
  			this._painter.removeClass("active");
  		},
  		// Rotate the image 90 degrees counter-clockwise
  		rotate_ccw: function () {
  			if(!this._image) return this._hideAllNav(1);
  			var _this = this;
  			//run task and show loading spinner, the task can take some time to run
  			this.set_loading(1).delay(200).promise().done(function() {
  				_this._doRotation(-90);
  				_this._resizeViewport();
  				//hide loading spinner
  				_this.hide_messagebox();
  			});
  			//hide all opened navigation
  			this._hideAllNav();
  		},
  		// Rotate the image 90 degrees clockwise
  		rotate_cw: function () {
  			if(!this._image) return this._hideAllNav(1);
  			var _this = this;
  			//run task and show loading spinner, the task can take some time to run
  			this.set_loading(1).delay(200).promise().done(function() {
  				_this._doRotation(90);
  				_this._resizeViewport();
  				//hide loading spinner
  				_this.hide_messagebox();
  			});
  			//hide all opened navigation
  			this._hideAllNav();
  		},
  		// Resize the image
  		resize_image: function () {
  			if(!this._image) return this._hideAllNav(1);
  			var _this = this;
  			this.set_loading(1).delay(200).promise().done(function() {
  				//perform resize begin
  				var canvas = document.createElement('canvas');
  				var ctx = canvas.getContext("2d");
  				canvas.width = _this._variables.resize_width;
  				canvas.height = _this._variables.resize_height;
  				ctx.drawImage(_this._image, 0, 0, canvas.width, canvas.height);
  				_this._create_image_with_datasrc(canvas.toDataURL("image/png"), function() {
  					_this.hide_messagebox();
  				});
  			});
  			this._hideAllNav();
  		},
  		// Open video element and start capturing live video from camera to later make a photo
  		camera_open: function() {
  			var getUserMedia;
  			var browserUserMedia = navigator.webkitGetUserMedia	||	// WebKit
  									 navigator.mozGetUserMedia	||	// Mozilla FireFox
  									 navigator.getUserMedia;			// 2013...
  			if (!browserUserMedia) return this.set_messagebox("Sorry, your browser doesn't support WebRTC!");
  			var _this = this;
  			getUserMedia = browserUserMedia.bind(navigator);
  			getUserMedia({
  					audio: false,
  					video: true
  				},
  				function(stream) {
  					var videoElement = _this._videobox.find("video")[0];
  					videoElement.src = URL.createObjectURL(stream);
  					//resize viewport
  					videoElement.onloadedmetadata = function() {
  						if(videoElement.videoWidth && videoElement.videoHeight) {
  							if(!_this._image) _this._image = {};
  							_this._image.width = videoElement.videoWidth;
  							_this._image.height = videoElement.videoHeight;
  							_this._resizeViewport();
  						}
  					};
  					_this._videobox.addClass("active");
  				},
  				function(err) {
  					return _this.set_messagebox("No video source detected! Please allow camera access!");
  				}
  			);
  		},
  		camera_close: function() {
  			this._videobox.removeClass("active");
  		},
  		take_photo: function() {
  			var _this = this;
  			var live = this._videobox.find("video")[0];
  			var canvas = document.createElement('canvas');
  			var ctx = canvas.getContext("2d");
  			canvas.width = live.clientWidth;
  			canvas.height = live.clientHeight;
  			ctx.drawImage(live, 0, 0, canvas.width, canvas.height);
  			this._create_image_with_datasrc(canvas.toDataURL("image/png"), function() {
  				_this._videobox.removeClass("active");
  			});
  		},
  		// Crop the image
  		crop_image: function() {
  			var crop = this._calculateCropWindow();
  			var _this = this;
  			this.set_loading(1).delay(200).promise().done(function() {
  				var canvas = document.createElement('canvas');
  				var ctx = canvas.getContext("2d");
  				canvas.width = crop.width;
  				canvas.height = crop.height;
  				ctx.drawImage(_this._image, crop.left, crop.top, crop.width, crop.height, 0, 0, crop.width, crop.height);
  				_this._create_image_with_datasrc(canvas.toDataURL("image/png"), function() {
  					_this.hide_messagebox();
  				});
  			});
  			this.crop_close();
  		},
  		crop_open: function () {
  			if(!this._image) return this._hideAllNav(1);
  			this._cropping.cropbox.addClass("active");
  			this._hideAllNav();
  		},
  		crop_close: function () {
  			this._cropping.cropbox.removeClass("active");
  		},
  		// Create and update image from datasrc
  		_create_image_with_datasrc: function(datasrc, callback, file, dataurl) {
  			var _this = this;
  			var img = document.createElement("img");
              if(dataurl) img.setAttribute('crossOrigin', 'anonymous');
  			if(file) img.file = file;
  			img.src = datasrc;
  			img.onload = function() {
  				if(dataurl) {
                      var canvas = document.createElement('canvas');
                      var ctx = canvas.getContext('2d');
                      canvas.width = img.width;
                      canvas.height = img.height;
                      ctx.drawImage(img, 0, 0);
                      img.src = canvas.toDataURL('image/png');
                  }
                  _this._image = img;
  				_this._resizeViewport();
  				_this._paintCanvas();
  				_this.options.imageUpdated(_this._image);
  				_this._mainbuttons.removeClass("active");
  				if(callback && typeof(callback) == "function") callback();
  				img.onload=null;
  			};
  		},
  		// Functions to controll cropping functionality (drag & resize cropping box)
  		_bindSelectionDrag: function() {
  			var _this = this;
  			var eventbox = this._cropping.cropframe;
  			var painter = this._painter;
  			var resizer = this._cropping.cropbox.find(".picedit_drag_resize_box_corner_wrap");
  			$(window).on("mousedown touchstart", function(e) {
  				var evtpos = (e.clientX) ? e : e.originalEvent.touches[0];
  				_this._cropping.x = evtpos.clientX;
     				_this._cropping.y = evtpos.clientY;
  				_this._cropping.w = eventbox[0].clientWidth;
     				_this._cropping.h = eventbox[0].clientHeight;
  				eventbox.on("mousemove touchmove", function(event) {
  					event.stopPropagation();
          			event.preventDefault();
  					_this._cropping.is_dragging = true;
  					if(!_this._cropping.is_resizing) _this._selection_drag_movement(event);
  				});
  				resizer.on("mousemove touchmove", function(event) {
  					event.stopPropagation();
          			event.preventDefault();
  					_this._cropping.is_resizing = true;
  					_this._selection_resize_movement(event);
  				});
  				painter.on("mousemove touchmove", function(event) {
  					event.stopPropagation();
          			event.preventDefault();
  					_this._painter_painting = true;
  					_this._painter_movement(event);
  				});
  			}).on("mouseup touchend", function() {
  				if (_this._painter_painting) {
  					_this._painter_merge_drawing();
  				}
  				_this._cropping.is_dragging = false;
  				_this._cropping.is_resizing = false;
  				_this._painter_painting = false;
  				_this._variables.prev_pos = false;
  				eventbox.off("mousemove touchmove");
  				resizer.off("mousemove touchmove");
  				painter.off("mousemove touchmove");
  			});
  		},
  		_selection_resize_movement: function(e) {
  			var cropframe = this._cropping.cropframe[0];
  			var evtpos = (e.clientX) ? e : e.originalEvent.touches[0];
  			cropframe.style.width = (this._cropping.w + evtpos.clientX - this._cropping.x) + 'px';
     			cropframe.style.height = (this._cropping.h + evtpos.clientY - this._cropping.y) + 'px';
  		},
  		_selection_drag_movement: function(e) {
  			var cropframe = this._cropping.cropframe[0];
  			var evtpos = (e.pageX) ? e : e.originalEvent.touches[0];
  			this._cropping.cropframe.offset({
  				top: evtpos.pageY - parseInt(cropframe.clientHeight / 2, 10),
  				left: evtpos.pageX - parseInt(cropframe.clientWidth / 2, 10)
  			});
  		},
  		_painter_movement: function(e) {
  			var pos = {};
  			var target = e.target || e.srcElement,
  			rect = target.getBoundingClientRect(),
  			evtpos = (e.clientX) ? e : e.originalEvent.touches[0];
  			pos.x = evtpos.clientX - rect.left;
  			pos.y = evtpos.clientY - rect.top;
  			if(!this._variables.prev_pos) {
  				return this._variables.prev_pos = pos;
  			}
  			this._painter_ctx.beginPath();
      		this._painter_ctx.moveTo(this._variables.prev_pos.x, this._variables.prev_pos.y);
      		this._painter_ctx.lineTo(pos.x, pos.y);
      		this._painter_ctx.stroke();
  			this._variables.prev_pos = pos;
  		},
  		_painter_merge_drawing: function() {
  			var canvas = document.createElement('canvas');
  			var ctx = canvas.getContext("2d");
  			var _this = this;
  			canvas.width = this._image.width;
  			canvas.height = this._image.height;
  			ctx.drawImage(this._image, 0, 0, canvas.width, canvas.height);
  			ctx.drawImage(this._painter_canvas, 0, 0, canvas.width, canvas.height);
  			if(canvas.width > 1280 && canvas.height > 800) {
  				this.set_loading().delay(200).promise().done(function() {
  					_this._create_image_with_datasrc(canvas.toDataURL("image/png"), function() {
  						_this.pen_tool_params_set();
  						_this.hide_messagebox();
  					});
  				});
  			}
  			else {
  				this._create_image_with_datasrc(canvas.toDataURL("image/png"), function() {
  					_this.pen_tool_params_set();
  				});
  			}
  		},
  		// Hide all opened navigation and active buttons (clear plugin's box elements)
  		_hideAllNav: function (message) {
  			if(message && message == 1) {
  				this.set_messagebox("Open an image or use your camera to make a photo!");
  			}
  			$(this.element).find(".picedit_nav_box").removeClass("active").find(".picedit_element").removeClass("active");
  		},
  		// Paint image on canvas
  		_paintCanvas: function () {
  			this._canvas.width = this._viewport.width;
      		this._canvas.height = this._viewport.height;
  			this._ctx.drawImage(this._image, 0, 0, this._viewport.width, this._viewport.height);
  			$(this.element).find(".picedit_canvas").css("display", "block");
  		},
  		// Helper function to translate crop window size to the actual crop size
  		_calculateCropWindow: function (){
  			var view = this._viewport;		//viewport sizes
  			var cropframe = this._cropping.cropframe[0];
  			var real = {						//image real sizes
  				"width": this._image.width,
  				"height": this._image.height
  			};
  			var crop = {						//crop area sizes and position
  				"width": cropframe.clientWidth,
  				"height": cropframe.clientHeight,
  				"top": (cropframe.offsetTop > 0) ? cropframe.offsetTop : 0.1,
  				"left": (cropframe.offsetLeft > 0) ? cropframe.offsetLeft : 0.1
  			};
  			if((crop.width + crop.left) > view.width) crop.width = view.width - crop.left;
  			if((crop.height + crop.top) > view.height) crop.height = view.height - crop.top;
  			//calculate width and height for the full image size
  			var width_percent = crop.width / view.width;
  			var height_percent = crop.height / view.height;
  			var area = {
  				"width": parseInt(real.width * width_percent, 10),
  				"height": parseInt(real.height * height_percent, 10)
  			};
  			//calculate actual top and left crop position
  			var top_percent = crop.top / view.height;
  			var left_percent = crop.left / view.width;
  			area.top = parseInt(real.height * top_percent, 10);
  			area.left = parseInt(real.width * left_percent, 10);
  			return area;
  		},
  		// Helper function to perform canvas rotation
  		_doRotation: function (degrees){
  			var rads=degrees*Math.PI/180;
  			//if rotation is 90 or 180 degrees try to adjust proportions
  			var newWidth, newHeight;
  			var c = Math.cos(rads);
  			var s = Math.sin(rads);
  			if (s < 0) { s = -s; }
  			if (c < 0) { c = -c; }
  			newWidth = this._image.height * s + this._image.width * c;
  			newHeight = this._image.height * c + this._image.width * s;
  			//create temporary canvas and context
  			var canvas = document.createElement('canvas');
  			var ctx = canvas.getContext("2d");
  			canvas.width = parseInt(newWidth, 10);
  			canvas.height = parseInt(newHeight, 10);
  			// calculate the centerpoint of the canvas
  			var cx=canvas.width/2;
  			var cy=canvas.height/2;
  			// draw the rect in the center of the newly sized canvas
  			ctx.clearRect(0, 0, canvas.width, canvas.height);
  			ctx.translate(cx, cy);
  			ctx.rotate(rads);
  			ctx.drawImage(this._image, -this._image.width / 2, -this._image.height / 2);
  			this._image.src = canvas.toDataURL("image/png");
  			this._paintCanvas();
  			this.options.imageUpdated(this._image);
  		},
  		// Resize the viewport (should be done on every image change)
  		_resizeViewport: function () {
  			//get image reference
  			var img = this._image;
  			//set correct viewport width
  			var viewport = {
  				"width": img.width,
  				"height": img.height
  			};
  			if(this.options.maxWidth != 'auto' && img.width > this.options.maxWidth) viewport.width = this.options.maxWidth;
  			if(this.options.maxHeight != 'auto' && img.height > this.options.maxHeight) viewport.height = this.options.maxHeight;
  			//calculate appropriate viewport size and resize the canvas
  			if(this.options.aspectRatio) {
  				var resizeWidth = img.width;
      			var resizeHeight = img.height;
  				var aspect = resizeWidth / resizeHeight;
  				if (resizeWidth > viewport.width) {
  				  viewport.width = parseInt(viewport.width, 10);
  				  viewport.height = parseInt(viewport.width / aspect, 10);
  				}
  				if (resizeHeight > viewport.height) {
  				  aspect = resizeWidth / resizeHeight;
  				  viewport.height = parseInt(viewport.height, 10);
  				  viewport.width = parseInt(viewport.height * aspect, 10);
  				}
  			}
  			//set the viewport size (resize the canvas)
  			$(this.element).css({
  				"width": viewport.width,
  				"height": viewport.height
  			});
  			//set the global viewport
  			this._viewport = viewport;
  			//update interface data (original image width and height)
  			this._setVariable("resize_width", img.width);
  			this._setVariable("resize_height", img.height);
  		},
  		// Bind click and action callbacks to all buttons with class: ".picedit_control"
  		_bindControlButtons: function() {
  			var _this = this;
  			$(this.element).find(".picedit_control").bind( "click", function() {
  				// check to see if the element has a data-action attached to it
  				var action = $(this).data("action");
  				if(action) {
  					_this[action](this);
  				}
  				// handle click actions on top nav buttons
  				else if($(this).hasClass("picedit_action")) {
  					$(this).parent(".picedit_element").toggleClass("active").siblings(".picedit_element").removeClass("active");
  					if($(this).parent(".picedit_element").hasClass("active")) 
  						$(this).closest(".picedit_nav_box").addClass("active");
  					else 
  						$(this).closest(".picedit_nav_box").removeClass("active");
  				}
  			});
  		},
  		// Bind input elements to the application variables
  		_bindInputVariables: function() {
  			var _this = this;
  			$(this.element).find(".picedit_input").bind( "change keypress paste input", function() {
  				// check to see if the element has a data-action attached to it
  				var variable = $(this).data("variable");
  				if(variable) {
  					var value = $(this).val();
  					_this._variables[variable] = value;
  				}
  				if((variable == "resize_width" || variable == "resize_height") && _this._variables.resize_proportions) {
  					var aspect = _this._image.width / _this._image.height;
  					if(variable == "resize_width") _this._setVariable("resize_height", parseInt(value / aspect, 10));
  					else _this._setVariable("resize_width", parseInt(value * aspect, 10));
  				}
  			});
  		},
  		// Set an interface variable and update the corresponding dom element (M-V binding)
  		_setVariable: function(variable, value) {
  			this._variables[variable] = value;
  			$(this.element).find('[data-variable="' + variable + '"]').val(value);
  		},
  		// form submitted
  		_formsubmit: function() {
  			if(!window.FormData) this.set_messagebox("Sorry, the FormData API is not supported!");
  			else {
  				var _this = this;
  				this.set_loading().delay(200).promise().done(function() {
  					_this._theformdata = new FormData(_this._theform[0]);
  					if(_this._image) {
  						var inputname = $(_this.inputelement).prop("name") || "file";
  						var inputblob = _this._dataURItoBlob(_this._image.src);
  						if(!_this._filename) _this._filename = inputblob.type.replace("/", ".");
  						else _this._filename = _this._filename.match(/^[^\.]*/) + "." + inputblob.type.match(/[^\/]*$/);
  						_this._theformdata.append(inputname, inputblob, _this._filename);
  					}
  					//send request
  					var request = new XMLHttpRequest();
                      request.onprogress = function(e) {
                          if(e.lengthComputable) var total = e.total;
                          else var total = Math.ceil(inputblob.size * 1.3);
                          var progress = Math.ceil(((e.loaded)/total)*100);
                          if (progress > 100) progress = 100;
                          _this.set_messagebox("Please Wait... Uploading... " + progress + "% Uploaded.", false, false);
                      };
  					request.open(_this._theform.prop("method"), _this._theform.prop("action"), true);
  					request.onload = function(e) {
  						if(this.status != 200) {
                              _this.set_messagebox("Server did not accept data!");
                          }
                          else {
                              if(_this.options.redirectUrl === true) window.location.reload();
  						    else if(_this.options.redirectUrl) window.location = _this.options.redirectUrl;
  						    else _this.set_messagebox("Data successfully submitted!");
                          }
  						_this.options.formSubmitted(this);
  					};
  					request.send(_this._theformdata);
  				});
  			}
  			return false;
  		},
  		_dataURItoBlob: function(dataURI) {
  			if(!dataURI) return null;
  			else var mime = dataURI.match(/^data\:(.+?)\;/);
  			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: mime[1]});
  		},
  		// Prepare the template here
  		_template: function() {
  			var template = 'compiled_template_markup';
  			var _this = this;
  			$(this.inputelement).hide().after(template).each(function() {
  				_this.element = $(_this.inputelement).next(".picedit_box");
  				_this.init();
  			});
  		}
  	};
  
      // You don't need to change something below:
      // A really lightweight plugin wrapper around the constructor,
      // preventing against multiple instantiations and allowing any
      // public function (ie. a function whose name doesn't start
      // with an underscore) to be called via the jQuery plugin,
      // e.g. $(element).defaultPluginName('functionName', arg1, arg2)
      $.fn[pluginName] = function ( options ) {
          var args = arguments;
  
          // Is the first parameter an object (options), or was omitted,
          // instantiate a new instance of the plugin.
          if (options === undefined || typeof options === 'object') {
              return this.each(function () {
  
                  // Only allow the plugin to be instantiated once,
                  // so we check that the element has no plugin instantiation yet
                  if (!$.data(this, 'plugin_' + pluginName)) {
  
                      // if it has no instance, create a new one,
                      // pass options to our plugin constructor,
                      // and store the plugin instance
                      // in the elements jQuery data object.
                      $.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
                  }
              });
  
          // If the first parameter is a string and it doesn't start
          // with an underscore or "contains" the `init`-function,
          // treat this as a call to a public method.
          } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
  
              // Cache the method call
              // to make it possible
              // to return a value
              var returns;
  
              this.each(function () {
                  var instance = $.data(this, 'plugin_' + pluginName);
  
                  // Tests that there's already a plugin-instance
                  // and checks that the requested public method exists
                  if (instance instanceof Plugin && typeof instance[options] === 'function') {
  
                      // Call the method of our plugin instance,
                      // and pass it the supplied arguments.
                      returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
                  }
  
                  // Allow instances to be destroyed via the 'destroy' method
                  if (options === 'destroy') {
                    $.data(this, 'plugin_' + pluginName, null);
                  }
              });
  
              // If the earlier cached method
              // gives a value back return the value,
              // otherwise return this to preserve chainability.
              return returns !== undefined ? returns : this;
          }
      };
  
  }(jQuery, window, document));
  $("#menu-toggle").click(function(e) {
          e.preventDefault();
          $("#wrapper").toggleClass("active");
  });
  