/**
* Copyright Franson Technology AB, Sweden, 2009
* http://gpsgate.com, http://franson.com
*
* author Fredrik Blomqvist
*
* @module Map
*
*/
/*
* todo: create a MultiIconMaker? specify several images than can either be changed depending on state and/or
* changed based on direction param. (in case user doesn't want to use vector graphics)
*/
var Franson = Franson || {};
/*
* namespace
* //class Franson.Map
* //static
*/
Franson.Map = Franson.Map || {};
/**
* note that you should (in general) not use this to set params but rather
* use a literal. Your literal will then be merged with these defaults.
* (mostly a test to see howto document optionals)
* todo: if ok use this object as default-template in constructor also
* literal
* @class Franson.Map.Marker.options
*/
Franson.Map.Marker.options =
{
/**
* @config title
* @type string
* @default null
*/
title: null,
/**
* @config clickable
* @type boolean
* @default true
*/
clickable: true,
/**
* @config draggable
* @type boolean
* @default false
*/
draggable: false,
hide: false
// ....
};
/**
* basic marker, interface similar to <a href="http://code.google.com/apis/maps/documentation/reference.html#GMarker">GMarker</a> <br />
* (todo: could actually mimic almost all of GMarker interface for convenience)
* todo: make this into a baseclass for inheritance?
* todo: create an isMarkerLike validator? (decide on minimal interface (and create a BaseMarker or IMarker?))
* @param {LatLng} latlng
* @param {Franson.Map.Marker.options} [options] see <a href="Franson.Map.Marker.options.html">default options</a>
* @config title
* @config clickable
* @class Franson.Map.Marker
* @extends Franson.Map.IOverlay
* @constructor
*/
Franson.Map.Marker = function(latlng, options)
{
/**
* @private
* @type LatLng
*/
this._latlng = latlng;
// todo: document
this._options = MochiKit.Base.setdefault(options, {
icon: null, // null means create default icon. string means image url. (todo: hmm, detect html-Image obj? so user can specify click-regions etc)
anchor: { x: 0, y: 0 }, // only really necessary if using bitmap icon (icon = 'url' above) (todo: if using this I guess we could just as well apply it to all icons?)
title: null, // string
hide: false, // initially hidden?
clickable: true, // todo: if using delayed attachment this is not necesary! (though it might be used if we want(not) to get overlays in the surface's click event?)
draggable: false
// color, style etc?
});
/**
* @private
* @type dojox.gfx.Shape
*/
this._icon = this._options.icon;
/**
* @private
*/
this._clickHandler = null;
/**
* @private
*/
this._dragHandlers = null;
/**
* @private
* @type Franson.Map.ILayer
*/
this._layer = null;
/**
* @private
* @type boolean
*/
this._hidden = false;
}; // Franson.Map.Marker
// methods
Franson.Map.Marker.prototype =
{
/**
* destructor
* @method destroy
*/
destroy: function()
{
this.remove();
disconnectAll(this);
this._icon = null;
this._layer = null;
},
/**
* @method getIcon
* @return {dojox.gfx.Shape}
*/
getIcon: function()
{
return this._icon;
},
// setIcon?
/**
* @method getTitle
* @return {string}
*/
getTitle: function()
{
return this._options.title;
},
/**
* @method hide
*/
hide: function()
{
if (!this._hidden)
{
this._hidden = true;
Franson.Graphics.hide(this._icon);
}
// let this always fire
signal(this, 'onvisibilitychanged', false);
},
/**
* @method show
*/
show: function()
{
if (this._hidden)
{
this._hidden = false;
Franson.Graphics.show(this._icon);
}
// let this always fire
signal(this, 'onvisibilitychanged', true);
},
/**
* @method isHidden
* @return {boolean}
*/
isHidden: function()
{
return this._hidden;
// return Franson.Graphics.isHidden(this._icon);
},
/**
* get the position
* @method getLatLng
* @return {LatLng}
*/
getLatLng: function()
{
return this._latlng;
},
/**
* set the position
* @method setLatLng
* @param {LatLng} latlng
*/
setLatLng: function(latlng)
{
this._latlng = latlng;
if (this._layer !== null) // can't issue a redraw if not initialize() has been called
this.redraw(true);
},
// todo: isDraggable etc?
/**
* @method initialize
* @param {Franson.Map.ILayer} layer
* @protected
*/
initialize: function(layer)
{
this._layer = layer;
if (this._options.icon == null)
{
// create default icon
this._options.icon = this._icon = Franson.Graphics.Shape.createPinIcon(null, this._layer.getRoot()); // last param is only for the IE case...
}
else if (typeof(this._options.icon) == 'string')
{
// or skip overwriting options.icon in this case?
this._options.icon = this._icon = Franson.Graphics.Shape.createBitmapIcon(this._options.icon, this._options.anchor, this._layer.getRoot()); // last param is only for the IE case...
}
//this._layer.getRoot().add(this._icon);
if (this._options.title !== null)
Franson.Graphics.setTitle(this._icon, this._options.title);
if (this._options.clickable)
{
Franson.Graphics.setCursor(this._icon, Franson.Util.isIE() ? 'hand' : 'pointer');
this._clickHandler = this._icon.connect('onclick', this, function(e)
{
this._icon.moveToFront(); // ok?
signal(this, 'onclick', this._latlng);
});
}
// todo: expose methods to enable/disable drag-mode also?
if (this._options.draggable)
{
// todo: implement the google-style helper cross below the marker if we want to be fancy.
var moveHandle = Franson.Graphics.enableMoveable(this._icon); // todo: fwd events from moving to be able to update the latlng + support dynamic enabling/disabling of draggable mode
this._dragHandlers = [
dojo.connect(moveHandle, 'onMoveStart', this, function()
{
signal(this, 'ondragstart', this._latlng);
}),
dojo.connect(moveHandle, 'onMove', this, function()
{
var pix = Franson.Graphics.getPosition(this._icon);
var prj = this._layer.getProjection();
var newLatLng = prj.fromDivPixelToLatLng(pix);
this._latlng = newLatLng;
signal(this, 'ondrag', this._latlng);
}),
dojo.connect(moveHandle, 'onMoveStop', this, function()
{
signal(this, 'ondragend', this._latlng);
})
];
}
if (this._options.hide)
this.hide();
},
/**
* @method redraw
* @param {boolean} [force=false]
* @protected
*/
redraw: function(force)
{
if (force && !this._hidden)
{
if (this._layer.getSurface()._getCullBounds().containsLatLng(this._latlng)) // todo: better culling
{
var pos = this._layer.getProjection().fromLatLngToDivPixel(this._latlng);
Franson.Graphics.setPosition(this._icon, pos);
Franson.Graphics.show(this._icon);
}
else
{
Franson.Graphics.hide(this._icon);
}
}
},
/**
* note that you should(must) not call this directly,
* rather use <code>map.removeOverlay(marker)</code> (which will call this)
* see <a href="http://code.google.com/apis/maps/documentation/reference.html#GOverlay.remove">GOverlay.remove()</a>
* @method remove
* @protected
*/
remove: function()
{
if (this._icon)
{
if (this._clickHandler != null)
{
this._icon.disconnect(this._clickHandler);
this._clickHandler = null;
}
if (this._dragHandlers != null)
{
forEach(this._dragHandlers, dojo.disconnect);
this._dragHandlers = null;
}
if (this._options.draggable)
{
Franson.Graphics.disableMoveable(this._icon);
}
this._icon.removeShape();
signal(this, 'onremove');
}
}
}; // Franson.Map.Marker.prototype
/**
* (if options.clickable)
* @event onclick
* @param {LatLng} latlng
*/
/**
* (if options.draggable)
* @event ondragstart
* @param {LatLng} latlng
*/
/**
* (if options.draggable)
* @event ondrag
* @param {LatLng} latlng
*/
/**
* (if options.draggable)
* @event ondragend
* @param {LatLng} latlng
*/
/**
* fired in <a href="#method_show">show()</a> and <a href="#method_hide">hide()</a> <br />
* see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMarker.visibilitychanged">GMarker.visibilitychanged</a>
* @event onvisibilitychanged
* @param {boolean} visible
*/
/**
* fired in <a href="#method_remove">remove()</a>
* @event onremove
*/
// todo: expose more click-events? dblclick etc