Tutoriel d'intégration de produits géolocalisés sur son site Kiubi
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
theme/fr
.gitignore
AUTHORS
LICENSE.md
README.md

README.md

Géolocalisation de produits

Introduction

Ce dépôt est un tutoriel qui explique comment utiliser l'API de son site Kiubi pour afficher une liste de produits sur une carte Google Maps avec la possibilité d'ajouter ces produits au panier directement à partir de la carte.

Prérequis

Ce tutoriel suppose que vous avez un site Kiubi et qu'il est bien configuré :

  • l'API est activée
  • le catalogue et l'ecommerce sont activés
  • le site est en thème personnalisé, basé sur Shiroi

Il est préférable d'être à l'aise avec la manipulation des thèmes personnalisés. En cas de besoin, le guide du designer est là.

Ce tutoriel est applicable à tout thème graphique mais les exemples de codes donnés sont optimisés pour un rendu basé sur le thème Shiroi.

Afficher des produits sur une Google Map

La liste des produits va être remplacée par une carte Google Maps sur laquelle chaque produit sera représenté par un marqueur situé à l'endroit défini sur la fiche produit dans le backoffice.

Un clic sur un marqueur affichera une bulle comportant le nom du produit ainsi que son image principale et une courte description, et si le produit est disponible il sera possible de l'ajouter au panier en un clic.

Mise en place

Modification du type de produit

Copiez le fichier de ce dépôt theme/fr/produits/simple/config.xml ou modifiez le fichier existant dans votre thème graphique pour définir les champs latitude et longitude au produit :

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- 
Configuration du produit "Produit simple"
-->
<!DOCTYPE type SYSTEM "http://www.kiubi-admin.com/DTD/typesproduits.dtd"> 
	<type tri="1">
		<desc>Produit simple</desc>
		<listechamps>
			<champ type="text" champ="texte11" intitule="Latitude"/>
			<champ type="text" champ="texte12" intitule="Longitude"/>
		</listechamps>
	</type>

Inclusions des bibliothèques javascript

Pour réaliser notre carte de produits il nous faut 3 librairies, le client JS Kiubi pour utiliser l’API, l’API Google Maps pour l’affichage de la carte ainsi qu’un plugin jQuery qui nous permettra de faciliter l’intégration des éléments sur la carte.

Le plugin jQuery utilisé est Gmap3 et est distribué sous licence GPL v3. Les sources sont disponibles à l’adresse suivante : http://gmap3.net/ On rajoute l’inclusion des trois fichiers grâce au module Injection de code et on met le code d’inclusion avant la balise </head> :

<script type="text/javascript" src="{cdn}/js/kiubi.api.pfo.jquery-1.0.min.js"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&amp;language=fr"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/gmap3/5.1.1/gmap3.min.js"></script>

Nous utilisons ici un cdn public tiers pour l’inclusion du plugin jQuery, vous pouvez également récupérer le fichier et le déposer dans votre thème puis l’inclure dans vos templates de pages.

Widget liste des produits

Pour ajouter la carte contenant la liste de nos produits dans le widget Liste des produits, on remplace le template theme/fr/widget/catalog/liste_produit/index.html du thème par celui de ce dépôt.

Allez sur la page du catalogue de votre site, une carte s'affiche avec les produits du catalogue chacun représenté par un marqueur sur la carte. Un clic sur un marqueur ouvre une bulle avec les informations sur le produit. Si le produit est disponible, un bouton d'ajout au panier est affiché et un clic sur ce bouton ajoute directement le produit au panier.

Explications

Examinons en détail le code HTML du template liste_produit/index.html :

La fonction showMap() va réaliser l’essentiel du travail en affichant la carte, récupérant les produits pour créer des points géolocalisés, puis permettre l’ajout au panier en un clic. Les premières lignes de cette fonction masque la liste par défaut des produits, et adapte certains styles de la page pour optimiser l’affichage de la carte. On limitera le nombre de marqueurs sur la carte en fonction de la configuration du widget sur le nombre de produits à afficher : {widget_limit}

var showMap = function() {
	var nb_markers = 0, max_markers = parseInt("{widget_limit}");
	$('section.section').css('width', '100%');
	$('#page').css('max-width', '100%');	
	$('#main').data('initfloat', $('#main').css('float'));
	$('#main').css('width', 'auto').css('float', 'none').css('margin', 0);
	$('.header_inner').css('padding', 0);
	$('.copyright').css('margin', 0);
	$('.copyright p').css('display', 'inline-block');
	$('footer').css('padding', 0).css('border', 'none');
	$('header#top .header_inner .menu_header').css('margin', 0).css('border', 'none');
	$('header#top h2').css({margin:0, position: 'absolute', top: '5px', 'font-size': '1.8em'});
	$('#liste-init,.chemin,#sub_header,#top h3,#top form,.sidebar,.logo,.menu_header time,footer .footer_inner,#message').hide();

Le code suivant va créer et afficher la carte Google Maps. Cette carte va prendre 100% de la largeur de la fenêtre du navigateur et va contenir un lien fixe en haut de la carte permettant de revenir à l’affichage par défaut.

$('#gmap').css('width', '100%').css('height', $(document).height()-$('header').height()-$('footer').height()).show().gmap3({
		map:{
			options:{
				zoom:6,
				scrollwheel: false
			}
		},
		panel:{
			options:{
			  content: '<a id="map-backto-list" href="javascript:;">Affichage classique de la liste des produits</a>',
			  bottom: 80,
			  left: 0
			}
		  }
	});	

En fonction de la configuration du widget, nous allons requêter via l’API la liste des produits du catalogue ou de la catégorie sélectionnée, tout en reprenant l’ensemble des paramètres de filtrages définis dans le widget. Une fois la requête terminée, nous ajoutons les points sur la carte avec la méthode addMarkers() puis enfin si les résultats sont paginés et qu’une autre page existe, et que notre nombre de points n’a pas atteint sa limite, on récupère la page suivante de produits.

var xtra = {
	'limit'        : parseInt("{widget_limit}"),
	'sort'         : "{widget_order_api}",
	'available'    : "{widget_dispo}",
	'in_stock'     : "{widget_stock}",
	'tags'         : "{widget_tags}",
	'tags_rule'    : ("{widget_tags_logique}" == "et" ? "and" : "or"),
	'extra_fields' : "texts,thumb,price_label,variants"
};
if('{widget_categorie_id}' == '') {		
	var q = kiubi.catalog.getProducts(xtra);
} else {
	var q = kiubi.catalog.getProductsByCategory(parseInt('{widget_categorie_id}'), xtra);
}
q.done(function(meta, data){
	addMarkers(data);
	if(kiubi.hasNextPage(meta) && nb_markers<max_markers){
		getNextPage(meta);
	}
});

La méthode getNextPage() récupère la page suivante de notre requête de produits et ajoute les points sur la carte. Cette méthode s’appelle elle même s’il y a encore une page suivante dans les résultats et si le nombre de points sur la carte n’a pas atteint le nombre maximal défini.

var getNextPage = function(meta) {
	var d = kiubi.getNextPage(meta);
	d.done(function(meta, data){
		addMarkers(data);
		if(kiubi.hasNextPage(meta) && nb_markers<max_markers){
			getNextPage(meta);
		}
	});
};

La méthode suivante ajoute les points sur la carte. Pour chaque produit, on vérifie s’il est bien géolocalisé, sinon on passe au suivant puis on définit le contenu html de l’infobulle qui sera affiché lors du clic sur le marqueur. Dans l’infobulle on affiche le titre du produit, son image principale, sa courte description et la liste des variantes. Ensuite on ajoute un marqueur sur notre carte en lui précisant sa géolocalisation et son contenu que nous venons de créer. Enfin, on se greffe sur l’événement click du point afin de créer ou réafficher l’infobulle contenant les informations.

var addMarkers = function(data) {
	for(var i=0; i<data.length; i++) {
		if(!data[i].text11 || !data[i].text12 || nb_markers>=max_markers) continue;
		nb_markers++;
		
		var html = '<div style="width:480px;height:320px">';
		html += '<h2>'+data[i].name+'</h2>';
		html += '<img style="float:left;margin-right:10px;" src="'+data[i].main_thumb.url_g_miniature+'"/>';				
		html += '<div>'+data[i].header+'</div><br/>';
		if(data[i].variants.length<2) {
			html += '<p class="prix" style="color: #A6A6A6;font-size: 1.2em;">'+data[i].variants[0].price_inc_vat_label;
			if(data[i].is_discounted)
				html += ' <del style="color: #DF3F52;font-size: 0.8em;">'+data[i].price_base_inc_vat_label+'</del>';
			html += '</p><br/>';
		}
		html += '<select class="p-info" '+(data[i].variants.length<2?' style="display:none;"':'')+'>';
		for(var v=0; v<data[i].variants.length; v++) {
			var va = data[i].variants[v];
			html += '<option value="'+va.id+'" data-available="'+(va.is_available && va.in_stock)+'">'+va.name+' : '+va.price_inc_vat_label+'</option>';
		}
		html += '</select>';
		html += '</div>';
		$('#gmap').gmap3({
			marker:{
				values:[
				  {latLng:[data[i].text11, data[i].text12], data:html},				  
				],
				events:{
					click: function(marker, event, context){
					var map = $(this).gmap3("get"),
					infowindow = $(this).gmap3({get:{name:"infowindow"}});
					if (infowindow){
					infowindow.open(map, marker);
						infowindow.setContent(context.data);
					} else {
						$(this).gmap3({
							infowindow:{
							anchor:marker,
							options:{content: context.data},
							events:{
								domready: function(){
									$(this).find('.p-nfo').change();
									}
								}
							}
						});
					}
					$('.p-info').change();
				}
			}				
		});
	}
};

Le code suivant se greffe sur l’événement de changement de valeur des listes de variantes dans les infobulles. On repère les éléments avec leur classe css p-info Si la variante sélectionnée est disponible, on ajoute un bouton d’ajout au panier, sinon on indique que l’article n’est pas disponible.

$(document).on('change', '.p-info', function(){
	$(this).parent().find('input,p:not(.prix)').remove();
	if($(this).find(':selected').data('available')==true) {
		$(this).after('<input type="submit" class="bouton map-addtocart" value="Ajouter au panier"/>');
	} else {
		$(this).after('<p class="alerte">Article non disponible</p>');
	}
});

Le code suivant se greffe sur l’événement click des boutons ajouter au panier dans les infobulles. On repère les éléments avec leur classe css map-addtocart On vérifie si la variante est bien disponible avant de l’ajouter au panier, puis on appelle l’API pour effectuer l’opération : kiubi.cart.addItem() Si le produit a correctement été ajouté au panier, la méthode done() est appelé, elle affiche un message à l’utilisateur, puis met à jour le nombre de produits et le montant du panier dans l’encart en haut à droite de la page. En cas d’erreur à l’ajout, la méthode fail() est appelée et affiche le message d’erreur à l’utilisateur.

$(document).on('click', '.map-addtocart', function(){
	$(this).parent().find('p:not(.prix)').remove();
	if($(this).prev('select').find(':selected').data('available')==true) {
		var me = $(this);
		var c = kiubi.cart.addItem($(this).prev('select').val(), '+1', null, {extra_fields: 'price_label'});
		c.done(function(meta, data){
			me.after('<p class="alerte">Produit ajouté au panier !</p>');
			$('.panier_link div').remove();
			$('.panier_link a').html(data.cart.items_count+' article'+(data.cart.items_count>1?'s':'')+' - '+data.cart.price_total_inc_vat_label);
		});
		c.fail(function(meta, error, data){
			me.after('<p class="erreur">'+error.message+'</p>');
		});
	}
});

Le code suivant est appelé au clic du lien statique sur la carte pour revenir à la liste normale. Cela supprime la carte, rétablit les styles de la page qui avaient été modifiés pour la carte et réaffiche la liste des produits.

$(document).on('click', '#map-backto-list', function(){
	$('#gmap').gmap3('destroy').hide();
	$('section.section').css('width', '980px');
	$('#page').css('max-width', '1060px');	
	$('#main').css('float', $('#main').data('initfloat'));
	$('#main').css('width', '').css('margin-bottom', '30px');
	$('.copyright').css('margin', '0 auto 20px 0');
	$('.copyright p').css('display', 'block');
	$('footer').css('padding', '20px 0').css('border-top', '1px solid #CCCCCC');
	$('header#top .header_inner .menu_header').css('margin', '0 0 40px').css('border-bottom', '1px solid #DDDDDD');
	$('header#top h2').css({margin: '15px 0', position: 'relative', top: '', 'font-size': '3em'});
	$('.header_inner').css('padding', '0 0 40px 0');
	$('#liste-init,.chemin,#sub_header,#top h3,#top form,.sidebar,.logo,.menu_header time,footer .footer_inner,#message').show();
});

Au chargement de la page, on masque la liste des produits, on ajoute un lien pour relancer l’affichage de la carte dans le cas où on revient à l’affichage par défaut, puis on affiche la carte en appelant showMap(). On se greffe également sur l'événement resize afin de réactualiser la carte et garder notre hauteur dynamique.

$(function(){
	$('#liste-init').hide();
	$('article.produits > p:first').append(' | <a href="javascript:showMap();">Afficher les produits sur une carte</a>');
	showMap();
	$(window).resize(function(){
		$('#gmap').gmap3('destroy').hide();
		showMap();
	});
});

Et voici les seules modifications html du template d’origine. On ajoute un conteneur pour la carte, et on englobe le reste du template par défaut dans un autre conteneur qui permet de masquer la liste lorsque la carte est affichée.

<div id="gmap"></div>
<div id="liste-init">
...
</div>

Exemple complet

<!-- 
Template du Widget "Liste des produits"
-->

<!-- BEGIN: main -->
<style>
section #gmap img { max-width: none; }
</style>
<script type="text/javascript">
var showMap = function() {
	var nb_markers = 0, max_markers = parseInt("{widget_limit}");
	$('section.section').css('width', '100%');
	$('#page').css('max-width', '100%');	
	$('#main').data('initfloat', $('#main').css('float'));
	$('#main').css('width', 'auto').css('float', 'none').css('margin', 0);
	$('.header_inner').css('padding', 0);
	$('.copyright').css('margin', 0);
	$('.copyright p').css('display', 'inline-block');
	$('footer').css('padding', 0).css('border', 'none');
	$('header#top .header_inner .menu_header').css('margin', 0).css('border', 'none');
	$('header#top h2').css({margin:0, position: 'absolute', top: '5px', 'font-size': '1.8em'});
	$('#liste-init,.chemin,#sub_header,#top h3,#top form,.sidebar,.logo,.menu_header time,footer .footer_inner,#message').hide();
	$('#gmap').css('width', '100%').css('height', $(document).height()-$('header').height()-$('footer').height()).show().gmap3({
		map:{
			options:{
				zoom:6,
				scrollwheel: false
			}
		},
		panel:{
			options:{
			  content: '<a id="map-backto-list" href="javascript:;">Affichage classique de la liste des produits</a>',
			  bottom: 80,
			  left: 0
			}
		  }
	});	
	$('#map-backto-list').css({'background-color':'#fff', color:'#000', padding:'10px 15px' }).parent().css('margin-top', '30px');
	var xtra = {
		'limit'        : parseInt("{widget_limit}"),
		'sort'         : "{widget_order_api}",
		'available'    : "{widget_dispo}",
		'in_stock'     : "{widget_stock}",
		'tags'         : "{widget_tags}",
		'tags_rule'    : ("{widget_tags_logique}" == "et" ? "and" : "or"),
		'extra_fields' : "texts,thumb,price_label,variants"
	};
	if('{widget_categorie_id}' == '') {		
		var q = kiubi.catalog.getProducts(xtra);
	} else {
		var q = kiubi.catalog.getProductsByCategory(parseInt('{widget_categorie_id}'), xtra);
	}
	q.done(function(meta, data){
		addMarkers(data);
		if(kiubi.hasNextPage(meta) && nb_markers<max_markers){
			getNextPage(meta);
		}
	});
	var getNextPage = function(meta) {
		var d = kiubi.getNextPage(meta);
		d.done(function(meta, data){
			addMarkers(data);
			if(kiubi.hasNextPage(meta) && nb_markers<max_markers){
				getNextPage(meta);
			}
		});
	};
	var addMarkers = function(data) {
		for(var i=0; i<data.length; i++) {
			if(!data[i].text11 || !data[i].text12 || nb_markers>=max_markers) continue;
			nb_markers++;
			
			var html = '<div style="width:480px;height:320px">';
			html += '<h2>'+data[i].name+'</h2>';
			html += '<img style="float:left;margin-right:10px;" src="'+data[i].main_thumb.url_g_miniature+'"/>';				
			html += '<div>'+data[i].header+'</div><br/>';
			if(data[i].variants.length<2) {
				html += '<p class="prix" style="color: #A6A6A6;font-size: 1.2em;">'+data[i].variants[0].price_inc_vat_label;
				if(data[i].is_discounted)
					html += ' <del style="color: #DF3F52;font-size: 0.8em;">'+data[i].price_base_inc_vat_label+'</del>';
				html += '</p><br/>';
			}
			html += '<select class="p-info" '+(data[i].variants.length<2?' style="display:none;"':'')+'>';
			for(var v=0; v<data[i].variants.length; v++) {
				var va = data[i].variants[v];
				html += '<option value="'+va.id+'" data-available="'+(va.is_available && va.in_stock)+'">'+va.name+' : '+va.price_inc_vat_label+'</option>';
			}
			html += '</select>';
			html += '</div>';
			$('#gmap').gmap3({
				marker:{
					values:[
					  {latLng:[data[i].text11, data[i].text12], data:html}					  
					],
					events:{
						click: function(marker, event, context){
						  var map = $(this).gmap3("get"),
							infowindow = $(this).gmap3({get:{name:"infowindow"}});
						  if (infowindow){
							infowindow.open(map, marker);
							infowindow.setContent(context.data);
						  } else {
							$(this).gmap3({
							  infowindow:{
								anchor:marker,
								options:{content: context.data},
								events:{
									domready: function(){
										$(this).find('.p-info').change();
									}
								}
							  }
							});
						  }
						  $('.p-info').change();
						}
					}
				}
			});
		}
	};		
};
$(document).on('change', '.p-info', function(){
	$(this).parent().find('input,p:not(.prix)').remove();
	if($(this).find(':selected').data('available')==true) {
		$(this).after('<input type="submit" class="bouton map-addtocart" value="Ajouter au panier"/>');
	} else {
		$(this).after('<p class="alerte">Article non disponible</p>');
	}
});

$(document).on('click', '.map-addtocart', function(){
	$(this).parent().find('p:not(.prix)').remove();
	if($(this).prev('select').find(':selected').data('available')==true) {
		var me = $(this);
		var c = kiubi.cart.addItem($(this).prev('select').val(), '+1', null, {extra_fields: 'price_label'});
		c.done(function(meta, data){
			me.after('<p class="alerte">Produit ajouté au panier !</p>');
			$('.panier_link div').remove();
			$('.panier_link a').html(data.cart.items_count+' article'+(data.cart.items_count>1?'s':'')+' - '+data.cart.price_total_inc_vat_label);
		});
		c.fail(function(meta, error, data){
			me.after('<p class="erreur">'+error.message+'</p>');
		});
	}
});

$(document).on('click', '#map-backto-list', function(){
	$('#gmap').gmap3('destroy').hide();
	$('section.section').css('width', '980px');
	$('#page').css('max-width', '1060px');	
	$('#main').css('float', $('#main').data('initfloat'));
	$('#main').css('width', '').css('margin-bottom', '30px');
	$('.copyright').css('margin', '0 auto 20px 0');
	$('.copyright p').css('display', 'block');
	$('footer').css('padding', '20px 0').css('border-top', '1px solid #CCCCCC');
	$('header#top .header_inner .menu_header').css('margin', '0 0 40px').css('border-bottom', '1px solid #DDDDDD');
	$('header#top h2').css({margin: '15px 0', position: 'relative', top: '', 'font-size': '3em'});
	$('.header_inner').css('padding', '0 0 40px 0');
	$('#liste-init,.chemin,#sub_header,#top h3,#top form,.sidebar,.logo,.menu_header time,footer .footer_inner,#message').show();
});

$(function(){
	$('#liste-init').hide();
	$('article.produits > p:first').append(' | <a href="javascript:showMap();">Afficher les produits sur une carte</a>');
	showMap();	
	$(window).resize(function(){
		$('#gmap').gmap3('destroy').hide();
		showMap();
	});
});
</script>
<div id="gmap"></div>
<div id="liste-init">
<article class="produits">
  <!-- BEGIN:intitule -->
  <header class="post_header">
    <h1>{intitule}</h1>
  </header>
  <!-- END:intitule -->
  <p><a href="{lien_affichage}=l">Affichage en liste</a> | <a href="{lien_affichage}=v">Affichage en vignette</a></p>
  <div class="tri">
    <select name="select" id="select" onchange="window.location.href=this.options[this.selectedIndex].value">
      <option value="" >Trier par...</option>
      <option value="{lien_tri}=po">Produit</option>
      <option value="{lien_tri}=pi">Prix</option>
      <option value="{lien_tri}=n">Note</option>
      <option value="{lien_tri}=d">Date de disponibilité</option>
    </select>
  </div>
  {liste_produits}
</article>
  <!-- BEGIN: nav2 -->
  <div class="nav">
    <!-- BEGIN: premier -->
    <a href="{lien_premier}" title="première page">première page</a>
    <!-- END: premier -->
    <!-- BEGIN: precedent -->
    <a href="{lien_precedent}" title="page précédente">page précédente</a>
    <!-- END: precedent -->
    <!-- BEGIN: pages -->
    <a href="{lien_page}" class="{selected}">{page}</a>
    <!-- END: pages -->
    <!-- BEGIN: suivant -->
    <a href="{lien_suivant}" title="page suivante">page suivante</a>
    <!-- END: suivant -->
    <!-- BEGIN: dernier -->
    <a href="{lien_dernier}" title="dernière page">dernière page</a>
    <!-- END: dernier -->
  </div>
  <!-- END: nav2 -->
</div>
<!-- END: main -->

Afficher les produits du panier sur une Google Map

Au dessus de la liste des produits du panier, une carte Google Maps est affichée. Elle comporte un marqueur pour chaque produit du panier. Le titre, l'image et la description du produit sont affichés dans une infobulle lors d'un clic sur chaque marqueur.

Mise en place

Dans la page du détail panier nous allons afficher une carte avec les produits présents dans le panier. Pour ce faire nous allons modifier le template du widget détail panier : theme/fr/widget/commandes/panier_detail/index.html avec celui présent dans ce dépôt.

Ajoutez un ou plusieurs produits dans votre panier, la page du détail panier comporte une carte avec autant de marqueurs que de produits dans le panier. Chaque marqueur peut être cliquer et une infobulle s'affiche avec les informations du produit.

Explications

Au chargement de la page nous récupérons le contenu du panier via l'API, puis affichons une carte Google Maps

$(function(){
	kiubi.cart.get().done(function(meta, data){
		$('#gmap').css('width', '100%').css('height', '300px').show().gmap3({
			map:{
				options:{
					zoom:5,
					scrollwheel: false
				}
			}		
		});

Nous parcourons ensuite chaque item du panier et récupérons le détail produit via l'API afin de connaitre sa latitude et longitude via les champs text11 et text12, si le produit n'est pas géolocalisé nous n'en tenons pas compte :

for(var i=0; i<data.items.length; i++) {
	kiubi.catalog.getProduct(data.items[i].product_id, {extra_fields:'texts'}).done(function(meta, data){
		if(!data.text11 || !data.text12) return;

Enfin, on construit chaque point sur la carte avec son infobulle contenant son nom, son image et sa courte description :

var html = '<div style="width:360px;height:150px">';
html += '<a href="'+data.url+'"><h2>'+data.name+'</h2></a>';
html += '<img style="float:left;margin-right:10px;" src="'+data.main_thumb.url_miniature+'"/>';
html += '<div>'+data.header+'</div><br/>';
html += '</div>';
$('#gmap').gmap3({
	marker:{
		values:[
		  {latLng:[data.text11, data.text12], data:html}		],
	events:{
		click: function(marker, event, context){
					var map = $(this).gmap3("get"),
					infowindow = $(this).gmap3({get:{name:"infowindow"}});
					if (infowindow){
						infowindow.open(map, marker);
						infowindow.setContent(context.data);
					} else {
						$(this).gmap3({
					    infowindow:{
							anchor:marker,
							options:{content: context.data}
						}
					});
				}
		}
	}
}

Enfin, on ajoute le conteneur html pour notre carte :

<div id="gmap"></div>

Exemple complet

<!-- 
Template du Widget "Détail du panier"
-->
<!-- BEGIN:main -->
<style>
section #gmap img { max-width: none; }
</style>
<ul class="chemin_commande">
  <li><a href="{baseLangue}/ecommerce/panier.html" title="Panier" class="actif">1. Panier</a></li>
  <li>2. Authentification</li>
  <li>3. Livraison</li>
  <li>4. Mode de paiement</li>
  <li>5. Validation</li>
</ul>
<article class="panier_detail">
  <!-- BEGIN:intitule -->
  <h1>{intitule}</h1>
  <!-- END:intitule -->
  <script type="text/javascript">
	$(function(){
		kiubi.cart.get().done(function(meta, data){
			$('#gmap').css('width', '100%').css('height', '300px').show().gmap3({
				map:{
					options:{
						zoom:5,
						scrollwheel: false
					}
				}		
			});
			for(var i=0; i<data.items.length; i++) {
				kiubi.catalog.getProduct(data.items[i].product_id, {extra_fields:'texts'}).done(function(meta, data){						
					if(!data.text11 || !data.text12) return;

					var html = '<div style="width:360px;height:150px">';
					html += '<a href="'+data.url+'"><h2>'+data.name+'</h2></a>';
					html += '<img style="float:left;margin-right:10px;" src="'+data.main_thumb.url_miniature+'"/>';	
					html += '<div>'+data.header+'</div><br/>';
					html += '</div>';
					$('#gmap').gmap3({
						marker:{
							values:[
							  {latLng:[data.text11, data.text12], data:html}					  
							],
							events:{
								click: function(marker, event, context){
								  var map = $(this).gmap3("get"),
									infowindow = $(this).gmap3({get:{name:"infowindow"}});
								  if (infowindow){
									infowindow.open(map, marker);
									infowindow.setContent(context.data);
								  } else {
									$(this).gmap3({
									  infowindow:{
										anchor:marker,
										options:{content: context.data}
									  }
									});
								  }
								}
							}
						}
					});
				});
			}
		});
	});
	</script>
	<div id="gmap"></div>
  <!-- BEGIN:produits -->
  <form method="post" action="">
    <table class="table_commande">
      <tr>
        <th colspan="2" scope="col" style="width: 100%; text-align: left;">{nb_produits} article{pluriel_produits}</th>
        <th nowrap="nowrap" scope="col">Prix</th>
        <th scope="col" style="text-align: center;">Qte</th>
        <th nowrap="nowrap" scope="col">Total</th>
        <th scope="col" class="produit_supp"> </th>
      </tr>
      <!-- BEGIN:produit -->
      <tr>
        <td style="width: {miniature_l}px;" class="produit_illustration"><!-- BEGIN:illustration -->
          <figure><a href="{lien_produit}" title="{intitule_produit|htmlentities} - Cliquez pour accéder"><img src="{racine}/media/miniature/{illustration}" alt="{intitule_produit|htmlentities}" class="illustration" /></a></figure>
          <!-- END:illustration -->
          <!-- BEGIN: noillustration -->
          <figure><a href="{lien_produit}" title="{intitule_produit|htmlentities} - Cliquez pour accéder"><img src="{racine}/{theme}/fr/images/produit_mini.gif" alt="{intitule_produit|htmlentities}" class="illustration" style="width: {miniature_l}px; height: {miniature_h}px;" /></a></figure>
          <!-- END: noillustration --></td>
        <td class="produit_info"><strong><a href="{lien_produit}">{intitule_produit}</a></strong><br />
          <!-- BEGIN:reference -->
          <p class="ref">Ref. : {reference}</p>
          <!-- END:reference -->
          <p class="variante">{intitule_variante}</p>
          <!-- BEGIN:accroche -->
          <p class="desc">{accroche}</p>
          <!-- END:accroche --></td>
        <td align="right" class="produit_pu">{prix_unitaire}</td>
        <td nowrap="nowrap" class="produit_qte"><input name="qt[{ref}]" type="text" class="textfield" value="{qt}" style="width:30px" />
          <!-- BEGIN:erreur_qt -->
          <div class="erreurs">max. : {max}</div>
          <!-- END:erreur_qt --></td>
        <td align="right" class="produit_tt">{prix_total}</td>
        <td class="produit_supp"><a href="{lien_supprimer}" title="Supprimer du panier">Supprimer</a></td>
      </tr>
      <!-- END:produit -->
      <!-- BEGIN:option -->
      <tr>
		<!-- BEGIN:simple -->
        <td colspan="2" style="text-align: left;">{intitule_option} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
		<!-- BEGIN:offerte -->
		<td> </td>
		<td style="text-align: center;">{qt}</td>
		<td>Offert</td>
		<!-- END:offerte -->
		<!-- BEGIN:payante -->
		<td>{prix_unitaire}</td>
		<td style="text-align: center;">{qt}</td>
        <td>{prix_total}</td>
		<!-- END:payante -->
		<!-- END:simple -->
		<!-- BEGIN:textarea -->
        <td colspan="2" style="text-align: left;">{intitule_option} : {valeur_option} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
		<!-- BEGIN:offerte -->
		<td> </td>
		<td style="text-align: center;">{qt}</td>
		<td>Offert</td>
		<!-- END:offerte -->
		<!-- BEGIN:payante -->
		<td>{prix_unitaire}</td>
		<td style="text-align: center;">{qt}</td>
        <td>{prix_total}</td>
		<!-- END:payante -->
		<!-- END:textarea -->
		<!-- BEGIN:select -->
        <td colspan="2" style="text-align: left;">{intitule_option} : {valeur_option} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
		<!-- BEGIN:offerte -->
		<td> </td>
		<td style="text-align: center;">{qt}</td>
		<td>Offert</td>
		<!-- END:offerte -->
		<!-- BEGIN:payante -->
		<td>{prix_unitaire}</td>
		<td style="text-align: center;">{qt}</td>
        <td>{prix_total}</td>
		<!-- END:payante -->
		<!-- END:select -->
        <td class="produit_supp"><a href="{lien_supprimer}" title="Supprimer du panier">Supprimer</a></td>
      </tr>
      <!-- END:option -->
      <!-- BEGIN:bon -->
      <tr>
        <td colspan="4" style="text-align: left;">{intitule_produit} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
        <td class="produit_tt">{prix_total}</td>
        <td class="produit_supp"><a href="{lien_supprimer}" title="Supprimer du panier">Supprimer</a></td>
      </tr>
      <!-- END:bon -->
      <!-- BEGIN:remise -->
      <tr>
        <td colspan="4" style="text-align: left;">{intitule_remise} <a href="{lien_supprimer}" title="Supprimer du panier" class="bt_supp">Supprimer</a></td>
        <td align="right"> </td>
        <td class="produit_supp"><a href="{lien_supprimer}" title="Supprimer du panier">Supprimer</a></td>
      </tr>
      <!-- END:remise -->
      <tr class="tva">
        <td colspan="4" style="text-align: left;"><strong>Total hors frais de livraison</strong></td>
        <td align="right"><strong>{total_articles}</strong></td>
        <td class="produit_supp"> </td>
      </tr>
      <!-- BEGIN:livrable -->
      <tr>
        <td colspan="4" style="text-align: left;">Frais de port applicables pour les livraisons vers : {zone_livraison}</td>
        <td align="right">{frais_port}</td>
        <td class="produit_supp"> </td>
      </tr>
      <!-- END:livrable -->
      <!-- BEGIN:nonlivrable -->
      <tr class="tva">
        <td colspan="4" style="text-align: left;">La commande dépasse le poids autorisé pour une livraison vers : {zone_livraison}</td>
        <td align="right">Non livrable</td>
        <td class="produit_supp"> </td>
      </tr>
      <!-- END:nonlivrable -->
      <!-- BEGIN:commande_HT -->
      <tr>
        <td colspan="4" style="text-align: left;"><strong>Total HT</strong></td>
        <td align="right"><strong>{total_HT}</strong></td>
        <td class="produit_supp"> </td>
      </tr>
      <tr>
        <td colspan="4" style="text-align: left;">TVA</td>
        <td align="right">{total_TVA}</td>
        <td class="produit_supp"> </td>
      </tr>
      <!-- END:commande_HT -->
      <tr class="ttc">
        <td colspan="5" class="total" style="text-align: left;"><span style="float: right;">{total_TTC}</span> Total TTC</td>
        <td class="total produit_supp"> </td>
      </tr>
    </table>
    <!-- BEGIN:options_disponibles -->
    <div class="option_commande">
      <h2>Envie d'une petite option ?</h2>
      <table>
        <!-- BEGIN:option -->
        <tr class="{type_option}">
          <!-- BEGIN:simple -->
          <td><label>
              <input name="options[]" value="{ref_option}" type="checkbox"/>
              {intitule_option}</label>
            <div class="option_desc">{description_option}</div></td>
          <!-- BEGIN:offerte -->
          <td>Offert</td>
          <!-- END:offerte -->
          <!-- BEGIN:payante -->
          <td>{prix_option}</td>
          <!-- END:payante -->
          <!-- END:simple -->
          <!-- BEGIN:textarea -->
          <td><label>
              <input name="options[]" value="{ref_option}" type="checkbox"/>
              {intitule_option}</label>
            <div class="option_desc">{description_option}</div>
            <textarea name="options_valeurs[{ref_option}]" rows="5">{valeur_option}</textarea></td>
          <!-- BEGIN:offerte -->
          <td>Offert</td>
          <!-- END:offerte -->
          <!-- BEGIN:payante -->
          <td>{prix_option}</td>
          <!-- END:payante -->
          <!-- END:textarea -->
          <!-- BEGIN:select -->
          <td><label>
              <input name="options[]" value="{ref_option}" type="checkbox"/>
              {intitule_option}</label>
            <div class="option_desc">{description_option}</div>
            <select name="options_valeurs[{ref_option}]">
              <!-- BEGIN:choix -->
              <option value="{valeur}" {selected}>{valeur}</option>
              <!-- END:choix -->
            </select></td>
          <!-- BEGIN:offerte -->
          <td>Offert</td>
          <!-- END:offerte -->
          <!-- BEGIN:payante -->
          <td>{prix_option}</td>
          <!-- END:payante -->
          <!-- END:select -->
          <td class="option_supp"> </td>
        </tr>
        <!-- END:option -->
      </table>
    </div>
    <!-- END:options_disponibles -->
    <div class="bon_reduction">
      <h2> Vous bénéficiez d'un bon de réduction ? </h2>
      <!-- BEGIN:erreur_bon -->
      <div class="erreurs">{erreur}</div>
      <!-- END:erreur_bon -->
      <label for="reduction">Renseignez le code de votre bon de réduction :</label>
      <input type="text" name="bon" value="{bon}" class="textfield" id="reduction" style="width: 40%;"  />
    </div>
    <p>
      <input type="submit" name="bt_commande" value="Commander" class="submit" title="Commander"/>
      <input type="submit" name="bt_recalcul" value="Recalculer" class="submit reset" title="Recalculer"/>
    </p>
    <input type="hidden" name="act" value="refresh" />
    <input type="hidden" name="ctl" value="{ctl}" />
    <p class="a_right"><a href="{lien_continuer}" class="bt_achats" title="Continuez vos achats">Continuez vos achats</a></p>
  </form>
  <!-- END:produits -->
  <!-- BEGIN:noproduits -->
  <p class="panier_vide">Votre panier est vide. <a href="/" title="Continuez vos achats">Retournez à l'accueil</a></p>
  <!-- END:noproduits -->
</article>
<!-- END:main -->