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    }