सदस्यःSurekha Kamath /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.dialog'], 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(
'
'<canvas id="canvas1" width="300" height="400" style="border: 1px solid black;">' + 'This text is displayed if your browser does not support HTML5 Canvas.' + '</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 <html> 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) { //{{Css image crop
var cic = { Image: "", Page: 0, bSize: 0, cWidth: 0, cHeight: 0, oTop: 0, oLeft: 0, Location: "", Description: "" };
var str="";
//|Image =TeluguVariJanapadaKalarupalu.djvu //|Page = 35 //|bSize = 351 //|cWidth = 274 //|cHeight = 165 //|oTop =210 //|oLeft = 17 //|Location = center //|Description = //}} 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 <body onLoad='init()'> // 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 (phab:T54542#555387) if (mw.user.options.get('usebetatoolbar') == 1) { $.when( mw.loader.using('ext.wikiEditor.toolbar'), $(document).ready ).then(customizeToolbar); } }); }
// Add the customizations to LiquidThreads' edit toolbar, if available mw.hook('ext.lqt.textareaCreated').add(customizeToolbar);
}); //}); //})(mediawiki);