001 /*
002 * Apache License
003 * Version 2.0, January 2004
004 * http://www.apache.org/licenses/
005 *
006 * Copyright 2008-2010 by chenillekit.org
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 */
014
015 package org.chenillekit.tapestry.core.components;
016
017 import org.apache.tapestry5.BindingConstants;
018 import org.apache.tapestry5.ClientElement;
019 import org.apache.tapestry5.ComponentResources;
020 import org.apache.tapestry5.MarkupWriter;
021 import org.apache.tapestry5.annotations.AfterRender;
022 import org.apache.tapestry5.annotations.BeginRender;
023 import org.apache.tapestry5.annotations.Environmental;
024 import org.apache.tapestry5.annotations.Import;
025 import org.apache.tapestry5.annotations.Parameter;
026 import org.apache.tapestry5.annotations.SetupRender;
027 import org.apache.tapestry5.annotations.SupportsInformalParameters;
028 import org.apache.tapestry5.ioc.annotations.Inject;
029 import org.apache.tapestry5.json.JSONArray;
030 import org.apache.tapestry5.json.JSONObject;
031 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
032 import org.chenillekit.tapestry.core.utils.XYDataItem;
033
034 import java.util.List;
035
036 /**
037 * chart component based on <a href="http://solutoire.com/flotr/">Flotr javascript library</a>.
038 *
039 * @version $Id: Chart.java 699 2010-09-08 14:59:13Z homburgs $
040 */
041 @SupportsInformalParameters
042 @Import(library = {"../excanvas.js", "chart/flotr.debug-0.2.0-test.js", "Chart.js"})
043 public class Chart implements ClientElement
044 {
045 /**
046 * The id used to generate a page-unique client-side identifier for the component. If a component renders multiple
047 * times, a suffix will be appended to the to id to ensure uniqueness.
048 */
049 @Parameter(value = "prop:componentResources.id", defaultPrefix = BindingConstants.LITERAL)
050 private String clientId;
051
052 /**
053 * the list of data item arrays.
054 */
055 @Parameter(name = "dataItems", required = false, defaultPrefix = BindingConstants.PROP)
056 private List<List<XYDataItem>> dataItemsList;
057
058 /**
059 * PageRenderSupport to get unique client side id.
060 */
061 @Environmental
062 private JavaScriptSupport javascriptSupport;
063
064 /**
065 * For blocks, messages, create actionlink, trigger event.
066 */
067 @Inject
068 private ComponentResources resources;
069
070 private String assignedClientId;
071
072 /**
073 * Tapestry render phase method.
074 * Initialize temporary instance variables here.
075 */
076 @SetupRender
077 void setupRender()
078 {
079 assignedClientId = javascriptSupport.allocateClientId(clientId);
080 }
081
082 /**
083 * Tapestry render phase method.
084 * Start a tag here, end it in afterRender
085 */
086 @BeginRender
087 void beginRender(MarkupWriter writer)
088 {
089 writer.element("div", "id", getClientId());
090 resources.renderInformalParameters(writer);
091 writer.end();
092 }
093
094 /**
095 * Tapestry render phase method. End a tag here.
096 */
097 @AfterRender
098 void afterRender()
099 {
100 JSONObject spec = new JSONObject();
101 JSONObject config = new JSONObject();
102 JSONArray dataArray = null;
103
104 //
105 // Let subclasses do more.
106 //
107 configure(config);
108
109 //
110 // do it only if user give us some values
111 //
112 if (dataItemsList != null && dataItemsList.size() > 0)
113 {
114 dataArray = new JSONArray();
115
116 for (List<XYDataItem> dataItems : dataItemsList)
117 {
118 JSONArray data = buildDataValues(dataItems);
119 dataArray.put(data);
120 }
121 }
122
123
124 //
125 // if the user doesn't give us some chart values we add an empty value array.
126 //
127 if (dataArray != null)
128 spec.put("data", dataArray);
129 else if (config.has("data"))
130 spec.put("data", config.get("data"));
131 else
132 spec.put("data", new JSONArray(new JSONArray()));
133
134 if (config.has("options"))
135 spec.put("options", config.get("options"));
136
137 spec.put("element", getClientId());
138
139 javascriptSupport.addInitializerCall("ckflotrchart",spec);
140 }
141
142 /**
143 * let us build the data value string for Flotr.
144 *
145 * @param dataItems a list of data items
146 *
147 * @return a JSON array containing the data items
148 */
149 private static JSONArray buildDataValues(List<XYDataItem> dataItems)
150 {
151 JSONArray data = new JSONArray();
152
153 for (XYDataItem dataItem : dataItems)
154 data.put(new JSONArray(dataItem.getXValue(), dataItem.getYValue()));
155
156 return data;
157 }
158
159 /**
160 * Invoked to allow subclasses to further configure the parameters passed to this component's javascript
161 * options. Subclasses may override this method to configure additional features of the Flotr.
162 * <p/>
163 * This implementation does nothing.
164 *
165 * @param config parameters object
166 */
167 protected void configure(JSONObject config)
168 {
169 }
170
171 /**
172 * Returns a unique id for the element. This value will be unique for any given rendering of a
173 * page. This value is intended for use as the id attribute of the client-side element, and will
174 * be used with any DHTML/Ajax related JavaScript.
175 */
176 public String getClientId()
177 {
178 return assignedClientId;
179 }
180 }