001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.graphic;
021 
022 import js.ArrayLike;
023 import js.Id;
024 import js.Js;
025 import js.ObjectLike;
026 import js.Vars;
027 import jsx.Configurable;
028 import jsx.core.ArrayLikes;
029 import jsx.dom.Styles;
030 import jsx.ui.Component;
031 
032 /**
033  * <p>A utility class providing static methods to create basic VML components.</p>
034  * <p>A VML component wraps a VML element and is {@link Configurable}.</p>
035  * 
036  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
037  */
038 public final class VML extends Configurable
039 {
040     private VML() {
041         super(null);
042     }
043 
044     /**
045      * <p>Creates a VML group component that wraps a <tt>&lt;v:group&gt;</tt> element.</p>
046      * @return The newly created VML component.
047      * @since 1.0
048      */
049     public static final Component group() {
050         return Component.createElement(GROUP);
051     }
052 
053     private static final String droppx(String s) {
054         return s.endsWith("px") ? s.substring(0, s.length() - 2) : s;
055     }
056 
057     /**
058      * <p>Appends a specified VML component to the VML group component.</p>
059      * @param g A VML group component.
060      * @param v A VML component to append to the group component.
061      * @since 1.0
062      */
063     public static final void appendChild(Component g, Component v) {
064         if (!isStyled(v)) {
065             ObjectLike gs = Component.style(g);
066             ObjectLike vs = Component.style(v);
067             Styles.width (vs, droppx(Styles.width (gs)));
068             Styles.height(vs, droppx(Styles.height(gs)));
069         }
070         Component.appendChild(g, v);
071     }
072 
073     /**
074      * <p>Sets the dimensions of the user coordination system of a VML component by 
075      * setting "coordsize" attribute of its underlying VML element.</p>
076      * @param w A string or number value of the X dimension.
077      * @param h A string or number value of the Y dimension.
078      * @since 1.0
079      * @see #coordSize(Component, Object, Object)
080      */
081     public static final void coordSize(Component v, Object w, Object h) {
082         Component.setAttribute(v, COORDSIZE, ArrayLikes.join(
083                 new Vars<Object>().add(w).add(h).var(),
084                 SPACE
085         ));
086     }
087 
088     /**
089      * <p>Sets the origin of the user coordination system of a VML component by 
090      * setting "coordorigin" attribute of its underlying VML element.</p>
091      * @param x A string or number value of the X coordinate of the base point.
092      * @param y A string or number value of the Y coordinate of the base point.
093      * @since 1.0
094      * @see #coordOrigin(Component, Object, Object)
095      */
096     public static final void coordOrigin(Component v, Object x, Object y) {
097         Component.setAttribute(v, COORDORIGIN, ArrayLikes.join(
098                 new Vars<Object>().add(x).add(y).var(),
099                 SPACE
100         ));
101     }
102 
103     /**
104      * <p>A constant string of a space.</p>
105      * @since 1.0
106      */
107     public final static String SPACE = " ";
108     /**
109      * <p>The name of the path attribute of a VML element.</p>
110      * @since 1.0
111      */
112     public final static String PATH  = "path";
113     /**
114      * <p>The name of the attribute of a VML element to specify the origin of 
115      * the user coordination system.</p>
116      * @since 1.0
117      */
118     public final static String COORDORIGIN = "coordorigin";
119     /**
120      * <p>The name of the attribute of a VML element to specify the dimensions of 
121      * the user coordination system.</p>
122      * @since 1.0
123      */
124     public final static String COORDSIZE = "coordsize";
125     /**
126      * <p>The name of the HTML tag of a VML group element.</p>
127      * @since 1.0
128      */
129     public final static String GROUP = "v:group";
130     /**
131      * <p>The name of the HTML tag of a VML shape element.</p>
132      * @since 1.0
133      */
134     public final static String SHAPE = "v:shape";
135     /**
136      * <p>The name of the HTML tag of a VML line element.</p>
137      * @since 1.0
138      */
139     public final static String LINE = "v:line";
140     /**
141      * <p>The name of the HTML tag of a VML polyline element.</p>
142      * @since 1.0
143      */
144     public final static String POLYLINE = "v:polyline";
145     /**
146      * <p>The name of the HTML tag of a VML curve element.</p>
147      * @since 1.0
148      */
149     public final static String CURVE = "v:curve";
150     /**
151      * <p>The name of the HTML tag of a VML rectangle element.</p>
152      * @since 1.0
153      */
154     public final static String RECT = "v:rect";
155     /**
156      * <p>The name of the HTML tag of a VML rounded rectangle element.</p>
157      * @since 1.0
158      */
159     public final static String ROUNDRECT = "v:roundrect";
160     /**
161      * <p>The name of the HTML tag of a VML ellipse element.</p>
162      * @since 1.0
163      */
164     public final static String OVAL = "v:oval";
165 
166     /**
167      * <p>Creates VML component that wraps a VML line element.</p>
168      * @return The newly created VML component.
169      * @since 1.0
170      * @see #lineFrom(Component, String)
171      * @see #lineFrom(Component, int, int)
172      * @see #lineTo(Component, String)
173      * @see #lineTo(Component, int, int)
174      */
175     public static final Component line() {
176         return Component.createElement(LINE);
177     }
178 
179     /**
180      * <p>Defines the starting point of a line by setting attributes to the VML 
181      * element wrapped by the specified component.</p>
182      * @param v The current VML component.
183      * @param p The start point of the line.
184      * @since 1.0
185      * @see #line()
186      * @see #lineFrom(Component, int, int)
187      * @see #lineTo(Component, String)
188      */
189     public static final void lineFrom(Component v, String p) {
190         Component.setAttribute(v, "from", p);
191     }
192 
193     /**
194      * <p>Defines the ending point of a line by setting attributes to the VML 
195      * element wrapped by the specified component.</p>
196      * @param v The current VML component.
197      * @param p The end point of the line.
198      * @since 1.0
199      * @see #line()
200      * @see #lineTo(Component, int, int)
201      * @see #lineFrom(Component, String)
202      */
203     public static final void lineTo(Component v, String p) {
204         Component.setAttribute(v, "to", p);
205     }
206 
207     /**
208      * <p>Defines the starting point of a line by setting attributes to the VML 
209      * element wrapped by the specified component.</p>
210      * @param v The current VML component.
211      * @param x The X coordinate of the start point of the line.
212      * @param y The Y coordinate of the start point of the line.
213      * @since 1.0
214      * @see #line()
215      * @see #lineFrom(Component, String)
216      * @see #lineTo(Component, int, int)
217      */
218     public static final void lineFrom(Component v, int x, int y) {
219         lineFrom(v, coord(x, y));
220     }
221 
222     /**
223      * <p>Defines the ending point of a line by setting attributes to the VML 
224      * element wrapped by the specified component.</p>
225      * @param v The current VML component.
226      * @param x The X coordinate of the end point of the line.
227      * @param y The Y coordinate of the end point of the line.
228      * @since 1.0
229      * @see #line()
230      * @see #lineTo(Component, String)
231      * @see #lineFrom(Component, int, int)
232      */
233     public static final void lineTo(Component v, int x, int y) {
234         lineTo(v, coord(x, y));
235     }
236 
237     /**
238      * <p>Creates VML component that wraps a VML line element.</p>
239      * @param x1 The X coordinate of the start point of the line.
240      * @param y1 The Y coordinate of the start point of the line.
241      * @param x2 The X coordinate of the end point of the line.
242      * @param y2 The Y coordinate of the end point of the line.
243      * @return The newly created VML component.
244      * @since 1.0
245      * @see #line()
246      */
247     public static final Component line(int x1, int y1, int x2, int y2) {
248         Component v = line();
249         lineFrom(v, x1, y1);
250         lineTo(v, x2, y2);
251         return v;
252     }
253 
254     /**
255      * <p>Creates VML component that wraps a VML polyline element.</p>
256      * @return The newly created VML component.
257      * @since 1.0
258      * @see #polyline(Component, ArrayLike)
259      * @see #polyline(ArrayLike)
260      */
261     public static final Component polyline() {
262         return Component.createElement(POLYLINE);
263     }
264 
265     /**
266      * <p>Defines the points of a polyline by setting attributes to the VML 
267      * element wrapped by the specified component.</p>
268      * @param v The current VML component.
269      * @param points The points that make up the polyline.
270      * @since 1.0
271      * @see #polyline()
272      * @see #polyline(ArrayLike)
273      */
274     public static final void polyline(Component v, ArrayLike<String> points) {
275         Component.setAttribute(v, "points", ArrayLikes.join(points, SPACE));
276     }
277 
278     /**
279      * <p>Creates VML component that wraps a VML polyline element.</p>
280      * @param points A list of pairs of points that define a set of straight line 
281      * segments. Points are specified in the coordinate system of the parent element, 
282      * either a group or the document.
283      * @return The newly created VML component.
284      * @since 1.0
285      * @see #polyline()
286      */
287     public static final Component polyline(ArrayLike<String> points) {
288         Component v = polyline();
289         polyline(v, points);
290         return v;
291     }
292 
293     private final static Id<Boolean> ISSTYLED = new Id<Boolean>();
294 
295     /**
296      * <p>Is the shape of a VML component specified in the CSS2 properties.</p>
297      * @param v The current VML component.
298      * @return <tt>true</true> if the shape of the underlying VML element of the current 
299      * component is also specified in the CSS2 properties. Otherwise, if the shape of the 
300      * underlying VML element of the current component is only defined by its attributes, 
301      * this method returns <tt>false</tt>.
302      * @since 1.0
303      */
304     public static final boolean isStyled(Component v) {
305         return Js.be(ini(v).var(ISSTYLED));
306     }
307 
308     private static final void border(Component v, String b) {
309         ObjectLike s = Component.style(v);
310         Styles.borderLeftWidth  (s, b);
311         Styles.borderTopWidth   (s, b);
312         Styles.borderRightWidth (s, b);
313         Styles.borderBottomWidth(s, b);
314     }
315 
316     /**
317      * <p>Creates VML component that wraps a VML rectangle element.</p>
318      * @return The newly created VML component.
319      * @since 1.0
320      */
321     public static final Component rect() {
322         Component v = Component.createElement(RECT);
323         border(v, "0px");
324         Component.setAttribute(v, "stroke", false);
325         ini(v).var(ISSTYLED, true);
326         return v;
327     }
328 
329     /**
330      * <p>Creates VML component that wraps a VML rounded rectangle element.</p>
331      * @return The newly created VML component.
332      * @since 1.0
333      * @see #roundrect(Component, double)
334      * @see #roundrect(double)
335      */
336     public static final Component roundrect() {
337         Component v = Component.createElement(ROUNDRECT);
338         border(v, "0px");
339         Component.setAttribute(v, "stroke", false);
340         ini(v).var(ISSTYLED, true);
341         return v;
342     }
343 
344     /**
345      * <p>Defines rounded corners for a rounded rectangle by setting attributes to the 
346      * VML element wrapped by the specified component.</p>
347      * @param v The current VML component.
348      * @param arcsize Defines rounded corners as a percentage of half the smaller 
349      * dimension of the rectangle. 0.0 (or "0%") for square corners, 1.0 (or "100%") for 
350      * smaller dimension forms a semi-circle.
351      * @since 1.0
352      * @see #roundrect()
353      */
354     public static final void roundrect(Component v, double arcsize) {
355         Component.setAttribute(v, "arcsize", arcsize);
356     }
357 
358     /**
359      * <p>Creates VML component that wraps a VML rounded rectangle element.</p>
360      * @param arcsize Defines rounded corners as a percentage of half the smaller 
361      * dimension of the rectangle. 0.0 (or "0%") for square corners, 1.0 (or "100%") for 
362      * smaller dimension forms a semi-circle.
363      * @return The newly created VML component.
364      * @since 1.0
365      * @see #roundrect()
366      */
367     public static final Component roundrect(double arcsize) {
368         Component v = roundrect();
369         roundrect(v, arcsize);
370         return v;
371     }
372 
373     /**
374      * <p>Creates VML component that wraps a VML oval element.</p>
375      * @return The newly created VML component.
376      * @since 1.0
377      * @see #ovalPosition(Component, String)
378      * @see #ovalPosition(Component, int, int)
379      * @see #ovalSize(Component, String)
380      * @see #ovalSize(Component, int, int)
381      * @see #oval(int, int, int, int)
382      */
383     public static final Component oval() {
384         Component v = Component.createElement(OVAL);
385         border(v, "0px");
386         Component.setAttribute(v, "stroke", false);
387         ini(v).var(ISSTYLED, true);
388         return v;
389     }
390 
391     /**
392      * <p>Defines the position of an oval by setting attributes to the VML 
393      * element wrapped by the specified component.</p>
394      * @param v The current VML component.
395      * @param p The position of the oval.
396      * @since 1.0
397      * @see #oval()
398      * @see #ovalPosition(Component, int, int)
399      * @see #oval(int, int, int, int)
400      */
401     public static final void ovalPosition(Component v, String p) {
402         Component.setAttribute(v, "position", p);
403     }
404 
405     /**
406      * <p>Defines the size of an oval by setting attributes to the VML 
407      * element wrapped by the specified component.</p>
408      * @param v The current VML component.
409      * @param s The size of the oval.
410      * @since 1.0
411      * @see #oval()
412      * @see #ovalSize(Component, int, int)
413      * @see #oval(int, int, int, int)
414      */
415     public static final void ovalSize(Component v, String s) {
416         Component.setAttribute(v, "size", s);
417     }
418 
419     /**
420      * <p>Defines the position of an oval by setting attributes to the VML 
421      * element wrapped by the specified component.</p>
422      * @param v The current VML component.
423      * @param x The X coordinate of the position of the oval.
424      * @param y The Y coordinate of the position of the oval.
425      * @since 1.0
426      * @see #ovalPosition(Component, String)
427      * @see #oval()
428      * @see #oval(int, int, int, int)
429      */
430     public static final void ovalPosition(Component v, int x, int y) {
431         ovalPosition(v, coord(x, y));
432     }
433 
434     /**
435      * <p>Defines the size of an oval by setting attributes to the VML 
436      * element wrapped by the specified component.</p>
437      * @param v The current VML component.
438      * @param w The X dimension of the oval.
439      * @param h The Y dimension of the oval.
440      * @since 1.0
441      * @see #ovalSize(Component, String)
442      * @see #oval()
443      * @see #oval(int, int, int, int)
444      */
445     public static final void ovalSize(Component v, int w, int h) {
446         ovalSize(v, coord(w, h));
447     }
448 
449     /**
450      * <p>Creates VML component that wraps a VML oval element.</p>
451      * @param x The X coordinate of the position of the oval.
452      * @param y The Y coordinate of the position of the oval.
453      * @param w The X dimension of the oval.
454      * @param h The Y dimension of the oval.
455      * @return The newly created VML component.
456      * @since 1.0
457      * @see #oval()
458      */
459     public static final Component oval(int x, int y, int w, int h) {
460         Component v = oval();
461         ovalPosition(v, x, y);
462         ovalSize(v, w, h);
463         return v;
464     }
465 
466     /**
467      * <p>Creates VML component that wraps a VML shape element.</p>
468      * @return The newly created VML component.
469      * @since 1.0
470      * @see #path(Component, ArrayLike)
471      * @see #path(ArrayLike)
472      */
473     public static final Component shape() {
474         return Component.createElement(SHAPE);
475     }
476 
477     /**
478      * <p>Defines path of an shape by setting a path created from the specified array of 
479      * commands to the VML element wrapped by the specified shape component.</p>
480      * @param v The current VML component.
481      * @param commands An array of path commands.
482      * @since 1.0
483      * @see #shape()
484      */
485     public static final void path(Component v, ArrayLike<String> commands) {
486         Component.setAttribute(v, PATH, ArrayLikes.join(commands, SPACE));
487     }
488 
489     /**
490      * <p>Creates VML component that wraps a VML shape element.</p>
491      * @param commands An array of path commands.
492      * @return The newly created VML component.
493      * @since 1.0
494      * @see #shape()
495      */
496     public static final Component path(ArrayLike<String> commands) {
497         Component v = shape();
498         path(v, commands);
499         return v;
500     }
501 
502     /**
503      * <p>Represents the specified coordinates in a string.</p>
504      * @param x The X coordinate.
505      * @param y The Y coordinate.
506      * @return A string representation for the coordinates.
507      * @since 1.0
508      */
509     public static final String coord(int x, int y) {
510         return ArrayLikes.join(
511                 new Vars<Object>().add(x).add(y).var(),
512                 SPACE
513         );
514     }
515 
516     /**
517      * <p>Creates an absolute move-to path command.</p>
518      * @param x The X coordinate of the point to move to.
519      * @param y The Y coordinate of the point to move to.
520      * @return A string value of the newly created path command.
521      * @since 1.0
522      */
523     public static final String moveTo(int x, int y) {
524         return Js.add("m", coord(x, y));
525     }
526 
527     /**
528      * <p>Creates a absolute line-to path command.</p>
529      * @param x The X coordinate of the point to line to.
530      * @param y The Y coordinate of the point to line to.
531      * @return A string value of the newly created path command.
532      * @since 1.0
533      */
534     public static final String lineTo(int x, int y) {
535         return Js.add("l", coord(x, y));
536     }
537 
538     /**
539      * <p>Creates a close path command.</p>
540      * @return A string value of the newly created path command.
541      * @since 1.0
542      */
543     public static final String close() {
544         return "x";
545     }
546 
547     /**
548      * <p>Creates an end path command.</p>
549      * @return A string value of the newly created path command.
550      * @since 1.0
551      */
552     public static final String end() {
553         return "e";
554     }
555 
556     /**
557      * <p>Creates a relative move-to path command.</p>
558      * @param x The relative X coordinate of the point to move to.
559      * @param y The relative Y coordinate of the point to move to.
560      * @return A string value of the newly created path command.
561      * @since 1.0
562      */
563     public static final String moveto(int x, int y) {
564         return Js.add("t", coord(x, y));
565     }
566 
567     /**
568      * <p>Creates a relative line-to path command.</p>
569      * @param x The relative X coordinate of the point to line to.
570      * @param y The relative Y coordinate of the point to line to.
571      * @return A string value of the newly created path command.
572      * @since 1.0
573      */
574     public static final String lineto(int x, int y) {
575         return Js.add("r", coord(x, y));
576     }
577 
578     /**
579      * <p>Fills with a CSS color by setting attributes to the VML element wrapped by 
580      * the specified component.</p>
581      * @param v The current VML component.
582      * @param color A string of CSS color value.
583      * @since 1.0
584      * @see #strokeColor(Component, String)
585      * @see #strokeWeight(Component, Object)
586      */
587     public static final void fillColor(Component v, String color) {
588         Component.setAttribute(v, "fillcolor", color);
589     }
590 
591     /**
592      * <p>Defines a stroke color by setting attributes to the VML element wrapped by 
593      * the specified component.</p>
594      * @param v The current VML component.
595      * @param color A string of CSS color value.
596      * @since 1.0
597      * @see #fillColor(Component, String)
598      * @see #strokeWeight(Component, Object)
599      */
600     public static final void strokeColor(Component v, String color) {
601         if (isStyled(v)) {
602             ObjectLike s = Component.style(v);
603             Styles.borderLeftColor  (s, color);
604             Styles.borderTopColor   (s, color);
605             Styles.borderRightColor (s, color);
606             Styles.borderBottomColor(s, color);
607         } else {
608             Component.setAttribute(v, "strokecolor", color);
609         }
610     }
611 
612     /**
613      * <p>Defines stroke weight by setting attributes to the VML element wrapped by 
614      * the specified component.</p>
615      * @param v The current VML component.
616      * @param w A number or string value of stroke weight.
617      * @since 1.0
618      * @see #fillColor(Component, String)
619      * @see #strokeColor(Component, String)
620      */
621     public static final void strokeWeight(Component v, Object w) {
622         if (isStyled(v)) {
623             border(v, Js.toString(w));
624         } else {
625             Component.setAttribute(v, "strokeweight", w);
626         }
627     }
628 }