/**
 * (c) 2006-2007 Sxoop Technologies Ltd.
 * 
 * This javascript file defines all of the image operations used by 
 * pxn8_tools_ui.js
 *
 */

var PXN8 = PXN8 || {};

/*****************************************************************************

SECTION: Photo-Editing functions
================================
As well as the core selection, zoom and initialization functions, the Pixenate&trade; 
javascript API is composed of a series of photo-editing functions which perform various
modifications to the photo by calling equivalent CGI functions in the Pixenate software running on 
the web server.

PXN8.tools
==========
This variable defines a namespace within which all of the Pixenate photo-editing 
functions are defined.
***/

PXN8.tools = {};

/*****************************************************************************

PXN8.tools.history()
====================
Go back in time 'offset' number of operations. Clients should not call this function
directly. Use the *PXN8.tools.undo()* and *PXN8.tools.redo()* functions instead.

Parameters
----------
* offset : A number (positive or negative) indicating how many operations to move forwards or backwards
in the user session stack.

Examples
--------
    PXN8.tools.history(-1);
    // same as PXN8.tools.undo();

    PXN8.tools.history(+1)
    // same sas PXN8.tools.redo();

Related
-------
PXN8.tools.undo PXN8.tools.redo

***/
PXN8.tools.history = function (offset)
{
    if (offset == 0){
        return;
    }
    if (PXN8.isUpdating()){
        alert (PXN8.strings.IMAGE_UPDATING);
        return;
    }
    var theImage = PXN8.dom.id("pxn8_image");
    if (!theImage){
        alert("Please wait for the image to load");
        return;
    }
    
    
    if (!offset) offset = -1;
    if (PXN8.opNumber == 0 && offset < 0){
        PXN8.show.alert(PXN8.strings.NO_MORE_UNDO);
        return;
    }
    if (PXN8.opNumber == PXN8.maxOpNumber && offset > 0){
        PXN8.show.alert(PXN8.strings.NO_MORE_REDO);
        return;
    } 
    
    if (offset < 0){

        var userOp = PXN8.getUserOperation(PXN8.opNumber);
        
        PXN8.show.alert("- " + userOp.operation, 500);
    }else{
        PXN8.log.append("redo: " + PXN8.opNumber);

        for (var i = 0;i < PXN8.history.length; i++){
            PXN8.log.append("redo: " + PXN8.history[i].operation);
        }
        var userOp = PXN8.getUserOperation(PXN8.opNumber+1);
        
        PXN8.show.alert("+ " + userOp.operation,500);
    }
    
    var index =  PXN8.opNumber + offset;

    var currentImageData = PXN8.images[index];

    if (!currentImageData){
        // 
        // wph 20070223: 
        // this could have potentially happenend
        // if the user clicked 'undo' before the new image loaded
        //
        alert("Error! PXN8.images[" + index + "] is undefined");
        return false;
    }
    
    PXN8.opNumber = index;
    
    PXN8.image.location = currentImageData.location;
    PXN8.image.width = currentImageData.width;
    PXN8.image.height = currentImageData.height;
    
    // point image at the array element was bad !
    // changes to PXN8.image were also reflected in 
    // the array element leading to a long bug-tracking session
    // REMEMBER this !!!
    //PXN8.image = PXN8.images[PXN8.opNumber];

    /**
     * wph 20070108 : don't unselect because some ON_IMAGE_CHANGE listeners
     * might want to automatically select the image whenever it's changed.
     * Better to unselect _before_ notifying listeners
     */
    PXN8.unselect();
    PXN8.replaceImage(PXN8.image.location);
    return false;
};

/**************************************************************************

PXN8.tools.undo
===============
Undo the last operation.

Related
-------
PXN8.tools.undoall PXN8.tools.redo PXN8.tools.redoall PXN8.tools.history

***/
PXN8.tools.undo = function()
{
    PXN8.tools.history(-1);
    return false;
};

/***************************************************************************

PXN8.tools.redo()
=================
Redo the last undone operation.

Related
-------
PXN8.tools.undo PXN8.tools.redoall PXN8.tools.history

***/
PXN8.tools.redo = function()
{
    PXN8.tools.history(+1);
    return false;
};
/***************************************************************************

PXN8.tools.undoall()
====================
Undo all changes that were made to the photo

Related
-------
PXN8.tools.undo PXN8.tools.redoall PXN8.tools.history PXN8.tools.redo

***/
PXN8.tools.undoall = function()
{
    PXN8.tools.history(0 - PXN8.opNumber);
    return false;
};

/**************************************************************************

PXN8.tools.redoall()
====================
Redo all changes made to the photo.

Related
-------
PXN8.tools.undo PXN8.tools.redoall PXN8.tools.history PXN8.tools.redo
***/
PXN8.tools.redoall = function()
{
    PXN8.tools.history(PXN8.maxOpNumber-PXN8.opNumber);
    return false;
};

/**************************************************************************

PXN8.tools.updateImage()
========================
This function takes an array of operations as a parameter and submits the 
operations to the server to update the image. This function is called by all other PXN8.tools.*
functions (except PXN8.tools.undo(), PXN8.tools.redo(),  PXN8.tools.undoall(), PXN8.tools.redoall() ).


Parameters
----------
* operations : An array of 'operations' which will be performed on the image.

Example
-------
The following code will crop the photo and rotate it by 90&deg; ...

    PXN8.tools.updateImage([
                            {operation: "crop", top: 40, left: 40, width: 200, height: 200},
                            {operation: "rotate", angle: 90}
                           ]);
                            
PXN8.tools.updateImage() can be used to combine multiple image-editing operations into a single
user operation (to the user it appears to be one operation even though the image goes through 
two transformations, being first normalized and then enhanced).
When the user clicks *Undo*, both the *crop* and *rotate* operations will be undone. 
PXN8.tools.updateImage() is really useful if you would like to create your own custom 'quick-fix' 
operations which are combinations of existing operations. 
Please see <a href="#api_operations">API Operations</a> for a full list of operations

***/
PXN8.tools.updateImage = function(ops)
{
    var theImage = PXN8.dom.id("pxn8_image");
    if (!PXN8.ready){
        alert("Please wait for the image to load");
        return;
    }

    /**
     * wph 20060909 : Don't increment PXN8.opNumber unless the
     * last operation has completed.
     */
    if (PXN8.isUpdating()){
        alert (PXN8.strings.IMAGE_UPDATING);
        return;
    }
    /**
     * increment PXN8.opNumber & add op to the history
     */
    PXN8.addOperations(ops);
        
};

/**************************************************************************

PXN8.tools.enhance()
====================
Apply a digital filter to enhance a noisy photo. This is useful for smoothing facial lines.

***/
PXN8.tools.enhance = function()
{
    PXN8.tools.updateImage([{operation: "enhance"}]);
};

/**************************************************************************

PXN8.tools.normalize()
======================
Transform photo to span the full range of color values. This results in a more
colorful, better balanced image.

***/
PXN8.tools.normalize = function()
{
    PXN8.tools.updateImage([{operation: "normalize"}]);
};

/**************************************************************************

PXN8.tools.instantFix()
=======================
instant_fix performs both 'enhance' and 'normalize'

Related
-------
PXN8.tools.enhance PXN8.tools.normalize

***/
PXN8.tools.instantFix = function()
{
    PXN8.tools.updateImage([ {operation: "normalize"},
                             {operation: "enhance"}
                           ]);
};

/**************************************************************************

PXN8.tools.spiritLevel()
========================
Fix the horizon on a photo: Uses two points (left and right) to ascertain 
what the correct angle of the photo should be.
This function is a wrapper for PXN8.tools.rotate().

Parameters
----------
* x1 : the X coordinate of the first point
* y1 : The Y coordinate of the first point
* x2 : The X coordinate of the second point
* y2 : The Y coordinate of the second point

Related 
-------
PXN8.tools.rotate

***/
PXN8.tools.spiritlevel = function(x1,y1,x2,y2)
{

    var opposite = y1 > y2 ? y1 - y2 : y2 - y1;
    var adjacent = x1 > x2 ? x1 - x2 : x2 - x1;
    var hypotenuse = Math.sqrt((opposite * opposite) + (adjacent * adjacent));
    var sineratio = opposite / hypotenuse;
    var RAD2DEG = 57.2957795;
    var rads = Math.atan2(sineratio,Math.sqrt(1 - (sineratio * sineratio)));
    var degrees = rads * RAD2DEG;
    if (y1 < y2){
        degrees = 360 - degrees;
    }
    PXN8.tools.rotate ({angle: degrees});
        
};
/**
 * -- TODO: document PXN8.tools.spiritlevel_mode for API reference
 * wph 20070426 : A new mode which simplifies the UI interaction for using the spirit-level tool.
 */

PXN8.tools.spiritlevel_mode = {};
PXN8.tools.spiritlevel_mode.clicks = [];
PXN8.tools.spiritlevel_mode.callback = null;
PXN8.tools.spiritlevel_mode.start = function(callback){
    var _ = PXN8.dom;
    var img = _.id("pxn8_image");
    var iw = img.width / PXN8.zoom.value();
    var ih = img.height / PXN8.zoom.value();
    var sel = _.id("pxn8_select_rect");
    
    sel.style.cursor = "pointer";
    PXN8.crosshairs.setEnabled(false);
    PXN8.resize.enable(["n","s","e","w","ne","se","nw","sw"],false);
    
    PXN8.select({top:0,left:0,width: iw/2,height: ih});
    PXN8.event.addListener(sel,"click",this.onclick);
    
    this.callback = callback;
};

PXN8.tools.spiritlevel_mode.end = function(){
    var _ = PXN8.dom;
    
    var pin1 = _.id('pxn8_flag_0');
    if (pin1){
        document.body.removeChild(pin1);
    }
    var pin2 = _.id('pxn8_flag_1');
    if (pin2){
        document.body.removeChild(pin2);
    }
    PXN8.unselect();
    var sel = _.id("pxn8_select_rect");
    sel.style.cursor = "move";
    PXN8.crosshairs.setEnabled(true);
    
    PXN8.resize.enable(["n","s","e","w","ne","se","nw","sw"],true);
    
    PXN8.event.removeListener(sel,"click",this.onclick);
    this.clicks = [];
};


PXN8.tools.spiritlevel_mode.onclick = function(event){
    var _ = PXN8.dom;
    var self = PXN8.tools.spiritlevel_mode;
    
    var img = _.id("pxn8_image");
    var iw = img.width / PXN8.zoom.value();
    var ih = img.height / PXN8.zoom.value();
    var sel = _.id("pxn8_select_rect");
    var pos = _.cursorPos(event);
    
    var flag = PXN8.dom.createFlag(pos.x,pos.y,"pxn8_flag_" + self.clicks.length);
    
    self.clicks.push(pos);
    
    PXN8.select({top:0,left:iw/2,width: iw/2,height: ih});
    
    if (self.clicks.length == 2){
        PXN8.event.removeListener(sel,"click",self.onclick);
        PXN8.tools.spiritlevel(self.clicks[0].x,self.clicks[0].y,self.clicks[1].x,self.clicks[1].y);
        
        var oldSize = PXN8.dom.eb("pxn8_image");
        
        PXN8.listener.onceOnly(PXN8.ON_IMAGE_CHANGE,function(){
                self.end();
                var newSize = PXN8.dom.id("pxn8_image");
                var hdiff = newSize.height - oldSize.height;
                var wdiff = newSize.width - oldSize.width;
                PXN8.select(wdiff,hdiff,oldSize.width-wdiff,oldSize.height-hdiff);
                
                self.callback();
            });
        
    }
};

/**************************************************************************

PXN8.tools.rotate()
===================
Rotate a photo or flip it.

Parameters
----------
* params : An object which must have at least one of the following properties...
  * angle : A number specifing the degrees through which the photo should be rotated.
  * flipvt : A boolean indicating whether or not the photo should be flipped vertically.
  * fliphz : A boolean indicating whether or not the photo should be flipped horizontally.

Examples
--------
To rotate a photo 90 degrees clockwise...
   
    PXN8.tools.rotate({angle: 90});

To flip a photo along the horizontal pane (mirror photo)...

    PXN8.tools.rotate({fliphz: true});

***/
PXN8.tools.rotate = function(params)
{
    if (!params.angle){ 
        params.angle = 0;
    }
    if (params.fliphz == null){
        params.fliphz = false;
    }
    if (params.flipvt == null){
        params.flipvt = false;
    }
    params.operation = "rotate";

    if (params.angle > 0 || params.flipvt || params.fliphz){
        PXN8.tools.updateImage([params]);
    }
};

/**************************************************************************

PXN8.tools.blur()
=================
Blur an area of the photo (or the entire photo).

Examples
--------
To blur the entire photo with a radius of 2x2...

    PXN8.tools.blur({radius: 2});

To blur an area of the photo...

    PXN8.tools.blur({radius: 2, top: 4, left: 40, width: 400, height: 200});

***/
PXN8.tools.blur = function (params)
{
    params.operation = "blur";
    PXN8.tools.updateImage([params]);
};

/**************************************************************************

PXN8.tools.colors()
===================
Change the brightness, saturation ,hue and contrast of a photo.

Examples
--------
To increase saturation by 20%...

    PXN8.tools.colors({saturation: 120});

To increase contrast & reduce brightness by 20 %

    PXN8.tools.colors({contrast: 1, brightness: 80});

To increase saturation, brightness, hue and contrast...

    PXN8.tools.colors ({brightness: 110, saturation: 110, hue: 180, contrast: 2});

Contrast must be in the range -5 to +5.
All other parameters must be in the range 0 - 200

***/
PXN8.tools.colors = function(param)
{
    if (!param.saturation) param.saturation = 100;
    if (!param.brightness) param.brightness = 100;
    if (!param.hue) param.hue = 100;
    if (!param.contrast) param.contrast = 0;
    param.operation = "colors";
    PXN8.tools.updateImage([param]);
};

/**************************************************************************

PXN8.tools.crop()
=================
Crop a photo to the dimensions provided. The most common way to call this is 
as follows...
    
    PXN8.tools.crop();

... which will simply crop the photo to the currently selected area. This is 
equivalent to ...
    PXN8.tools.crop(PXN8.getSelection());

Parameters
----------
* geometry : An object with *width*, *height*, *top* and *left* properties.

Examples
--------
Crop the photo begining at 10 pixels from left, 200 pixels from the top and 
extending 40 pixels to the right and 80 pixels to the bottom...

    PXN8.tools.crop({top: 10, left: 200, width: 40, height: 80});

***/
PXN8.tools.crop = function (params) 
{
    /**
     * wph 20070526 - use current selection if no params provided
     */
    if (!params){
        params = PXN8.getSelection();
    }
    
    if (params.width <= 0 || params.height <= 0){
        PXN8.show.alert(PXN8.strings.CROP_SELECT_AREA);
        return;
    }
    params.operation = "crop";
    PXN8.tools.updateImage([params]);
};

/**************************************************************************

PXN8.tools.preview_crop
=======================
A utility function to allow the user to preview what a crop based on the current 
selection would look like.

Parameters
----------
* timeoutMillis : Exit the preview mode after the timeoutMillis milliseconds.

***/
PXN8.tools.preview_crop = function (timeout)
{
    timeout = timeout || 3500;
    
    var preview = function(borderColor,borderOpacity,handleOpacity){
        var _ = PXN8.dom;
        var rects = ["left","right","top","bottom"];
        for (var i  = 0;i < rects.length; i++){
            var rect = _.id("pxn8_" + rects[i] + "_rect");
            rect.style.backgroundColor = borderColor;
            _.opacity(rect,borderOpacity);
        }
        for (var i in PXN8.resize.handles){
            if (typeof PXN8.resize.handles[i] != "function"){
                var handle = _.id( i + "_handle");
                _.opacity(handle,handleOpacity);
            }
        }
    };
    preview("white",1.00,0);
    
    setTimeout(function(){
            var _ = PXN8.style.notSelected;
            preview(_.color,_.opacity,1.00);
        },timeout);
};

/**************************************************************************

PXN8.tools.filter()
===================
Apply a 'lens-filter' effect to the photo.This mimics the effect
of using those tinted lens filters on SLR to create more dramatic
skies.

Parameters
----------
* params : An object with *top*, *color* and *opacity* properties. (see below).

Examples
--------
The following javascript code will produce the image on the right...

    PXN8.tools.filter({top: 125, color: '#ffa500', opacity: 80});

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225filter.jpg"/></td></tr>
</table>

The *top* property specifies where the filter should trail off completely. The filter
is always applied starting at the top of the photo. If you would like the filter to begin
at the bottom of the photo you should use the following code instead...

    PXN8.tools.updateImage([
                           {operation: "rotate", flipvt: true},
                           {operation: "filter", top: 125, color: '#ffa500', opacity: 80},
                           {operation: "rotate", flipvt: true},
                           ]);

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225filterflip.jpg"/></td></tr>
</table>

***/
PXN8.tools.filter = function (params)
{
    params.color = escape(params.color);
    params.operation = "filter";
    PXN8.tools.updateImage([params]);
};

/**************************************************************************

PXN8.tools.interlace()
======================
Adds TV-like scan-lines overlaying the photo to make it appear like it is a 
screen-grab from broadcast TV.

Parameters
----------
* params : An object with *color* and *opacity* properties.

Examples
--------

    PXN8.tools.interlace({color: '#ffffff', opacity: 66 });

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225interlace.jpg"/></td></tr>
</table>

***/
PXN8.tools.interlace = function(params)
{
    params.color = escape(params.color);
    params.operation = "interlace";
    PXN8.tools.updateImage([params]);
};

/**************************************************************************

PXN8.tools.lomo()
=================
This function adds a 'lomo' effect to the photo. This is an atmospheric and artistic 
effect that darkens the corners and (optionally) saturates the colors so that the photo
appears to have been taken using a Russian LOMO&trade; camera.

Parameters
----------
* params : An object with *opacity* (numeric) and *saturate* (boolean) properties.

Examples
--------

    PXN8.tools.lomo({opacity: 30, saturate: true});

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225lomo.jpg"/></td></tr>
</table>

***/
PXN8.tools.lomo = function(params)
{
    params.operation = "lomo";
    PXN8.tools.updateImage([params]);
};

/**************************************************************************

PXN8.tools.fill_flash()
=======================
Adds a fill-flash effect to the photo to brighten it. This is more
subtle than using the PXN8.tools.colors() function as it composites a
duplicate layer on top of the existing image using the 'SCREEN'
compositing operation.

Parameters
----------
* luminosity : A value between 1 and 100 (default value is 50 if parameter not passed).

Examples
--------
    
    PXN8.tools.fill_flash();

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225fillflash.jpg"/></td></tr>
</table>

***/
PXN8.tools.fill_flash = function(opacity)
{
    var operation = {operation: "fill_flash"};
    if (opacity){
        operation.opacity = Math.max(0,Math.min(100,opacity));
    }else{
        operation.opacity = 50;
    }
    
    PXN8.tools.updateImage([operation]);
};

/**************************************************************************

PXN8.tools.snow()
=================
Adds snowflakes to the photo. This is basically a wrapper around PXN8.tools.overlay().

Examples
--------

    PXN8.tools.snow();

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225snow.jpg"/></td></tr>
</table>

Related
-------
PXN8.tools.overlay()

***/
PXN8.tools.snow = function ()
{
    //
    // snowflakes.png must be in the pixenate/images/overlays directory !!!!
    //
    PXN8.tools.overlay({image: "snowflakes.png", tile: "true"});
};

/**************************************************************************

PXN8.tools.overlay
==================
Overlays an image on top of the photo.
The overlay tool is useful for superimposing clip-art and borders on top of photos.

Parameters
----------

* params : An object with the following properties...
  * image : (compulsory) The image property should be the name of an overlay image (either .png or .gif) stored in the *${pixenate}/images/overlays/* directory (where ${pixenate} is the name of the directory where pixenate is located. This is the image which will be overlaid on top of the user's photo. 
  * tile  : (optional) A boolean indicating whether the image should be tiled (repeated in the x and y direction).
  * x : (optional) The X coordinate where the left-most side of the overlay will appear (ignored if *tile* is true).
  * y : (optional) The Y coordinate where the top-most side of the overlay will appear (ignored if *tile* is true).
  * width: (optional) The width to which the overlay will be resized. If the width property is not present then the overlay will not be resized.
  * height: (optional) The height to which the overlay will be resized.

Examples
--------

Please see the <a href="example-speech-bubbles.html">Speech bubbles</a> Example.

Related
-------
PXN8.tools.snow() PXN8.tools.add_text()

***/
PXN8.tools.overlay = function(params)
{
    params.operation="overlay";
    PXN8.tools.updateImage([params]);
};


/**************************************************************************

PXN8.tools.add_text()
=====================
Adds text to a photo.

Parameters
----------

* params : An object with the following properties...
  * text : (this is the only compulsory property) The text to add.
  * gravity : A string specifying where the text will appear. Possible values are "North", "South", "East", "West", "NorthWest", "SouthWest", "NorhtEast", "SouthEast", "Center".
  * top : The Y coordinate where the text will appear (ignored if *gravity* is present).
  * left : The X coordinate where the text will appear (ignored if *gravity* is present).
  * width : The width of the text (text will be resized to fit the width).
  * height: The height of the text (text will be resized to fit the height).
  * font : The font family to use when adding text (default is Arial or Courier depending on what fonts are installed on the server).
  * fill : The font color (expressed as a &hash prefixed hexadecimal string).
  * pointsize: The size of the font.

Examples
--------
To position text on the photo using the *gravity* property...

    PXN8.tools.add_text({fill : '#ffffff', font: 'Arial', pointsize: 20, text: 'Hello World', gravity: 'NorthEast'});

<img src="pigeon300x225textgravity.jpg"/>

To position text on the photo using the left and top coordinates...

    PXN8.tools.add_text({fill : '#ffffff', font: 'Arial', pointsize: 20, text: 'Hello World', top: 40, left: 50});

<img src="pigeon300x225textxy.jpg"/>

Related
-------
PXN8.tools.overlay()

***/
PXN8.tools.add_text = function(params)
{
   params.operation = "add_text";
   params.fill = escape(params.fill);
   
   //
   // wph 20070309 : allow doublequotes inside text string
   //
   params.text = params.text.replace(/\"/g,"\\\"");
   
   PXN8.tools.updateImage([params]);
};

/**************************************************************************

PXN8.tools.whiten()
===================
Whitens off-color teeth. 

Parameters
----------
* geometry : An object containing *top*, *left*, *width* and *height* coordinates. (usually obtained from the current user selection)

Related
-------
PXN8.tools.fixredeye()

***/
PXN8.tools.whiten = function (params)
{
    params.operation = "whiten";
    PXN8.tools.updateImage([params]);
};

/**************************************************************************

PXN8.tools.fixredeye()
======================
Removes 'red-eye' from the specified area.

Parameters
----------
* geometry : An object containing *top*, *left*, *width* and *height* coordinates. (usually obtained from the current user selection)


Examples
--------

    PXN8.tools.fixredeye({top: 40, left: 60, width: 75, height: 75});

Alternatively the *geometry* parameter can instead be an Array of geometry objects.

    PXN8.tools.fixredeye([{top: 40, left: 60, width: 75, height: 75},
                         {top: 50, left: 200, width: 80, height: 94}]);

You must supply one or more rectangles to which the redeye fix will be applied.
The rectangles should be approximately centered on the eye.

Related
-------
PXN8.tools.whiten()

***/
PXN8.tools.fixredeye = function(params)
{
    if (!params){
        params = PXN8.getSelection();
    }
    if (PXN8.isArray(params)){
        for (var i = 0;i < params.length; i++){
            params[i].operation = "fixredeye";
        }
        PXN8.tools.updateImage(params);
    }else{
        params.operation = "fixredeye";
        PXN8.tools.updateImage([params]);
    }
};

/**************************************************************************

PXN8.tools.resize()
===================
Resize an image to the specified width and height.

Parameters
----------
* width : The new desired width of the image.
* height: The new desired height of the image.

***/
PXN8.tools.resize = function(width, height)
{
    PXN8.tools.updateImage([{"operation": "resize", "width": width, "height": height}]);
};

/**************************************************************************

PXN8.tools.roundedcorners()
===========================
Add rounded corners to the image.

Parameters
----------
* color : The color of the corners. 
* radius : The radius of the rounded corners.

Examples
--------

    PXN8.tools.roundedcorners('#ffffff',32);

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225rounded.jpg"/></td></tr>
</table>

***/
PXN8.tools.roundedcorners = function(color, radius)
{
    PXN8.tools.updateImage([{"operation":"roundcorners",
                             "color": escape(color), 
                             "radius":radius}]);
};

/**************************************************************************

PXN8.tools.sepia()
==================
Add a sepia-tone effect to the image.

Parameters
----------
* color : the color of the 'tint' to use when applying the effect. ('#a28a65' seems to be a good sepia color).

Examples
--------

    PXN8.tools.sepia('#a28a65');

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225sepia.jpg"/></td></tr>
</table>

Related
-------
PXN8.tools.grayscale()

***/
PXN8.tools.sepia = function(color)
{
    PXN8.tools.updateImage([{"operation":"sepia",
                             "color": escape(color)}]);
};

/**************************************************************************

PXN8.tools.grayscale()
======================
Make the image grayscale (black & white).

Examples
--------
    PXN8.tools.grayscale();

<table>
<tr><td>Before</td><td>After</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225grayscale.jpg"/></td></tr>
</table>

Related
-------
PXN8.tools.sepia()

***/
PXN8.tools.grayscale = function()
{
    PXN8.tools.updateImage([{"operation":"grayscale"}]);
};

/**************************************************************************

PXN8.tools.charcoal()
=====================
Create a charcoal drawing from an image.

Parameters
----------
* radius : (A value between 1 and 8). Defines how acute the effect will be. 

Examples
--------

<table>
<tr><td>Before</td><td>radius = 2</td><td>radius = 5</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225ch2.jpg"/></td><td><img src="pigeon300x225ch5.jpg"/></td></tr>
</table>

Related
-------
PXN8.tools.oilpaint()
***/
PXN8.tools.charcoal = function(radius)
{
    PXN8.tools.updateImage([{"operation" : "charcoal", "radius" : radius}]);
};

/**************************************************************************

PXN8.tools.oilpaint()
=====================
Create an oil-painting from a photo.

Parameters
----------
* radius : (A value between 1 and 8). Defines how acute the effect will be. 

Examples
--------

<table>
<tr><td>Before</td><td>radius = 2</td><td>radius = 5</td></tr>
<tr><td><img src="pigeon300x225.jpg"/></td><td><img src="pigeon300x225oil2.jpg"/></td><td><img src="pigeon300x225oil5.jpg"/></td></tr>
</table>

Related
-------
PXN8.tools.charcoal()

***/
PXN8.tools.oilpaint = function(radius)
{
    PXN8.tools.updateImage([{"operation" : "oilpaint", "radius" : radius}]);
};

/*========================================================================

PXN8.tools.unsharpmask()
========================
Uses the unsharpmask algorithm to sharpen an image.

*/
PXN8.tools.unsharpmask = function(params)
{
    var operation = {"operation": "unsharpmask"};
    if (params){
        for (var i in params){
            operation[i] = params[i];
        }
    }
    PXN8.tools.updateImage([operation]);
};

/**************************************************************************

PXN8.tools.fetch()
==================
Fetches an image either from a remote server or the server's own filesystem.
This is not normally called directly by client code.

***/
PXN8.tools.fetch = function(params)
{
    var operation = {"operation" : "fetch"};
    if (params){
        for (var i in params){
            operation[i] = params[i];
        }
    }
    PXN8.tools.updateImage([operation]);
};

