Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Importing project.

  • Loading branch information...
commit 4af1de3e1e4b4452402726b996636e293463fd5f 0 parents
@borgar borgar authored
278 GPL-LICENSE.txt
@@ -0,0 +1,278 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
20 MIT-LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2009 Borgar Þorsteinsson, http://borgar.net/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 Makefile
@@ -0,0 +1,36 @@
+SRC_DIR = ./src
+
+UGLIFY ?= `which uglifyjs`
+
+SOURCE_FILES = \
+ ${SRC_DIR}/shim-head.js \
+ ${SRC_DIR}/Vml.js \
+ ${SRC_DIR}/VmlScene.js \
+ ${SRC_DIR}/VmlPanel.js \
+ ${SRC_DIR}/VmlEvents.js \
+ ${SRC_DIR}/VmlImage.js \
+ ${SRC_DIR}/VmlLabel.js \
+ ${SRC_DIR}/VmlWedge.js \
+ ${SRC_DIR}/shim-tail.js
+
+all: msie min
+
+msie: protovis-msie.js
+min: protovis-msie.min.js
+
+protovis-msie.js: $(SOURCE_FILES) Makefile
+ @@echo "Building" $@
+ @@cat $(SOURCE_FILES) > $@
+
+%.min.js: %.js Makefile
+ @@if test ! -z ${UGLIFY}; then \
+ echo "Building" $@; \
+ ${UGLIFY} < $< > $@; \
+ else \
+ echo "You must have ${UGLIFY} installed in order to minify the library."; \
+ fi
+
+clean:
+ rm -f protovis-msie.js protovis-msie.min.js
+
+.PHONY: all msie min clean
43 README.md
@@ -0,0 +1,43 @@
+# Protovis MSIE-VML compatibility layer
+
+A compatibility layer for [Protovis][pv] that adds support for [VML][vml] compatible browsers (Internet Explorer 7 and 8).
+
+## How to use this:
+
+As you include Protovis as you normally would, include protovis-msie.js __after__ it:
+
+ <script src="protovis.min.js"></script>
+ <script src="protovis-msie.min.js"></script>
+
+The software should kick in on browsers that support VML, but don't support SVG, and procede to translate the visualization into VML.
+
+There is no harm in including the file on other browsers but you can still choose to conditionally include the MSIE support:
+
+ <!--[if lte IE 8]><script src="protovis-msie.min.js"></script><![endif]-->
+
+This saves non-VML browsers the trouble of downloading the code.
+
+
+## What can it do
+
+The shim can translate lines, areas, panels, rules, labels and most basic things into VML at a fairly acceptable running speed. It has trouble with massive/complicated visualizations but most simple charts work fine.
+
+
+## Where does it fail?
+
+Things known not to work are:
+
+* Polar interpolation for lines is missing.
+* Rotated labels are incorrectly positioned.
+* Label shadow is missing.
+* Label text does not support opacity.
+* Zoom property is not supported.
+
+The VML layer works quite well for visualizations that are static or not overly complex. However MSIE versions before 9 are slow and it will likely never be viable to run large and complex animated Protovis visualizations on those browsers.
+
+Other than that you will simply have to experiment.
+
+
+
+[pv]: http://mbostock.github.com/protovis/
+[vml]: http://www.w3.org/TR/NOTE-VML
1,024 protovis-msie.js
@@ -0,0 +1,1024 @@
+/*!
+ * Protovis MSIE/VML addon
+ * Copyright (C) 2011 by DataMarket <http://datamarket.com>
+ * Dual licensed under the terms of the MIT or GPL Version 2 software licenses.
+ *
+ * This software includes code from jQuery, http://jquery.com/
+ * jQuery is licensed under the MIT or GPL Version 2 license.
+ *
+ * This software includes code from the Protovis, http://mbostock.github.com/protovis/
+ * Protovis is licensed under the BSD license.
+ *
+ */
+
+// detect SVG support
+pv.have_SVG = !!(
+ document.createElementNS &&
+ document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect
+);
+
+// detect VML support
+pv.have_VML = (function (d,a,b) {
+ a = d.createElement('div');
+ a.innerHTML = '<v:shape adj="1" />';
+ b = a.firstChild;
+ b.style.behavior = 'url(#default#VML)';
+ return b ? typeof b.adj === 'object' : true;
+})(document);
+
+// MSIE does not support indexOf on arrays
+if ( !Array.prototype.indexOf ) {
+ Array.prototype.indexOf = function (s, from) {
+ var n = this.length >>> 0,
+ i = (!isFinite(from) || from < 0) ? 0 : (from > this.length) ? this.length : from;
+ for (; i < n; i++) { if ( this[i] === s ) { return i; } }
+ return -1;
+ };
+}
+
+// only run if we need to
+if ( !pv.have_SVG && pv.have_VML ){(function(){
+
+if ( typeof Date.now === 'function' ) {
+ Date.now = function () { return new Date() * 1; };
+}
+
+
+var vml = {
+
+ round: function(n){ return Math.round( n * 21.6 ); },
+
+ styles: null,
+
+ pre: '<v:',
+ post: ' class="msvml">',
+
+ block: { 'group':1, 'shape':1, 'shapetype':1, 'line':1,
+ 'polyline':1, 'curve':1, 'rect':1, 'roundrect':1,
+ 'oval':1, 'arc':1, 'image':1 },
+ ends: { 'butt':'flat','round':'round','square':'square','flat':'flat'},
+ joins: { 'bevel':'bevel','round':'round','miter':'miter'},
+ cursorstyles: {
+ 'hand': 'pointer',
+ 'crosshair': 1, 'pointer': 1, 'move': 1, 'text': 1,
+ 'wait': 1, 'help': 1, 'progress': 1,
+ 'n-resize': 1, 'ne-resize': 1, 'nw-resize': 1, 's-resize': 1,
+ 'se-resize': 1, 'sw-resize': 1, 'e-resize': 1, 'w-resize': 1
+ },
+
+ text_shim: null,
+ _textcache: {},
+ text_dims: function ( text, font ) {
+ if ( !(font in vml._textcache) ) {
+ vml._textcache[ font ] = {};
+ }
+ if ( text in vml._textcache[ font ] ) {
+ return vml._textcache[ font ][ text ];
+ }
+ var shim = vml.text_shim;
+ shim.style.font = font;
+ shim.innerText = text;
+ return (vml._textcache[ font ][ text ] = {
+ fontsize: parseInt( shim.style.fontSize, 10 ),
+ height: shim.offsetHeight,
+ width: shim.offsetWidth
+ });
+ },
+
+ d2r: Math.PI * 2 / 360, // is this used more than once?
+
+ get_dim: function ( attr, target ) {
+ var o = target || {};
+ // reformat the most common attributes
+ o.translate_x = 0;
+ o.translate_y = 0;
+ if ( attr.transform ) {
+ var t = /translate\((\d+(?:\.\d+)?)(?:,(\d+(?:\.\d+)?))?\)/.exec( attr.transform );
+ if ( t && t[1] ) { o.translate_x = parseFloat( t[1] ); }
+ if ( t && t[2] ) { o.translate_y = parseFloat( t[2] ); }
+ var r = /rotate\((\d+\.\d+|\d+)\)/.exec( attr.transform );
+ if ( r ) { o.rotation = parseFloat( r[1] ) % 360; }
+ // var scale_x = 1, scale_y = 1,
+ // var s = /scale\((\d+)(?:,(\d+))?\)/i.exec( value );
+ // if ( s && s[1] ) { scale[0] = parseInt( s[1], 10 ); }
+ // if ( s && s[2] ) { scale[1] = parseInt( s[2], 10 ); }
+ }
+ o.x = parseFloat( attr.x||0 );
+ o.y = parseFloat( attr.y||0 );
+ if ( 'width' in attr ) {
+ o.width = parseInt( attr.width, 10 );
+ }
+ if ( 'height' in attr ) {
+ o.height = parseInt( attr.height, 10 );
+ }
+ return o;
+ },
+
+ elm_defaults: {
+
+ "g": {
+ rewrite: 'span',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr );
+ elm.style.cssText = "position:absolute;zoom:1;left:"+
+ (d.translate_x + d.x)+"px;top:"+
+ (d.translate_y + d.y)+"px;";
+ }
+ },
+
+ "line": {
+ rewrite: 'shape',
+ attr: function ( attr, style, elm ) {
+ var x1 = parseFloat( attr.x1 || 0 ),
+ y1 = parseFloat( attr.y1 || 0 ),
+ x2 = parseFloat( attr.x2 || 0 ),
+ y2 = parseFloat( attr.y2 || 0 ),
+ r = vml.round;
+ elm.coordorigin = "0,0";
+ elm.coordsize = "21600,21600";
+ vml.path( elm ).v = 'M '+ r(x1) + ' ' + r(y1) + ' L ' + r(x2) + ' ' + r(y2) + ' E';
+ vml.stroke( elm, attr );
+ },
+ css: "top:0px;left:0px;width:1000px;height:1000px"
+ },
+
+ "rect": {
+ rewrite: 'shape',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr ),
+ p = vml.path( elm ),
+ r = vml.round;
+ elm.coordorigin = "0,0";
+ elm.coordsize = "21600,21600";
+ var x = r(d.translate_x + d.x),
+ y = r(d.translate_y + d.y),
+ w = r(d.width),
+ h = r(d.height);
+ p.v = 'M ' + x + ' ' + y +
+ ' L ' + (x + w) + ' ' + y +
+ ' L ' + (x + w) + ' ' + (y + h) +
+ ' L ' + x + ' ' + (y + h) +
+ ' x';
+ vml.stroke( elm, attr );
+ vml.fill( elm, attr );
+ },
+ css: "top:0px;left:0px;width:1000px;height:1000px"
+ },
+
+ "path": {
+ rewrite: 'shape',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr ),
+ es = elm.style;
+ es.left = (d.translate_x + d.x) + "px";
+ es.top = (d.translate_y + d.y) + "px";
+ elm.coordorigin = "0,0";
+ elm.coordsize = "21600,21600";
+ vml.path( elm, attr.d );
+ vml.fill( elm, attr );
+ vml.stroke( elm, attr );
+ },
+ css: "top:0px;left:0px;width:1000px;height:1000px"
+ },
+
+ "circle": {
+ /* This version of circles is crisper but seems slower
+ rewrite: 'shape',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr ),
+ r = vml.round( parseFloat( attr.r || 0 ) ),
+ cx = parseFloat( attr.cx || 0 ),
+ cy = parseFloat( attr.cy || 0 ),
+ es = elm.style;
+ es.left = (d.translate_x + d.x + cx + 0.3) + "px";
+ es.top = (d.translate_y + d.y + cy + 0.3) + "px";
+ elm.coordorigin = "0,0";
+ elm.coordsize = "21600,21600";
+ vml.path( elm ).v = "ar-" + r + ",-" + r + "," + r + "," + r + ",0,0,0,0x";
+ vml.fill( elm, attr );
+ vml.stroke( elm, attr );
+ },
+ css: "top:0px;left:0px;width:1000px;height:1000px"
+ */
+ rewrite: 'oval',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr ),
+ es = elm.style,
+ cx = parseFloat( attr.cx || 0 ) + 0.7,
+ cy = parseFloat( attr.cy || 0 ) + 0.7,
+ r = parseFloat( attr.r || 0 ) + 0.5;
+ es.top = ( d.translate_y + cy - r ) + "px";
+ es.left = ( d.translate_x + cx - r ) + "px";
+ es.width = ( r * 2 ) + "px";
+ es.height = ( r * 2 ) + "px";
+ vml.fill( elm, attr );
+ vml.stroke( elm, attr );
+ },
+ },
+
+ "text": {
+ rewrite: 'span'
+ },
+
+ "svg": {
+ rewrite: 'span',
+ css: 'position:relative;overflow:hidden;display:inline-block;~display:block;'
+ },
+
+ // this allows reuse of the createElement function for actual VML
+ "vml:path": { rewrite: 'path' },
+ "vml:stroke": { rewrite: 'stroke' },
+ "vml:fill": { rewrite: 'fill' }
+
+ },
+
+ // cloning elements is a lot faster than creating them
+ _elmcache: {
+ 'span': document.createElement( 'span' ),
+ 'div': document.createElement( 'div' )
+ },
+
+ createElement: function ( type, reformat ) {
+ var elm,
+ cache = vml._elmcache,
+ helper = vml.elm_defaults[ type ] || {};
+ var tagName = helper.rewrite || type;
+ if ( tagName in cache ) {
+ elm = cache[ tagName ].cloneNode( false );
+ }
+ else {
+ cache[ tagName ] = document.createElement( vml.pre + tagName + vml.post );
+ if ( tagName in vml.block ) {
+ cache[ tagName ].className += ' msvml_block';
+ }
+ elm = cache[ tagName ].cloneNode( false );
+ }
+ helper.css && (elm.style.cssText = helper.css);
+ return elm;
+ },
+
+
+ // hex values lookup table
+ _hex: pv.range(0,256).map(function(i){ return pv.Format.pad("0",2,i.toString(16)); }),
+ _colorcache: {},
+ color: function ( value, rgb ) {
+ // TODO: deal with opacity here ?
+ if ( !(value in vml._colorcache) && (rgb = /^rgb\((\d+),(\d+),(\d+)\)$/i.exec( value )) ) {
+ vml._colorcache[value] = '#' + vml._hex[rgb[1]] + vml._hex[rgb[2]] + vml._hex[rgb[3]];
+ }
+ return vml._colorcache[ value ] || value;
+ },
+
+
+ fill: function ( elm, attr ) {
+ var fill = elm.getElementsByTagName( 'fill' )[0];
+ if ( !fill ) {
+ fill = elm.appendChild( vml.createElement( 'vml:fill' ) );
+ }
+ if ( !attr.fill || attr.fill === 'none' ) {
+ fill.on = false;
+ }
+ else {
+ fill.on = 'true';
+ fill.color = vml.color( attr.fill );
+ fill.opacity = parseFloat( attr['fill-opacity'] || '1' ) || '1';
+ }
+ },
+
+
+ stroke: function ( elm, attr ) {
+ var stroke = elm.getElementsByTagName( 'stroke' )[0];
+ if ( !stroke ) {
+ stroke = elm.appendChild( vml.createElement( 'vml:stroke' ) );
+ }
+ if ( !attr.stroke || attr.stroke === 'none' ) {
+ stroke.on = 'false';
+ stroke.weight = '0';
+ }
+ else {
+ stroke.on = 'true';
+ stroke.weight = parseFloat( attr['stroke-width'] || '1' ) / 1.25;
+ stroke.color = vml.color( attr.stroke ) || 'black';
+ stroke.opacity = parseFloat( attr['stroke-opacity'] || '1' ) || '1';
+ stroke.joinstyle = vml.joins[ attr['stroke-linejoin'] ] || 'miter';
+ }
+ },
+
+ path: function ( elm, svgpath ) {
+ var p = elm.getElementsByTagName( 'path' )[0];
+ if ( !p ) {
+ p = elm.appendChild( vml.createElement( 'vml:path' ) );
+ }
+ if ( arguments.length > 1 ) {
+ p.v = vml.rewritePath( svgpath );
+ }
+ return p;
+ },
+
+
+ init: function () {
+ if ( !vml.text_shim ) {
+ vml.text_shim = document.getElementById('pv_vml_text_shim') || document.createElement('span');
+ vml.text_shim.id = 'protovisvml_text_shim';
+ vml.text_shim.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline-block;white-space:nowrap;";
+ document.body.appendChild( vml.text_shim );
+ }
+ if ( !vml.styles ) {
+ vml.styles = document.getElementById('protovisvml_styles') || document.createElement("style");
+ vml.styles.id = 'protovisvml_styles';
+ document.documentElement.firstChild.appendChild( vml.styles );
+ vml.styles.styleSheet.addRule( '.msvml', 'behavior:url(#default#VML);' );
+ vml.styles.styleSheet.addRule( '.msvml_block', 'position:absolute;top:0;left:0;' );
+ try {
+ if ( !document.namespaces.v ) { document.namespaces.add( 'v', 'urn:schemas-microsoft-com:vml' ); }
+ }
+ catch (e) {
+ vml.pre = '<';
+ vml.post = ' class="msvml" xmlns="urn:schemas-microsoft.com:vml">';
+ }
+ }
+ },
+
+ // SVG->VML path conversion - This converts a SVG path to a VML path
+ //
+ // Things that are missing:
+ // - Multiple sets of coords.
+ // Some commands (lineto,curveto,..) can take multiple sets of coords.
+ // Because Protovis always supplies the command between arguments, this isn't
+ // implemented, but it would be trivial to complete this.
+ // - ARCs need solving
+ _pathcache: {},
+ rewritePath:function ( p, deb ) {
+ var x = 0, y = 0, round = vml.round;
+
+ if ( !p ) { return p; }
+ if ( p in vml._pathcache ) { return vml._pathcache[p]; }
+
+ // clean up overly detailed fractions (8.526512829121202e-148)
+ p = p.replace( /(\d*)((\.*\d*)(e ?-?\d*))/g, "$1");
+
+ var bits = p.match( /([MLHVCSQTAZ][^MLHVCSQTAZ]*)/gi );
+ var np = [], lastcurve = [];
+ for ( var i=0,bl=bits.length; i<bl; i++ ) {
+ var itm = bits[i],
+ op = itm.charAt( 0 ),
+ args = itm.substring( 1 ).split( /[, ]/ );
+
+ switch ( op ) {
+
+ case 'M': // moveto (absolute)
+ op = 'm';
+ x = round( args[0] );
+ y = round( args[1] );
+ args = [ x, y ];
+ break;
+ case 'm': // moveto (relative)
+ op = 'm';
+ x += round( args[0] );
+ y += round( args[1] );
+ args = [ x, y ];
+ break;
+
+ case "A": // TODO: arc (absolute):
+ // SVG: rx ry x-axis-rotation large-arc-flag sweep-flag x y
+ // VML: http://www.w3.org/TR/NOTE-VML
+ /*var rx = round( args[0] ),
+ ry = round( args[1] ),
+ xrot = round( args[2] ),
+ lrg = round( args[3] ),
+ sweep = round( args[4] );*/
+ op = 'l';
+ args = [ (x = round( args[5] )),
+ (y = round( args[6] )) ];
+ break;
+
+ case "L": // lineTo (absolute)
+ op = 'l';
+ args = [ (x = round( args[0] )),
+ (y = round( args[1] )) ];
+ break;
+ case "l": // lineTo (relative)
+ op = 'l';
+ args = [ (x = x + round( args[0] )),
+ (y = y + round( args[1] )) ];
+ break;
+
+ case "H": // horizontal lineto (absolute)
+ op = 'l';
+ args = [ (x = round( args[0] )), y ];
+ break;
+ case "h": // horizontal lineto (relative)
+ op = 'l';
+ args = [ (x = x + round( args[0] )), y ];
+ break;
+
+ case "V": // vertical lineto (absolute)
+ op = 'l';
+ args = [ x, (y = round( args[0] )) ];
+ break;
+ case "v": // vertical lineto (relative)
+ op = 'l';
+ args = [ x, (y = y + round( args[0] )) ];
+ break;
+
+ case "C": // curveto (absolute)
+ op = 'c';
+ lastcurve = args = [
+ round(args[0]), round(args[1]),
+ round(args[2]), round(args[3]),
+ (x = round( args[4] )),
+ (y = round( args[5] ))
+ ];
+ break;
+ case "c": // curveto (relative)
+ op = 'c';
+ lastcurve = args = [
+ x + round(args[0]),
+ y + round(args[1]),
+ x + round(args[2]),
+ y + round(args[3]),
+ (x = x + round( args[4] )),
+ (y = y + round( args[5] ))
+ ];
+ break;
+
+ case "S": // shorthand/smooth curveto (absolute)
+ op = 'c';
+ lastcurve = args = [
+ lastcurve[4] + (lastcurve[4] - lastcurve[2]),
+ lastcurve[5] + (lastcurve[5] - lastcurve[3]),
+ round(args[0]),
+ round(args[1]),
+ (x = round( args[2] )),
+ (y = round( args[3] ))
+ ];
+ break;
+ case "s": // shorthand/smooth curveto (relative)
+ op = 'c';
+ lastcurve = args = [
+ lastcurve[4] + (lastcurve[4] - lastcurve[2]),
+ lastcurve[5] + (lastcurve[5] - lastcurve[3]),
+ x + round(args[0]),
+ y + round(args[1]),
+ (x = x + round( args[2] )),
+ (y = y + round( args[3] ))
+ ];
+ break;
+
+ case "Q": // quadratic Bézier curveto (absolute)
+ op = 'c';
+ var x1 = round( args[0] ),
+ y1 = round( args[1] ),
+ x2 = round( args[2] ),
+ y2 = round( args[3] );
+ args = [
+ ~~(x + (x1 - x) * 2 / 3),
+ ~~(y + (y1 - y) * 2 / 3),
+ ~~(x1 + (x2 - x1) / 3),
+ ~~(y1 + (y2 - y1) / 3),
+ (x = x2),
+ (y = y2)
+ ];
+ break;
+ case "q": // TODO: quadratic Bézier (relative)
+ op = 'l';
+ x += round( args[2] );
+ y += round( args[3] );
+ args = [ x, y ];
+ break;
+
+ // TODO: T/t (Shorthand/smooth quadratic Bézier curveto)
+
+ case "Z":
+ case "z":
+ op = 'xe';
+ args = [];
+ break;
+
+ default:
+ // unsupported path command
+ op = '';
+ args = [];
+ }
+ np.push( op, args.join(',') );
+ }
+ return ( vml._pathcache[p] = (np.join('') + 'e') );
+ }
+
+};
+
+pv.VmlScene = {
+
+ // The pre-multipled scale, based on any enclosing transforms.
+ scale: 1,
+
+ // The set of supported events.
+ events: [
+ "mousewheel",
+ "mousedown",
+ "mouseup",
+ "mouseover",
+ "mouseout",
+ "mousemove",
+ "click",
+ "dblclick"
+ ],
+
+ // implicit values are not used for VML, assigned render faster and we have
+ // no desire to keep the DOM clean here - only to make it work!
+ implicit: { css: {} },
+
+ copy_functions: function ( obj ) {
+ for ( var name in obj ) {
+ if ( typeof obj[name] === 'function' && !(name in pv.VmlScene) ) {
+ pv.VmlScene[ name ] = obj[ name ];
+ }
+ }
+ }
+
+};
+
+
+// copy helper methods from SvgScene onto our new Scene
+pv.VmlScene.copy_functions( pv.SvgScene );
+pv.Scene = pv.VmlScene;
+
+
+pv.VmlScene.expect = function (e, type, attr, style) {
+ style = style || {};
+ var helper = vml.elm_defaults[type] || {},
+ _type = helper.rewrite || type;
+
+ if ( e ) {
+ if ( e.tagName.toUpperCase() !== _type.toUpperCase() ) {
+ var n = vml.createElement( type );
+ e.parentNode.replaceChild( n, e );
+ e = n;
+ }
+ }
+ else {
+ e = vml.createElement( type );
+ }
+
+ if ( 'attr' in helper ) {
+ helper.attr( attr, style, e );
+ }
+
+ if ( attr.cursor in vml.cursorstyles ) {
+ var curs = vml.cursorstyles[attr.cursor];
+ style.cursor = ( curs === 1 ) ? attr.cursor : curs;
+ }
+
+ for (var name in style) {
+ var value = style[name];
+ if (value == null) e.style.removeAttribute(name); // cssText
+ else e.style[name] = value;
+ }
+
+ return e;
+};
+
+
+pv.VmlScene.append = function(e, scenes, index) {
+ // FIXME: hooks the scene onto the element --- this is probably hemorrhaging memory in MSIE
+ // it is only ever used by the envent displatcher so it should probably be stored in a cache
+ e.$scene = {scenes:scenes, index:index};
+ // attach a title to element
+ e = this.title(e, scenes[index]);
+ if ( !e.parentNode || e.parentNode.nodeType === 11 ) { // 11 == documentFragment
+ scenes.$g.appendChild( e );
+ }
+ return e.nextSibling;
+};
+
+
+pv.VmlScene.title = function(e, s) {
+ e.title = s.title || "";
+ return e;
+};
+
+
+
+// mostly the same code as pv.SvgScene.panel, but with less MSIE crashing...
+pv.VmlScene.panel = function(scenes) {
+ var g = scenes.$g, e = g && g.firstChild;
+ for (var i = 0; i < scenes.length; i++) {
+ var s = scenes[i];
+
+ /* visible */
+ if (!s.visible) continue;
+
+ /* svg */
+ if (!scenes.parent) {
+ s.canvas.style.display = "inline-block";
+ s.canvas.style.zoom = 1;
+ if (g && (g.parentNode != s.canvas)) {
+ g = s.canvas.firstChild;
+ e = g && g.firstChild;
+ }
+ if ( !g ) {
+ vml.init(); // turn VML on if it isn't allready
+ g = s.canvas.appendChild( vml.createElement( "svg" ) );
+ for (var j = 0; j < this.events.length; j++) {
+ g.addEventListener
+ ? g.addEventListener(this.events[j], this.dispatch, false)
+ : g.attachEvent("on" + this.events[j], this.dispatch);
+ }
+ e = g.firstChild;
+ }
+ scenes.$g = g;
+ var w = (s.width + s.left + s.right),
+ h = (s.height + s.top + s.bottom);
+ g.style.width = w + 'px';
+ g.style.height = h + 'px';
+ g.style.clip = "rect(0px " + w + "px " + h + "px 0px)";
+ }
+
+ /* fill */
+ e = this.fill( e, scenes, i );
+
+ /* transform (push) */
+ var k = this.scale,
+ t = s.transform,
+ x = s.left + t.x,
+ y = s.top + t.y;
+ this.scale *= t.k;
+
+ /* children */
+ for (var j = 0; j < s.children.length; j++) {
+ s.children[j].$g = e = this.expect(e, "g", {
+ "transform": "translate(" + x + "," + y + ")" + (t.k != 1 ? " scale(" + t.k + ")" : "")
+ });
+ this.updateAll(s.children[j]);
+ if ( !e.parentNode || e.parentNode.nodeType === 11 ) {
+ g.appendChild(e);
+ var helper = vml.elm_defaults[ e.svgtype ];
+ if ( helper && typeof helper.onappend === 'function' ) {
+ helper.onappend( e, scenes[i] );
+ }
+ }
+ e = e.nextSibling;
+ }
+
+ /* transform (pop) */
+ this.scale = k;
+
+ /* stroke */
+ e = this.stroke( e, scenes, i );
+
+ }
+ return e;
+};
+
+// Much of the event rewriting code is copyed and watered down
+// from the jQuery library's event hander. We have the luxury
+// of knowing that we're on MSIE<9 so we can despense with some
+// fixes for other browsers.
+(function(){
+
+ var returnTrue = function () { return true; };
+ var returnFalse = function () { return false; };
+ var _event_props = ["altKey","attrChange","attrName","bubbles","button",
+ "cancelable","charCode","clientX","clientY","ctrlKey",
+ "currentTarget","data","detail","eventPhase","fromElement",
+ "handler","keyCode","layerX","layerY","metaKey",
+ "newValue","offsetX","offsetY","pageX","pageY","prevValue",
+ "relatedNode","relatedTarget","screenX","screenY",
+ "shiftKey","srcElement","target","toElement","view","wheelDelta","which"];
+
+ function IEvent ( src ) {
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+ this.isDefaultPrevented = returnFalse;
+ if (src.defaultPrevented || src.returnValue === false || src.getPreventDefault && src.getPreventDefault()) {
+ this.isDefaultPrevented = returnTrue;
+ }
+ }
+ else {
+ this.type = src;
+ }
+ this.timeStamp = Date.now();
+ }
+ IEvent.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+ var e = this.originalEvent;
+ if ( !e ) { return; }
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+ // otherwise set the returnValue property of the original event to false (IE)
+ }
+ else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+ };
+
+
+ vml.fixEvent = function ( ev ) {
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = ev;
+ ev = new IEvent( originalEvent );
+
+ for (var i=0,l=_event_props.length; i<l; i++) {
+ var prop = _event_props[i];
+ ev[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !ev.target ) {
+ ev.target = ev.srcElement || document;
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !ev.relatedTarget && ev.fromElement ) {
+ ev.relatedTarget = (ev.fromElement === ev.target)
+ ? ev.toElement
+ : ev.fromElement;
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( ev.pageX == null && ev.clientX != null ) {
+ var doc = document.documentElement,
+ body = document.body;
+ ev.pageX = ev.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ ev.pageY = ev.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( ev.which == null && (ev.charCode != null || ev.keyCode != null) ) {
+ ev.which = ev.charCode != null
+ ? ev.charCode
+ : ev.keyCode;
+ }
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !ev.metaKey && ev.ctrlKey ) {
+ ev.metaKey = ev.ctrlKey;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !ev.which && ev.button !== undefined ) {
+ ev.which = (ev.button & 1 ? 1 : ( ev.button & 2 ? 3 : ( ev.button & 4 ? 2 : 0 ) ));
+ }
+
+ // Mousewheel delta
+ if ( ev.type === "mousewheel" ) {
+ ev.wheel = ev.wheelDelta;
+ }
+
+ return ev;
+ }
+
+})();
+
+
+
+// replace the listener with something a little more elaborate
+pv.listener = function(f, target) {
+ return f.$listener || (f.$listener = function(e) {
+ try {
+ pv.event = vml.fixEvent( e || window.event );
+ return f.call( this, pv.event );
+ }
+ catch (e) {
+ pv.error(e);
+ }
+ finally {
+ delete pv.event;
+ }
+ });
+};
+
+
+pv.listen = function(target, type, listener) {
+ listener = pv.listener(listener, target);
+ if ( target === window ) {
+ target = document.documentElement;
+ }
+ return target.addEventListener
+ ? target.addEventListener(type, listener, false)
+ : target.attachEvent("on" + type, listener);
+};
+
+
+
+pv.VmlScene.dispatch = pv.listener(function(e) {
+ var t = e.target.$scene;
+ if ( t && pv.Mark.dispatch(e.type, t.scenes, t.index) ) {
+ e.preventDefault();
+ }
+});
+
+//
+pv.VmlScene.image = function(scenes) {
+ var e = scenes.$g.firstChild;
+ for (var i = 0; i < scenes.length; i++) {
+ var s = scenes[i];
+
+ /* visible */
+ if (!s.visible) continue;
+
+ /* fill */
+ e = this.fill(e, scenes, i);
+
+ /* image */
+ if ( s.image ) {
+ // There is no canvas support in MSIE
+ }
+ else {
+ e = new Image();
+ e.src = s.url;
+ var st = e.style;
+ st.position = 'absolute';
+ st.top = s.top;
+ st.left = s.left;
+ st.width = s.width;
+ st.height = s.height;
+ st.cursor = s.cursor;
+ st.msInterpolationMode = 'bicubic';
+ }
+ e = this.append(e, scenes, i);
+
+ /* stroke */
+ e = this.stroke(e, scenes, i);
+ }
+ return e;
+};
+
+
+pv.VmlScene.label = function(scenes) {
+ var e = scenes.$g.firstChild,
+ round = Math.round;
+ for (var i = 0; i < scenes.length; i++) {
+ var s = scenes[i];
+
+ // visible
+ if (!s.visible) continue;
+ var fill = s.textStyle;
+ if (!fill.opacity || !s.text) continue;
+
+ var attr = {};
+ if ( s.cursor ) { attr.cursor = s.cursor; }
+
+ // measure text
+ var txt = s.text.replace( /\s+/g, '\xA0' );
+ var label = vml.text_dims( txt, s.font );
+
+ var dx = 0, dy = 0;
+
+ if ( s.textBaseline === 'middle' ) {
+ dy -= label.fontsize / 2;
+ }
+ else if ( s.textBaseline === 'top' ) {
+ dy += s.textMargin;
+ }
+ else if ( s.textBaseline === 'bottom' ) {
+ dy -= s.textMargin + label.fontsize;
+ }
+
+ if ( s.textAlign === 'center' ) {
+ dx -= label.width / 2;
+ }
+ else if ( s.textAlign === 'right' ) {
+ dx -= label.width + s.textMargin;
+ }
+ else if ( s.textAlign === 'left' ) {
+ dx += s.textMargin;
+ }
+
+ e = this.expect(e, "text", attr, {
+ "font": s.font,
+ // "text-shadow": s.textShadow,
+ "textDecoration": s.textDecoration,
+ 'top': Math.round( s.top + dy ) + 'px',
+ 'left': Math.round( s.left + dx ) + 'px',
+ 'position': 'absolute',
+ 'display': 'block',
+ 'lineHeight': 1,
+ 'whiteSpace': 'nowrap',
+ 'zoom': 1,
+ 'cursor': 'default',
+ 'color': vml.color( fill.color ) || 'black'
+ });
+ e.innerText = txt;
+
+ // Rotation is broken in serveral different ways:
+ // 1. it looks REALLY ugly
+ // 2. it is incredibly slow
+ // 3. rotated text is offset completely wrong and it takes a ton of math to correct it
+ // when text is rotated we need to switch to a VML textpath solution
+ var rotation = 180 * s.textAngle / Math.PI;
+ if ( rotation ) {
+ var r = (~~rotation % 360) * vml.d2r,
+ ct = Math.cos(r),
+ st = Math.sin(r);
+ e.style.filter = ['progid:DXImageTransform.Microsoft.Matrix(',
+ 'M11=', ct.toFixed( 8 ), ',',
+ 'M12=', -st.toFixed( 8 ), ',',
+ 'M21=', st.toFixed( 8 ), ',',
+ 'M22=', ct.toFixed( 8 ), ',sizingMethod=\'auto expand\')";'].join('');
+ }
+ else {
+ e.style.filter = '';
+ }
+
+ e = this.append(e, scenes, i);
+ }
+ return e;
+};
+pv.VmlScene.wedge = function(scenes) {
+ var e = scenes.$g.firstChild,
+ round = vml.round;
+ for (var i = 0; i < scenes.length; i++) {
+ var s = scenes[i];
+
+ // visible
+ if (!s.visible) continue;
+ var fill = s.fillStyle, stroke = s.strokeStyle;
+ if (!fill.opacity && !stroke.opacity) continue;
+
+ // create element sans path
+ e = this.expect(e, "path", {
+ "pointer-events": s.events,
+ "cursor": s.cursor,
+ "transform": "translate(" + s.left + "," + s.top + ")",
+ "d": '', // we deal with the path afterwards
+ "fill": fill.color,
+ "fill-rule": "evenodd",
+ "fill-opacity": fill.opacity || null,
+ "stroke": stroke.color,
+ "stroke-opacity": stroke.opacity || null,
+ "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
+ });
+
+ // add path
+ var p = e.getElementsByTagName( 'path' )[0];
+ if ( !p ) {
+ p = vml.make( 'path' );
+ e.appendChild( p );
+ }
+
+ // Arc path from bigfix/protovis
+ var r1 = round(s.innerRadius),
+ r2 = round(s.outerRadius),
+ d;
+ if (s.angle >= 2 * Math.PI) {
+ if (r1) {
+ d = "AE0,0 " + r2 + "," + r2 + " 0 23592960"
+ + "AL0,0 " + r1 + "," + r1 + " 0 23592960";
+ }
+ else {
+ d = "AE0,0 " + r2 + "," + r2 + " 0 23592960";
+ }
+ }
+ else {
+ var sa = Math.round(s.startAngle / Math.PI * 11796480),
+ a = Math.round(s.angle / Math.PI * 11796480);
+ if (r1) {
+ d = "AE 0,0 " + r2 + "," + r2 + " " + -sa + " " + -a
+ + " 0,0 " + r1 + "," + r1 + " " + -(sa + a) + " " + a
+ + "X";
+ }
+ else {
+ d = "M0,0"
+ + "AE0,0 " + r2 + "," + r2 + " " + -sa + " " + -a
+ + "X";
+ }
+ }
+ p.v = d;
+
+ e = this.append(e, scenes, i);
+
+ }
+ return e;
+};
+
+// end VML override
+})();}
13 protovis-msie.min.js
@@ -0,0 +1,13 @@
+/*!
+ * Protovis MSIE/VML addon
+ * Copyright (C) 2011 by DataMarket <http://datamarket.com>
+ * Dual licensed under the terms of the MIT or GPL Version 2 software licenses.
+ *
+ * This software includes code from jQuery, http://jquery.com/
+ * jQuery is licensed under the MIT or GPL Version 2 license.
+ *
+ * This software includes code from the Protovis, http://mbostock.github.com/protovis/
+ * Protovis is licensed under the BSD license.
+ *
+ */// detect SVG support
+pv.have_SVG=!!document.createElementNS&&!!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,pv.have_VML=function(a,b,c){return b=a.createElement("div"),b.innerHTML='<v:shape adj="1" />',c=b.firstChild,c.style.behavior="url(#default#VML)",c?typeof c.adj=="object":!0}(document),Array.prototype.indexOf||(Array.prototype.indexOf=function(a,b){var c=this.length>>>0,d=!isFinite(b)||b<0?0:b>this.length?this.length:b;for(;d<c;d++)if(this[d]===a)return d;return-1}),!pv.have_SVG&&pv.have_VML&&function(){typeof Date.now=="function"&&(Date.now=function(){return new Date*1});var a={round:function(a){return Math.round(a*21.6)},styles:null,pre:"<v:",post:' class="msvml">',block:{group:1,shape:1,shapetype:1,line:1,polyline:1,curve:1,rect:1,roundrect:1,oval:1,arc:1,image:1},ends:{butt:"flat",round:"round",square:"square",flat:"flat"},joins:{bevel:"bevel",round:"round",miter:"miter"},cursorstyles:{hand:"pointer",crosshair:1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"nw-resize":1,"s-resize":1,"se-resize":1,"sw-resize":1,"e-resize":1,"w-resize":1},text_shim:null,_textcache:{},text_dims:function(b,c){c in a._textcache||(a._textcache[c]={});if(b in a._textcache[c])return a._textcache[c][b];var d=a.text_shim;return d.style.font=c,d.innerText=b,a._textcache[c][b]={fontsize:parseInt(d.style.fontSize,10),height:d.offsetHeight,width:d.offsetWidth}},d2r:Math.PI*2/360,get_dim:function(a,b){var c=b||{};c.translate_x=0,c.translate_y=0;if(a.transform){var d=/translate\((\d+(?:\.\d+)?)(?:,(\d+(?:\.\d+)?))?\)/.exec(a.transform);d&&d[1]&&(c.translate_x=parseFloat(d[1])),d&&d[2]&&(c.translate_y=parseFloat(d[2]));var e=/rotate\((\d+\.\d+|\d+)\)/.exec(a.transform);e&&(c.rotation=parseFloat(e[1])%360)}return c.x=parseFloat(a.x||0),c.y=parseFloat(a.y||0),"width"in a&&(c.width=parseInt(a.width,10)),"height"in a&&(c.height=parseInt(a.height,10)),c},elm_defaults:{g:{rewrite:"span",attr:function(b,c,d){var e=a.get_dim(b);d.style.cssText="position:absolute;zoom:1;left:"+(e.translate_x+e.x)+"px;top:"+(e.translate_y+e.y)+"px;"}},line:{rewrite:"shape",attr:function(b,c,d){var e=parseFloat(b.x1||0),f=parseFloat(b.y1||0),g=parseFloat(b.x2||0),h=parseFloat(b.y2||0),i=a.round;d.coordorigin="0,0",d.coordsize="21600,21600",a.path(d).v="M "+i(e)+" "+i(f)+" L "+i(g)+" "+i(h)+" E",a.stroke(d,b)},css:"top:0px;left:0px;width:1000px;height:1000px"},rect:{rewrite:"shape",attr:function(b,c,d){var e=a.get_dim(b),f=a.path(d),g=a.round;d.coordorigin="0,0",d.coordsize="21600,21600";var h=g(e.translate_x+e.x),i=g(e.translate_y+e.y),j=g(e.width),k=g(e.height);f.v="M "+h+" "+i+" L "+(h+j)+" "+i+" L "+(h+j)+" "+(i+k)+" L "+h+" "+(i+k)+" x",a.stroke(d,b),a.fill(d,b)},css:"top:0px;left:0px;width:1000px;height:1000px"},path:{rewrite:"shape",attr:function(b,c,d){var e=a.get_dim(b),f=d.style;f.left=e.translate_x+e.x+"px",f.top=e.translate_y+e.y+"px",d.coordorigin="0,0",d.coordsize="21600,21600",a.path(d,b.d),a.fill(d,b),a.stroke(d,b)},css:"top:0px;left:0px;width:1000px;height:1000px"},circle:{rewrite:"oval",attr:function(b,c,d){var e=a.get_dim(b),f=d.style,g=parseFloat(b.cx||0)+.7,h=parseFloat(b.cy||0)+.7,i=parseFloat(b.r||0)+.5;f.top=e.translate_y+h-i+"px",f.left=e.translate_x+g-i+"px",f.width=i*2+"px",f.height=i*2+"px",a.fill(d,b),a.stroke(d,b)}},text:{rewrite:"span"},svg:{rewrite:"span",css:"position:relative;overflow:hidden;display:inline-block;~display:block;"},"vml:path":{rewrite:"path"},"vml:stroke":{rewrite:"stroke"},"vml:fill":{rewrite:"fill"}},_elmcache:{span:document.createElement("span"),div:document.createElement("div")},createElement:function(b,c){var d,e=a._elmcache,f=a.elm_defaults[b]||{},g=f.rewrite||b;return g in e?d=e[g].cloneNode(!1):(e[g]=document.createElement(a.pre+g+a.post),g in a.block&&(e[g].className+=" msvml_block"),d=e[g].cloneNode(!1)),f.css&&(d.style.cssText=f.css),d},_hex:pv.range(0,256).map(function(a){return pv.Format.pad("0",2,a.toString(16))}),_colorcache:{},color:function(b,c){return!(b in a._colorcache)&&(c=/^rgb\((\d+),(\d+),(\d+)\)$/i.exec(b))&&(a._colorcache[b]="#"+a._hex[c[1]]+a._hex[c[2]]+a._hex[c[3]]),a._colorcache[b]||b},fill:function(b,c){var d=b.getElementsByTagName("fill")[0];d||(d=b.appendChild(a.createElement("vml:fill"))),!c.fill||c.fill==="none"?d.on=!1:(d.on="true",d.color=a.color(c.fill),d.opacity=parseFloat(c["fill-opacity"]||"1")||"1")},stroke:function(b,c){var d=b.getElementsByTagName("stroke")[0];d||(d=b.appendChild(a.createElement("vml:stroke"))),!c.stroke||c.stroke==="none"?(d.on="false",d.weight="0"):(d.on="true",d.weight=parseFloat(c["stroke-width"]||"1")/1.25,d.color=a.color(c.stroke)||"black",d.opacity=parseFloat(c["stroke-opacity"]||"1")||"1",d.joinstyle=a.joins[c["stroke-linejoin"]]||"miter")},path:function(b,c){var d=b.getElementsByTagName("path")[0];return d||(d=b.appendChild(a.createElement("vml:path"))),arguments.length>1&&(d.v=a.rewritePath(c)),d},init:function(){a.text_shim||(a.text_shim=document.getElementById("pv_vml_text_shim")||document.createElement("span"),a.text_shim.id="protovisvml_text_shim",a.text_shim.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline-block;white-space:nowrap;",document.body.appendChild(a.text_shim));if(!a.styles){a.styles=document.getElementById("protovisvml_styles")||document.createElement("style"),a.styles.id="protovisvml_styles",document.documentElement.firstChild.appendChild(a.styles),a.styles.styleSheet.addRule(".msvml","behavior:url(#default#VML);"),a.styles.styleSheet.addRule(".msvml_block","position:absolute;top:0;left:0;");try{document.namespaces.v||document.namespaces.add("v","urn:schemas-microsoft-com:vml")}catch(b){a.pre="<",a.post=' class="msvml" xmlns="urn:schemas-microsoft.com:vml">'}}},_pathcache:{},rewritePath:function(b,c){var d=0,e=0,f=a.round;if(!b)return b;if(b in a._pathcache)return a._pathcache[b];b=b.replace(/(\d*)((\.*\d*)(e ?-?\d*))/g,"$1");var g=b.match(/([MLHVCSQTAZ][^MLHVCSQTAZ]*)/gi),h=[],i=[];for(var j=0,k=g.length;j<k;j++){var l=g[j],m=l.charAt(0),n=l.substring(1).split(/[, ]/);switch(m){case"M":m="m",d=f(n[0]),e=f(n[1]),n=[d,e];break;case"m":m="m",d+=f(n[0]),e+=f(n[1]),n=[d,e];break;case"A":m="l",n=[d=f(n[5]),e=f(n[6])];break;case"L":m="l",n=[d=f(n[0]),e=f(n[1])];break;case"l":m="l",n=[d=d+f(n[0]),e=e+f(n[1])];break;case"H":m="l",n=[d=f(n[0]),e];break;case"h":m="l",n=[d=d+f(n[0]),e];break;case"V":m="l",n=[d,e=f(n[0])];break;case"v":m="l",n=[d,e=e+f(n[0])];break;case"C":m="c",i=n=[f(n[0]),f(n[1]),f(n[2]),f(n[3]),d=f(n[4]),e=f(n[5])];break;case"c":m="c",i=n=[d+f(n[0]),e+f(n[1]),d+f(n[2]),e+f(n[3]),d=d+f(n[4]),e=e+f(n[5])];break;case"S":m="c",i=n=[i[4]+(i[4]-i[2]),i[5]+(i[5]-i[3]),f(n[0]),f(n[1]),d=f(n[2]),e=f(n[3])];break;case"s":m="c",i=n=[i[4]+(i[4]-i[2]),i[5]+(i[5]-i[3]),d+f(n[0]),e+f(n[1]),d=d+f(n[2]),e=e+f(n[3])];break;case"Q":m="c";var o=f(n[0]),p=f(n[1]),q=f(n[2]),r=f(n[3]);n=[~~(d+(o-d)*2/3),~~(e+(p-e)*2/3),~~(o+(q-o)/3),~~(p+(r-p)/3),d=q,e=r];break;case"q":m="l",d+=f(n[2]),e+=f(n[3]),n=[d,e];break;case"Z":case"z":m="xe",n=[];break;default:m="",n=[]}h.push(m,n.join(","))}return a._pathcache[b]=h.join("")+"e"}};pv.VmlScene={scale:1,events:["mousewheel","mousedown","mouseup","mouseover","mouseout","mousemove","click","dblclick"],implicit:{css:{}},copy_functions:function(a){for(var b in a)typeof a[b]=="function"&&!(b in pv.VmlScene)&&(pv.VmlScene[b]=a[b])}},pv.VmlScene.copy_functions(pv.SvgScene),pv.Scene=pv.VmlScene,pv.VmlScene.expect=function(b,c,d,e){e=e||{};var f=a.elm_defaults[c]||{},g=f.rewrite||c;if(b){if(b.tagName.toUpperCase()!==g.toUpperCase()){var h=a.createElement(c);b.parentNode.replaceChild(h,b),b=h}}else b=a.createElement(c);"attr"in f&&f.attr(d,e,b);if(d.cursor in a.cursorstyles){var i=a.cursorstyles[d.cursor];e.cursor=i===1?d.cursor:i}for(var j in e){var k=e[j];k==null?b.style.removeAttribute(j):b.style[j]=k}return b},pv.VmlScene.append=function(a,b,c){return a.$scene={scenes:b,index:c},a=this.title(a,b[c]),(!a.parentNode||a.parentNode.nodeType===11)&&b.$g.appendChild(a),a.nextSibling},pv.VmlScene.title=function(a,b){return a.title=b.title||"",a},pv.VmlScene.panel=function(b){var c=b.$g,d=c&&c.firstChild;for(var e=0;e<b.length;e++){var f=b[e];if(!f.visible)continue;if(!b.parent){f.canvas.style.display="inline-block",f.canvas.style.zoom=1,c&&c.parentNode!=f.canvas&&(c=f.canvas.firstChild,d=c&&c.firstChild);if(!c){a.init(),c=f.canvas.appendChild(a.createElement("svg"));for(var g=0;g<this.events.length;g++)c.addEventListener?c.addEventListener(this.events[g],this.dispatch,!1):c.attachEvent("on"+this.events[g],this.dispatch);d=c.firstChild}b.$g=c;var h=f.width+f.left+f.right,i=f.height+f.top+f.bottom;c.style.width=h+"px",c.style.height=i+"px",c.style.clip="rect(0px "+h+"px "+i+"px 0px)"}d=this.fill(d,b,e);var j=this.scale,k=f.transform,l=f.left+k.x,m=f.top+k.y;this.scale*=k.k;for(var g=0;g<f.children.length;g++){f.children[g].$g=d=this.expect(d,"g",{transform:"translate("+l+","+m+")"+(k.k!=1?" scale("+k.k+")":"")}),this.updateAll(f.children[g]);if(!d.parentNode||d.parentNode.nodeType===11){c.appendChild(d);var n=a.elm_defaults[d.svgtype];n&&typeof n.onappend=="function"&&n.onappend(d,b[e])}d=d.nextSibling}this.scale=j,d=this.stroke(d,b,e)}return d},function(){function e(a){if(a&&a.type){this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=c;if(a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault())this.isDefaultPrevented=b}else this.type=a;this.timeStamp=Date.now()}var b=function(){return!0},c=function(){return!1},d=["altKey","attrChange","attrName","bubbles","button","cancelable","charCode","clientX","clientY","ctrlKey","currentTarget","data","detail","eventPhase","fromElement","handler","keyCode","layerX","layerY","metaKey","newValue","offsetX","offsetY","pageX","pageY","prevValue","relatedNode","relatedTarget","screenX","screenY","shiftKey","srcElement","target","toElement","view","wheelDelta","which"];e.prototype={preventDefault:function(){this.isDefaultPrevented=b;var a=this.originalEvent;if(!a)return;a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=b;var a=this.originalEvent;if(!a)return;a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=b,this.stopPropagation()},isDefaultPrevented:c,isPropagationStopped:c,isImmediatePropagationStopped:c},a.fixEvent=function(a){var b=a;a=new e(b);for(var c=0,f=d.length;c<f;c++){var g=d[c];a[g]=b[g]}a.target||(a.target=a.srcElement||document),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=document.documentElement,i=document.body;a.pageX=a.clientX+(h&&h.scrollLeft||i&&i.scrollLeft||0)-(h&&h.clientLeft||i&&i.clientLeft||0),a.pageY=a.clientY+(h&&h.scrollTop||i&&i.scrollTop||0)-(h&&h.clientTop||i&&i.clientTop||0)}return a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==undefined&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0),a.type==="mousewheel"&&(a.wheel=a.wheelDelta),a}}(),pv.listener=function(b,c){return b.$listener||(b.$listener=function(c){try{return pv.event=a.fixEvent(c||window.event),b.call(this,pv.event)}catch(c){pv.error(c)}finally{delete pv.event}})},pv.listen=function(a,b,c){return c=pv.listener(c,a),a===window&&(a=document.documentElement),a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},pv.VmlScene.dispatch=pv.listener(function(a){var b=a.target.$scene;b&&pv.Mark.dispatch(a.type,b.scenes,b.index)&&a.preventDefault()}),pv.VmlScene.image=function(a){var b=a.$g.firstChild;for(var c=0;c<a.length;c++){var d=a[c];if(!d.visible)continue;b=this.fill(b,a,c);if(!d.image){b=new Image,b.src=d.url;var e=b.style;e.position="absolute",e.top=d.top,e.left=d.left,e.width=d.width,e.height=d.height,e.cursor=d.cursor,e.msInterpolationMode="bicubic"}b=this.append(b,a,c),b=this.stroke(b,a,c)}return b},pv.VmlScene.label=function(b){var c=b.$g.firstChild,d=Math.round;for(var e=0;e<b.length;e++){var f=b[e];if(!f.visible)continue;var g=f.textStyle;if(!g.opacity||!f.text)continue;var h={};f.cursor&&(h.cursor=f.cursor);var i=f.text.replace(/\s+/g," "),j=a.text_dims(i,f.font),k=0,l=0;f.textBaseline==="middle"?l-=j.fontsize/2:f.textBaseline==="top"?l+=f.textMargin:f.textBaseline==="bottom"&&(l-=f.textMargin+j.fontsize),f.textAlign==="center"?k-=j.width/2:f.textAlign==="right"?k-=j.width+f.textMargin:f.textAlign==="left"&&(k+=f.textMargin),c=this.expect(c,"text",h,{font:f.font,textDecoration:f.textDecoration,top:Math.round(f.top+l)+"px",left:Math.round(f.left+k)+"px",position:"absolute",display:"block",lineHeight:1,whiteSpace:"nowrap",zoom:1,cursor:"default",color:a.color(g.color)||"black"}),c.innerText=i;var m=180*f.textAngle/Math.PI;if(m){var n=~~m%360*a.d2r,o=Math.cos(n),p=Math.sin(n);c.style.filter=["progid:DXImageTransform.Microsoft.Matrix(","M11=",o.toFixed(8),",","M12=",-p.toFixed(8),",","M21=",p.toFixed(8),",","M22=",o.toFixed(8),",sizingMethod='auto expand')\";"].join("")}else c.style.filter="";c=this.append(c,b,e)}return c},pv.VmlScene.wedge=function(b){var c=b.$g.firstChild,d=a.round;for(var e=0;e<b.length;e++){var f=b[e];if(!f.visible)continue;var g=f.fillStyle,h=f.strokeStyle;if(!g.opacity&&!h.opacity)continue;c=this.expect(c,"path",{"pointer-events":f.events,cursor:f.cursor,transform:"translate("+f.left+","+f.top+")",d:"",fill:g.color,"fill-rule":"evenodd","fill-opacity":g.opacity||null,stroke:h.color,"stroke-opacity":h.opacity||null,"stroke-width":h.opacity?f.lineWidth/this.scale:null});var i=c.getElementsByTagName("path")[0];i||(i=a.make("path"),c.appendChild(i));var j=d(f.innerRadius),k=d(f.outerRadius),l;if(f.angle>=2*Math.PI)j?l="AE0,0 "+k+","+k+" 0 23592960"+"AL0,0 "+j+","+j+" 0 23592960":l="AE0,0 "+k+","+k+" 0 23592960";else{var m=Math.round(f.startAngle/Math.PI*11796480),n=Math.round(f.angle/Math.PI*11796480);j?l="AE 0,0 "+k+","+k+" "+ -m+" "+ -n+" 0,0 "+j+","+j+" "+ -(m+n)+" "+n+"X":l="M0,0AE0,0 "+k+","+k+" "+ -m+" "+ -n+"X"}i.v=l,c=this.append(c,b,e)}return c}}()
463 src/Vml.js
@@ -0,0 +1,463 @@
+var vml = {
+
+ round: function(n){ return Math.round( n * 21.6 ); },
+
+ styles: null,
+
+ pre: '<v:',
+ post: ' class="msvml">',
+
+ block: { 'group':1, 'shape':1, 'shapetype':1, 'line':1,
+ 'polyline':1, 'curve':1, 'rect':1, 'roundrect':1,
+ 'oval':1, 'arc':1, 'image':1 },
+ ends: { 'butt':'flat','round':'round','square':'square','flat':'flat'},
+ joins: { 'bevel':'bevel','round':'round','miter':'miter'},
+ cursorstyles: {
+ 'hand': 'pointer',
+ 'crosshair': 1, 'pointer': 1, 'move': 1, 'text': 1,
+ 'wait': 1, 'help': 1, 'progress': 1,
+ 'n-resize': 1, 'ne-resize': 1, 'nw-resize': 1, 's-resize': 1,
+ 'se-resize': 1, 'sw-resize': 1, 'e-resize': 1, 'w-resize': 1
+ },
+
+ text_shim: null,
+ _textcache: {},
+ text_dims: function ( text, font ) {
+ if ( !(font in vml._textcache) ) {
+ vml._textcache[ font ] = {};
+ }
+ if ( text in vml._textcache[ font ] ) {
+ return vml._textcache[ font ][ text ];
+ }
+ var shim = vml.text_shim;
+ shim.style.font = font;
+ shim.innerText = text;
+ return (vml._textcache[ font ][ text ] = {
+ fontsize: parseInt( shim.style.fontSize, 10 ),
+ height: shim.offsetHeight,
+ width: shim.offsetWidth
+ });
+ },
+
+ d2r: Math.PI * 2 / 360, // is this used more than once?
+
+ get_dim: function ( attr, target ) {
+ var o = target || {};
+ // reformat the most common attributes
+ o.translate_x = 0;
+ o.translate_y = 0;
+ if ( attr.transform ) {
+ var t = /translate\((\d+(?:\.\d+)?)(?:,(\d+(?:\.\d+)?))?\)/.exec( attr.transform );
+ if ( t && t[1] ) { o.translate_x = parseFloat( t[1] ); }
+ if ( t && t[2] ) { o.translate_y = parseFloat( t[2] ); }
+ var r = /rotate\((\d+\.\d+|\d+)\)/.exec( attr.transform );
+ if ( r ) { o.rotation = parseFloat( r[1] ) % 360; }
+ // var scale_x = 1, scale_y = 1,
+ // var s = /scale\((\d+)(?:,(\d+))?\)/i.exec( value );
+ // if ( s && s[1] ) { scale[0] = parseInt( s[1], 10 ); }
+ // if ( s && s[2] ) { scale[1] = parseInt( s[2], 10 ); }
+ }
+ o.x = parseFloat( attr.x||0 );
+ o.y = parseFloat( attr.y||0 );
+ if ( 'width' in attr ) {
+ o.width = parseInt( attr.width, 10 );
+ }
+ if ( 'height' in attr ) {
+ o.height = parseInt( attr.height, 10 );
+ }
+ return o;
+ },
+
+ elm_defaults: {
+
+ "g": {
+ rewrite: 'span',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr );
+ elm.style.cssText = "position:absolute;zoom:1;left:"+
+ (d.translate_x + d.x)+"px;top:"+
+ (d.translate_y + d.y)+"px;";
+ }
+ },
+
+ "line": {
+ rewrite: 'shape',
+ attr: function ( attr, style, elm ) {
+ var x1 = parseFloat( attr.x1 || 0 ),
+ y1 = parseFloat( attr.y1 || 0 ),
+ x2 = parseFloat( attr.x2 || 0 ),
+ y2 = parseFloat( attr.y2 || 0 ),
+ r = vml.round;
+ elm.coordorigin = "0,0";
+ elm.coordsize = "21600,21600";
+ vml.path( elm ).v = 'M '+ r(x1) + ' ' + r(y1) + ' L ' + r(x2) + ' ' + r(y2) + ' E';
+ vml.stroke( elm, attr );
+ },
+ css: "top:0px;left:0px;width:1000px;height:1000px"
+ },
+
+ "rect": {
+ rewrite: 'shape',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr ),
+ p = vml.path( elm ),
+ r = vml.round;
+ elm.coordorigin = "0,0";
+ elm.coordsize = "21600,21600";
+ var x = r(d.translate_x + d.x),
+ y = r(d.translate_y + d.y),
+ w = r(d.width),
+ h = r(d.height);
+ p.v = 'M ' + x + ' ' + y +
+ ' L ' + (x + w) + ' ' + y +
+ ' L ' + (x + w) + ' ' + (y + h) +
+ ' L ' + x + ' ' + (y + h) +
+ ' x';
+ vml.stroke( elm, attr );
+ vml.fill( elm, attr );
+ },
+ css: "top:0px;left:0px;width:1000px;height:1000px"
+ },
+
+ "path": {
+ rewrite: 'shape',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr ),
+ es = elm.style;
+ es.left = (d.translate_x + d.x) + "px";
+ es.top = (d.translate_y + d.y) + "px";
+ elm.coordorigin = "0,0";
+ elm.coordsize = "21600,21600";
+ vml.path( elm, attr.d );
+ vml.fill( elm, attr );
+ vml.stroke( elm, attr );
+ },
+ css: "top:0px;left:0px;width:1000px;height:1000px"
+ },
+
+ "circle": {
+ /* This version of circles is crisper but seems slower
+ rewrite: 'shape',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr ),
+ r = vml.round( parseFloat( attr.r || 0 ) ),
+ cx = parseFloat( attr.cx || 0 ),
+ cy = parseFloat( attr.cy || 0 ),
+ es = elm.style;
+ es.left = (d.translate_x + d.x + cx + 0.3) + "px";
+ es.top = (d.translate_y + d.y + cy + 0.3) + "px";
+ elm.coordorigin = "0,0";
+ elm.coordsize = "21600,21600";
+ vml.path( elm ).v = "ar-" + r + ",-" + r + "," + r + "," + r + ",0,0,0,0x";
+ vml.fill( elm, attr );
+ vml.stroke( elm, attr );
+ },
+ css: "top:0px;left:0px;width:1000px;height:1000px"
+ */
+ rewrite: 'oval',
+ attr: function ( attr, style, elm ) {
+ var d = vml.get_dim( attr ),
+ es = elm.style,
+ cx = parseFloat( attr.cx || 0 ) + 0.7,
+ cy = parseFloat( attr.cy || 0 ) + 0.7,
+ r = parseFloat( attr.r || 0 ) + 0.5;
+ es.top = ( d.translate_y + cy - r ) + "px";
+ es.left = ( d.translate_x + cx - r ) + "px";
+ es.width = ( r * 2 ) + "px";
+ es.height = ( r * 2 ) + "px";
+ vml.fill( elm, attr );
+ vml.stroke( elm, attr );
+ },
+ },
+
+ "text": {
+ rewrite: 'span'
+ },
+
+ "svg": {
+ rewrite: 'span',
+ css: 'position:relative;overflow:hidden;display:inline-block;~display:block;'
+ },
+
+ // this allows reuse of the createElement function for actual VML
+ "vml:path": { rewrite: 'path' },
+ "vml:stroke": { rewrite: 'stroke' },
+ "vml:fill": { rewrite: 'fill' }
+
+ },
+
+ // cloning elements is a lot faster than creating them
+ _elmcache: {
+ 'span': document.createElement( 'span' ),
+ 'div': document.createElement( 'div' )
+ },
+
+ createElement: function ( type, reformat ) {
+ var elm,
+ cache = vml._elmcache,
+ helper = vml.elm_defaults[ type ] || {};
+ var tagName = helper.rewrite || type;
+ if ( tagName in cache ) {
+ elm = cache[ tagName ].cloneNode( false );
+ }
+ else {
+ cache[ tagName ] = document.createElement( vml.pre + tagName + vml.post );
+ if ( tagName in vml.block ) {
+ cache[ tagName ].className += ' msvml_block';
+ }
+ elm = cache[ tagName ].cloneNode( false );
+ }
+ helper.css && (elm.style.cssText = helper.css);
+ return elm;
+ },
+
+
+ // hex values lookup table
+ _hex: pv.range(0,256).map(function(i){ return pv.Format.pad("0",2,i.toString(16)); }),
+ _colorcache: {},
+ color: function ( value, rgb ) {
+ // TODO: deal with opacity here ?
+ if ( !(value in vml._colorcache) && (rgb = /^rgb\((\d+),(\d+),(\d+)\)$/i.exec( value )) ) {
+ vml._colorcache[value] = '#' + vml._hex[rgb[1]] + vml._hex[rgb[2]] + vml._hex[rgb[3]];
+ }
+ return vml._colorcache[ value ] || value;
+ },
+
+
+ fill: function ( elm, attr ) {
+ var fill = elm.getElementsByTagName( 'fill' )[0];
+ if ( !fill ) {
+ fill = elm.appendChild( vml.createElement( 'vml:fill' ) );
+ }
+ if ( !attr.fill || attr.fill === 'none' ) {
+ fill.on = false;
+ }
+ else {
+ fill.on = 'true';
+ fill.color = vml.color( attr.fill );
+ fill.opacity = parseFloat( attr['fill-opacity'] || '1' ) || '1';
+ }
+ },
+
+
+ stroke: function ( elm, attr ) {
+ var stroke = elm.getElementsByTagName( 'stroke' )[0];
+ if ( !stroke ) {
+ stroke = elm.appendChild( vml.createElement( 'vml:stroke' ) );
+ }
+ if ( !attr.stroke || attr.stroke === 'none' ) {
+ stroke.on = 'false';
+ stroke.weight = '0';
+ }
+ else {
+ stroke.on = 'true';
+ stroke.weight = parseFloat( attr['stroke-width'] || '1' ) / 1.25;
+ stroke.color = vml.color( attr.stroke ) || 'black';
+ stroke.opacity = parseFloat( attr['stroke-opacity'] || '1' ) || '1';
+ stroke.joinstyle = vml.joins[ attr['stroke-linejoin'] ] || 'miter';
+ }
+ },
+
+ path: function ( elm, svgpath ) {
+ var p = elm.getElementsByTagName( 'path' )[0];
+ if ( !p ) {
+ p = elm.appendChild( vml.createElement( 'vml:path' ) );
+ }
+ if ( arguments.length > 1 ) {
+ p.v = vml.rewritePath( svgpath );
+ }
+ return p;
+ },
+
+
+ init: function () {
+ if ( !vml.text_shim ) {
+ vml.text_shim = document.getElementById('pv_vml_text_shim') || document.createElement('span');
+ vml.text_shim.id = 'protovisvml_text_shim';
+ vml.text_shim.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline-block;white-space:nowrap;";
+ document.body.appendChild( vml.text_shim );
+ }
+ if ( !vml.styles ) {
+ vml.styles = document.getElementById('protovisvml_styles') || document.createElement("style");
+ vml.styles.id = 'protovisvml_styles';
+ document.documentElement.firstChild.appendChild( vml.styles );
+ vml.styles.styleSheet.addRule( '.msvml', 'behavior:url(#default#VML);' );
+ vml.styles.styleSheet.addRule( '.msvml_block', 'position:absolute;top:0;left:0;' );
+ try {
+ if ( !document.namespaces.v ) { document.namespaces.add( 'v', 'urn:schemas-microsoft-com:vml' ); }
+ }
+ catch (e) {
+ vml.pre = '<';
+ vml.post = ' class="msvml" xmlns="urn:schemas-microsoft.com:vml">';
+ }
+ }
+ },
+
+ // SVG->VML path conversion - This converts a SVG path to a VML path
+ //
+ // Things that are missing:
+ // - Multiple sets of coords.
+ // Some commands (lineto,curveto,..) can take multiple sets of coords.
+ // Because Protovis always supplies the command between arguments, this isn't
+ // implemented, but it would be trivial to complete this.
+ // - ARCs need solving
+ _pathcache: {},
+ rewritePath:function ( p, deb ) {
+ var x = 0, y = 0, round = vml.round;
+
+ if ( !p ) { return p; }
+ if ( p in vml._pathcache ) { return vml._pathcache[p]; }
+
+ // clean up overly detailed fractions (8.526512829121202e-148)
+ p = p.replace( /(\d*)((\.*\d*)(e ?-?\d*))/g, "$1");
+
+ var bits = p.match( /([MLHVCSQTAZ][^MLHVCSQTAZ]*)/gi );
+ var np = [], lastcurve = [];
+ for ( var i=0,bl=bits.length; i<bl; i++ ) {
+ var itm = bits[i],
+ op = itm.charAt( 0 ),
+ args = itm.substring( 1 ).split( /[, ]/ );
+
+ switch ( op ) {
+
+ case 'M': // moveto (absolute)
+ op = 'm';
+ x = round( args[0] );
+ y = round( args[1] );
+ args = [ x, y ];
+ break;
+ case 'm': // moveto (relative)
+ op = 'm';
+ x += round( args[0] );
+ y += round( args[1] );
+ args = [ x, y ];
+ break;
+
+ case "A": // TODO: arc (absolute):
+ // SVG: rx ry x-axis-rotation large-arc-flag sweep-flag x y
+ // VML: http://www.w3.org/TR/NOTE-VML
+ /*var rx = round( args[0] ),
+ ry = round( args[1] ),
+ xrot = round( args[2] ),
+ lrg = round( args[3] ),
+ sweep = round( args[4] );*/
+ op = 'l';
+ args = [ (x = round( args[5] )),
+ (y = round( args[6] )) ];
+ break;
+
+ case "L": // lineTo (absolute)
+ op = 'l';
+ args = [ (x = round( args[0] )),
+ (y = round( args[1] )) ];
+ break;
+ case "l": // lineTo (relative)
+ op = 'l';
+ args = [ (x = x + round( args[0] )),
+ (y = y + round( args[1] )) ];
+ break;
+
+ case "H": // horizontal lineto (absolute)
+ op = 'l';
+ args = [ (x = round( args[0] )), y ];
+ break;
+ case "h": // horizontal lineto (relative)
+ op = 'l';
+ args = [ (x = x + round( args[0] )), y ];
+ break;
+
+ case "V": // vertical lineto (absolute)
+ op = 'l';
+ args = [ x, (y = round( args[0] )) ];
+ break;
+ case "v": // vertical lineto (relative)
+ op = 'l';
+ args = [ x, (y = y + round( args[0] )) ];
+ break;
+
+ case "C": // curveto (absolute)
+ op = 'c';
+ lastcurve = args = [
+ round(args[0]), round(args[1]),
+ round(args[2]), round(args[3]),
+ (x = round( args[4] )),
+ (y = round( args[5] ))
+ ];
+ break;
+ case "c": // curveto (relative)
+ op = 'c';
+ lastcurve = args = [
+ x + round(args[0]),
+ y + round(args[1]),
+ x + round(args[2]),
+ y + round(args[3]),
+ (x = x + round( args[4] )),
+ (y = y + round( args[5] ))
+ ];
+ break;
+
+ case "S": // shorthand/smooth curveto (absolute)
+ op = 'c';
+ lastcurve = args = [
+ lastcurve[4] + (lastcurve[4] - lastcurve[2]),
+ lastcurve[5] + (lastcurve[5] - lastcurve[3]),
+ round(args[0]),
+ round(args[1]),
+ (x = round( args[2] )),
+ (y = round( args[3] ))
+ ];
+ break;
+ case "s": // shorthand/smooth curveto (relative)
+ op = 'c';
+ lastcurve = args = [
+ lastcurve[4] + (lastcurve[4] - lastcurve[2]),
+ lastcurve[5] + (lastcurve[5] - lastcurve[3]),
+ x + round(args[0]),
+ y + round(args[1]),
+ (x = x + round( args[2] )),
+ (y = y + round( args[3] ))
+ ];
+ break;
+
+ case "Q": // quadratic Bézier curveto (absolute)
+ op = 'c';
+ var x1 = round( args[0] ),
+ y1 = round( args[1] ),
+ x2 = round( args[2] ),
+ y2 = round( args[3] );
+ args = [
+ ~~(x + (x1 - x) * 2 / 3),
+ ~~(y + (y1 - y) * 2 / 3),
+ ~~(x1 + (x2 - x1) / 3),
+ ~~(y1 + (y2 - y1) / 3),
+ (x = x2),
+ (y = y2)
+ ];
+ break;
+ case "q": // TODO: quadratic Bézier (relative)
+ op = 'l';
+ x += round( args[2] );
+ y += round( args[3] );
+ args = [ x, y ];
+ break;
+
+ // TODO: T/t (Shorthand/smooth quadratic Bézier curveto)
+
+ case "Z":
+ case "z":
+ op = 'xe';
+ args = [];
+ break;
+
+ default:
+ // unsupported path command
+ op = '';
+ args = [];
+ }
+ np.push( op, args.join(',') );
+ }
+ return ( vml._pathcache[p] = (np.join('') + 'e') );
+ }
+
+};
+
165 src/VmlEvents.js
@@ -0,0 +1,165 @@
+// Much of the event rewriting code is copyed and watered down
+// from the jQuery library's event hander. We have the luxury
+// of knowing that we're on MSIE<9 so we can despense with some
+// fixes for other browsers.
+(function(){
+
+ var returnTrue = function () { return true; };
+ var returnFalse = function () { return false; };
+ var _event_props = ["altKey","attrChange","attrName","bubbles","button",
+ "cancelable","charCode","clientX","clientY","ctrlKey",
+ "currentTarget","data","detail","eventPhase","fromElement",
+ "handler","keyCode","layerX","layerY","metaKey",
+ "newValue","offsetX","offsetY","pageX","pageY","prevValue",
+ "relatedNode","relatedTarget","screenX","screenY",
+ "shiftKey","srcElement","target","toElement","view","wheelDelta","which"];
+
+ function IEvent ( src ) {
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+ this.isDefaultPrevented = returnFalse;
+ if (src.defaultPrevented || src.returnValue === false || src.getPreventDefault && src.getPreventDefault()) {
+ this.isDefaultPrevented = returnTrue;
+ }
+ }
+ else {
+ this.type = src;
+ }
+ this.timeStamp = Date.now();
+ }
+ IEvent.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+ var e = this.originalEvent;
+ if ( !e ) { return; }
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+ // otherwise set the returnValue property of the original event to false (IE)
+ }
+ else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+ };
+
+
+ vml.fixEvent = function ( ev ) {
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = ev;
+ ev = new IEvent( originalEvent );
+
+ for (var i=0,l=_event_props.length; i<l; i++) {
+ var prop = _event_props[i];
+ ev[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !ev.target ) {
+ ev.target = ev.srcElement || document;
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !ev.relatedTarget && ev.fromElement ) {
+ ev.relatedTarget = (ev.fromElement === ev.target)
+ ? ev.toElement
+ : ev.fromElement;
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( ev.pageX == null && ev.clientX != null ) {
+ var doc = document.documentElement,
+ body = document.body;
+ ev.pageX = ev.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ ev.pageY = ev.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( ev.which == null && (ev.charCode != null || ev.keyCode != null) ) {
+ ev.which = ev.charCode != null
+ ? ev.charCode
+ : ev.keyCode;
+ }
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !ev.metaKey && ev.ctrlKey ) {
+ ev.metaKey = ev.ctrlKey;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !ev.which && ev.button !== undefined ) {
+ ev.which = (ev.button & 1 ? 1 : ( ev.button & 2 ? 3 : ( ev.button & 4 ? 2 : 0 ) ));
+ }
+
+ // Mousewheel delta
+ if ( ev.type === "mousewheel" ) {
+ ev.wheel = ev.wheelDelta;
+ }
+
+ return ev;
+ }
+
+})();
+
+
+
+// replace the listener with something a little more elaborate
+pv.listener = function(f, target) {
+ return f.$listener || (f.$listener = function(e) {
+ try {
+ pv.event = vml.fixEvent( e || window.event );
+ return f.call( this, pv.event );
+ }
+ catch (e) {
+ pv.error(e);
+ }
+ finally {
+ delete pv.event;
+ }
+ });
+};
+
+
+pv.listen = function(target, type, listener) {
+ listener = pv.listener(listener, target);
+ if ( target === window ) {
+ target = document.documentElement;
+ }
+ return target.addEventListener
+ ? target.addEventListener(type, listener, false)
+ : target.attachEvent("on" + type, listener);
+};
+
+
+
+pv.VmlScene.dispatch = pv.listener(function(e) {
+ var t = e.target.$scene;
+ if ( t && pv.Mark.dispatch(e.type, t.scenes, t.index) ) {
+ e.preventDefault();
+ }
+});
38 src/VmlImage.js
@@ -0,0 +1,38 @@
+
+//
+pv.VmlScene.image = function(scenes) {
+ var e = scenes.$g.firstChild;
+ for (var i = 0; i < scenes.length; i++) {
+ var s = scenes[i];
+
+ /* visible */
+ if (!s.visible) continue;
+
+ /* fill */
+ e = this.fill(e, scenes, i);
+
+ /* image */
+ if ( s.image ) {
+ // There is no canvas support in MSIE
+ }
+ else {
+ e = new Image();
+ e.src = s.url;
+ var st = e.style;
+ st.position = 'absolute';
+ st.top = s.top;
+ st.left = s.left;
+ st.width = s.width;
+ st.height = s.height;
+ st.cursor = s.cursor;
+ st.msInterpolationMode = 'bicubic';
+ }
+ e = this.append(e, scenes, i);
+
+ /* stroke */
+ e = this.stroke(e, scenes, i);
+ }
+ return e;
+};
+
+
80 src/VmlLabel.js
@@ -0,0 +1,80 @@
+pv.VmlScene.label = function(scenes) {
+ var e = scenes.$g.firstChild,
+ round = Math.round;
+ for (var i = 0; i < scenes.length; i++) {
+ var s = scenes[i];
+
+ // visible
+ if (!s.visible) continue;
+ var fill = s.textStyle;
+ if (!fill.opacity || !s.text) continue;
+
+ var attr = {};
+ if ( s.cursor ) { attr.cursor = s.cursor; }
+
+ // measure text
+ var txt = s.text.replace( /\s+/g, '\xA0' );
+ var label = vml.text_dims( txt, s.font );
+
+ var dx = 0, dy = 0;
+
+ if ( s.textBaseline === 'middle' ) {
+ dy -= label.fontsize / 2;
+ }
+ else if ( s.textBaseline === 'top' ) {
+ dy += s.textMargin;
+ }
+ else if ( s.textBaseline === 'bottom' ) {
+ dy -= s.textMargin + label.fontsize;
+ }
+
+ if ( s.textAlign === 'center' ) {
+ dx -= label.width / 2;
+ }
+ else if ( s.textAlign === 'right' ) {
+ dx -= label.width + s.textMargin;
+ }
+ else if ( s.textAlign === 'left' ) {
+ dx += s.textMargin;
+ }
+
+ e = this.expect(e, "text", attr, {
+ "font": s.font,
+ // "text-shadow": s.textShadow,
+ "textDecoration": s.textDecoration,
+ 'top': Math.round( s.top + dy ) + 'px',
+ 'left': Math.round( s.left + dx ) + 'px',
+ 'position': 'absolute',
+ 'display': 'block',
+ 'lineHeight': 1,
+ 'whiteSpace': 'nowrap',
+ 'zoom': 1,
+ 'cursor': 'default',
+ 'color': vml.color( fill.color ) || 'black'
+ });
+ e.innerText = txt;
+
+ // Rotation is broken in serveral different ways:
+ // 1. it looks REALLY ugly
+ // 2. it is incredibly slow
+ // 3. rotated text is offset completely wrong and it takes a ton of math to correct it
+ // when text is rotated we need to switch to a VML textpath solution
+ var rotation = 180 * s.textAngle / Math.PI;
+ if ( rotation ) {
+ var r = (~~rotation % 360) * vml.d2r,
+ ct = Math.cos(r),
+ st = Math.sin(r);
+ e.style.filter = ['progid:DXImageTransform.Microsoft.Matrix(',
+ 'M11=', ct.toFixed( 8 ), ',',
+ 'M12=', -st.toFixed( 8 ), ',',
+ 'M21=', st.toFixed( 8 ), ',',
+ 'M22=', ct.toFixed( 8 ), ',sizingMethod=\'auto expand\')";'].join('');
+ }
+ else {
+ e.style.filter = '';
+ }
+
+ e = this.append(e, scenes, i);
+ }
+ return e;
+};
71 src/VmlPanel.js
@@ -0,0 +1,71 @@
+// mostly the same code as pv.SvgScene.panel, but with less MSIE crashing...
+pv.VmlScene.panel = function(scenes) {
+ var g = scenes.$g, e = g && g.firstChild;
+ for (var i = 0; i < scenes.length; i++) {
+ var s = scenes[i];
+
+ /* visible */
+ if (!s.visible) continue;
+
+ /* svg */
+ if (!scenes.parent) {
+ s.canvas.style.display = "inline-block";
+ s.canvas.style.zoom = 1;
+ if (g && (g.parentNode != s.canvas)) {
+ g = s.canvas.firstChild;
+ e = g && g.firstChild;
+ }
+ if ( !g ) {
+ vml.init(); // turn VML on if it isn't allready
+ g = s.canvas.appendChild( vml.createElement( "svg" ) );
+ for (var j = 0; j < this.events.length; j++) {
+ g.addEventListener
+ ? g.addEventListener(this.events[j], this.dispatch, false)
+ : g.attachEvent("on" + this.events[j], this.dispatch);
+ }
+ e = g.firstChild;
+ }
+ scenes.$g = g;
+ var w = (s.width + s.left + s.right),
+ h = (s.height + s.top + s.bottom);
+ g.style.width = w + 'px';
+ g.style.height = h + 'px';
+ g.style.clip = "rect(0px " + w + "px " + h + "px 0px)";
+ }
+
+ /* fill */
+ e = this.fill( e, scenes, i );
+
+ /* transform (push) */
+ var k = this.scale,
+ t = s.transform,
+ x = s.left + t.x,
+ y = s.top + t.y;
+ this.scale *= t.k;
+
+ /* children */
+ for (var j = 0; j < s.children.length; j++) {
+ s.children[j].$g = e = this.expect(e, "g", {
+ "transform": "translate(" + x + "," + y + ")" + (t.k != 1 ? " scale(" + t.k + ")" : "")
+ });
+ this.updateAll(s.children[j]);
+ if ( !e.parentNode || e.parentNode.nodeType === 11 ) {
+ g.appendChild(e);
+ var helper = vml.elm_defaults[ e.svgtype ];
+ if ( helper && typeof helper.onappend === 'function' ) {
+ helper.onappend( e, scenes[i] );
+ }
+ }
+ e = e.nextSibling;
+ }
+
+ /* transform (pop) */
+ this.scale = k;
+
+ /* stroke */
+ e = this.stroke( e, scenes, i );
+
+ }
+ return e;
+};
+
92 src/VmlScene.js
@@ -0,0 +1,92 @@
+pv.VmlScene = {
+
+ // The pre-multipled scale, based on any enclosing transforms.
+ scale: 1,
+
+ // The set of supported events.
+ events: [
+ "mousewheel",
+ "mousedown",
+ "mouseup",
+ "mouseover",
+ "mouseout",
+ "mousemove",
+ "click",
+ "dblclick"
+ ],
+
+ // implicit values are not used for VML, assigned render faster and we have
+ // no desire to keep the DOM clean here - only to make it work!
+ implicit: { css: {} },
+
+ copy_functions: function ( obj ) {
+ for ( var name in obj ) {
+ if ( typeof obj[name] === 'function' && !(name in pv.VmlScene) ) {
+ pv.VmlScene[ name ] = obj[ name ];
+ }
+ }
+ }
+
+};
+
+
+// copy helper methods from SvgScene onto our new Scene
+pv.VmlScene.copy_functions( pv.SvgScene );
+pv.Scene = pv.VmlScene;
+
+
+pv.VmlScene.expect = function (e, type, attr, style) {
+ style = style || {};
+ var helper = vml.elm_defaults[type] || {},
+ _type = helper.rewrite || type;
+
+ if ( e ) {
+ if ( e.tagName.toUpperCase() !== _type.toUpperCase() ) {
+ var n = vml.createElement( type );
+ e.parentNode.replaceChild( n, e );
+ e = n;
+ }
+ }
+ else {
+ e = vml.createElement( type );
+ }
+
+ if ( 'attr' in helper ) {
+ helper.attr( attr, style, e );
+ }
+
+ if ( attr.cursor in vml.cursorstyles ) {
+ var curs = vml.cursorstyles[attr.cursor];
+ style.cursor = ( curs === 1 ) ? attr.cursor : curs;
+ }
+
+ for (var name in style) {
+ var value = style[name];
+ if (value == null) e.style.removeAttribute(name); // cssText
+ else e.style[name] = value;
+ }
+
+ return e;
+};
+
+
+pv.VmlScene.append = function(e, scenes, index) {
+ // FIXME: hooks the scene onto the element --- this is probably hemorrhaging memory in MSIE
+ // it is only ever used by the envent displatcher so it should probably be stored in a cache
+ e.$scene = {scenes:scenes, index:index};
+ // attach a title to element
+ e = this.title(e, scenes[index]);
+ if ( !e.parentNode || e.parentNode.nodeType === 11 ) { // 11 == documentFragment
+ scenes.$g.appendChild( e );
+ }
+ return e.nextSibling;
+};
+
+
+pv.VmlScene.title = function(e, s) {
+ e.title = s.title || "";
+ return e;
+};
+
+
+
66 src/VmlWedge.js
@@ -0,0 +1,66 @@
+pv.VmlScene.wedge = function(scenes) {
+ var e = scenes.$g.firstChild,
+ round = vml.round;
+ for (var i = 0; i < scenes.length; i++) {
+ var s = scenes[i];
+
+ // visible
+ if (!s.visible) continue;
+ var fill = s.fillStyle, stroke = s.strokeStyle;
+ if (!fill.opacity && !stroke.opacity) continue;
+
+ // create element sans path
+ e = this.expect(e, "path", {
+ "pointer-events": s.events,
+ "cursor": s.cursor,
+ "transform": "translate(" + s.left + "," + s.top + ")",
+ "d": '', // we deal with the path afterwards
+ "fill": fill.color,
+ "fill-rule": "evenodd",
+ "fill-opacity": fill.opacity || null,
+ "stroke": stroke.color,
+ "stroke-opacity": stroke.opacity || null,
+ "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
+ });
+
+ // add path
+ var p = e.getElementsByTagName( 'path' )[0];
+ if ( !p ) {
+ p = vml.make( 'path' );
+ e.appendChild( p );
+ }
+
+ // Arc path from bigfix/protovis
+ var r1 = round(s.innerRadius),
+ r2 = round(s.outerRadius),
+ d;
+ if (s.angle >= 2 * Math.PI) {
+ if (r1) {
+ d = "AE0,0 " + r2 + "," + r2 + " 0 23592960"
+ + "AL0,0 " + r1 + "," + r1 + " 0 23592960";
+ }
+ else {
+ d = "AE0,0 " + r2 + "," + r2 + " 0 23592960";
+ }
+ }
+ else {
+ var sa = Math.round(s.startAngle / Math.PI * 11796480),
+ a = Math.round(s.angle / Math.PI * 11796480);
+ if (r1) {
+ d = "AE 0,0 " + r2 + "," + r2 + " " + -sa + " " + -a
+ + " 0,0 " + r1 + "," + r1 + " " + -(sa + a) + " " + a
+ + "X";
+ }
+ else {
+ d = "M0,0"
+ + "AE0,0 " + r2 + "," + r2 + " " + -sa + " " + -a
+ + "X";
+ }
+ }
+ p.v = d;
+
+ e = this.append(e, scenes, i);
+
+ }
+ return e;
+};
469 src/protovis-msie.js
@@ -0,0 +1,469 @@
+var vml = {
+
+ round: function(n){ return Math.round( n * 21.6 ); },
+
+ styles: null,
+
+ pre: '<v:',
+ post: ' class="msvml">',
+
+ block: { 'group':1, 'shape':1, 'shapetype':1, 'line':1,
+ 'polyline':1, 'curve':1, 'rect':1, 'roundrect':1,
+ 'oval':1, 'arc':1, 'image':1 },
+ ends: { 'butt':'flat','round':'round','square':'square','flat':'flat'},
+ joins: { 'bevel':'bevel','round':'round','miter':'miter'},
+ cursorstyles: {
+ 'hand': 'pointer',
+ 'crosshair': 1, 'pointer': 1, 'move': 1, 'text': 1,
+ 'wait': 1, 'help': 1, 'progress': 1,
+ 'n-resize': 1, 'ne-resize': 1, 'nw-resize': 1, 's-resize': 1,
+ 'se-resize': 1, 'sw-resize': 1, 'e-resize': 1, 'w-resize': 1
+ },
+
+ text_shim: null,
+ _textcache: {},
+ text_dims: function ( text, font ) {
+ if ( !(font in vml._textcache) ) {
+ vml._textcache[ font ] = {};
+ }
+ if ( text in vml._textcache[ font ] ) {
+ return vml._textcache[ font ][ text ];
+ }
+ var shim = vml.text_shim;
+ shim.style.font = font;
+ shim.innerText = text;
+ return (vml._textcache[ font ][ text ] = {