User:Arjunaraoc/scripts/cropimage.js
Jump to navigation
Jump to search
Note: After saving, changes may not occur immediately. Click here to learn how to bypass your browser's cache.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Cmd-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (Cmd-Shift-R on a Mac)
- Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Clear the cache in Tools → Preferences
For details and instructions about other browsers, see Wikipedia:Bypass your cache.
Code that you insert on this page could contain malicious content capable of compromising your account. If you are unsure whether code you are adding to this page is safe, you can ask at the central discussion page, Scriptorium. The code will be executed when previewing this page under some skins, including Monobook. You can in the interim if you wish to refresh the content sooner under another skin. |
Documentation for this script can be added at User:Arjunaraoc/scripts/cropimage. This script seems to have an accompanying .css page at User:Arjunaraoc/scripts/cropimage.css. |
/**
* 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 = $('<div></div>')
.html(
'<div id="container">' +
'<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>' +
'</div>'
// pageLinks.join( '<br /><li>' ) + '</ul>'
)
.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'), $(document).ready
).then(customizeToolbar);
}
});
}
// Add the customizations to LiquidThreads' edit toolbar, if available
mw.hook('ext.lqt.textareaCreated').add(customizeToolbar);
});
//});
//})(mediawiki);