Source: renderers/BaseRenderer.js

/**
 * @namespace Oracle
 */

/**
 * @namespace Oracle.BDD
 */

/**
 * @namespace Oracle.BDD.Portlets
 */

/**
 * @namespace Oracle.BDD.Portlets.Visualization
 */

/**
 * @namespace Oracle.BDD.Portlets.Visualization.Renderers
 * 
 */
Ext.ns('Oracle.BDD.Portlets.Visualization.Renderers');

/**
 * Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer is the JavaScript
 * class from which all Custom Visualization renderer classes must extend.
 * Subclasses of the BaseRenderer must implement the init() function which
 * servers as the starting point for querying and visualizing data.
 * <p>
 * The init component is expected to call the executeQuery() function, supplying
 * it with a queryConfig object acquired with the
 * {{this.getQueryConfig(queryName)}} function.
 * <p>
 * Additionally, the executeQuery() function is supplied with a callback
 * function which takes an Oracle.BDD.Portlets.Visualization.Model.QueryResult
 * object as its first argument. This function is responsible for the initial
 * rendering of the visualization using the returned data.
 * <p>
 * In this example, Oracle.BDD.Portlets.Visualization.Renderers.DonutPieChart
 * renderer class is defined. It contains the required init() function
 * implementation that is called when the renderer is first initialized.
 * <p>
 * In the init() function, the query named "eql" is executed and the results are
 * passed to the renderVisualization function. The renderVisualization is then
 * responsible for rendering the visualization with the provided data.
 * 
 * <pre>
 * Oracle.BDD.Portlets.Visualization.Renderers.DonutPieChart = Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer
 * 		.extend({
 * 
 * 			init : function(queryConfig) {
 * 
 * 				var queryConfig = this.getQueryConfig(&quot;eql&quot;);
 * 				this.executeQuery(queryConfig, true, this.renderVisualization);
 * 
 * 			},
 * 
 * 			renderVisualization : function(queryResults) {
 * 
 * 				// rendering code here
 * 
 * 		}
 * 		});
 * </pre>
 * 
 * @class Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer
 * @extends Ext.Container
 */
Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer = Ext
		.extend(
				Ext.Container,
				{

					/**
					 * Displays the provided error message in the portlet
					 * container.
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {string}
					 *            err Error message to display. Optional.
					 *            Displays default error message if none of
					 *            provided.
					 * @param {boolean}
					 *            clear Indicates if canvas should be cleared
					 */
					displayError : function(err, clear) {
						if (clear)
							this.getCanvas().clear();
						this.fireEvent('displaymessage', this, err, true);
					},

					/**
					 * Displays the provided message in the portlet container.
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {string}
					 *            msg Message to display
					 * @param {boolean}
					 *            clear Indicates if canvas should be cleared
					 */
					displayMessage : function(msg, clear) {
						if (clear)
							this.getCanvas().clear();
						this.fireEvent('displaymessage', this, msg, false);
					},

					/**
					 * Executes a query against the Dgraph, updates the
					 * renderer's data records and then executes the provided
					 * callback.
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {QueryConfig|string}
					 *            queryConfig A QueryConfig object defining the
					 *            query to execute or the name of eql query to
					 *            execute
					 * @param {boolean}
					 *            mask boolean indicating if the load mask
					 *            should display until query response is
					 *            received.
					 * @param {function}
					 *            callback The callback function to execute
					 *            after data are updated. Optional.
					 * @param {primative|Object|Object[]}
					 *            callbackArgs An array, object, or primative to
					 *            pass through to the callback function as
					 *            argument
					 * @param {function}
					 *            errorCallback The callback function to execute
					 *            in the case of an error response from query.
					 *            If not specified, the default error message is
					 *            rendered to the canvas. Optional.
					 * @param {primative|Object|Object[]}
					 *            errorCallbackArgs An array, object, or
					 *            primative to pass through to the error
					 *            callback function as argument
					 */
					executeQuery : function(queryObj, mask, callback,
							callbackArgs, errorCallback, errorCallbackArgs) {

						if (Ext.isString(queryObj)) {
							queryObj = this.getQueryConfig(queryObj);
						}

						if (!this._baseQuery
								|| !this._baseQuery.hasOwnProperty("callback")) {
							this._baseQuery = {
								queryName : queryObj.getQueryName(),
								callback : callback,
								callbackArgs : callbackArgs
							};
						}

						this.fireEvent('executequery', this, queryObj, mask,
								callback, callbackArgs, errorCallback,
								errorCallbackArgs);
					},

					/**
					 * Returns the singleton
					 * Oracle.BDD.Portlets.Visualization.Model.Canvas object
					 * representing the drawing space on which the visualization
					 * is rendered. If the canvas has already rendered, then a
					 * reference to the existing canvas obj is return (and the
					 * config argument is ignored); otherwise a newly
					 * initialized canvas object is returned.
					 * 
					 * <p>
					 * The Canvas configuration object has two properties that
					 * may be configured:
					 * <p>
					 * margins: Set the margins of the canvas tagName: By
					 * 
					 * For example, to set margins and configure the canvas root
					 * to be a <DIV> element instead of the default \<G\>
					 * wrapped in an \<SVG\> element:
					 * <p>
					 * 
					 * <pre>
					 * var canvas = getCanvas({
					 * 	margins : {
					 * 		left : 50,
					 * 		right : 50,
					 * 		top : 100,
					 * 		bottom : 100
					 * 	},
					 * 	tagName : 'div'
					 * })
					 * </pre>
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {Object}
					 *            config Canvas configuration object. Optional.
					 * @param {boolean}
					 *            force Forces the re-initialization even if
					 *            canvas has been rendered. Optional.
					 * @return {Canvas} The canvas
					 */
					getCanvas : function(config, force) {

						if (!this._canvas || force
								|| (config && !this._canvas.isRendered())) {

							if (this._canvas)
								this._canvas.clear();

							config = config || {};

							config.rendererId = this.id;
							config.container = this.container;
							config.width = this.width;
							config.height = this.height;

							this._canvas = new Oracle.BDD.Portlets.Visualization.Model.Canvas(
									config);
						}

						return this._canvas;
					},

					/**
					 * Returns a singleton
					 * {@link Oracle.BDD.Portlets.Visualization.Util.ColorManager}
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {Object}
					 *            config Color Manager configuration object.
					 *            Optional.
					 * @return {ColorManager} The default ColorMamanger
					 * 
					 * <pre>
					 * Color Manager configuration example: 
					 * 
					 * var colorMgr = getColorManager({
					 *    library: [&quot;#6B486B&quot;, &quot;#718BAE&quot;, &quot;#ABBE23&quot;],
					 *    scaleDomain: [&quot;Sunday&quot;,&quot;Monday&quot;,&quot;Tuesday&quot;,&quot;Wednesday&quot;,&quot;Thursday&quot;,&quot;Friday&quot;,&quot;Saturday&quot;]
					 * });
					 * </pre>
					 */
					getColorManager : function(config) {

						/**
						 * Returns a singleton Color Manager.
						 */
						if (!this._colorManager) {
							this._colorManager = new Oracle.BDD.Portlets.Visualization.Util.ColorManager(
									config);
						} else if (config) {
							if (config.library)
								this._colorManager.setLibrary(config.library);
							if (config.scaleDomain)
								this._colorManager
										.setScaleDomain(config.scaleDomain);
						}

						return this._colorManager;
					},

					/**
					 * Returns the
					 * {@link Oracle.BDD.Portlets.Visualization.Model.QueryConfig}
					 * of the given name.
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {string}
					 *            queryName The queryName.
					 * @return {QueryConfig} The QueryConfig
					 */
					getQueryConfig : function(queryName) {

						var config = {
							queryName : queryName
						};

						if (queryName
								&& this._queryTokens.hasOwnProperty(queryName)) {
							config.tokens = this._queryTokens[queryName];
						} else if (this._baseQuery && this._baseQuery.tokens) {
							config.tokens = this._baseQuery.tokens;
						}

						return new Oracle.BDD.Portlets.Visualization.Model.QueryConfig(
								config);
					},

					/**
					 * Utility function to return a value or array of values as
					 * a String formatted for use within an EQL WHERE or HAVING
					 * clause. For example, the values array ["blue', "red",
					 * "green"] returns as {"blue', "red", "green"}.
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {Object|Object[]}
					 *            values Value or values to convert
					 * @param {Token}
					 *            type The token of the attribute that provided
					 *            the values
					 * @return{string} The formatted string
					 */
					getValueExpressionForToken : function(values, token) {

						if (!token.getAttributeKey || !token.getAttributeKey())
							return;

						var type = token.getDataType();
						var isSingleAssign = token.isSingleAssign();

						var returnValues = [];
						var iterValues = isSingleAssign ? [ values ] : values;
						var iso = d3.time.format.iso;

						Ext.each(iterValues, function(v) {
							if (type === 'mdex:string-set'
									|| type === 'mdex:string') {
								returnValues.push("'" + v + "'");
							} else if (type === 'mdex:dateTime-set'
									|| type === 'mdex:dateTime') {
								returnValues.push("TO_DATETIME('" + iso(v)
										+ "')");
							} else if (type === 'mdex:duration-set'
									|| type === 'mdex:duration') {
								returnValues.push("TO_DURATION('" + v + "')");
							} else if (type === 'mdex:time-set'
									|| type === 'mdex:time') {
								returnValues.push("TO_TIME('" + v + "')");
							} else if (type === 'mdex:geocode-set'
									|| type === 'mdex:geocode') {
								returnValues.push("TO_GEOCODE('" + v + "')");
							} else {
								returnValues.push(v);
							}
						});

						return isSingleAssign ? returnValues[0] : "{"
								+ returnValues.join(',') + "}";

					},

					/**
					 * <b>Required implementation.</b> All subclasses of the
					 * BaseRenderer must implement this function. This function
					 * is called when the renderer is first initialized and
					 * serves as the starting point for the renderer to executes
					 * its initial query for data and define how to respond to
					 * its results. This is accomplished by including a call to
					 * executeQuery in the implementation.
					 * <p>
					 * For example:
					 * 
					 * <pre>
					 *    init: function() {
					 *       
					 *       var queryConfig = this.getQueryConfig(&quot;eql&quot;);
					 *       this.executeQuery(queryConfig, true, this.renderVisualization);
					 * 
					 *   },
					 * 
					 *   renderVisualization: function(queryResults) {
					 *   
					 *      // rendering code here
					 *      
					 *   }
					 * </pre>
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @abstract
					 */
					init : function() {
						this.displayError(this.resources
								.getMessage("cvp.view.error.noinit"), true);
					},

					/**
					 * Convenience method to add a refinement and execute the
					 * query in one call. See {@linkcode #addRefinement} and
					 * {@linkcode #executeQuery}
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {QueryConfig}
					 *            queryConfig A QueryConfig object defining the
					 *            query to execute
					 * @param {string}
					 *            tokenName The name of the attribute token the
					 *            attribute of which the refinement is to apply
					 * @param {string|Number|Object[]}
					 *            value The value(s) to refine by
					 * @param {function}
					 *            callback The callback function to execute
					 *            after data are updated. Optional.
					 * @param {primative|Object|Object[]}
					 *            callbackArgs An array, object, or primative to
					 *            pass through to the callback function as
					 *            argument
					 * @param {function}
					 *            errorCallback The callback function to execute
					 *            in the case of an error response from query.
					 *            If not specified, the default error message is
					 *            rendered to the canvas. Optional.
					 * @param {primative|Object|Object[]}
					 *            errorCallbackArgs An array, object, or
					 *            primative to pass through to the error
					 *            callback function as argument
					 */
					refine : function(queryConfig, tokenName, value, callback,
							callbackArgs, errorCallback, errorCallbackArgs) {

						queryConfig.addRefinement(tokenName, value);
						this.executeQuery(queryConfig, true, callback,
								callbackArgs, errorCallback, errorCallbackArgs);

					},

					/**
					 * Utility function to truncate text that exceeds the
					 * provided width and appends ellipses to the text. This
					 * would normally be accomplished with CSS (text-overflow:
					 * ellipsis;), but SVG elements do not support this.
					 * 
					 * @memberof Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer#
					 * @param {SVGElement}
					 *            svgElmt The SVG element containing the text to
					 *            truncate
					 * @param {Number}
					 *            width The width to constrain the text to
					 * @param {Number}
					 *            padding Padding to apply. Optional.
					 */
					truncateText : function(svgElmt, width, padding) {
						padding = padding || 0;
						var elmt = d3.select(svgElmt);
						if (elmt && elmt.node().getComputedTextLength) {
							var textLength = elmt.node()
									.getComputedTextLength(), text = elmt
									.text();
							while (textLength > (width - (2 * padding))
									&& text.length > 0) {
								text = text.slice(0, -1);
								elmt.text(text + '...');
								textLength = elmt.node()
										.getComputedTextLength();
							}
						}
					},

					/**
					 * Private functions. Direct use of these functions is NOT
					 * supported *
					 */

					/**
					 * @private
					 */
					NS_BDD_PREFIX : "http://www.oracle.com/bdd/",

					/**
					 * @private
					 */
					constructor : function(config) {

						Ext.apply(this, config);

						this._baseQuery = {};
						this._canvas = undefined;
						this._queryTokens = {};

						/* Declare namespace for bdd:tooltip attribute */
						d3.ns.prefix.bdd = this.NS_BDD_PREFIX;

						this.cls = "visualization-portlet-renderer";

						this.addEvents({
							'executequery' : true,
							'displaymessage' : true
						});

						Oracle.BDD.Portlets.Visualization.Renderers.BaseRenderer.superclass.constructor
								.call(this);

					},

					/**
					 * @private
					 */
					executeRenderer : function(viewModel, callback,
							callbackArgs) {

						var queryResults = new Oracle.BDD.Portlets.Visualization.Model.QueryResults(
								viewModel);

						if (viewModel.queryConfig) {
							this._queryTokens[viewModel.queryConfig.queryName] = viewModel.queryConfig.tokens;
						}

						if (this._baseQuery
								&& viewModel.queryConfig.queryName === this._baseQuery.queryName) {
							this._baseQuery.queryResults = queryResults;
						}

						this._baseQuery.tokens = queryResults.getQueryConfig()
								.getTokens();

						this.executeQueryCallback(queryResults, callback,
								callbackArgs);
					},

					/**
					 * @private
					 */
					executeQueryCallback : function(queryResults, callback,
							callbackArgs) {

						if (!callback && this._baseQuery) {
							callback = callback || this._baseQuery.callback;
							callbackArgs = callbackArgs
									|| this._baseQuery.callbackArgs;
						}

						var args = [ queryResults ].concat(callbackArgs);

						if (callback && queryResults) {
							callback.apply(this, args);
							this.initializeTooltips();
						}

					},

					/**
					 * Resize behavior. If newWidth or newheight differs from
					 * the dimensions of the canvas, then the canvas is cleared,
					 * resized and the visualization re-rendered.
					 * 
					 * @private
					 * @param {Number}
					 *            newWidth The new width to set the canvas
					 * @param {Number}
					 *            newHeight The new height to set the canvas
					 */
					handleResize : function(newWidth, newHeight) {

						var canvasRoot = Ext.get(this.id + "_wrapper");
						var canvasWidth = canvasRoot ? canvasRoot
								.getComputedWidth() : newWidth;
						var canvasHeight = canvasRoot ? canvasRoot
								.getComputedHeight() : newHeight;
						if (canvasWidth != newWidth
								|| canvasHeight != newHeight
								&& this._baseQuery.queryResults) {
							this.getCanvas().clear();
							this._canvas = undefined;
							this.width = newWidth;
							this.height = newHeight;
							this.executeQueryCallback(
									this._baseQuery.queryResults,
									this._baseQuery.callback,
									this._baseQuery.callbackArgs);
						}
					},

					/**
					 * @private
					 */
					initializeTooltips : function() {

						var self = this;
						var tipNodes = Ext.get(this.id).query('[*|tooltip]');

						Ext.each(tipNodes, function(node, idx, allElmts) {

							var nodeNS = node.getAttributeNodeNS(
									self.NS_BDD_PREFIX, "tooltip");

							if (nodeNS) {
								var value = nodeNS.value;

								new Ext.ToolTip({
									target : node,
									renderTo : Ext.getBody(),
									html : value,
									listeners : {
										beforeshow : function(tip) {

											var val = tip.target.getAttribute(
													"tooltip",
													self.NS_BDD_PREFIX);
											tip.update(val);

											return !Ext.isEmpty(val);
										}
									}
								});
							}
						});
					},

					/**
					 * @private
					 */
					initializeRenderer : function(tokens) {

						this._queryTokens = {};

						this._baseQuery = this._baseQuery || {};
						this._baseQuery.tokens = tokens;

						/* for 1.1.0 compatibility */
						var queryConfig = new Oracle.BDD.Portlets.Visualization.Model.QueryConfig(
								this._baseQuery);

						this.init(queryConfig);
					}
				});
// @ sourceURL=BaseRenderer.js