/** * ----------------------------------------------------------------------------- * file: CanvasDraw.ui.drawingGraph.js * file version: 1.0.0 * date: 2011-10-17 * * A jQuery plugin provided by the piwigo's plugin "GrumPluginClasses" * * ----------------------------------------------------------------------------- * Author : Grum * email : grum@piwigo.com * website : http://photos.grum.fr * PWG user : http://forum.phpwebgallery.net/profile.php?id=3706 * * << May the Little SpaceFrog be with you ! >> * ----------------------------------------------------------------------------- * * The drawing graph provide all methods and properties to draw charts * * It use: * - 1 layer for graphs * - 1 layer for the grid & axis * - 1 layer for the cursor * * :: HISTORY :: * * | release | date | * | 1.0.0 | 2011-10-17 | first release * | | | * | | | * | | | * | | | * | | | * | | | * */ ( function($) { /* * plugin 'public' functions */ var publicMethods = { /** * initialize the object * * @param Object opt : options */ init : function (opt) { return this.each(function() { // default values for the plugin var $this=$(this), data = $this.data('options'), objects = $this.data('objects'), properties = $this.data('properties'), canvasObjects = $this.data('canvasObjects'), options = { margins:new CDMargins(), legend:new CDLegend(), axis:{ front:new CDAxis(), back:null }, cursor: { visible:true, color:'rgba(128,128,128,0.2)', verticalType:'bar' // 'bar' or 'line' }, display: { width:320, height:200, legend:false, }, events: { mousePositionChange:null, mouseEnter:null, mouseLeave:null, mouseClick:null }, }; // if options given, merge it // if(opt) $.extend(options, opt); ==> options are set by setters $this.data('options', options); if(!properties) { $this.data('properties', { initialized:false, mouseIsOver:false, cursor:{ x:0, y:0, axis:{ XY:{ front:{H:-1, V:-1}, back:{H:-1, V:-1} } }, }, typeGraph:'xy', // 'xy', 'pie' } ); properties=$this.data('properties'); } if(!objects) { objects = { viewport:$('
', { 'class':'ui-drawingGraph-viewport' } ), sheet:$('', { 'class':'ui-drawingGraph-sheet' } ) .bind('mousemove', function (event) { privateMethods.mousePositionUpdate($this, event.originalEvent.layerX, event.originalEvent.layerY); } ) .bind('mouseleave', function (event) { privateMethods.mouseLeave($this); } ) .bind('mouseenter', function (event) { privateMethods.mouseEnter($this, event.originalEvent.layerX, event.originalEvent.layerY); } ) .bind('click', function (event) { privateMethods.mouseClick($this, event.originalEvent.layerX, event.originalEvent.layerY); } ), canvasCursor:$('', { 'class':'ui-drawingGraph-cursor' } ), canvasAxis:$('', { 'class':'ui-drawingGraph-axis' } ), canvasGraph:$('', { 'class':'ui-drawingGraph-graph' } ) }; canvasObjects = { cursor:new CDDrawing(objects.canvasCursor.get(0)), axis:new CDDrawing(objects.canvasAxis.get(0)), graph:new CDDrawing(objects.canvasGraph.get(0)), }; $this .html('') .addClass('ui-drawingGraph') .bind('click.drawingGraph', function () { // } ) .bind('mouseenter.drawingGraph', function () { properties.mouseIsOver=true; } ) .bind('mouseleave.drawingGraph', function () { properties.mouseIsOver=false; } ) .append(objects.viewport.append(objects.sheet.append(objects.canvasAxis).append(objects.canvasCursor).append(objects.canvasGraph))); $this.data('objects', objects); $this.data('canvasObjects', canvasObjects); } privateMethods.setOptions($this, opt); } ); }, // init /** * object destroy methods to clean memory */ destroy : function () { return this.each( function() { // default values for the plugin var $this=$(this); $this .unbind('.drawingGraph') .css( { width:'', height:'' } ); } ); }, // destroy /** * initialize the options * * @param Object value : options properties as an object */ options: function (value) { return this.each(function() { privateMethods.setOptions($(this), value); } ); }, // options /** * define the size (witdh & height) of the current drawing area * * @param Object value : an object with width&height properties * @return Object : if no parameter are given, returns an object with the * width & height properties */ display : function (value) { if(value!=null) { // set selected value return this.each(function() { privateMethods.setDisplay($(this), value); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.display); } else { return(null); } } }, /** * define the cursor state * * @param Object value : an object with the cursor properties (visible, * color) * @return Object : if no parameter are given, returns an object with the * cursor properties */ cursor : function (value) { if(value!=null) { // set selected value return this.each(function() { privateMethods.setCursor($(this), value); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.cursor); } else { return(null); } } }, /** * define the margins * * @param Object value : a CDMargins object or an object with left, top, right, bottom properties * @return Object : if no parameter are given, returns a CDMargins object */ margins : function (value) { if(value!=null && value instanceof CDMargins) { // set selected value return this.each(function() { privateMethods.setMargins($(this), value); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.margins); } else { return(null); } } }, /** * define the legend * * @param Object value : a CDLegend object or an object with left, top, right, bottom properties * @return Object : if no parameter are given, returns a CDLegend object */ legend : function (value) { if(value!=null && value instanceof CDLegend) { // set selected value return this.each(function() { privateMethods.setLegend($(this), value); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.legend); } else { return(null); } } }, /** * define the front axis * * @param Object value : a CDAxis object * @return Object : if no parameter are given, returns a CDAxis object */ axisFront : function (value) { if(value!=null && value instanceof CDAxis) { // set selected value return this.each(function() { privateMethods.setAxis($(this), value, true); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.axis.front); } else { return(null); } } }, /** * define the back axis * * @param Object value : a CDAxis object * @return Object : if no parameter are given, returns a CDAxis object */ axisBack : function (value) { if(value!=null && value instanceof CDAxis) { // set selected value return this.each(function() { privateMethods.setAxis($(this), value, false); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.axis.back); } else { return(null); } } }, /** * return the canvas objects * * @return Object : an object with axis, graph and cursor canvas */ canvasObjects : function () { var canvasObjects=this.data('canvasObjects'); if(canvasObjects) { return(canvasObjects); } else { return( { cursor:null, axis:null, graph:null } ); } }, /** * define the function to be triggered when the mouse moves on the drawing * area * * @param Function value : a function to be triggered * @return Function : if no paramater is given, return the function */ mousePositionChange: function (value) { if(value!=null && $.isFunction(value)) { // set selected value return this.each(function() { privateMethods.setEventMousePositionChange($(this), value); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.events.mousePositionChange); } else { return(null); } } }, // mousePositionChange /** * define the function to be triggered when the mouse enter on the drawing * area * * @param Function value : a function to be triggered * @return Function : if no paramater is given, return the function */ mouseEnter: function (value) { if(value!=null && $.isFunction(value)) { // set selected value return this.each(function() { privateMethods.setEventMouseEnter($(this), value); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.events.mouseEnter); } else { return(null); } } }, // mouseEnter /** * define the function to be triggered when the mouse leaves the drawing * area * * @param Function value : a function to be triggered * @return Function : if no paramater is given, return the function */ mouseLeave: function (value) { if(value!=null && $.isFunction(value)) { // set selected value return this.each(function() { privateMethods.setEventMouseLeave($(this), value); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.events.mouseLeave); } else { return(null); } } }, // mouseLeave /** * define the function to be triggered when the mouse is clicked on the * drawing area * * @param Function value : a function to be triggered * @return Function : if no paramater is given, return the function */ mouseClick: function (value) { if(value!=null && $.isFunction(value)) { // set selected value return this.each(function() { privateMethods.setEventMouseClick($(this), value); } ); } else { // return the selected value var options=this.data('options'); if(options) { return(options.events.mouseClick); } else { return(null); } } }, // mouseClick /** * force the drawing area to refresh display */ refresh: function () { return this.each(function() { privateMethods.draw($(this)); } ); } }, // methods /* * plugin 'private' methods */ privateMethods = { /** * initialise the object with given options * * @param object value: options to be set */ setOptions : function (object, value) { var properties=object.data('properties'), options=object.data('options'); if(!$.isPlainObject(value)) return(false); properties.initialized=false; if(value.axis==null) value.axis={}; if(value.events==null) value.events={}; privateMethods.setMargins(object, (value.margins!=null)?value.margins:options.margins); privateMethods.setLegend(object, (value.legend!=null)?value.legend:options.legend); privateMethods.setCursor(object, (value.cursor!=null)?value.cursor:options.cursor); privateMethods.setAxis(object, (value.axis.front!=null)?value.axis.front:options.axis.front, true); privateMethods.setAxis(object, (value.axis.back!=null)?value.axis.back:options.axis.back, false); privateMethods.setDisplay(object, (value.display!=null)?value.display:options.display); privateMethods.setEventMousePositionChange(object, (value.events.mousePositionChange!=null)?value.events.mousePositionChange:options.events.mousePositionChange); privateMethods.setEventMouseLeave(object, (value.events.mouseLeave!=null)?value.events.mouseLeave:options.events.mouseLeave); privateMethods.setEventMouseEnter(object, (value.events.mouseEnter!=null)?value.events.mouseEnter:options.events.mouseEnter); privateMethods.setEventMouseClick(object, (value.events.mouseClick!=null)?value.events.mouseClick:options.events.mouseClick); properties.initialized=true; privateMethods.draw(object); }, /** * setDisplay allows to resize the sheetSizeport * * @param object value: an object with width&height properties * @return object: display options properties */ setDisplay : function (object, value) { var properties=object.data('properties'), objects=object.data('objects'), options=object.data('options'), canvasObjects=object.data('canvasObjects'), triggerSize=false, axisBox={ width:0, height:0 }; if(value.width==null) value.width=options.display.width; if(value.height==null) value.height=options.display.height; if(value.width>0 && value.height>0 && (value.width!=options.display.width || value.height!=options.display.height || !properties.initialized) ) { delete canvasObjects.cursor; delete canvasObjects.axis; delete canvasObjects.graph; options.display.width=value.width; options.display.height=value.height; objects.sheet.css( { width:options.display.width+'px', height:options.display.height+'px' } ); objects.viewport.attr("height", options.display.height).attr("width", options.display.width); objects.canvasCursor.attr("height", options.display.height).attr("width", options.display.width); objects.canvasAxis.attr("height", options.display.height).attr("width", options.display.width); objects.canvasGraph.attr("height", options.display.height).attr("width", options.display.width); object.attr("height", options.display.height).attr("width", options.display.width); object.css( { width:options.display.width+'px', height:options.display.height+'px' } );//attr("height", options.display.height).attr("width", options.display.width); canvasObjects.cursor=new CDDrawing(objects.canvasCursor.get(0)); canvasObjects.axis=new CDDrawing(objects.canvasAxis.get(0)); canvasObjects.graph=new CDDrawing(objects.canvasGraph.get(0)); axisBox=privateMethods.axisBox(object); if(options.axis.front!=null) options.axis.front.set( { size:{ width:axisBox.width, //options.display.width-options.margins.get().left-options.margins.get().right, height:axisBox.height //options.display.height-options.margins.get().top-options.margins.get().bottom } } ); if(options.axis.back!=null) options.axis.back.set( { size:{ width:axisBox.width, //options.display.width-options.margins.get().left-options.margins.get().right, height:axisBox.height //options.display.height-options.margins.get().top-options.margins.get().bottom } } ); if(properties.initialized) { /* * redraw graph */ } } return(options.display); }, // setDisplay /** * define the cursor properties * * @param object value: the cursor properties (color, visible, verticalType) * @return object: cursor options values */ setCursor : function (object, value) { var properties=object.data('properties'), objects=object.data('objects'), options=object.data('options'), redraw=false; if(value.color==null) value.color=options.cursor.color; if(value.visible==null) value.visible=options.cursor.visible; if(value.verticalType==null) value.verticalType=options.cursor.verticalType; if((value.visible===true || value.visible===false) && (value.verticalType=='bar' || value.verticalType=='line') && (value.visible!=options.cursor.visible || value.color!=options.cursor.color || value.verticalType!=options.cursor.verticalType || !properties.initialized) ) { if(options.cursor.color!=value.color || options.cursor.verticalType!=value.verticalType || options.cursor.visible!=value.visible) redraw=true; options.cursor.visible=value.visible; options.cursor.color=value.color; options.cursor.verticalType=value.verticalType; objects.canvasCursor.css('display', options.cursor.visible?'block':'none'); if(options.cursor.visible && redraw) privateMethods.drawCursor(object); } return(options.cursor); }, // setCursor /** * define the margins * * @param CDMargins value: a CDMargins object * @return CDMargins: the CDMargins applied */ setMargins : function (object, value) { var properties=object.data('properties'), objects=object.data('objects'), options=object.data('options'); if(value instanceof CDMargins && (!options.margins.equals(value) || !properties.initialized) ) { options.margins.set(value); privateMethods.draw(object); } return(options.margins); }, // setMargins /** * define the legend * * @param CDLegend value: a CDLegend object * @return CDLegend: current CDLegend applied */ setLegend : function (object, value) { var properties=object.data('properties'), objects=object.data('objects'), options=object.data('options'); if(value instanceof CDLegend && (!options.legend.equals(value) || !properties.initialized) ) { options.legend.set(value); privateMethods.draw(object); } return(options.legend); }, // setLegend /** * define the axis * * @param CDAxis value: a CDAxis object * @param boolean front: true if function is called to define the front axis, * false if function is called to define the baxk axis * @return CDAxis: the CDAxis object applied */ setAxis : function (object, value, front) { var properties=object.data('properties'), objects=object.data('objects'), options=object.data('options'); if(value instanceof CDAxis) { if(front) { delete options.axis.front; options.axis.front=value; } else { delete options.axis.back; options.axis.back=value; } privateMethods.draw(object); } return(front?options.axis.front:options.axis.back); }, // setAxis /** * this function is used to set a callback when mouse position changed over * the viewport */ setEventMousePositionChange : function (object, value) { var properties=object.data('properties'), options=object.data('options'); options.events.mousePositionChange=value; object.unbind('drawingGraphMousePositionChange'); if(value) object.bind('drawingGraphMousePositionChange', options.events.mousePositionChange); return(options.events.mousePositionChange); }, //setEventMousePositionChange /** * this function is used to set a callback when mouse leave the viewport */ setEventMouseLeave : function (object, value) { var properties=object.data('properties'), options=object.data('options'); options.events.mouseLeave=value; object.unbind('drawingGraphMouseLeave'); if(value) object.bind('drawingGraphMouseLeave', options.events.mouseLeave); return(options.events.mouseLeave); }, //setEventMouseLeave /** * this function is used to set a callback when mouse enter the viewport */ setEventMouseEnter : function (object, value) { var properties=object.data('properties'), options=object.data('options'); options.events.mouseEnter=value; object.unbind('drawingGraphMouseEnter'); if(value) object.bind('drawingGraphMouseEnter', options.events.mouseEnter); return(options.events.mouseEnter); }, //setEventMouseEnter /** * this function is used to set a callback when mouse is clicked on the * viewport */ setEventMouseClick : function (object, value) { var properties=object.data('properties'), options=object.data('options'); options.events.mouseClick=value; object.unbind('drawingGraphMouseClick'); if(value) object.bind('drawingGraphMouseClick', options.events.mouseClick); return(options.events.mouseClick); }, // setEventMouseClick /** * this function is called when the mouse moves on the drawing area * - calculate the axis position * - draw cursor * - trigger function event if any * * Note: works only if the axis mode is 'XY'. * * @param integer valueX: x position on the canvas * @param integer valueY: y position on the canvas */ mousePositionUpdate : function (object, valueX, valueY) { var properties=object.data('properties'), options=object.data('options'), work={ margins:options.margins.get(), legend:options.legend.get(), axis:{ front:(options.axis.front!=null)?options.axis.front.get():null, back:(options.axis.back!=null)?options.axis.back.get():null }, values:[], axisValues:{}, axisPos:{ x:0, y:0 }, updated:false }, /** * returns for each series, the value associated with the cursor's position * * @param Array series: series * @param integer position: horinzontal cursor position * @return Array: array of series values */ calculateSeriesValues = function (series, position) { var tmpSerie=null, returned=[]; for(var i=0;i