001/* Attribute for generating the HTML file with JavaScript to plot simulation
002   results. This base class cannot be instanced.
003
004 Copyright (c) 2012-2014 The Regents of the University of California.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027
028 */
029
030package ptolemy.vergil.basic.export.web;
031
032import java.util.HashMap;
033
034import ptolemy.data.BooleanToken;
035import ptolemy.data.IntToken;
036import ptolemy.data.expr.Parameter;
037import ptolemy.data.expr.StringParameter;
038import ptolemy.data.type.BaseType;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.NamedObj;
042
043///////////////////////////////////////////////////////////////////
044////BasicJSPlotter
045/**
046 * Base class for attributes generating the HTML file with JavaScript to plot
047 * simulation results for "Export to Web". This class provides parameters that
048 * control how the figure should be plot. The generated HTML page consists of
049 * three parts: the head, the body and the JavaScript in the head. Methods are
050 * provided to modify the contents of these parts.
051 *
052 * @author Baobing (Brian) Wang
053 * @version $Id$
054 * @since Ptolemy II 10.0
055 * @Pt.ProposedRating Red (cxh)
056 * @Pt.AcceptedRating Red (cxh)
057 */
058public abstract class JSPlotterAttribute extends WebContent
059        implements WebExportable {
060
061    /** Create an instance for each parameter.
062     *  @param container The container.
063     *  @param name The name.
064     *  @exception IllegalActionException If the superclass throws it.
065     *  @exception NameDuplicationException If the superclass throws it.
066     */
067    public JSPlotterAttribute(NamedObj container, String name)
068            throws IllegalActionException, NameDuplicationException {
069        super(container, name);
070        _icon.setIconText("JS");
071        displayText.setDisplayName("Graph title");
072        displayText.setExpression("Ptolemy II Simulation Result");
073        setExpression("Customize by clicking \"Configure\"");
074        setVisibility(NOT_EDITABLE);
075
076        width.setVisibility(NONE);
077        height.setExpression("1");
078        height.setVisibility(NONE);
079
080        outputHTMLFile = new StringParameter(this, "outputHTMLFile");
081        outputHTMLFile.setDisplayName("Output HTML file name");
082        outputHTMLFile.setExpression("JSPlotter.html");
083
084        linkTarget = new LinkTarget(this, "linkTarget");
085        linkTarget.setDisplayName("The location to open the HTML page");
086        linkTarget.setExpression("_blank");
087
088        dataJSON = new StringParameter(this, "dataJSON");
089        dataJSON.setDisplayName("Input data series in JSON format");
090
091        eventsJSON = new StringParameter(this, "eventsJSON");
092        eventsJSON.setDisplayName("Input event series in JSON format");
093
094        saveDataToFile = new Parameter(this, "saveDataToFile");
095        saveDataToFile.setDisplayName(
096                "Save data and event series to a " + "separate file");
097        saveDataToFile.setExpression("false");
098        saveDataToFile.setTypeEquals(BaseType.BOOLEAN);
099
100        outputDataFile = new StringParameter(this, "outputDataFile");
101        outputDataFile.setDisplayName("Output data file name");
102        outputDataFile.setExpression("simulationResult.txt");
103
104        graphWidth = new Parameter(this, "graphWidth");
105        graphWidth.setTypeEquals(BaseType.INT);
106        graphWidth.setDisplayName("Graph min width");
107        graphWidth.setExpression("400");
108
109        graphHeight = new Parameter(this, "graphHeight");
110        graphHeight.setTypeEquals(BaseType.INT);
111        graphHeight.setDisplayName("Graph min height");
112        graphHeight.setExpression("500");
113
114        autoResize = new Parameter(this, "autoResize");
115        autoResize.setDisplayName("Auto-resize the graph");
116        autoResize.setExpression("true");
117        autoResize.setTypeEquals(BaseType.BOOLEAN);
118
119        enableLegend = new Parameter(this, "enableLegend");
120        enableLegend.setDisplayName("Show Legend");
121        enableLegend.setExpression("true");
122        enableLegend.setTypeEquals(BaseType.BOOLEAN);
123
124        horizontalAlign = new StringParameter(this, "horizontalAlign");
125        horizontalAlign.setDisplayName("Legend horizontal align");
126        horizontalAlign.addChoice("center");
127        horizontalAlign.addChoice("left");
128        horizontalAlign.addChoice("right");
129        horizontalAlign.setExpression("center");
130
131        verticalAlign = new StringParameter(this, "verticalAlign");
132        verticalAlign.setDisplayName("Legend vertical align");
133        verticalAlign.addChoice("top");
134        verticalAlign.addChoice("middle");
135        verticalAlign.addChoice("bottom");
136        verticalAlign.setExpression("bottom");
137
138        dataConnectWidth = new Parameter(this, "dataConnectWidth");
139        dataConnectWidth.setDisplayName("Data series connect line width");
140        dataConnectWidth.setExpression("2");
141        dataConnectWidth.setTypeEquals(BaseType.INT);
142
143        enableDataMarker = new Parameter(this, "enableDataMarker");
144        enableDataMarker.setDisplayName("Enable data series point marker");
145        enableDataMarker.setExpression("false");
146        enableDataMarker.setTypeEquals(BaseType.BOOLEAN);
147
148        dataMarkerRadius = new Parameter(this, "dataMarkerRadius");
149        dataMarkerRadius.setDisplayName("Data series marker radius");
150        dataMarkerRadius.setExpression("3");
151        dataMarkerRadius.setTypeEquals(BaseType.INT);
152
153        eventsConnectWidth = new Parameter(this, "eventsConnectWidth");
154        eventsConnectWidth.setDisplayName("Event series connect line width");
155        eventsConnectWidth.setExpression("0");
156        eventsConnectWidth.setTypeEquals(BaseType.INT);
157
158        enableEventsMarker = new Parameter(this, "enableEventsMarker");
159        enableEventsMarker.setDisplayName("Enable event series point marker");
160        enableEventsMarker.setExpression("true");
161        enableEventsMarker.setTypeEquals(BaseType.BOOLEAN);
162
163        eventsMarkerRadius = new Parameter(this, "eventsMarkerRadius");
164        eventsMarkerRadius.setDisplayName("Event series marker radius");
165        eventsMarkerRadius.setExpression("3");
166        eventsMarkerRadius.setTypeEquals(BaseType.INT);
167
168        xAxisMode = new StringParameter(this, "xAxisMode");
169        xAxisMode.setDisplayName("X axis type");
170        xAxisMode.addChoice("linear");
171        xAxisMode.addChoice("datetime");
172        xAxisMode.setExpression("linear");
173
174        drawVerticalGridLine = new Parameter(this, "drawVerticalGridLine");
175        drawVerticalGridLine.setDisplayName("Draw vertical grid line");
176        drawVerticalGridLine.setExpression("false");
177        drawVerticalGridLine.setTypeEquals(BaseType.BOOLEAN);
178
179        xAxisTitle = new StringParameter(this, "xAxisTitle");
180        xAxisTitle.setDisplayName("X axis title");
181        xAxisTitle.setExpression("X Axis");
182
183        yAxisMode = new StringParameter(this, "yAxisMode");
184        yAxisMode.setDisplayName("Y axis type");
185        yAxisMode.addChoice("linear");
186        yAxisMode.addChoice("logarithmic");
187        yAxisMode.setExpression("linear");
188
189        drawHorizontalGridLine = new Parameter(this, "drawHorizontalGridLine");
190        drawHorizontalGridLine.setDisplayName("Draw horizontal grid line");
191        drawHorizontalGridLine.setExpression("true");
192        drawHorizontalGridLine.setTypeEquals(BaseType.BOOLEAN);
193
194        yAxisTitle = new StringParameter(this, "yAxisTitle");
195        yAxisTitle.setDisplayName("Y axis title");
196        yAxisTitle.setExpression("Y Axis");
197
198        customContent = new StringParameter(this, "customContent");
199        customContent.setDisplayName("Custom content");
200        customContent.setExpression("");
201    }
202
203    ///////////////////////////////////////////////////////////////////
204    ////                         parameters                        ////
205
206    /** Parameter specifying if the figure should be auto-resized based on the
207     *  window size. This is a boolean that defaults to true.
208     */
209    public Parameter autoResize;
210
211    /** Parameter specifying text to be inserted into dygraph constructor call.
212     *  Workaround for specifying complicated options not covered by other
213     *  parameters, for example, series-specific parameters.
214     */
215    public StringParameter customContent;
216
217    /** Parameter specifying the width of the stroke connecting data points
218     *  This is an int that defaults to 2.
219     */
220    public Parameter dataConnectWidth;
221
222    /** Parameter giving the data traces in JSON format to be plot.
223     *  This defaults to ""
224     */
225    public StringParameter dataJSON;
226
227    /** Parameter specifying if the horizontal grid line should be drawn.
228     *  This is a boolean that defaults to true.
229     */
230    public Parameter drawHorizontalGridLine;
231
232    /** Parameter specifying the radius of markers for data points.
233     *  This is an int that defaults to 3.
234     */
235    public Parameter dataMarkerRadius;
236
237    /** Parameter specifying if the vertical grid line should be drawn.
238     *  This is a boolean that defaults to false.
239     */
240    public Parameter drawVerticalGridLine;
241
242    /** Parameter specifying if markers should be drawn for data points.
243     *  This is a boolean that defaults to false.
244     */
245    public Parameter enableDataMarker;
246
247    /** Parameter specifying if markers should be drawn for event points.
248     *  This is a boolean that defaults to true.
249     */
250    public Parameter enableEventsMarker;
251
252    /** Parameter specifying if the legend should be shown on the figure.
253     *  This a boolean that defaults to true.
254     */
255    public Parameter enableLegend;
256
257    /** Parameter specifying the width of the stroke connecting event points
258     *  This is an int that defaults to 0.
259     */
260    public Parameter eventsConnectWidth;
261
262    /** Parameter specifying the radius of markers for event points.
263     *  This is an int that defaults to 3.
264     */
265    public Parameter eventsMarkerRadius;
266
267    /** Parameter giving the event traces in JSON format to be plot.
268     *  This defaults to ""
269     */
270    public StringParameter eventsJSON;
271
272    /** Parameter specifying the width of the figure.
273     *  This is an int that defaults to 400px.
274     */
275    public Parameter graphWidth;
276
277    /** Parameter specifying the height of the figure.
278     *  This is an int that defaults to 500px.
279     */
280    public Parameter graphHeight;
281
282    /** Parameter specifying the horizontal position of the legend on the figure.
283     *  This defaults to "center".
284     */
285    public StringParameter horizontalAlign;
286
287    /** Parameter specifying the target for the link.
288     *  The possibilities are:
289     *  <ul>
290     *  <li><b>_lightbox</b>: Open in a lightbox-style popup frame.
291     *  <li><b>_blank</b>: Open in a new window or tab.
292     *  <li><b>_self</b>: Open in the same frame as it was clicked.
293     *  <li><b>_parent</b>: Open in the parent frameset.
294     *  <li><b>_top</b>: Open in the full body of the window.
295     *  <li><b><i>framename</i></b>: Open in a named frame.
296     *  </ul>
297     *  The default is "_lightbox".
298     */
299    public LinkTarget linkTarget;
300
301    /** Parameter specifying the name of the file to store the data and event traces.
302     *  This defaults to "simulationResult.txt".
303     */
304    public StringParameter outputDataFile;
305
306    /** Parameter specifying the name of the generated HTML file.
307     *  This defaults to "JSPlotter.html".
308     */
309    public StringParameter outputHTMLFile;
310
311    /** Parameter specifying if the data and event traces should be saved in a
312     *  separated file. This is a boolean that defaults to false.
313     */
314    public Parameter saveDataToFile;
315
316    /** Parameter specifying the vertical position of the legend on the figure.
317     *  This defaults to "bottom".
318     */
319    public StringParameter verticalAlign;
320
321    /** Parameter specifying how to parse the X axis value.
322     *  This defaults to "linear".
323     */
324    public StringParameter xAxisMode;
325
326    /** Parameter specifying the title of the X axis.
327     *  This defaults to "X Axis".
328     */
329    public StringParameter xAxisTitle;
330
331    /** Parameter specifying how to parse the Y axis value.
332     *  This defaults to "linear".
333     */
334    public StringParameter yAxisMode;
335
336    /** Parameter specifying the title of the Y axis.
337     *  This defaults to "Y Axis".
338     */
339    public StringParameter yAxisTitle;
340
341    ///////////////////////////////////////////////////////////////////
342    ////                         public methods                    ////
343
344    /** BasicJSPlotter is of type text/html.
345     *  @return The string "text/html"
346     */
347    @Override
348    public String getMimeType() {
349        return "text/html";
350    }
351
352    /** Return true, since new content should overwrite old.
353     *  @return true, since new content should overwrite old
354     */
355    @Override
356    public boolean isOverwriteable() {
357        return true;
358    }
359
360    /** Get the parameter values from the GUI input.
361     *  @return The hash map containing the values of all parameters
362     *  @exception IllegalActionException
363     */
364    public HashMap<String, String> getBasicConfig()
365            throws IllegalActionException {
366        _config.put("outputHTMLFile", outputHTMLFile.stringValue().trim());
367        _config.put("dataJSON", dataJSON.stringValue().trim());
368        _config.put("eventsJSON", eventsJSON.stringValue().trim());
369        _config.put("saveDataToFile",
370                ((BooleanToken) saveDataToFile.getToken()).toString().trim());
371        _config.put("outputDataFile", outputDataFile.stringValue().trim());
372
373        _config.put("graphTitle", displayText.stringValue().trim());
374        _config.put("graphWidth",
375                ((IntToken) graphWidth.getToken()).toString().trim());
376        _config.put("graphHeight",
377                ((IntToken) graphHeight.getToken()).toString().trim());
378        _config.put("autoResize",
379                ((BooleanToken) autoResize.getToken()).toString().trim());
380
381        _config.put("enableLegend",
382                ((BooleanToken) enableLegend.getToken()).toString().trim());
383        _config.put("horizontalAlign", horizontalAlign.stringValue().trim());
384        _config.put("verticalAlign", verticalAlign.stringValue().trim());
385
386        _config.put("dataConnectWidth",
387                ((IntToken) dataConnectWidth.getToken()).toString().trim());
388        _config.put("enableDataMarker",
389                ((BooleanToken) enableDataMarker.getToken()).toString().trim());
390        _config.put("dataMarkerRadius",
391                ((IntToken) dataMarkerRadius.getToken()).toString().trim());
392
393        _config.put("eventsConnectWidth",
394                ((IntToken) eventsConnectWidth.getToken()).toString().trim());
395        _config.put("enableEventsMarker",
396                ((BooleanToken) enableEventsMarker.getToken()).toString()
397                        .trim());
398        _config.put("eventsMarkerRadius",
399                ((IntToken) eventsMarkerRadius.getToken()).toString().trim());
400
401        _config.put("xAxisMode", xAxisMode.stringValue().trim());
402        _config.put("drawVerticalGridLine",
403                ((BooleanToken) drawVerticalGridLine.getToken()).toString()
404                        .trim());
405        _config.put("xAxisTitle", xAxisTitle.stringValue().trim());
406
407        _config.put("yAxisMode", yAxisMode.stringValue().trim());
408        _config.put("drawHorizontalGridLine",
409                ((BooleanToken) drawHorizontalGridLine.getToken()).toString()
410                        .trim());
411        _config.put("yAxisTitle", yAxisTitle.stringValue().trim());
412        return _config;
413    }
414
415    /** Get the body content.
416     *  @return The string containing the body content
417     */
418    public String getBodyContent() {
419        return _bodyContent.toString();
420    }
421
422    /** Get the head content.
423     *  @return The string containing the head content
424     */
425    public String getHeaderContent() {
426        _headerContent.append("\t\t<script>\n");
427        _headerContent.append(_scriptContent.toString());
428        _headerContent.append("\t\t</script>\n");
429        return _headerContent.toString();
430    }
431
432    /** Get the content of the whole HTML page.
433     *  @return The string containing the content of the whole HTML page
434     */
435    public String getHTMLPageContent() {
436        StringBuffer pageContent = new StringBuffer();
437        pageContent.append(
438                "<!DOCTYPE HTML>\n<html>\n\t<head>\n\t\t<meta charset=\"utf-8\">\n");
439        pageContent.append(getHeaderContent());
440        pageContent.append("\t</head>\n\n\t<body>\n");
441        pageContent.append(getBodyContent());
442        pageContent.append("\t</body>\n</html>");
443        return pageContent.toString();
444    }
445
446    /** Insert a string to the body with auto-indent.
447     * @param content The content to be inserted
448     */
449    public void insertBodyContent(String content) {
450        _bodyContent.append("\t\t" + content);
451    }
452
453    /** Insert a string to the header with auto-indent.
454     *  @param isJavaScript Whether the inserted content is JavaScript code
455     *  @param autoIndent Whether the content should be auto-indented
456     *  @param content the content to be inserted
457     */
458    public void insertHeaderContent(boolean isJavaScript, boolean autoIndent,
459            String content) {
460        if (isJavaScript && autoIndent) {
461            _scriptContent.append("\t\t\t" + content);
462        } else if (!isJavaScript & autoIndent) {
463            _headerContent.append("\t\t" + content);
464        } else if (isJavaScript && !autoIndent) {
465            _scriptContent.append(content);
466        } else {
467            _headerContent.append(content);
468        }
469    }
470
471    ///////////////////////////////////////////////////////////////////
472    ////                         protected methods                 ////
473
474    /** Provide content to the specified web exporter to be
475     *  included in a web page for the container of this object.
476     *  This class defines an href attribute to associate with
477     *  the area of the image map corresponding to its container.
478     *
479     *  @param exporter  The web exporter to write content to
480     *  @exception IllegalActionException If evaluating the value
481     *   of this parameter fails, or creating a web attribute fails.
482     */
483    @Override
484    protected void _provideAttributes(WebExporter exporter)
485            throws IllegalActionException {
486        WebAttribute webAttribute;
487        NamedObj container = getContainer();
488        _config = new HashMap<String, String>();
489        _scriptContent = new StringBuffer();
490        _headerContent = new StringBuffer();
491        _bodyContent = new StringBuffer();
492
493        if (container != null) {
494            // Last argument specifies to overwrite any previous value defined.
495            if (!outputHTMLFile.stringValue().trim().equals("")) {
496                // Create link attribute and add to exporter.
497                // Content should only be added once (onceOnly -> true).
498                webAttribute = WebAttribute.createWebAttribute(container,
499                        "hrefWebAttribute", "href");
500                webAttribute.setExpression(outputHTMLFile.stringValue());
501                exporter.defineAttribute(webAttribute, true);
502            }
503
504            String targetValue = linkTarget.stringValue();
505            if (!targetValue.trim().equals("")) {
506                if (targetValue.equals("_lightbox")) {
507                    // Strangely, the class has to be "iframe".
508                    // I don't understand why it can't be "lightbox".
509
510                    // Create class attribute and add to exporter.
511                    // Content should only be added once (onceOnly -> true).
512                    webAttribute = WebAttribute.appendToWebAttribute(container,
513                            "classWebAttribute", "class", "iframe");
514                    exporter.defineAttribute(webAttribute, true);
515                } else {
516
517                    // Create target attribute and add to exporter.
518                    // Content should only be added once (onceOnly -> true).
519                    webAttribute = WebAttribute.createWebAttribute(
520                            getContainer(), "targetWebAttribute", "target");
521                    webAttribute.setExpression(targetValue);
522                    exporter.defineAttribute(webAttribute, true);
523                }
524            }
525        }
526    }
527
528    ///////////////////////////////////////////////////////////////////
529    ////                         private variables                 ////
530
531    // String buffer storing the body content
532    private StringBuffer _bodyContent;
533
534    // HashMap storing the values of all parameters
535    private HashMap<String, String> _config;
536
537    // String buff storing the head content
538    private StringBuffer _headerContent;
539
540    // String buffer storing the JavaScript content in the head
541    private StringBuffer _scriptContent;
542}