[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