Map Extensibility Demo
The Map Extensibility Demo illustrates how to safely create an extensibilty model for your site. This illustration shows how you can define a policy for accessing APIs specific to your site. In this example, imagine you have a map control that you want to let developers interact with and extend. How do you also ensure the third-party developer code cannot destabilize your site or access your user's trusted data.
We created a customized policy that allows Gadgets to access a shared map using a subset of the Virtual Map API.
Each Gadget interacts with the map independently. Points added by one gadget or the site are not visible to the other Gadgets. If the Gadget is removed, all its associated points and event handlers are automatically cleaned up.
Sample Map Gadgets
Simple Click and Pin
Click and Pin Default Shapes
Creating your Gadget
Your Gadget is created as a simple HTML page. We added two simple extensions for defining your policy and obtaining a reference to the map.
Adding the Map Policy
You must explicity request access to the Map APIs. This step allows the site to define its own instantiation policy. For example, in this example, no user approval is necessary to interact with the map. However, another site may choose to require the user to approve access to the Map capability.
Currently, we use the HTML Link element to define policy requirements. This approach is subject to change in the future.
<LINK REL="policy" HREF="map.pol" REQUIRED>
The required attribute specifies whether the Gadget is
usable without the supported policy. To see the impact
of this attribute, try running these Gadgets in the
test host that does not support maps. If REQUIRED is
specified, the Gadget will fail to load. If ommitted,
the Gadget will attempt to load but will generate
errors when attempting to call the Map APIs. To avoid
the errors, you will typically wrap the calls in a try
-catch block. In the future, we will add mechanisms to
query whether the policy was exposed to your Gadget.
Accessing the Shared Map
A reference to the map control can be obtained in one of two ways.
The map policy exposes a new method, GetMapInstance(), which
returns a reference to the map.
We also wired up the VEMap constructor, new VEMap(), to return
a reference to the Map. This simplifies copying and pasting existing sample code
and executing within the sandbox.
Supported Map API
For this illustration we chose to support a subset of the Map API. With additional effort, we could add policies to almost the entire Map SDK. We hope our chose subset provides enough functionality for you to experiment and learn from the demonstration. For more information about the following APIs, we recommend you see the Virtual Earth Map SDK.
Global Methods and Types
- GetMapInstance()
- VEMap
- VEShape
- VEShapeType
- VEPixel
- VELatLong
VEShape
- new VEShape(...)
- SetTitle(...)
- SetDescription(...)
- SetAltitude(...)
- GetID()
- GetTitle()
- GetDescription()
- Show()
- Hide()
VEShapeType
- Pushpin
- Polyline
- Polygon
VELatLong
- new VELatLong(...)
- Altitude
- AltitudeMode
- Latitude
- Longitude
VEMapEvent
- error
- eventName
- mapStyle
- sceneID
- sceneOrientation
- zoomLevel
- birdseyeSceneOrientation
- birdseyeSceneID
- leftMouseButton
- rightMouseButton
- middleMouseButton
- mouseWheelChange
- latLong
- mapX
- mapY
Supported Events
- onclick
- ondoubleclick
- onchangemapstyle
- onchangeview
- onstartpan
- onendpan
- onstartzoom
- onendzoom
- oninitmode
- onerror
VELatLongRectangle
- TopLeftLatLong
- BottomRightLatLong
- TopRightLatLong
- BottomLeftLatLong
VEMapMode
- Mode2D
- Mode3D
VEMapStyle
- Road
- Shaded
- Aerial
- Hybrid
- Oblique
- Birdseye
- BirdseyeHybrid
VEPixel
- new VEPixel(...)
- x
- y
VEMap
- new VEMap(...)
- AddShape(...)
- DeleteShape(...)
- GetMapView(...)
- GetMapMode(...)
- GetMapStyle(...)
- PixelToLatLong(...)
- GetCenter()
- GetZoomLevel()
- HideAllShapeLayers()
- ShowAllShapeLayers()
- DeleteAllShapes()
- AttachEvent(...)
- DetachEvent(...)
- Clear()
The Custom Map Policy
A custom policy is defined for exposing secure access to the map. You can use this pattern to safely expose your own extensibility model to APIs on your site.Below is a fragment of the Map Policy.
$Sandbox.registerPolicy(
{
"__global__": //Window Object
{
"i":
{
"GetMapInstance": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = context.sandbox.immutables.__map__;
context.r.__box__ = { __type__: "VEMap" };
}
},
"g": {
"VEMap": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = VEMap;
context.r.__box__ = { __type__: "VEMap" };
},
"VEShape": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = { __box__: { __type__: "VEShape"} };
},
"VEShapeType": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = { __box__: { __type__: "VEShapeType"} };
},
"VEPixel": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = { __box__: { __type__: "VEPixel"} };
},
"VELatLong": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = { __box__: { __type__: "VELatLong"} };
}
}
},
"VEShape":
{
"c": {
"__default__": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = new VEShape(context.args[0], context.args[1]);
context.r.__box__ = { __type__: "VEShapeInstance", __owner__: context.sandbox.id };
}
}
},
"VEShapeInstance":
{
"i":
{
"SetTitle": $Rule.Scope,
"SetDescription": $Rule.Scope,
"GetAltitude": $Rule.Scope,
"GetID": $Rule.Scope,
"GetTitle": $Rule.Scope,
"GetDescription": $Rule.Scope,
"Show": $Rule.Scope,
"Hide": $Rule.Scope
}
},
"VEShapeType":
{
"g":
{
"Pushpin": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = VEShapeType.Pushpin;
},
"Polyline": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = VEShapeType.Polyline;
},
"Polygon": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = VEShapeType.Polygon;
}
}
},
"VELatLong":
{
"g":
{
"Altitude": $Rule.Allow,
"AltitudeMode": $Rule.Allow,
"Latitude": $Rule.Allow,
"Longitude": $Rule.Allow
},
"c":
{
"__default__": function(context) {
context.invoke = $Rule.Invoke.AUGMENT;
context.r = new VELatLong(context.args[0], context.args[1]);
context.r.__box__ = { __type__: "VELatLong" };
}
}
},
"VEMapEvent":
{
"g":
{
"error": $Rule.Allow,
"eventName": $Rule.Allow,
"mapStyle": $Rule.Allow,
"sceneID": $Rule.Allow,
"sceneOrientation": $Rule.Allow,
"zoomLevel": $Rule.Allow,
"birdseyeSceneOrientation": $Rule.Allow,
"birdseyeSceneID": $Rule.Allow,
"leftMouseButton": $Rule.Allow,
"rightMouseButton": $Rule.Allow,
"middleMouseButton": $Rule.Allow,
"mouseWheelChange": $Rule.Allow,
"latLong": $Rule.Allow,
"mapX": $Rule.Allow,
"mapY": $Rule.Allow,
"elementID": $Rule.Deny // todo - wire up only to objects you create or the entire map
}
},
// ... More Policies
// Constructor (new policy association)
"__initialize__": function(context) {
context.sandbox.immutables.__map__ = { events: {}, instance: GetMapInstance() };
},
// Destructor - clean-up
"__dispose__": function(context) {
var me = context.sandbox.immutables.__map__;
var instance = me.instance;
var layer = me.layer;
var evList = me.events;
if (layer)
me.instance.DeleteShapeLayer(layer);
for (var ev in evList)
if (evList[ev])
instance.DetachEvent(ev, evList[ev].__handler__);
context.sandbox.immutables.__map__ = instance = layer = evList = null;
}
},
"map.pol" // Register the Name
)
}