This commit is contained in:
Petr Pridal
2012-06-22 14:53:53 +02:00
15 changed files with 1015 additions and 94 deletions

130
src/api/popup.js Normal file
View File

@@ -0,0 +1,130 @@
goog.provide('ol.popup');
goog.require('ol.Popup');
goog.require('ol.map');
/**
* @typedef {ol.Popup|Object} popup
*/
ol.PopupLike;
/**
* @export
* @param {ol.PopupLike} opt_arg popup object literal.
* @return {ol.Popup} the popup.
*/
ol.popup = function(opt_arg){
if (opt_arg instanceof ol.Popup) {
return opt_arg;
}
/** @type {ol.Map} */
var map;
/** @type {ol.Loc|ol.Feature|undefined} */
var anchor;
/** @type {string|undefined} */
var placement;
/** @type {string|undefined} */
var content;
/** @type {string|undefined} */
var template;
if (arguments.length == 1 && goog.isDef(opt_arg)) {
if (goog.isObject(opt_arg)) {
map = opt_arg['map'];
anchor = opt_arg['anchor'];
placement = opt_arg['placement'];
content = opt_arg['content'];
template = opt_arg['template'];
}
}
var popup = new ol.Popup(map, anchor);
if (goog.isDef(anchor)) {
popup.setAnchor(anchor);
}
if (goog.isDef(placement)) {
popup.setPlacement(placement);
}
if (goog.isDef(content)) {
popup.setContent(content);
}
if (goog.isDef(template)) {
popup.setTemplate(template);
}
return popup;
};
/**
* @export
* @param {ol.Loc|ol.Feature=} opt_arg a feature or a location.
* @return {ol.Popup|ol.Feature|ol.Loc|undefined} Result.
*/
ol.Popup.prototype.anchor = function(opt_arg){
if (arguments.length == 1 && goog.isDef(opt_arg)) {
this.setAnchor(opt_arg);
return this;
}
else {
return this.getAnchor();
}
};
/**
* @export
* @param {ol.Map=} opt_arg the map .
* @return {ol.Popup|ol.Map|undefined} the map or the popup.
*/
ol.Popup.prototype.map = function(opt_arg){
if (arguments.length == 1 && goog.isDef(opt_arg)) {
this.setMap(opt_arg);
return this;
}
else {
return this.getMap();
}
};
/**
* @export
* @param {string=} opt_arg the content for the map (HTML makrkup)
* @return {ol.Popup|string|undefined} the content or the popup.
*/
ol.Popup.prototype.content = function(opt_arg){
if (arguments.length == 1 && goog.isDef(opt_arg)) {
this.setContent(opt_arg);
return this;
}
else {
return this.getContent();
}
};
/**
* @export
* @param {string=} opt_arg the template to be used to generate the content
* @return {ol.Popup|string|undefined} the template or the popup.
*/
ol.Popup.prototype.template = function(opt_arg){
if (arguments.length == 1 && goog.isDef(opt_arg)) {
this.setTemplate(opt_arg);
return this;
}
else {
return this.getTemplate();
}
};

View File

@@ -12,6 +12,7 @@ goog.require("ol.feature");
goog.require("ol.projection");
goog.require("ol.layer.xyz");
goog.require("ol.layer.osm");
goog.require("ol.popup");
goog.require("ol.Tile");
goog.require("ol.TileSet");
goog.require("ol.TileCache");

View File

@@ -385,6 +385,25 @@ ol.Map.prototype.moveByPx = function(dx, dy) {
// call moveByPx on renderers
};
/**
* @param {ol.Loc} loc the location being requested
* @returns {Object} the
*/
ol.Map.prototype.getViewportPosition = function(loc) {
//TODO: delegate this to the renderers
//stub for now to get popups working
return {x: 200, y: 300};
};
/**
* @returns {Element} the map overlay element
*/
ol.Map.prototype.getMapOverlay = function() {
//TODO: delegate this to the renderers
//stub for now to get popups working
return this.mapOverlay_
};
/**
* @export
*/

281
src/ol/Popup.js Normal file
View File

@@ -0,0 +1,281 @@
goog.provide('ol.Popup');
goog.require('ol.Map');
goog.require('ol.Loc');
goog.require('ol.Feature');
//goog.require('goog.dom');
//goog.require('goog.style');
/**
* @export
* @constructor
* @param {ol.Map} map the map on which the popup is placed.
* @param {ol.Loc|ol.Feature=} opt_anchor the anchor object for the popup.
* @param {string=} opt_placement the placement of the arrow on the popup.
* @param {boolean=} opt_close include a close button on the popup
*/
ol.Popup = function(map, opt_anchor, opt_placement, opt_close) {
/**
* @private
* @type {ol.Map}
*/
this.map_ = map;
/**
* @private
* @type {ol.Loc|ol.Feature|undefined}
*/
this.anchor_ = opt_anchor;
/**
* can be 'top','bottom','right','left','auto'
* TODO: 'auto' not yet implemented
* @private
* @type {!string}
*/
this.placement_ = goog.isDefAndNotNull(opt_placement)?opt_placement:'top';
/**
* include a close button on the popup - defaults to true.
* @private
* @type {boolean|undefined}
*/
this.closeButton_ = goog.isDefAndNotNull(opt_close) ? opt_close : true;
/**
* @private
* @type {string|undefined}
*/
this.content_ = undefined;
/**
* @private
* @type {string|undefined}
*/
this.template_ = undefined;
/**
* @private
* @type {Element}
*/
this.container_ = null;
/**
* @private
* @type {number}
*/
this.arrowOffset_ = 32; //FIXME: set this from CSS dynamically somehow?
};
/**
* @const
*/
ol.Popup.CLASS_NAME = 'ol-popup';
/**
* @return {ol.Map} Projection.
*/
ol.Popup.prototype.getMap = function() {
return this.map_;
};
/**
* @param {ol.Map} map the map object to hold this popup.
*/
ol.Popup.prototype.setMap = function(map) {
this.map_ = map;
};
/**
* @return {ol.Feature|ol.Loc|undefined} the anchor .
*/
ol.Popup.prototype.getAnchor = function() {
return this.anchor_;
};
/**
* @param {ol.Feature|ol.Loc} anchor the anchor location to place this popup.
*/
ol.Popup.prototype.setAnchor = function(anchor) {
this.anchor_ = anchor;
};
/**
* @return {string|undefined} the placement value relative to the anchor.
*/
ol.Popup.prototype.getPlacement = function() {
return this.placement_;
};
/**
* @param {string} placement where to place this popup relative to the anchor.
*/
ol.Popup.prototype.setPlacement = function(placement) {
if (!goog.isNull(this.container_)) {
goog.dom.classes.remove(this.container_,
ol.Popup.CLASS_NAME+'-'+this.placement_);
goog.dom.classes.add(this.container_,ol.Popup.CLASS_NAME+'-'+placement);
}
this.placement_ = placement;
};
/**
* @return {string|undefined} static content to be displayed in the popup (HTML)
*/
ol.Popup.prototype.getContent = function() {
return this.content_;
};
/**
* @param {string} content the content to be displayed this popup.
*/
ol.Popup.prototype.setContent = function(content) {
this.content_ = content;
};
/**
* @private
* @returns {string} generates the content
*/
ol.Popup.prototype.generateContent_ = function() {
//set the content
if ( goog.isDefAndNotNull(this.content_) ) {
return this.content_;
} else {
if ( goog.isDefAndNotNull(this.template_) &&
(this.anchor_ instanceof ol.Feature)) {
//set content from feature attributes on the template
//TODO: this.setContent(template.apply(this.anchor.getAttributes()));
return this.template_; //stub to return something
} else {
return '<br>';
}
}
};
/**
* @return {string|undefined} the anchor .
*/
ol.Popup.prototype.getTemplate = function() {
return this.template_;
};
/**
* @param {string} template the map object to hold this popup.
*/
ol.Popup.prototype.setTemplate = function(template) {
this.template_ = template;
};
/**
* Open the popup.
* @export
* @param {ol.Feature|ol.Loc} opt_arg feature or location for the anchor
*/
ol.Popup.prototype.open = function(opt_arg) {
if (goog.isDef(opt_arg)) {
this.setAnchor(opt_arg);
}
//create popup container if it's not created already
if (goog.isNull(this.container_)) {
this.container_ = goog.dom.createElement('div');
goog.dom.classes.add(this.container_,
ol.Popup.CLASS_NAME, ol.Popup.CLASS_NAME+'-'+this.placement_);
if (this.closeButton_) {
var closeButton = goog.dom.createElement('div');
goog.dom.appendChild(this.container_, closeButton);
goog.dom.classes.add(closeButton, ol.Popup.CLASS_NAME+'-close');
}
this.map_.getEvents().register('click', this.clickHandler, this);
goog.dom.appendChild(this.map_.getMapOverlay(), this.container_);
}
this.childContent_=goog.dom.htmlToDocumentFragment(this.generateContent_());
goog.dom.appendChild(this.container_, this.childContent_);
//position the element
if (this.anchor_ instanceof ol.Feature) {
this.pos_ = this.anchor_.getGeometry().getCentroid();
} else {
this.pos_ = this.anchor_;
}
var popupPosPx = this.map_.getViewportPosition(this.pos_);
var popupSize = goog.style.getSize(this.container_);
switch(this.placement_) {
default:
case 'auto':
//TODO: switch based on map quadrant
break;
case 'top':
case 'bottom':
popupPosPx.x -= popupSize.width / 2.0;
if (this.placement_ == "bottom") {
popupPosPx.y -= popupSize.height + this.arrowOffset_;
} else {
popupPosPx.y += this.arrowOffset_;
}
break;
case 'left':
case 'right':
popupPosPx.y -= popupSize.height / 2.0;
if (this.placement_ == "right") {
popupPosPx.x -= popupSize.width + this.arrowOffset_;
} else {
popupPosPx.x += this.arrowOffset_;
}
break;
};
this.moveTo_(popupPosPx);
};
/**
* @param px - {goog.} the top and left position of the popup div.
*/
ol.Popup.prototype.moveTo_ = function(px) {
if (goog.isDefAndNotNull(px)) {
goog.style.setPosition(this.container_, px.x, px.y);
}
};
/**
* Click handler
* @param {Event} evt the event generated by a click
*/
ol.Popup.prototype.clickHandler = function(evt) {
var target = /** @type {Node} */ evt.target;
if (goog.dom.classes.has(target,ol.Popup.CLASS_NAME+'-close')) {
this.close();
}
};
/**
* Clean up.
* @export
*/
ol.Popup.prototype.close = function() {
goog.dom.removeChildren(this.container_);
goog.dom.removeNode(this.container_);
};
/**
* Clean up.
* @export
*/
ol.Popup.prototype.destroy = function() {
for (var key in this) {
delete this[key];
}
};

View File

@@ -12,13 +12,19 @@ goog.require('goog.functions');
/**
* @constructor
* @param {Element} target The element that will be dragged.
* @param {ol.event.Events} target The Events instance that handles events.
* @extends {goog.fx.Dragger}
* @implements {ol.event.ISequence}
* @export
*/
ol.event.Drag = function(target) {
goog.base(this, target);
goog.base(this, target.getElement());
/**
* @private
* @type {ol.event.Events}
*/
this.target_ = target;
/**
* @private
@@ -47,6 +53,7 @@ ol.event.Drag.prototype.dispatchEvent = function(e) {
e.type = ol.event.Drag.EventType.DRAGEND;
}
}
this.target_.dispatchEvent(/** @type {Event} */ (e));
return goog.base(this, 'dispatchEvent', e);
};
@@ -67,11 +74,6 @@ ol.event.Drag.prototype.doDrag = function(e, x, y, dragFromScroll) {
/** @override */
ol.event.Drag.prototype.defaultAction = function(x, y) {};
/** @inheritDoc */
ol.event.Drag.prototype.getEventTypes = function() {
return ol.event.Drag.EventType;
};
/** @inheritDoc */
ol.event.Drag.prototype.destroy = ol.event.Drag.prototype.dispose;

View File

@@ -47,7 +47,7 @@ ol.event.isMultiTouch = function(evt) {
* @constructor
* @extends {goog.events.EventTarget}
* @param {Object} object The object we are creating this instance for.
* @param {!EventTarget=} opt_element An optional element that we want to
* @param {!Element=} opt_element An optional element that we want to
* listen to browser events on.
* @param {boolean=} opt_includeXY Should the 'xy' property automatically be
* created for browser pointer events? In general, this should be false. If
@@ -71,7 +71,7 @@ ol.event.Events = function(object, opt_element, opt_includeXY, opt_sequences) {
/**
* @private
* @type {EventTarget}
* @type {Element}
* The element that this instance listens to mouse events on.
*/
this.element_ = null;
@@ -115,7 +115,7 @@ ol.event.Events.prototype.setIncludeXY = function(includeXY) {
};
/**
* @return {EventTarget} The element that this instance currently
* @return {Element} The element that this instance currently
* listens to browser events on.
*/
ol.event.Events.prototype.getElement = function() {
@@ -126,18 +126,17 @@ ol.event.Events.prototype.getElement = function() {
* Attach this instance to a DOM element. When called, all browser events fired
* on the provided element will be relayed by this instance.
*
* @param {EventTarget} element A DOM element to attach
* @param {Element} element A DOM element to attach
* browser events to. If called without this argument, all browser events
* will be detached from the element they are currently attached to.
*/
ol.event.Events.prototype.setElement = function(element) {
var types, t;
var types = goog.events.EventType, t;
if (this.element_) {
types = this.getBrowserEventTypes();
for (t in types) {
// register the event cross-browser
goog.events.unlisten(
this.element_, types[t], this.handleBrowserEvent, false, this
this.element_, types[t], this.dispatchEvent, true, this
);
}
this.destroySequences();
@@ -145,25 +144,21 @@ ol.event.Events.prototype.setElement = function(element) {
}
this.element_ = element || null;
if (goog.isDefAndNotNull(element)) {
this.createSequences(element);
types = this.getBrowserEventTypes();
this.createSequences();
for (t in types) {
// register the event cross-browser
goog.events.listen(
element, types[t], this.handleBrowserEvent, false, this
element, types[t], this.dispatchEvent, true, this
);
}
}
};
/**
* @param {EventTarget} target
*/
ol.event.Events.prototype.createSequences = function(target) {
ol.event.Events.prototype.createSequences = function() {
for (var i=0, ii=this.sequenceProviders_.length; i<ii; ++i) {
this.sequences_.push(
new ol.event.SEQUENCE_PROVIDER_MAP[this.sequenceProviders_[i]](
target
this
)
);
}
@@ -176,18 +171,6 @@ ol.event.Events.prototype.destroySequences = function() {
this.sequences_ = [];
};
/**
* @return {Object.<string, string>}
*/
ol.event.Events.prototype.getBrowserEventTypes = function() {
var types = {};
goog.object.extend(types, goog.events.EventType);
for (var i=this.sequences_.length-1; i>=0; --i) {
goog.object.extend(types, this.sequences_[i].getEventTypes());
}
return types;
};
/**
* Register a listener for an event.
*
@@ -257,39 +240,40 @@ ol.event.Events.prototype.triggerEvent = function(type, opt_evt) {
};
/**
* Basically just a wrapper to the triggerEvent() function, but takes
* care to set a property 'xy' on the event with the current mouse position.
* Basically just a wrapper to the parent's dispatchEvent() function, but takes
* care to set a property 'xy' on the event with the current mouse position and
* normalize clientX and clientY for multi-touch events.
*
* @param {Event} evt
* @param {Event} evt Event object.
* @return {boolean} If anyone called preventDefault on the event object (or
* if any of the handlers returns false this will also return false.
*/
ol.event.Events.prototype.handleBrowserEvent = function(evt) {
ol.event.Events.prototype.dispatchEvent = function(evt) {
var type = evt.type,
listeners = goog.events.getListeners(this.element_, type, false)
.concat(goog.events.getListeners(this.element_, type, true));
if (!listeners || listeners.length === 0) {
if (listeners && listeners.length > 0) {
// noone's listening, bail out
return;
}
// add clientX & clientY to all events - corresponds to average x, y
var touches = evt.touches;
if (touches && touches[0]) {
var x = 0;
var y = 0;
var num = touches.length;
var touch;
for (var i=0; i<num; ++i) {
touch = touches[i];
x += touch.clientX;
y += touch.clientY;
// add clientX & clientY to all events - corresponds to average x, y
var touches = evt.touches;
if (touches && touches[0]) {
var x = 0;
var y = 0;
var num = touches.length;
var touch;
for (var i=0; i<num; ++i) {
touch = touches[i];
x += touch.clientX;
y += touch.clientY;
}
evt.clientX = x / num;
evt.clientY = y / num;
}
if (this.includeXY_) {
evt.xy = goog.style.getRelativePosition(evt, this.element_);
}
evt.clientX = x / num;
evt.clientY = y / num;
}
if (this.includeXY_) {
var element = /** @type {!Element} */ this.element_;
evt.xy = goog.style.getRelativePosition(evt, element);
}
this.dispatchEvent(evt);
return goog.base(this, 'dispatchEvent', evt);
};
/**

View File

@@ -1,19 +1,21 @@
goog.provide('ol.event.ISequence');
/**
* Interface for event sequences
* Interface for event sequences. Event sequences map sequences of native
* browser events to high level events that the sequence provides.
*
* Implementations are expected to call dispatchEvent on the {@code target} to
* to fire their high level events.
*
* Implementations can expect the {@code target}'s {@code getElement()} method
* to return an {Element} at construction time.
*
* @interface
* @param {Element} target The element that will be listened to for browser
* events.
* @param {ol.event.Events} target The Events instance that receives the
* sequence's events.
*/
ol.event.ISequence = function(target) {};
/**
* @return {Object.<string, string>} element
*/
ol.event.ISequence.prototype.getEventTypes = function() {};
/**
* Destroys the sequence
*/

View File

@@ -32,3 +32,12 @@ ol.geom.Geometry.prototype.setBounds = function(bounds) {
this.bounds_ = bounds;
return this;
};
/**
* @returns ol.Loc
*/
ol.geom.Geometry.prototype.getCentroid = function() {
//FIXME: stub only to get popups working
return new ol.Loc(-76,45);
};