[OpenLayers-Dev] [VML] Cloning VML nodes

RICHARD Didier didier.richard at ign.fr
Fri Aug 6 13:28:50 EDT 2010


No help from experts ?)
Am I out of scope ?

Regards,

didier

> Hi devs,
>
> I am developing a client side print control. Basically, the idea is to
> clone the map's div into a new window.
>
> Everything works (FF, Chrome, Safari, Opera --almost there is still CSS
> issues--) except for IE and for VML nodes ...
>
> Digging the web, I found that to get access to VML nodes' attributes (all
> of them not only the casual attributes), it is required to remove the node
> from the document. This has been done and all attributes for all shapes
> are accessed.
>
>
> Here is the code of the importNode() function, I am using :
>
>
>     if (!window.Node || !Node.ELEMENT_NODE) {
>         Node= {
>             ELEMENT_NODE: 1,
>             ATTRIBUTE_NODE: 2,
>             TEXT_NODE: 3,
>             CDATA_SECTION_NODE: 4,
>             ENTITY_REFERENCE_NODE: 5,
>             ENTITY_NODE: 6,
>             PROCESSING_INSTRUCTION_NODE: 7,
>             COMMENT_NODE: 8,
>             DOCUMENT_NODE: 9,
>             DOCUMENT_TYPE_NODE: 10,
>             DOCUMENT_FRAGMENT_NODE: 11,
>             NOTATION_NODE: 12
>         };
>     };
>
>     /**
>      * APIFunction: importNode
>      * Clone a node that belongs to a different document than the target
>      * document. Only ELEMENT_NODE, TEXT_NODE, CDATA_SECTION_NODE and
>      * COMMENT_NODE are supported.
>      *
>      * Parameters:
>      * doc - {DOMElement} the target document.
>      * externalNode - {DOMElement} the node to clone.
>      * deepCopy - {Boolean} indicator of cloning inner nodes and
> attributes.
>      *
>      * Returns:
>      * {DOMElement} the cloned node or null if not possible.
>      */
>     OpenLayers.Element.importNode= function(doc, externalNode, deepCopy) {
>         if (doc.importNode) {
>             // FF, Opera, Safari, Chrome :
>             return doc.importNode(externalNode, deepCopy);
>         }
>         // IE
>         var clonedNode= null, vmlNode= null, detached= false;
>         // FIXME: should be a constant of OpenLayers.Renderer.VML :
>         var shapes= ['shape','rect', 'oval', 'fill', 'stroke',
> 'imagedata', 'group','textbox'];
>         switch (externalNode.nodeType) {
>         case Node.ELEMENT_NODE:
>             // FIXME: VML nodes are not properly copied ...
>             if (deepCopy && externalNode.tagName &&
> OpenLayers.Util.indexOf(shapes,externalNode.tagName)!=-1) {
>                 //OpenLayers.Console.log('name=['+externalNode.tagName+']
> urn=['+externalNode.tagUrn+']:');
>                 //OpenLayers.Console.log('=====('+externalNode.attributes.length+')('+(externalNode.childNodes
> || []).length+')');
>                 // VML node: detach it from the source document to get
> access all attributes :
>                 if (externalNode.__detached!==true) {//not yet detached
>                     //OpenLayers.Console.log('===== detaching');
>                     vmlNode=
> externalNode.document.createElement('div');//dummy
> node
>                     vmlNode.id= 'dummy_'+externalNode.id;
>                     externalNode.parentNode.insertBefore(vmlNode,externalNode);
>                     externalNode.parentNode.removeChild(externalNode);
>                     externalNode.__detached= true;
>                 }
>                 detached= true;
>                 //OpenLayers.Console.log('=====('+externalNode.attributes.length+')('+(externalNode.childNodes
> || []).length+')');
>             }
>
>             clonedNode= doc.createElement(externalNode.nodeName);
>             /* does the node have any attributes to add? */
>             if (externalNode.attributes &&
> externalNode.attributes.length>0) {
>                 for (var i= 0, il= externalNode.attributes.length; i<il;
> i++) {
>                     var att= externalNode.attributes[i];
>                     if (detached===true && att.nodeName==='__detached') {
> continue; }
>                     clonedNode.setAttribute(att.nodeName,
> externalNode.getAttribute(att.nodeName));
>                 }
>             }
>             /* are we going after children too, and does the node have
> any? */
>             if (deepCopy && externalNode.childNodes &&
> externalNode.childNodes.length>0) {
>                 for (var i= 0, il= externalNode.childNodes.length; i<il;
> i++) {
>                     var node= externalNode.childNodes[i];
>                     if (detached===true) {
>                         node.__detached= true;
>                     }
>                     clonedNode.appendChild(OpenLayers.Element.importNode(doc,
> node, deepCopy));
>                     if (detached===true) {
>                         node.removeAttribute('__detached');
>                     }
>                 }
>             }
>
>             if (vmlNode) {
>                 // insert node back ...
>                 externalNode.removeAttribute('__detached');
>                 vmlNode.parentNode.insertBefore(externalNode,vmlNode);
>                 vmlNode.parentNode.removeChild(vmlNode);
>                 vmlNode= null;
>             }
>             break;
>         case Node.TEXT_NODE:
>         case Node.CDATA_SECTION_NODE:
>             clonedNode= doc.createTextNode(externalNode.nodeValue);
>             break;
>         case Node.COMMENT_NODE:
>             clonedNode= doc.createCommentNode(externalNode.nodeValue);
>             break;
>         default:
>             //OpenLayers.Console.log('unsupported
> '+externalNode.nodeType+'=['+externalNode.innerHTML+']');
>             break;
>         }
>         return clonedNode;
>     };
>
>
> On the popup window side, I set the document.namespaces as
> OpenLayers.Renderer.VML does it. Here is the control (under construction)
> that does the job :
>
>
> /*
>  * Copyright (c) 2008-2010 Institut Geographique National France, released
> under the
>  * BSD license.
>  */
> /*
>  * @requires OpenLayers/Control.js
>  */
> /**
>  * Class: OpenLayers.Control.PrintMap
>  * Implements a button control for printing the current map.
>  *
>  * Inherits from:
>  *  - <OpenLayers.Control>
>  */
> OpenLayers.Control.PrintMap= OpenLayers.Class(OpenLayers.Control, {
>
>     /**
>      * Property: type
>      * {String} The type of <OpenLayers.Control> -- When added to a
>      *     <Control.Panel>, 'type' is used by the panel to determine how
> to
>      *     handle our events.
>      */
>     type: OpenLayers.Control.TYPE_BUTTON,
>
>     /**
>      * APIProperty: title
>      * {String} Print page's title (i18n used).
>      *      Defaults to *'olControlPrintMap.title'*
>      */
>     title: null,
>
>     /**
>      * APIProperty: popupSettings
>      * {String} Attributes of the popup window that will contain the
> printable
>      * page.
>      *      Defaults to
> *'toolbar=no,location=no,directories=no,menubar=no,scrollbars=no'*
>      */
>     popupSettings:
> "toolbar=no,location=no,directories=no,menubar=yes,scrollbars=no",
>
>     /**
>      * APIProperty: cntrlsVisibility
>      * {Object} List of control's class name to hide during the printing
>      * preparation.
>      *      Each object holds :
>      *      * visible : current status of the control to hide;
>      *      * toggleFunc : function to call for hidding the control. This
>      *      function is assumed to have one boolean parameter;
>      *      * scope : context for call 'toggleFunc', if none the map is
>      *      used.
>      */
>     cntrlsVisibility: null,
>
>     /**
>      * APIProperty: onLoad
>      * {String} Javascript code to invoke when loading the popup window.
>      *      Defaults to *"self.print();self.close();"*
>      */
>     onLoad: "self.print();self.close();",
>
>     /**
>      * Constructor: OpenLayers.Control.PrintMap
>      * Build a simple print preview button.
>      *
>      * Parameters:
>      * options - {Object} any options usefull for control.
>      */
>     initialize: function(options) {
>         OpenLayers.Control.prototype.initialize.apply(this, arguments);
>         if (!this.title) {
>             this.title= this.displayClass+'.title';
>         }
>     },
>
>     /**
>      * APIMethod: trigger
>      * Do the print by openning a popup that contains the map's div
> content to
>      * be printed.
>      *      Can be overwritten to modify the printing output.
>      */
>     trigger: function() {
>         // open a new window by copying the inner content of the map's div
> ...
>         // add attributions/originators for each layer on the window.
>         if (!this.map) { return; }
>         // hide controls :
>         for (var cn in this.cntrlsVisibility) {
>             var cntrl= this.map.getControlsByClass(cn)[0];
>             if (cntrl && cntrl.div.style.display!='none') {
>                 this.cntrlsVisibility[cn].visible= true;
>                 this.cntrlsVisibility[cn].toggleFunc.apply(this.cntrlsVisibility[cn].scope
> || this.map,[false])
>             }
>         }
>         var T= this.getPageContent();
>         var printableDocument= null;
>         var settings= this.popupSettings;
>         if (!settings.match(/width/i)) {
>             settings+=",width="+(16+this.map.div.clientWidth);
>         }
>         if (!settings.match(/height/i)) {
>             settings+=",height="+(16+this.map.div.clientHeight);
>         }
>         printableDocument= window.open((T.isUrl? T.html:""),"",settings);
>         if (printableDocument) {
>             if (!T.isUrl) {
>                 printableDocument.document.open();
>                 printableDocument.document.write(T.html);
>                 if (!!document.namespaces) {
>                     for (var i= 0, l= document.namespaces.length; i<l;
> i++) {
>                         var ns= document.namespaces.item(i);
>                         printableDocument.document.namespaces.add(ns.name,
> ns.urn);
>                     }
>                 }
>                 // we return the imported clone of the map's div to
> preserve rendered SVG/VML/...
>                 var mapDiv=
> OpenLayers.Element.importNode(printableDocument.document,this.map.div,true);
>                 if (mapDiv) {
>                     printableDocument.document.getElementById('container_'+this.id).appendChild(mapDiv);
>                 }
>             }
>             printableDocument.document.close();
>         }
>         // show controls :
>         for (var cn in this.cntrlsVisibility) {
>             if (this.cntrlsVisibility[cn].visible===true) {
>                 this.cntrlsVisibility[cn].visible= false;
>                 this.cntrlsVisibility[cn].toggleFunc.apply(this.cntrlsVisibility[cn].scope
> || this.map,[true])
>             }
>         }
>     },
>
>     /**
>      * Method: getStyles
>      * Retrieve CSS rules governing this map.
>      *
>      * Returns:
>      * {String} the CSS links et styles found in the current map.
>      */
>     getStyles: function() {
>         var csses= '';
>         for (var i= 0, li= document.styleSheets.length; i<li; i++) {
>             var css= document.styleSheets.item(i);
>             var ownerNode= css.owningElement || css.ownerNode;
>             if (css.href) { //LINK
>                 csses+=
> '<link '+
>   'rel="stylesheet" '+
>   'type="text/css" '+
>   (ownerNode.id? 'id="'+ownerNode.id+'" ':'')+
>   'href="'+css.href+'"'+
> '/>\n';
>             } else {        //STYLE
>                 csses+=
> '<style type="text/css"'+(ownerNode.id? '
> id="'+ownerNode.id+'"':'')+'>\n'+
> '<!--\n';
>                 var rules= css.rules || css.cssRules;
>                 for (var j= 0, lj= rules.length; j<lj; j++) {
>                     var rule= rules.item(j);
>                     csses+=
> rule.selectorText+'{'+rule.style.cssText+'}\n';
>                 }
>                 csses+=
> '  -->\n'+
> '</style>\n';
>             }
>         }
>         return csses;
>     },
>
>     /**
>      * APIMethod: getPageContent
>      * Build page content or URL to print.
>      *
>      * Returns:
>      * {Object} with an indicator of page content or URL ('isUrl' field),
>      * the HTML code or URL for the page to print ('html' field) and the
> base URL
>      * of the popup window ('base' field).
>      */
>     getPageContent: function() {
>         var base= document.location.pathname.split('?')[0];
>         var parts= base.split('/');
>         base= parts.pop();
>         base= parts.join('/');
>         var baseUrl=
>             document.location.protocol+'//'+
>             document.location.hostname+
>             (document.location.port? ':'+document.location.port : '')+
>             base+'/';
>         var page=
> '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n'+
> '<html xmlns="http://www.w3.org/1999/xhtml">\n'+
>   '<head>\n'+
>     '<title>'+OpenLayers.i18n(this.title)+'</title>\n'+
>     '<meta http-equiv="Content-Type" content="text/html;
> charset=UTF-8"/>\n'+
>     '<base url="'+baseUrl+'"/>\n'+
>     this.getStyles()+
>     '<style type="text/css">\n'+
>     '<!--\n'+
>       '@media print {\n'+
>         'body{display:block!important;}\n'+
>       '}\n'+
>     '  -->\n'+
>     '</style>\n'+
>   '</head>\n'+
>   '<body onload="'+this.onLoad+'">\n'+
>     '<center>\n'+
>       '<div id="container_'+this.id+'"></div>\n'+
>     '</center>\n'+
>   '</body>\n'+
> '</html>\n';
>         return {'isUrl':false, 'html':page, 'base':baseUrl};
>     },
>
>     /**
>      * Constant: CLASS_NAME
>      * {String} *"OpenLayers.Control.PrintMap"*
>      */
>     CLASS_NAME: "OpenLayers.Control.PrintMap"
> });
>
>
> Tests (based on panel.html and kml-layer.html) :
>
> <html xmlns="http://www.w3.org/1999/xhtml">
>   <head>
>     <title>OpenLayers: Control Panel</title>
>     <link rel="stylesheet" href="../theme/default/style.css"
> type="text/css" />
>     <link rel="stylesheet" href="style.css" type="text/css" />
>     <style type="text/css">
>         .olControlPanel div {
>           display:block;
>           width:  24px;
>           height: 24px;
>           margin: 5px;
>           background-color:red;
>         }
>
>         .olControlPanel .olControlMouseDefaultsItemActive {
>           background-color: blue;
>           background-image: url("../theme/default/img/pan_on.png");
>         }
>         .olControlPanel .olControlMouseDefaultsItemInactive {
>           background-color: orange;
>           background-image: url("../theme/default/img/pan_off.png");
>         }
>         .olControlPanel .olControlZoomBoxItemInactive {
>           width:  22px;
>           height: 22px;
>           background-color: orange;
>           background-image: url("../img/drag-rectangle-off.png");
>         }
>         .olControlPanel .olControlZoomBoxItemActive {
>           width:  22px;
>           height: 22px;
>           background-color: blue;
>           background-image: url("../img/drag-rectangle-on.png");
>         }
>         .olControlPanel .olControlZoomToMaxExtentItemInactive {
>           width:  18px;
>           height: 18px;
>           background-image: url("../img/zoom-world-mini.png");
>         }
>         .olControlPanel .olControlPrintMapItemActive,
>         .olControlPanel .olControlPrintMapItemInactive {
>           width: 24px;
>           height: 22px;
>           background-image: url("../theme/default/img/draw_point_on.png");
>         }
>
>     </style>
>     <script src="../lib/Firebug/firebug.js"></script>
>     <script src="../lib/OpenLayers.js"></script>
>
>     <script type="text/javascript">
>         var lon = 5;
>         var lat = 40;
>         var zoom = 5;
>         var map, layer;
>
>         function init(){
>             map = new OpenLayers.Map( 'map', { controls: [] } );
>             layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
>                     "http://labs.metacarta.com/wms/vmap0", {layers:
> 'basic'} );
>             map.addLayer(layer);
>
>             map.addLayer(new OpenLayers.Layer.GML("KML", "kml/lines.kml",
>                {
>                 format: OpenLayers.Format.KML,
>                 formatOptions: {
>                   extractStyles: true,
>                   extractAttributes: true,
>                   maxDepth: 2
>                 }
>                }));
>
>             zb = new OpenLayers.Control.ZoomBox(
>                 {title:"Zoom box: Selecting it you can zoom on an area by
> clicking and dragging."});
>             var panel = new OpenLayers.Control.Panel({defaultControl:
> zb});
>             panel.addControls([
>                 new OpenLayers.Control.MouseDefaults(
>                     {title:'You can use the default mouse
> configuration'}),
>                 zb,
>                 new OpenLayers.Control.PrintMap(
>                     {title:"preview",onLoad:'javascript:void(0);'})
>             ]);
>             map.addControl(panel);
>
>             map.zoomToExtent(new
> OpenLayers.Bounds(-112.306698,36.017792,-112.03204,36.18087));
>         }
>     </script>
>   </head>
>   <body onload="init()">
>     <h1 id="title">Custom Control.Panel</h1>
>     <p id="shortdesc">
>       Create a custom control.panel, styled entirely with
>       CSS, and add your own controls to it.
>     </p>
>     <div id="panel"></div>
>     <div id="map" class="smallmap"></div>
>
>   </body>
> </html>
>
>
>
> In the end, all VML nodes are copied (with all of their attributes), but
> they never show in the popup window !(
>
> Any advice/hint ?
> Did I miss something in the process ?
>
> Regards,
>
> didier
> --
> RICHARD Didier - Chef du pôle technique du Géoportail
> 2/4, avenue Pasteur - 94165 Saint Mandé Cedex
> Tél : +33 (0) 1 43 98 83 23
> _______________________________________________
> Dev mailing list
> Dev at openlayers.org
> http://openlayers.org/mailman/listinfo/dev
>


-- 
RICHARD Didier - Chef du pôle technique du Géoportail
2/4, avenue Pasteur - 94165 Saint Mandé Cedex
Tél : +33 (0) 1 43 98 83 23



More information about the Dev mailing list