User:Arjunaraoc/scripts/cropimage.js

/** * Tutorial script: ExtractImage ("Extract area typically image from pdf/djvu files of pages") * * A user script which adds a link to the toolbox to show a pop-up * dialog with page graphic of a book and  enable a selection of an area(typically image) in it. * * Demonstrates: * - Use of the API * - Use of jQuery * - Use of ResourceLoader and some of the default modules that come with it * - Use of localization * * (Be bold and improve it!) * * Authors: * Arjuna Rao Chavala, 2015, Public domain * */ //$(document).ready(function { //$(function(mw) { // Import the jQuery dialog plugin before starting the rest of this script mw.loader.using(['jquery.ui'], function {   //        var cancel = function {    //            $("#myDialog").dialog("close");    //        }    var dialogobjcreated;    function renderCropImageDialog(pageLinks) {        dialogobj = $(this);        //    	dialogobj.dialog('destroy');        var cictext=[""];        var scale=1.50;        $dialog = $('  ')            .html( ' ' +               '' + 'This text is displayed if your browser does not support HTML5 Canvas.' + ' ' +               ' '                //				pageLinks.join( ' ' ) + '' )           .dialog({ autoOpen: true, title: 'Crop image!', dialogClass: "no-close", width: '70%', modal: true, closeOnEscape: false, //               open: function (event, ui) { //	 $(".ui-dialog-titlebar-close", ui.dialog || ui).hide; //		 		},               buttons: { "Done": function { getTemplatetext(cictext,scale); console.log(cictext[0]); //add to document var $txt = $('#wpTextbox1'); //							$txt.prepend(cictext[0]); //did not work on page namespace $("#wpTextbox1").val(cictext[0]+$("#wpTextbox1").val); $(this).dialog('destroy').remove; },                   "Cancel": function(event, ui) { $(this).dialog('destroy').remove; //cancel }               },            });    }    function cropimage {            var myPageLinks = [];            // remdering and data preparations to be done            renderCropImageDialog(myPageLinks);			shapeinit;        }        // By Simon Sarris        // www.simonsarris.com        // sarris@acm.org        //        // Code from the following pages merged by Andrew Clark (amclark7@gmail.com):        //   http://simonsarris.com/blog/510-making-html5-canvas-useful        //   http://simonsarris.com/blog/225-canvas-selecting-resizing-shape        // Last update June 2013        //        // Free to use and distribute at will        // So long as you are nice to people, etc

// Constructor for Shape objects to hold data for all drawn objects. // For now they will just be defined as rectangles. function Shape(state, x, y, w, h, fill) { "use strict"; // This is a very simple and unsafe constructor. All we're doing is checking if the values exist. // "x || 0" just means "if there is a value for x, use that. Otherwise use 0." // But we aren't checking anything else! We could put "Lalala" for the value of x        this.state = state; this.x = x || 0; this.y = y || 0; this.w = w || 1; this.h = h || 1; this.fill = fill || '#AAAAAA'; }

// Draws this shape to a given context Shape.prototype.draw = function(ctx, optionalColor) { "use strict"; var i, cur, half; ctx.fillStyle = this.fill; ctx.fillRect(this.x, this.y, this.w, this.h); if (this.state.selection === this) { ctx.strokeStyle = this.state.selectionColor; ctx.lineWidth = this.state.selectionWidth; ctx.strokeRect(this.x, this.y, this.w, this.h);

// draw the boxes half = this.state.selectionBoxSize / 2;

// 0 1  2            // 3     4            // 5  6  7

// top left, middle, right this.state.selectionHandles[0].x = this.x - half; this.state.selectionHandles[0].y = this.y - half;

this.state.selectionHandles[1].x = this.x + this.w / 2 - half; this.state.selectionHandles[1].y = this.y - half;

this.state.selectionHandles[2].x = this.x + this.w - half; this.state.selectionHandles[2].y = this.y - half;

//middle left this.state.selectionHandles[3].x = this.x - half; this.state.selectionHandles[3].y = this.y + this.h / 2 - half;

//middle right this.state.selectionHandles[4].x = this.x + this.w - half; this.state.selectionHandles[4].y = this.y + this.h / 2 - half;

//bottom left, middle, right this.state.selectionHandles[6].x = this.x + this.w / 2 - half; this.state.selectionHandles[6].y = this.y + this.h - half;

this.state.selectionHandles[5].x = this.x - half; this.state.selectionHandles[5].y = this.y + this.h - half;

this.state.selectionHandles[7].x = this.x + this.w - half; this.state.selectionHandles[7].y = this.y + this.h - half; ctx.fillStyle = this.state.selectionBoxColor; for (i = 0; i < 8; i += 1) { cur = this.state.selectionHandles[i]; ctx.fillRect(cur.x, cur.y, this.state.selectionBoxSize, this.state.selectionBoxSize); }       }    };

// Determine if a point is inside the shape's bounds Shape.prototype.contains = function(mx, my) { "use strict"; // All we have to do is make sure the Mouse X,Y fall in the area between // the shape's X and (X + Height) and its Y and (Y + Height) return (this.x <= mx) && (this.x + this.w >= mx) && (this.y <= my) && (this.y + this.h >= my); };	var myState=[]; //need to access it for return values for shape code var imageObj = new Image; var curResourceCounter = 0; var initcomplete = false; function CanvasState(canvas) { "use strict"; // **** First some setup! ****       this.canvas = canvas; this.width = canvas.width; this.height = canvas.height; this.ctx = canvas.getContext('2d'); // This complicates things a little but but fixes mouse co-ordinate problems // when there's a border or padding. See getMouse for more detail var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop, html, i;       if (document.defaultView && document.defaultView.getComputedStyle) { this.stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(canvas, null).paddingLeft, 10) || 0; this.stylePaddingTop = parseInt(document.defaultView.getComputedStyle(canvas, null).paddingTop, 10) || 0; this.styleBorderLeft = parseInt(document.defaultView.getComputedStyle(canvas, null).borderLeftWidth, 10) || 0; this.styleBorderTop = parseInt(document.defaultView.getComputedStyle(canvas, null).borderTopWidth, 10) || 0; }       // Some pages have fixed-position bars (like the stumbleupon bar) at the top or left of the page // They will mess up mouse coordinates and this fixes that html = document.body.parentNode; this.htmlTop = html.offsetTop; this.htmlLeft = html.offsetLeft; // **** Keep track of state! ****       this.valid = false; // when set to false, the canvas will redraw everything this.shapes = []; // the collection of things to be drawn this.dragging = false; // Keep track of when we are dragging this.resizeDragging = false; // Keep track of resize this.expectResize = -1; // save the # of the selection handle // the current selected object. In the future we could turn this into an array for multiple selection this.selection = null; this.dragoffx = 0; // See mousedown and mousemove events for explanation this.dragoffy = 0; // 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        this.selectionHandles = []; for (i = 0; i < 8; i += 1) { this.selectionHandles.push(new Shape(this)); }   	this.displayedImageWidth = 0; this.displayedImageHeight = 0; this.horScalingFactor = 0; this.verScalingFactor = 0; //       this.img = imageObj;// keep imageObj outside of myState // **** Then events! ****       // This is an example of a closure! // Right here "this" means the CanvasState. But we are making events on the Canvas itself, // and when the events are fired on the canvas the variable "this" is going to mean the canvas! // Since we still want to use this particular CanvasState in the events we have to save a reference to it. // This is our reference! myState = this; //fixes a problem where double clicking causes text to get selected on the canvas canvas.addEventListener('selectstart', function(e) {           e.preventDefault;            return false;        }, false); // Up, down, and move are for dragging canvas.addEventListener('mousedown', function(e) {           var mouse, mx, my, shapes, l, i, mySel;            if (myState.expectResize !== -1) {                myState.resizeDragging = true;                return;            }            mouse = myState.getMouse(e);            mx = mouse.x;            my = mouse.y;            shapes = myState.shapes;            l = shapes.length;            for (i = l - 1; i >= 0; i -= 1) {                if (shapes[i].contains(mx, my)) {                    mySel = shapes[i];                    // Keep track of where in the object we clicked                    // so we can move it smoothly (see mousemove)                    myState.dragoffx = mx - mySel.x;                    myState.dragoffy = my - mySel.y;                    myState.dragging = true;                    myState.selection = mySel;                    myState.valid = false;                    return; }           }            // havent returned means we have failed to select anything. // If there was an object selected, we deselect it           if (myState.selection) { myState.selection = null; myState.valid = false; // Need to clear the old selection border }       }, true);        canvas.addEventListener('mousemove', function(e) { var mouse = myState.getMouse(e), mx = mouse.x,               my = mouse.y,                oldx, oldy, i, cur; if (myState.dragging) { mouse = myState.getMouse(e); // We don't want to drag the object by its top-left corner, we want to drag it               // from where we clicked. Thats why we saved the offset and use it here myState.selection.x = mouse.x - myState.dragoffx; myState.selection.y = mouse.y - myState.dragoffy; myState.valid = false; // Something's dragging so we must redraw } else if (myState.resizeDragging) { // time ro resize! oldx = myState.selection.x;               oldy = myState.selection.y;

// 0 1  2                // 3     4                // 5  6  7                switch (myState.expectResize) { case 0: myState.selection.x = mx; myState.selection.y = my; myState.selection.w += oldx - mx; myState.selection.h += oldy - my; break; case 1: myState.selection.y = my; myState.selection.h += oldy - my; break; case 2: myState.selection.y = my; myState.selection.w = mx - oldx; myState.selection.h += oldy - my; break; case 3: myState.selection.x = mx; myState.selection.w += oldx - mx; break; case 4: myState.selection.w = mx - oldx; break; case 5: myState.selection.x = mx; myState.selection.w += oldx - mx; myState.selection.h = my - oldy; break; case 6: myState.selection.h = my - oldy; break; case 7: myState.selection.w = mx - oldx; myState.selection.h = my - oldy; break; }

myState.valid = false; // Something's dragging so we must redraw }

// if there's a selection see if we grabbed one of the selection handles if (myState.selection !== null && !myState.resizeDragging) { for (i = 0; i < 8; i += 1) { // 0 1  2                    // 3     4                    // 5  6  7

cur = myState.selectionHandles[i];

// we dont need to use the ghost context because // selection handles will always be rectangles if (mx >= cur.x && mx <= cur.x + myState.selectionBoxSize &&                       my >= cur.y && my <= cur.y + myState.selectionBoxSize) { // we found one! myState.expectResize = i;                       myState.valid = false;

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 myState.resizeDragging = false; myState.expectResize = -1; this.style.cursor = 'auto'; }       }, true);        canvas.addEventListener('mouseup', function(e) { myState.dragging = false; myState.resizeDragging = false; myState.expectResize = -1; if (myState.selection !== null) { if (myState.selection.w < 0) { myState.selection.w = -myState.selection.w;                   myState.selection.x -= myState.selection.w;                } if (myState.selection.h < 0) { myState.selection.h = -myState.selection.h;                   myState.selection.y -= myState.selection.h;                } }       }, true);        // double click for making new shapes .. not needed        //canvas.addEventListener('dblclick', function(e) { // var mouse = myState.getMouse(e); // myState.addShape(new Shape(myState, mouse.x - 10, mouse.y - 10, 20, 20, 'rgba(0,255,0,.6)')); //}, true);       // **** Options! ****        this.selectionColor = '#CC0000';        this.selectionWidth = 2;        this.selectionBoxSize = 6;        this.selectionBoxColor = 'darkred';        this.interval = 30;        //  this.bSize=0; //to store displayed image width        setInterval(function { myState.draw; }, myState.interval);   }

CanvasState.prototype.addShape = function(shape) { "use strict"; this.shapes.push(shape); this.valid = false; };

CanvasState.prototype.clear = function { "use strict"; this.ctx.clearRect(0, 0, this.width, this.height); };

// While draw is called as often as the INTERVAL variable demands, // It only ever does something if the canvas gets invalidated by our code CanvasState.prototype.draw = function { "use strict"; var ctx, shapes, l, i, shape, mySel, img; // if our state is invalid, redraw and validate! if (!this.valid) { ctx = this.ctx; shapes = this.shapes; img = imageObj; this.clear; //log shape location //   console.log("Scaled image width, height:",displayedImageWidth,displayedImageHeight); //   console.log("Ext Image location:", shapes[0].x,shapes[0].y); //   console.log("Ext extents:",shapes[0].w,shapes[0].h); //output Template text // ** Add stuff you want drawn in the background all the time here ** //*** Background picture on which graphic part is to be selected** //add background image if (curResourceCounter > 0) { // if height is more than width make height is equal to canvas height if (img.height >= img.width) { this.displayedImageHeight = this.height; this.verScalingFactor = this.height / img.height; this.displayedImageWidth = Math.round(this.verScalingFactor * img.width); this.horScalingFactor = this.verScalingFactor; } else { this.displayedImageWidth = this.width; this.horScalingFactor = this.width / img.width; this.displayedImageHeight = Math.round(this.horScalingFactor * img.height); this.verScalingFactor = this.horScalingFactor; }               // if width is more than height  make width equal to canvas height //  this.bSize=displayedImageWidth; //               imgSmall.removeAttr("class").removeAttr("height").removeAttr("style").css("width", "inherit"); // append to DOM for drawing //				$("canvas1").append(imgSmall); ctx.drawImage(img, 0, 0, this.displayedImageWidth, this.displayedImageHeight); //first time setting of shape for center of image if (initcomplete ){ shapes[0].x = Math.round(this.displayedImageWidth / 2 - 0.1 * this.displayedImageWidth); shapes[0].y = Math.round(this.displayedImageHeight / 2 - 0.1 * this.displayedImageHeight); shapes[0].w = Math.round(0.2 * this.displayedImageWidth); shapes[0].h = Math.round(0.2 * this.displayedImageHeight); initcomplete = false; }           }            // draw all shapes l = shapes.length; for (i = 0; i < l; i += 1) { shape = shapes[i]; // We can skip the drawing of elements that have moved off the screen: if (shape.x <= this.width && shape.y <= this.height &&                   shape.x + shape.w >= 0 && shape.y + shape.h >= 0) { shapes[i].draw(ctx); }           }            // draw selection // right now this is just a stroke along the edge of the selected Shape if (this.selection !== null) { ctx.strokeStyle = this.selectionColor; ctx.lineWidth = this.selectionWidth; mySel = this.selection; ctx.strokeRect(mySel.x, mySel.y, mySel.w, mySel.h); }           // ** Add stuff you want drawn on top all the time here ** this.valid = true; }   };    // Creates an object with x and y defined, set to the mouse position relative to the state's canvas // If you wanna be super-correct this can be tricky, we have to worry about padding and borders CanvasState.prototype.getMouse = function(e) { "use strict"; var element = this.canvas, offsetX = 0, offsetY = 0, mx, my;

// Compute the total offset if (element.offsetParent !== undefined) { do { offsetX += element.offsetLeft; offsetY += element.offsetTop; element = element.offsetParent; } while (element); }

// Add padding and border style widths to offset // Also add the offsets in case there's a position:fixed bar offsetX += this.stylePaddingLeft + this.styleBorderLeft + this.htmlLeft; offsetY += this.stylePaddingTop + this.styleBorderTop + this.htmlTop;

mx = e.pageX - offsetX; my = e.pageY - offsetY;

// We return a simple javascript object (a hash) with x and y defined return { x: mx, y: my       }; };   var getTemplatetext = function(a,scale) { //   	var cictext=a[0]; //  console.log('{{Css image crop'); str = imageObj.src; if (str===null){ alert('Wait for scan image of page to load completely and then try'); }       cic.Image = str.substring(str.search("px-") + 3, str.search(".jpg")); str = str.match(/page[0-9]+/); cic.Page = str[0].replace("page", ""); cic.bSize = Math.round(myState.displayedImageWidth*scale); cic.cWidth = Math.round(myState.shapes[0].w*scale); cic.cHeight = Math.round(myState.shapes[0].h*scale); cic.oTop = Math.round(myState.shapes[0].y*scale); cic.oLeft = Math.round(myState.shapes[0].x*scale); cic.Location = "center"; cic.Description = ""; //compose string //{"Image":"file:///home/arjun/extractimagejq/images/page35-3091px-TeluguVariJanapadaKalarupalu.djvu.jpg","Page":1,"bSize":281,"cWidth":56,"cHeight":80,"oTop":160,"oLeft":112,"Location":"","Description":""} cictext = "{{Css image crop\n"; cictext += "|Image = " + cic.Image + "\n"; cictext += "|Page = " + cic.Page + "\n"; cictext += "|bSize = " + cic.bSize + "\n"; cictext += "|cWidth = " + cic.cWidth + "\n"; cictext += "|cHeight = " + cic.cHeight + "\n"; cictext += "|oTop = " + cic.oTop + "\n"; cictext += "|oLeft = " + cic.oLeft + "\n"; cictext += "|Location = " + cic.Location + "\n"; cictext += "|Description = " + cic.Description + "\n}}\n"; a[0]=cictext; return true; };

// If you dont want to use  // You could uncomment this init reference and place the script reference inside the body tag //init;

function shapeinit { "use strict";

var s = new CanvasState(document.getElementById('canvas1'));

// add a large green rectangle dummy will be set after img size is available s.addShape(new Shape(s, 0, 0, 50, 50, 'rgba(0,205,0,0.7)')); var imgSmall = $('.prp-page-image img'); imageObj.height = imgSmall.attr('height'); imageObj.width = imgSmall.attr('width'); imageObj.onload = function { ++curResourceCounter; initcomplete = true; myState.valid = false; //       alert("img loaded "+imageObj.src); };     imageObj.src = imgSmall.attr('src'); //     alert("img src "+imageObj.src); // add a green-blue rectangle // s.addShape(new Shape(s, 240, 120, 40, 40, 'rgba(2,165,165,0.7)')); // add a smaller purple rectangle // s.addShape(new Shape(s, 5, 60, 25, 25, 'rgba(150,150,250,0.7)')); }   var customizeToolbar = function { if (mw.config.get("wgCanonicalNamespace")=="Page") { //		if ( typeof $ != 'undefined' && typeof $.fn.wikiEditor != 'undefined' ) {$( function {       $('#wpTextbox1').wikiEditor('addToToolbar', { section: 'advanced', group: 'format', tools: { "strikethrough": { label: 'Crop image', type: 'button', //                   filters: ['body.ns-250, body.ns-page'], icon: '//upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Crop_button.svg/23px-Crop_button.svg.png', action: { type: 'callback', execute: function(context) { cropimage; }                   }                }            }     	});		}    };    /* Check if view is in edit mode and that the required modules are available. Then, customize the toolbar … */    if (mw.toolbar) {        mw.loader.using('user.options', function { // This can be the string "0" if the user disabled the preference (T54542) if (mw.user.options.get('usebetatoolbar') == 1) { $.when(                   mw.loader.using('ext.wikiEditor'), $(document).ready                ).then(customizeToolbar); }       });    }

// Add the customizations to LiquidThreads' edit toolbar, if available mw.hook('ext.lqt.textareaCreated').add(customizeToolbar); }); //}); //})(mediawiki);