Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial Version

  • Loading branch information...
commit b34d9aecefd8184d77fafef152ba5b0b6568a02e 0 parents
@pulkit110 authored
79 css/ImageEditor.css
@@ -0,0 +1,79 @@
+/*
+ * ImageEditor Canvas
+ * These width and height scale the canvas rather than changing its dimensions.
+.flc-image-canvas {
+ width: 500px;
+ height: 500px;
+}
+*/
+
+.fl-image-editor-border {
+ border: 2px solid red;
+}
+
+/*
+ * Misc
+ */
+
+/* hidden */
+.fl-image-editor-hidden {
+ display: none;
+}
+
+/* dimmed */
+.fl-image-editor-dim {
+ opacity: 0.4;
+ filter:alpha(opacity=40);
+}
+
+
+.fl-image-editor-buttons {
+ margin-top: 1em;
+ text-align:right;
+}
+
+/* General Buttons */
+.fl-image-editor-buttons button {
+ border-width: 1px;
+ border-style: solid;
+ font-weight: bolder;
+ font-size: 1.06em;
+ color: #FFF;
+ height: 32px;
+ padding:0px;
+ margin-right: 1em;
+ outline: none;
+ cursor: pointer;
+ width: 8.4em;
+}
+
+.fl-image-editor-buttons button:focus {
+ outline: 2px solid #142B8C;
+}
+
+.fl-image-editor-buttons .fl-image-editor-button-resize {
+ background-color: #74B74A;
+ border-color: #006838;
+}
+
+.fl-image-editor-buttons .fl-image-editor-button-resize:hover {
+ background-color: #519325;
+ border-color: #142B8C;
+}
+
+.fl-image-editor-buttons .fl-image-editor-button-crop {
+ background-color: #74B74A;
+ border-color: #006838;
+}
+
+.fl-image-editor-buttons .fl-image-editor-button-crop:hover {
+ background-color: #519325;
+ border-color: #142B8C;
+}
+
+/* disabled button */
+.fl-image-editor-buttons .fl-image-editor-dim, .fl-image-editor-buttons .fl-image-editor-dim:hover {
+ background-color: #999;
+ border-color: #666666;
+ cursor: auto;
+}
7 css/ImageEditorDemo.css
@@ -0,0 +1,7 @@
+/*
+ * ImageEditor Canvas
+ */
+#image-space {
+ width: 70%;
+ height: 70%;
+}
64 html/ImageEditorDemo.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <title>Image Editor Demo</title>
+
+ <link rel="stylesheet" type="text/css" href="../../../framework/fss/css/fss-reset-global.css" />
+ <link rel="stylesheet" type="text/css" href="../../../framework/fss/css/fss-base-global.css" />
+ <link rel="stylesheet" type="text/css" href="../../../framework/fss/css/fss-text.css" />
+ <link rel="stylesheet" type="text/css" href="../../../framework/fss/css/fss-layout.css" />
+ <link rel="stylesheet" type="text/css" href="../../uploader/css/Uploader.css" />
+ <link rel="stylesheet" type="text/css" href="../css/ImageEditor.css" />
+ <link rel="stylesheet" type="text/css" href="../css/ImageEditorDemo.css" />
+
+ <!-- Fluid and jQuery Dependencies -->
+ <script type="text/javascript" src="../../../lib/jquery/core/js/jquery.js"></script>
+ <script type="text/javascript" src="../../../lib/jquery/ui/js/jquery.ui.core.js"></script>
+ <script type="text/javascript" src="../../../framework/core/js/jquery.keyboard-a11y.js"></script>
+ <script type="text/javascript" src="../../../lib/jquery/plugins/scrollTo/js/jquery.scrollTo.js"></script>
+ <script type="text/javascript" src="../../../lib/swfobject/js/swfobject.js"></script>
+ <script type="text/javascript" src="../../../lib/swfupload/js/swfupload.js"></script>
+ <script type="text/javascript" src="../../../framework/core/js/Fluid.js"></script>
+ <script type="text/javascript" src="../../../framework/core/js/FluidDocument.js"></script>
+ <script type="text/javascript" src="../../../framework/core/js/FluidView.js"></script>
+ <script type="text/javascript" src="../../../framework/core/js/DataBinding.js"></script>
+ <script type="text/javascript" src="../../../framework/core/js/FluidIoC.js"></script>
+ <script type="text/javascript" src="../../../framework/enhancement/js/ProgressiveEnhancement.js"></script>
+
+ <!-- Uploader dependencies -->
+ <script type="text/javascript" src="../../uploader/js/Uploader.js"></script>
+ <script type="text/javascript" src="../../uploader/js/FileQueue.js"></script>
+ <script type="text/javascript" src="../../progress/js/Progress.js"></script>
+ <script type="text/javascript" src="../../uploader/js/FileQueueView.js"></script>
+ <script type="text/javascript" src="../../uploader/js/FlashUploaderSupport.js"></script>
+ <script type="text/javascript" src="../../uploader/js/Flash9UploaderSupport.js"></script>
+ <script type="text/javascript" src="../../uploader/js/HTML5UploaderSupport.js"></script>
+ <script type="text/javascript" src="../../uploader/js/DemoUploadManager.js"></script>
+ <script type="text/javascript" src="../../uploader/js/MimeTypeExtensions.js"></script>
+
+ <script type="text/javascript" src="../js/ImageEditorDemo.js"></script>
+
+ <!-- Image Editor dependencies -->
+ <script type="text/javascript" src="../js/CropperUI.js"></script>
+ <script type="text/javascript" src="../js/ImageEditor.js"></script>
+
+ </head>
+
+ <body>
+ <div id="uploader-contents"></div>
+ <!-- <div id="image-space"><canvas class="flc-image-canvas fl-image-canvas"></canvas></div> -->
+ <div id="image-space">
+ <canvas class="flc-image-canvas" width=750px height=750px></canvas>
+
+ <div class="fl-image-editor-buttons">
+ <button type="button" class="flc-image-editor-button-resize fl-image-editor-button-resize">Resize</button>
+ <button type="button" class="flc-image-editor-button-crop fl-image-editor-button-crop">Crop</button>
+ </div>
+ </div>
+ <div id="server-error"></div>
+
+
+
+ </body>
+</html>
201 include/vitals.inc.php
@@ -0,0 +1,201 @@
+<?php
+if (!defined('FLUID_IG_INCLUDE_PATH')) { exit; }
+
+define('FLUID_IG_DEVEL', 1);
+
+// set the default timezone to avoid the warning of "cannot rely on system timezone"
+date_default_timezone_set('America/New_York');
+
+// get the protocol
+if (isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) == 'on')) {
+ $server_protocol = 'https://';
+} else {
+ $server_protocol = 'http://';
+}
+
+// Calculate the base href
+$dir_deep = substr_count(FLUID_IG_INCLUDE_PATH, '..');
+$url_parts = explode('/', $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']);
+$_base_href = array_slice($url_parts, 0, count($url_parts) - $dir_deep-1);
+$_base_href = $server_protocol . implode('/', $_base_href).'/';
+
+$endpos = strlen($_base_href);
+
+$_base_href = substr($_base_href, 0, $endpos);
+$_base_path = substr($_base_href, strlen($server_protocol . $_SERVER['HTTP_HOST']));
+
+define('FLUID_IG_BASE_HREF', $_base_href);
+
+/**
+ * Get the list of all the sub-directories in the given directory
+ * @access public
+ * @param string $directory the directory to search in
+ * @return an array of all the sub-directories
+ */
+function getAllDirs($directory) {
+ $result = array();
+ $handle = opendir($directory);
+ while ($datei = readdir($handle))
+ {
+ if (($datei != '.') && ($datei != '..'))
+ {
+ $file = $directory.$datei;
+ if (is_dir($file)) {
+ $result[] = $file;
+ }
+ }
+ }
+ closedir($handle);
+ return $result;
+}
+
+/**
+* Enables the deletion of a directory even if it is not empty
+* @access public
+* @param string $directory the directory to delete
+* @return boolean whether the deletion was successful
+*/
+function remove_dir($directory) {
+ if(!$opendir = @opendir($directory)) {
+ return false;
+ }
+
+ while(($readdir=readdir($opendir)) !== false) {
+ if (($readdir !== '..') && ($readdir !== '.')) {
+ $readdir = trim($readdir);
+
+ clearstatcache(); /* especially needed for Windows machines: */
+
+ if (is_file($directory.'/'.$readdir)) {
+ if(!@unlink($directory.'/'.$readdir)) {
+ return false;
+ }
+ } else if (is_dir($directory.'/'.$readdir)) {
+ /* calls itself to clear subdirectories */
+ if(!remove_dir($directory.'/'.$readdir)) {
+ return false;
+ }
+ }
+ }
+ } /* end while */
+
+ @closedir($opendir);
+
+ if(!@rmdir($directory)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Scan through the given $directory, remove the sub-folders that are older than the given seconds.
+ * @access public
+ * @param string $directory the path to the folder
+ * integer $secs_to_live the seconds that the folder should not be deleted since its creation
+ * @return boolean
+ */
+function clean_history($directory, $secs_to_live) {
+ $check_point = strtotime("-".$secs_to_live." seconds");
+
+ $allDirs = getAllDirs($directory);
+
+ $highestKnown = 0;
+ foreach ($allDirs as $one_dir) {
+ $currentValue = filectime($one_dir);
+ $currentMValue = filemtime($one_dir);
+
+ if ($currentMValue > $currentValue) {
+ $currentValue = $currentMValue;
+ }
+
+ if ($currentValue < $check_point) {
+ remove_dir($one_dir);
+ }
+ }
+ return true;
+}
+
+/**
+ * Return error message with the http status code 403
+ * @access public
+ * @param string err_string the error message
+ * integer return_err_in_html 1/0. Return error message in a complete html
+ */
+function return_error($err_string, $return_err_in_html) {
+ if ($return_err_in_html) {
+ $error = '<html><p><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>';
+ }
+
+ $error .= $err_string;
+
+ if ($return_err_in_html) {
+ $error .= '</p></html>';
+ }
+
+ header("HTTP/1.1 403 Forbidden", TRUE, 403);
+ header('Content-length: '. strlen($error));
+ echo $error;
+}
+
+/**
+ * Return success msg with http status code 200
+ * @access public
+ * @param $success_string the success message
+ */
+function return_success($success_string) {
+ echo "<html><body><p>".$success_string."</p></body></html>";
+}
+
+/**
+ * This function is used for printing variables for debugging.
+ * @access public
+ * @param mixed $var The variable to output
+ * @param string $title The name of the variable, or some mark-up identifier.
+ */
+function debug($var, $title='') {
+ if (!defined('FLUID_IG_DEVEL') || !FLUID_IG_DEVEL) {
+ return;
+ }
+
+ echo '<pre style="border: 1px black solid; padding: 0px; margin: 10px;" title="debugging box">';
+ if ($title) {
+ echo '<h4>'.$title.'</h4>';
+ }
+
+ ob_start();
+ print_r($var);
+ $str = ob_get_contents();
+ ob_end_clean();
+
+ $str = str_replace('<', '&lt;', $str);
+
+ $str = str_replace('[', '<span style="color: red; font-weight: bold;">[', $str);
+ $str = str_replace(']', ']</span>', $str);
+ $str = str_replace('=>', '<span style="color: blue; font-weight: bold;">=></span>', $str);
+ $str = str_replace('Array', '<span style="color: purple; font-weight: bold;">Array</span>', $str);
+ echo $str;
+ echo '</pre>';
+}
+
+/**
+ * This function is used for printing variables into log file for debugging.
+ * @access public
+ * @param mixed $var The variable to output
+ * @param string $log The location of the log file. If not provided, use the default one.
+ */
+function debug_to_log($var, $log='') {
+ if (!defined('FLUID_IG_DEVEL') || !FLUID_IG_DEVEL) {
+ return;
+ }
+
+ if ($log == '') $log = 'temp/debug.log';
+
+ $handle = fopen($log, 'a');
+ fwrite($handle, "\n\n");
+ fwrite($handle, date("F j, Y, g:i a"));
+ fwrite($handle, "\n");
+ fwrite($handle, var_export($var,1));
+
+ fclose($handle);
+}
+?>
485 js/CropperUI.js
@@ -0,0 +1,485 @@
+var Cropper = new function() {
+ // holds all our boxes
+ var boxes2 = [];
+
+ // New, holds the 8 tiny boxes that will be our selection handles
+ // the selection handles will be in this order:
+ // 0 1 2
+ // 3 4
+ // 5 6 7
+ var selectionHandles = [];
+
+ // Hold canvas information
+ var canvas;
+ var ctx;
+ var WIDTH;
+ var HEIGHT;
+ var resizeFactor;
+ var image;
+ var INTERVAL = 20; // how often, in milliseconds, we check to see if a redraw is needed
+ var imageX;
+ var imageY;
+
+ var isDrag = false;
+ var isResizeDrag = false;
+ var expectResize = -1; // New, will save the # of the selection handle if the mouse is over one.
+ var mx, my; // mouse coordinates
+
+ // when set to true, the canvas will redraw everything
+ // invalidate() just sets this to false right now
+ // we want to call invalidate() whenever we make a change
+ var canvasValid = false;
+
+ // The node (if any) being selected.
+ var mySel = null;
+
+ // The selection color and width. Right now we have a red selection with a small width
+ var mySelColor = '#CC0000';
+ var mySelWidth = 0;
+ var mySelBoxColor = 'darkred'; // New for selection boxes
+ var mySelBoxSize = 6;
+
+ var blurStyle = 'rgba(0,0,0,0.4)';
+
+ // we use a fake canvas to draw individual shapes for selection testing
+ var ghostcanvas;
+ var gctx; // fake canvas context
+
+ // since we can drag from anywhere in a node
+ // instead of just its x/y corner, we need to save
+ // the offset of the mouse when we start dragging.
+ var offsetx, offsety;
+
+ // Padding and border style widths for mouse offsets
+ var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
+
+ // Box object to hold data
+ function Box2() {
+ this.x = 0;
+ this.y = 0;
+ this.w = 1; // default width and height?
+ this.h = 1;
+ this.fill = '#444444';
+ }
+
+ // New methods on the Box class
+ Box2.prototype = {
+ // we used to have a solo draw function
+ // each box is responsible for its own drawing
+ // mainDraw() will call this with the normal canvas
+ // cropperMouseDown will call this with the ghost canvas with 'black'
+ draw: function(context, optionalColor) {
+ if (context === gctx) {
+ context.fillStyle = 'black'; // always want black for the ghost canvas
+ } else {
+ context.fillStyle = this.fill;
+ }
+
+ // We can skip the drawing of elements that have moved off the screen:
+ if (this.x > WIDTH || this.y > HEIGHT)
+ return;
+ if (this.x + this.w < 0 || this.y + this.h < 0)
+ return;
+
+ //correct h and w if they get negative
+ if (this.h < 0) {
+ tempY = this.y + this.h;
+ tempH = -this.h;
+ } else {
+ tempY = this.y;
+ tempH = this.h;
+ }
+
+ if (this.w < 0) {
+ tempX = this.x + this.w;
+ tempW = -this.w;
+ } else {
+ tempX = this.x;
+ tempW = this.w;
+ }
+
+ //Draw the rectangle for cropping
+ context.fillRect(this.x,this.y,this.w,this.h);
+
+ //draw blurred area around the rectangle
+ context.fillStyle = blurStyle;
+ context.fillRect(0,0,WIDTH,tempY);
+ context.fillRect(0, tempY, tempX, HEIGHT-tempY);
+ context.fillRect(tempX + tempW, tempY, WIDTH - (tempX + tempW), HEIGHT - tempY);
+ context.fillRect(tempX, tempY + tempH, tempW, HEIGHT-tempY);
+
+ // draw selection
+ // this is a stroke along the box and also 8 new selection handles
+ if (mySel === this) {
+ context.strokeStyle = mySelColor;
+ context.lineWidth = mySelWidth;
+ context.strokeRect(this.x,this.y,this.w,this.h);
+
+ // draw the boxes
+
+ var half = mySelBoxSize / 2;
+
+ // 0 1 2
+ // 3 4
+ // 5 6 7
+
+ // top left, middle, right
+ selectionHandles[0].x = this.x-half;
+ selectionHandles[0].y = this.y-half;
+
+ selectionHandles[1].x = this.x+this.w/2-half;
+ selectionHandles[1].y = this.y-half;
+
+ selectionHandles[2].x = this.x+this.w-half;
+ selectionHandles[2].y = this.y-half;
+
+ //middle left
+ selectionHandles[3].x = this.x-half;
+ selectionHandles[3].y = this.y+this.h/2-half;
+
+ //middle right
+ selectionHandles[4].x = this.x+this.w-half;
+ selectionHandles[4].y = this.y+this.h/2-half;
+
+ //bottom left, middle, right
+ selectionHandles[6].x = this.x+this.w/2-half;
+ selectionHandles[6].y = this.y+this.h-half;
+
+ selectionHandles[5].x = this.x-half;
+ selectionHandles[5].y = this.y+this.h-half;
+
+ selectionHandles[7].x = this.x+this.w-half;
+ selectionHandles[7].y = this.y+this.h-half;
+
+ context.fillStyle = mySelBoxColor;
+ for (var i = 0; i < 8; i ++) {
+ var cur = selectionHandles[i];
+ context.fillRect(cur.x, cur.y, mySelBoxSize, mySelBoxSize);
+ }
+ }
+ } // end draw
+ }
+
+ //Initialize a new Box, add it, and invalidate the canvas
+ function addRect(x, y, w, h, fill) {
+ var rect = new Box2;
+ rect.x = x;
+ rect.y = y;
+ rect.w = w
+ rect.h = h;
+ rect.fill = fill;
+ boxes2.push(rect);
+ invalidate();
+ }
+
+ // initialize our canvas, add a ghost canvas, set draw loop
+ // then add everything we want to intially exist on the canvas
+ this.init = function(that) {
+ canvas = that.imageCanvas.get()[0];
+ HEIGHT = canvas.height;
+ WIDTH = canvas.width;
+ ctx = canvas.getContext('2d');
+ resizeFactor = that.resizeFactor;
+ image = that.image;
+ imageX = that.imageX;
+ imageY = that.imageY;
+
+ ghostcanvas = document.createElement('canvas');
+ ghostcanvas.height = HEIGHT;
+ ghostcanvas.width = WIDTH;
+ gctx = ghostcanvas.getContext('2d');
+
+ //fixes a problem where double clicking causes text to get selected on the canvas
+ canvas.onselectstart = function () {
+ return false;
+ }
+ // fixes mouse co-ordinate problems when there's a border or padding
+ // see getMouse for more detail
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingLeft'], 10) || 0;
+ stylePaddingTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingTop'], 10) || 0;
+ styleBorderLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderLeftWidth'], 10) || 0;
+ styleBorderTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderTopWidth'], 10) || 0;
+ }
+
+ // make mainDraw() fire every INTERVAL milliseconds
+ that.cropperID = setInterval(mainDraw, INTERVAL);
+
+ // set our events. Up and down are for dragging,
+ // double click is for making new boxes
+ canvas.onmousedown = cropperMouseDown;
+ canvas.onmouseup = cropperMouseUp;
+ canvas.onmousemove = cropperMouseMove;
+
+ // set up the selection handle boxes
+ for (var i = 0; i < 8; i ++) {
+ var rect = new Box2;
+ selectionHandles.push(rect);
+ }
+
+ // add the rectangle for cropping area
+ addRect(240, 120, 40, 40, 'rgba(2,165,165,0.0)');
+
+ }
+
+ //wipes the canvas context
+ function clear(c) {
+ c.clearRect(0, 0, WIDTH, HEIGHT);
+ }
+
+ // Main draw loop.
+ // While draw is called as often as the INTERVAL variable demands,
+ // It only ever does something if the canvas gets invalidated by our code
+ function mainDraw() {
+ if (canvasValid == false) {
+ clear(ctx);
+
+ // Add stuff you want drawn in the background all the time here
+ drawImage(ctx, image, resizeFactor, imageX, imageY);
+
+ // draw all boxes
+ var l = boxes2.length;
+ for (var i = 0; i < l; i++) {
+ boxes2[i].draw(ctx); // we used to call drawshape, but now each box draws itself
+ }
+
+ canvasValid = true;
+ }
+ }
+
+ // Happens when the mouse is moving inside the canvas
+ function cropperMouseMove(e) {
+ if (isDrag) {
+ getMouse(e);
+
+ mySel.x = mx - offsetx;
+ mySel.y = my - offsety;
+
+ // something is changing position so we better invalidate the canvas!
+ invalidate();
+ } else if (isResizeDrag) {
+ // time ro resize!
+ var oldx = mySel.x;
+ var oldy = mySel.y;
+
+ // 0 1 2
+ // 3 4
+ // 5 6 7
+ switch (expectResize) {
+ case 0:
+ mySel.x = mx;
+ mySel.y = my;
+ mySel.w += oldx - mx;
+ mySel.h += oldy - my;
+ break;
+ case 1:
+ mySel.y = my;
+ mySel.h += oldy - my;
+ break;
+ case 2:
+ mySel.y = my;
+ mySel.w = mx - oldx;
+ mySel.h += oldy - my;
+ break;
+ case 3:
+ mySel.x = mx;
+ mySel.w += oldx - mx;
+ break;
+ case 4:
+ mySel.w = mx - oldx;
+ break;
+ case 5:
+ mySel.x = mx;
+ mySel.w += oldx - mx;
+ mySel.h = my - oldy;
+ break;
+ case 6:
+ mySel.h = my - oldy;
+ break;
+ case 7:
+ mySel.w = mx - oldx;
+ mySel.h = my - oldy;
+ break;
+ }
+
+ invalidate();
+ }
+
+ getMouse(e);
+
+ // if the mouse is in box, then change the cursor
+ var l = boxes2.length;
+ var i = 0;
+ for (i = 0; i < l; i++) {
+ if (mx >= boxes2[i].x && mx <= boxes2[i].x + boxes2[i].w && my >= boxes2[i].y && my <= boxes2[i].y + boxes2[i].h) {
+ this.style.cursor='move';
+ break;
+ }
+ }
+ if (i == l) {
+ this.style.cursor='auto';
+ }
+
+ // if there's a selection see if we grabbed one of the selection handles
+ if (mySel !== null && !isResizeDrag) {
+ for (var i = 0; i < 8; i++) {
+ // 0 1 2
+ // 3 4
+ // 5 6 7
+
+ var cur = selectionHandles[i];
+
+ // we dont need to use the ghost context because
+ // selection handles will always be rectangles
+ if (mx >= cur.x && mx <= cur.x + mySelBoxSize &&
+ my >= cur.y && my <= cur.y + mySelBoxSize) {
+ // we found one!
+ expectResize = i;
+ invalidate();
+
+ switch (i) {
+ case 0:
+ this.style.cursor='nw-resize';
+ break;
+ case 1:
+ this.style.cursor='n-resize';
+ break;
+ case 2:
+ this.style.cursor='ne-resize';
+ break;
+ case 3:
+ this.style.cursor='w-resize';
+ break;
+ case 4:
+ this.style.cursor='e-resize';
+ break;
+ case 5:
+ this.style.cursor='sw-resize';
+ break;
+ case 6:
+ this.style.cursor='s-resize';
+ break;
+ case 7:
+ this.style.cursor='se-resize';
+ break;
+ }
+ return;
+ }
+
+ }
+ // not over a selection box, return to normal
+ isResizeDrag = false;
+ expectResize = -1;
+ }
+
+ }
+
+ // Happens when the mouse is clicked in the canvas
+ function cropperMouseDown(e) {
+ getMouse(e);
+
+ //we are over a selection box
+ if (expectResize !== -1) {
+ isResizeDrag = true;
+ return;
+ }
+
+ clear(gctx);
+ var l = boxes2.length;
+ for (var i = l-1; i >= 0; i--) {
+ // draw shape onto ghost context
+ boxes2[i].draw(gctx, 'black');
+
+ // get image data at the mouse x,y pixel
+ var imageData = gctx.getImageData(mx, my, 1, 1);
+ var index = (mx + my * imageData.width) * 4;
+
+ // if the mouse pixel exists, select and break
+ if (imageData.data[3] > 0) {
+ mySel = boxes2[i];
+ offsetx = mx - mySel.x;
+ offsety = my - mySel.y;
+ mySel.x = mx - offsetx;
+ mySel.y = my - offsety;
+ isDrag = true;
+
+ invalidate();
+ clear(gctx);
+ return;
+ }
+
+ }
+ // havent returned means we have selected nothing
+ mySel = null;
+ // clear the ghost canvas for next time
+ clear(gctx);
+ // invalidate because we might need the selection border to disappear
+ invalidate();
+ }
+
+ function cropperMouseUp() {
+ isDrag = false;
+ isResizeDrag = false;
+ expectResize = -1;
+
+ if (mySel) {
+ if (mySel.h < 0) {
+ mySel.y += mySel.h;
+ mySel.h *= -1;
+ }
+
+ if (mySel.w < 0) {
+ mySel.x += mySel.w;
+ mySel.w *= -1;
+ }
+ }
+ }
+
+ // adds a new node
+ function myDblClick(e) {
+ getMouse(e);
+ // for this method width and height determine the starting X and Y, too.
+ // so I left them as vars in case someone wanted to make them args for something and copy this code
+ var width = 20;
+ var height = 20;
+ //addRect(mx - (width / 2), my - (height / 2), width, height, 'rgba(220,205,65,0.7)');
+ }
+
+ function invalidate() {
+ canvasValid = false;
+ }
+
+ // Sets mx,my to the mouse position relative to the canvas
+ // unfortunately this can be tricky, we have to worry about padding and borders
+ function getMouse(e) {
+ var element = canvas, offsetX = 0, offsetY = 0;
+
+ if (element.offsetParent) {
+ do {
+ offsetX += element.offsetLeft;
+ offsetY += element.offsetTop;
+ } while ((element = element.offsetParent));
+ }
+
+ // Add padding and border style widths to offset
+ offsetX += stylePaddingLeft;
+ offsetY += stylePaddingTop;
+
+ offsetX += styleBorderLeft;
+ offsetY += styleBorderTop;
+
+ mx = e.pageX - offsetX;
+ my = e.pageY - offsetY
+ }
+
+ function drawImage(imageCanvasContext, image, resizeFactor) {
+ imageCanvasContext.drawImage(image, imageX, imageY, image.width/resizeFactor, image.height/resizeFactor);
+ }
+
+ this.reset = function() {
+ boxes2 = [];
+ canvas.onmousedown = null;
+ canvas.onmouseup = null;
+ canvas.onmousemove = null;
+ }
+}
212 js/ImageEditor.js
@@ -0,0 +1,212 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010-2011 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
+*/
+
+// Declare dependencies
+/*global window, fluid_1_4:true, jQuery*/
+
+// JSLint options
+/*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */
+
+var fluid_1_4 = fluid_1_4 || {};
+
+/****************
+ * Image Editor *
+ ****************/
+
+(function ($, fluid) {
+
+ //we'll add some private methods here
+
+ var enableElement = function (that, elm) {
+ elm.prop("disabled", false);
+ elm.removeClass(that.options.styles.dim);
+ };
+
+ var disableElement = function (that, elm) {
+ elm.prop("disabled", true);
+ elm.addClass(that.options.styles.dim);
+ };
+
+ var showElement = function (that, elm) {
+ elm.removeClass(that.options.styles.hidden);
+ };
+
+ var hideElement = function (that, elm) {
+ elm.addClass(that.options.styles.hidden);
+ };
+
+ var bindDOMEvents = function (that) {
+ that.locate("resizeButton").click(function () {
+ //TODO: Bind resize event
+ //that.start();
+ });
+
+ that.locate("cropButton").click(function () {
+ setupCrop(that);
+ });
+ };
+
+ var setupCrop = function (that) {
+ if (that.cropStarted) {
+ that.cropStarted = false;
+ enableElement(that, that.resizeButton);
+ clearInterval(that.cropperID);
+ clear (that);
+ drawImage (that);
+ Cropper.reset();
+ } else {
+ disableElement(that, that.resizeButton);
+ that.cropStarted = true;
+ Cropper.init(that);
+ }
+
+ }
+
+ function clear(that) {
+ that.imageCanvas.get()[0].getContext('2d').clearRect(0, 0, that.imageCanvas.width, that.imageCanvas.height);
+ }
+
+ function drawImage (that) {
+ var imageCanvas = that.imageCanvas.get()[0];
+ var imageCanvasContext = imageCanvas.getContext('2d'); // Obtain the context
+
+ // Maintain aspect ratio while resizing larger image to smaller canvas.
+ if (that.image.height > imageCanvas.height || that.image.width > imageCanvas.width) {
+ var heightRatio = that.image.height/imageCanvas.height;
+ var widthRatio = that.image.width/imageCanvas.width;
+
+ that.resizeFactor = (heightRatio < widthRatio)?widthRatio:heightRatio;
+
+ that.imageX = (imageCanvas.width - that.image.width/that.resizeFactor)/2;
+ that.imageY = (imageCanvas.height - that.image.height/that.resizeFactor)/2;
+
+ imageCanvasContext.drawImage(that.image, that.imageX, that.imageY, that.image.width/that.resizeFactor, that.image.height/that.resizeFactor);
+
+ } else {
+ that.resizeFactor = 1;
+ imageCanvasContext.drawImage(img, 0, 0);
+ }
+ }
+
+ var setupImageEditor = function (that) {
+
+ // Inject canvas element to the container
+ //that.container.append('<canvas class=\"flc-image-canvas\" id =\"fl-image-canvas\"></canvas>');
+ that.imageCanvas = that.locate("imageCanvas");
+ that.menuBar = that.locate("menuBar");
+ that.resizeButton = that.locate("resizeButton");
+ that.cropButton = that.locate("cropButton");
+
+ that.cropStarted = false;
+
+ that.imageCanvas.addClass (that.options.styles.border);
+
+ disableElement(that, that.cropButton);
+ disableElement(that, that.resizeButton);
+
+ bindDOMEvents(that);
+
+ // Uploader uses application-style keyboard conventions, so give it a suitable role.
+ //that.container.attr("role", "application");
+ };
+
+ /**
+ * Instantiates a new Image Editor component.
+ *
+ * @param {Object} container the DOM element in which the Image Editor lives
+ * @param {Object} options configuration options for the component.
+ */
+ fluid.imageEditor = function (container, options) {
+ var that = fluid.initView("fluid.imageEditor", container, options);
+
+ setupImageEditor(that);
+
+ that.setImage = function (imageURL) {
+
+ that.image = new Image(); // Create a new img element
+
+ that.image.onload = function() {
+
+ drawImage (that);
+
+ enableElement(that, that.cropButton);
+ enableElement(that, that.resizeButton);
+ }
+ that.image.src = imageURL; // Set the source path
+ }
+
+ //that.displayElement.hide();
+
+
+ return that;
+ };
+
+ fluid.defaults("fluid.imageEditor", {
+ gradeNames: "fluid.viewComponent",
+ selectors: {
+ imageCanvas: ".flc-image-canvas", // required, the canvas element that shows the image
+ menuBar: ".flc-menu-bar", //required, provides different functions
+ resizeButton: ".flc-image-editor-button-resize", //required, Resize Button
+ cropButton: ".flc-image-editor-button-crop" //required, Crop Button
+ },
+
+ styles: {
+ disabled: "fl-image-editor-disabled",
+ hidden: "fl-image-editor-hidden",
+ dim: "fl-image-editor-dim",
+ border: "fl-image-editor-border"
+ },
+
+ //TODO: Change as needed
+ strings: {
+ //Empty value for ariaBusyText will default to aria-valuenow.
+ ariaBusyText: "Progress is %percentComplete percent complete",
+ ariaDoneText: "Progress is complete."
+ },
+
+ // progress display and hide animations, use the jQuery animation primatives, set to false to use no animation
+ // animations must be symetrical (if you hide with width, you'd better show with width) or you get odd effects
+ // see jQuery docs about animations to customize
+ showAnimation: {
+ params: {
+ opacity: "show"
+ },
+ duration: "slow",
+ //callback has been deprecated and will be removed as of 1.5, instead use onProgressBegin event
+ callback: null
+ }, // equivalent of $().fadeIn("slow")
+
+ hideAnimation: {
+ params: {
+ opacity: "hide"
+ },
+ duration: "slow",
+ //callback has been deprecated and will be removed as of 1.5, instead use afterProgressHidden event
+ callback: null
+ }, // equivalent of $().fadeOut("slow")
+
+ events: {
+ onProgressBegin: null,
+ afterProgressHidden: null
+ },
+
+ minWidth: 5, // 0 length indicators can look broken if there is a long pause between updates
+ delay: 0, // the amount to delay the fade out of the progress
+ speed: 200, // default speed for animations, pretty fast
+ animate: "forward", // suppport "forward", "backward", and "both", any other value is no animation either way
+ initiallyHidden: true, // supports progress indicators which may always be present
+ updatePosition: false
+ });
+ //we'll put our default options here
+
+})(jQuery, fluid_1_4);
41 js/ImageEditorDemo.js
@@ -0,0 +1,41 @@
+jQuery(document).ready(function () {
+ // Load the Uploader's template via AJAX and inject it into this page.
+ var templateURLSelector = "../../uploader/html/Uploader.html .fl-uploader";
+ $("#uploader-contents").load(templateURLSelector, null, function () {
+
+ var myImageEditor = fluid.imageEditor("#image-space");
+
+ // Initialize the Uploader
+ var myUpload = fluid.uploader(".flc-uploader", {
+ components: {
+ strategy: {
+ options: {
+ flashMovieSettings: {
+ flashURL: "../../../lib/swfupload/flash/swfupload.swf",
+ flashButtonImageURL: "../../uploader/images/browse.png"
+ }
+ }
+ }
+ },
+ queueSettings: {
+ // Set the uploadURL to the URL for posting files to your server.
+ uploadURL: "../uploader.php?session=tempSession"
+ },
+ listeners: {
+ onFileSuccess: function (file, responseText, xhr) {
+ alert(responseText);
+
+ myImageEditor.setImage(responseText);
+ },
+ onFileError: function (file, error, status, xhr) {
+ alert(error);
+ // example assumes that the server code passes the reason of the failure in the xhr
+ $('#server-error').append(file.name + " - " + xhr.responseText + "<br />");
+ },
+ afterUploadComplete: function () {
+ //alert("Uploder next step");
+ }
+ }
+ });
+ });
+});
96 uploader.php
@@ -0,0 +1,96 @@
+<?php
+
+/*
+Copyright 2011 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
+*/
+
+define('FLUID_IG_INCLUDE_PATH', 'include/');
+
+include("include/vitals.inc.php");
+
+// The constants
+$allowed_file_extensions = array('gif', 'png', 'jpg', 'jpeg', 'tif', 'tiff'); // The array of allowed file extensions: gif, png, jpg, tif
+$secs_to_timeout = 3600; // The seconds to keep the uploaded images
+$temp_dir = 'temp/';
+
+// Remove all the folders that are older than 3600 seconds
+clean_history($temp_dir, $secs_to_timeout);
+
+if (isset($_REQUEST['isSingleUploader']) && $_REQUEST['isSingleUploader']) {
+ $_REQUEST['session'] = 'single';
+ $return_err_in_html = 1;
+} else {
+ $return_err_in_html = 0;
+}
+
+// Error checkings:
+// 1. whether the file is received;
+// 2. whether session id is provided;
+// 3. whether the file extension is allowed;
+// 4. whether $temp_dir exists;
+// 5. whether the file has been uploaded.
+
+// 1. Return error if there is no file received
+if (count($_FILES) == 0) {
+ return_error("No file is received at server.", $return_err_in_html);
+ exit;
+}
+
+// 2. Return error if the session id is not given
+if (!isset($_REQUEST['session']) || strlen($_REQUEST['session']) == 0) {
+ return_error("Session ID is not provided.", $return_err_in_html);
+ exit;
+}
+
+foreach ($_FILES as $name => $file_data) {
+ // 3. Return error if the file extension is not in the list that is allowed
+ $file_name = $file_data['name'];
+ $file_extension = strtolower(substr($file_name, strrpos($file_name, '.') + 1));
+
+ if (!in_array($file_extension, $allowed_file_extensions)) {
+ return_error('File extension <span style="font: bold">'.$file_extension.'</span> is not allowed.', $return_err_in_html);
+ exit;
+ }
+
+ // 4. Return error if $temp_dir does not exist
+ if (!file_exists($temp_dir)) {
+ return_error('Temp folder <span style="font: bold">'.$temp_dir.'</span> does not exist.', $return_err_in_html);
+ exit;
+ }
+
+ // Find or even create the image folder, if it does not exist, for this round of upload
+ $image_folder = $temp_dir . $_REQUEST['session'].'/';
+ if (!file_exists($image_folder) && !mkdir($image_folder)) {
+ return_error('Cannot create image folder <span style="font: bold">'.$image_folder.'</span>.', $return_err_in_html);
+ exit;
+ }
+ // END OF error checking
+
+ $destination = $image_folder.$file_name;
+ // 5. Return error if the file has been uploaded
+ if (file_exists($destination)) {
+ echo FLUID_IG_BASE_HREF.$destination;
+ return_error($file_name.' has already been uploaded.', $return_err_in_html);
+ exit;
+ }
+
+ // Copy the uploaded file into the image folder
+ move_uploaded_file($file_data['tmp_name'], $destination);
+
+ if (isset($_REQUEST['isSingleUploader']) && $_REQUEST['isSingleUploader']) {
+ // At single file uploader, display the uploaded image right after upload
+ echo '<a href="'.FLUID_IG_BASE_HREF.'uploader.html">Back to image gallery demo</a><br/><br/>';
+ echo '<img src="'.htmlentities(FLUID_IG_BASE_HREF.$destination).'" alt="'.$file_name.'" />';
+ } else {
+ // At multi-file uploader, return the url to the uploaded image
+ echo FLUID_IG_BASE_HREF.$destination;
+ }
+}
+?>
Please sign in to comment.
Something went wrong with that request. Please try again.