ember-cli-g-maps
Advanced tools
Comparing version 0.0.12 to 0.0.13
/* globals GMaps: true, google: true */ | ||
import Ember from 'ember'; | ||
import gmapProtoExt from 'ember-cli-g-maps/utils/g-maps/prototype-ext'; | ||
import GMapMarkers from 'ember-cli-g-maps/mixins/g-maps/markers'; | ||
import GMapPolygons from 'ember-cli-g-maps/mixins/g-maps/polygons'; | ||
import GMapCircles from 'ember-cli-g-maps/mixins/g-maps/circles'; | ||
import Ember from 'ember'; | ||
import GMapMarkers from 'ember-cli-g-maps/mixins/g-maps/markers'; | ||
import GMapPolygons from 'ember-cli-g-maps/mixins/g-maps/polygons'; | ||
import GMapCircles from 'ember-cli-g-maps/mixins/g-maps/circles'; | ||
import GMapPolylines from 'ember-cli-g-maps/mixins/g-maps/polylines'; | ||
@@ -11,9 +11,3 @@ const { on, merge, uuid, computed, observer } = Ember; | ||
////////////////////////////////// | ||
// Extend the Prototype of GMaps | ||
///////////////////////////////// | ||
gmapProtoExt(); | ||
//////////////////////////////// | ||
export default Ember.Component.extend(Ember.Evented, GMapMarkers, GMapPolygons, GMapCircles, { | ||
export default Ember.Component.extend(Ember.Evented, GMapMarkers, GMapPolygons, GMapCircles, GMapPolylines, { | ||
map: null, | ||
@@ -26,3 +20,2 @@ name: null, | ||
// Map Events | ||
@@ -60,7 +53,7 @@ _gmapEvents: [ | ||
insertGMap: on('didInsertElement', function() { | ||
const events = this.get('_gmapEvents'); | ||
const events = this.get('_gmapEvents'); | ||
const configProps = ['lat', 'lng', 'zoom']; | ||
let config = this.getProperties.apply(this, configProps); | ||
config = merge(this._requiredProperties, config); | ||
config.div = `#${this.element.id}`; | ||
let config = this.getProperties.apply(this, configProps); | ||
config = merge(this._requiredProperties, config); | ||
config.div = `#${this.element.id}`; | ||
@@ -75,2 +68,4 @@ const map = new GMaps( config ); | ||
if( !this.get(events[i]) ) { continue; } | ||
// Add GMaps event listener on google map instance | ||
GMaps.on(events[i], map.map, (e) => this.send(events[i], e)); | ||
@@ -106,2 +101,7 @@ } | ||
this.get('gMap').maps.remove(this.get('name')); | ||
// Run after Mixin willDestroyElement | ||
Ember.run.later(() => { | ||
this.get('map').destroy(); | ||
}); | ||
}), | ||
@@ -108,0 +108,0 @@ |
@@ -12,2 +12,3 @@ import Ember from 'ember'; | ||
/* Supported: | ||
props: [ | ||
@@ -44,2 +45,3 @@ 'lat', | ||
], | ||
*/ | ||
@@ -46,0 +48,0 @@ validate: function() { |
@@ -12,2 +12,3 @@ import Ember from 'ember'; | ||
/* Supported: | ||
props: [ | ||
@@ -61,2 +62,3 @@ 'lat', | ||
], | ||
*/ | ||
@@ -83,4 +85,3 @@ validate: function() { | ||
// If marker has visible window, trigger open | ||
if(!m.hasInfoWindow && m.infoWindow && m.infoWindow.visible) { | ||
m.hasInfoWindow = true; | ||
if(marker.infoWindow && marker.infoWindow.visible) { | ||
marker.infoWindow.addListener('closeclick', function() { | ||
@@ -87,0 +88,0 @@ marker.infoWindow.set('visible', false); |
import Ember from 'ember'; | ||
import childCollection from 'ember-cli-g-maps/utils/g-maps/child-collection'; | ||
const { isArray } = Ember; | ||
@@ -13,2 +12,3 @@ | ||
/* Supported: | ||
props: [ | ||
@@ -43,2 +43,3 @@ 'clickable', | ||
], | ||
*/ | ||
@@ -45,0 +46,0 @@ validate: function() { |
@@ -1,4 +0,2 @@ | ||
import Ember from 'ember'; | ||
import configurator from 'ember-cli-g-maps/utils/g-maps/configurator'; | ||
@@ -18,14 +16,2 @@ const { merge, uuid, on, computed, observer } = Ember; | ||
// TODO: Make these configurable | ||
const gmapCreate = { | ||
polygons: 'drawPolygon', | ||
markers: 'addMarker', | ||
polylines: 'drawPolyline', | ||
circles: 'drawCircle', | ||
rectangles: 'drawRectangle', | ||
overlays: 'drawOverlay', | ||
routes: 'drawRoute', | ||
controls: 'addControl' | ||
}; | ||
settings = merge(defaults, settings); | ||
@@ -37,22 +23,18 @@ | ||
const namespace = globalNamespace+capitalize(settings.namespace); | ||
const model = settings.model; | ||
// TODO: abstract instance methods | ||
const removeItem = function(item, map) { | ||
if(item.setMap) { | ||
item.setMap(null); | ||
} | ||
//////////////////////////////////////////// | ||
// Child Collection Factory Configuration | ||
/////////////////////////////////////////// | ||
if(settings.onRemoveItem) { | ||
settings.onRemoveItem(item, map); | ||
} | ||
const model = settings.model; | ||
const namespace = globalNamespace+capitalize(settings.namespace); | ||
const addMethod = Ember.String.singularize(`add${model[0].toUpperCase()}${model.slice(1)}`); | ||
const removeMethod = Ember.String.singularize(`remove${model[0].toUpperCase()}${model.slice(1)}`); | ||
const afterAddChild = settings.onAddedItem || noop; | ||
const beforeRemoveChild = settings.onRemoveItem || noop; | ||
return item; | ||
}; | ||
const addedItem = settings.onAddedItem || noop; | ||
///////////////////////////////////////// | ||
// Child Collection Mixin Configuration | ||
//////////////////////////////////////// | ||
//////////////////////////////////// | ||
// Child Collection Mixin Factory | ||
/////////////////////////////////// | ||
@@ -68,14 +50,2 @@ return { | ||
/** | ||
* Supported properties for the Child Collection | ||
*/ | ||
[namespace+'Props']: settings.props, | ||
/** | ||
* Supported events for the Child Collection | ||
*/ | ||
[namespace+'Events']: settings.events, | ||
/** | ||
* Optional method to ensure correct parent data for Child Collection | ||
@@ -87,3 +57,3 @@ */ | ||
/** | ||
* Optional method to remove any bound google.map.events ect | ||
* Optional method to remove event listeners ect. | ||
*/ | ||
@@ -94,22 +64,2 @@ [namespace+'OnDestroy']: on('willDestroyElement', settings.onDestroy || noop), | ||
/** | ||
* [Computed property to map over parent model items, checking and returning ids] | ||
* @param {[Child Collection item]} | ||
* @return {[Single level Array of Parent Model ids]} | ||
*/ | ||
[`${namespace}Ids`]: computed.map(`${model}.@each.id`, function(m) { | ||
if( !m.id ) { throw new Error(`${model} items require an id`); } | ||
return m.id; | ||
}), | ||
/** | ||
* [Computed property to determine differences between Parent and GMap models] | ||
* @return {[Boolean]} | ||
*/ | ||
[`${namespace}Updated`]: computed(`${namespace}Ids`, `${model}.@each.id`, { | ||
get: utils._wasModelUpdated(`${namespace}Ids`, model) | ||
}), | ||
/** | ||
* [Observer to sync the Parent with the GMap model, of the same name] | ||
@@ -120,52 +70,46 @@ * @requires {[Parent Model]} | ||
*/ | ||
[`${namespace}Sync`]: observer('isMapLoaded', `${model}.@each.id`, function() { | ||
let parentModel = this.get(model); | ||
const map = this.get('map'); | ||
const createMethod = gmapCreate[model]; | ||
[`${namespace}Sync`]: observer('isMapLoaded', `${model}.[]`, function() { | ||
const map = this.get('map'); | ||
let parentModel = this.get(model); | ||
// If Items require syncing | ||
if(!this.get('isMapLoaded') || !this.get(`${namespace}Updated`)) { return; } | ||
if(!this.get('isMapLoaded')) { return; } | ||
// Remove (deleted) Items from GMap | ||
for(let i = 0, l = map[model].length; i < l; i++) { | ||
let item = map[model][i]; | ||
let id = item.details.id; | ||
for(let i = 0, l = parentModel.length; i < l; i++) { | ||
let item = parentModel[i]; | ||
let mapChild = map[model][i]; | ||
let addedMapItem = null; | ||
if(utils._isItemRemoved(id, parentModel) === false) { | ||
continue; // Item not removed | ||
// Map store doesn't have child | ||
if(!mapChild) { | ||
addedMapItem = map[addMethod](item); | ||
} | ||
removeItem(item, map); | ||
// If map index item is different from model item | ||
else if(utils._modelVsMapChildDiff(item, mapChild)) { | ||
// Remove Item from GMap store | ||
map[model].splice(i, 1); | ||
l--; | ||
i--; | ||
} | ||
// Somethings different, so just rerender it! | ||
beforeRemoveChild(mapChild, map); | ||
map[ removeMethod ](mapChild); | ||
// Build supported config parameters | ||
const confProps = configurator.getConfigParams( | ||
[`${namespace}Props`, `${namespace}Events`], | ||
this | ||
); | ||
// Add to end of map[model] | ||
addedMapItem = map[ addMethod ](item); | ||
// Add (unadded) Item to GMap | ||
for(let i = 0, l = parentModel.length; i < l; i++) { | ||
let item = parentModel[i]; | ||
let id = item.id; | ||
// So here we adjust it to be the current index | ||
map[model].splice(i, 0, map[model].pop()); | ||
} | ||
// Child Item is already on map | ||
if( id && map.hasChild(id, model) ) { continue; } | ||
// Hook for mixin | ||
if(addedMapItem) { | ||
afterAddChild(item, addedMapItem, map); | ||
} | ||
} | ||
let config = configurator.getConfig(confProps, item); | ||
// Remove any map children out of sync with model | ||
while(map[model].length !== parentModel.length) { | ||
let mapChild = map[model][map[model].length - 1]; | ||
// Merge marker source data into marker.details | ||
config.details = merge( | ||
configurator.getModelProperties(item), | ||
config.details || {} | ||
); | ||
const mapItem = map[createMethod](config); | ||
addedItem(item, mapItem, map); | ||
// Hook for mixin | ||
beforeRemoveChild(mapChild, map); | ||
map[removeMethod](mapChild); | ||
} | ||
@@ -176,29 +120,14 @@ }) | ||
_isItemRemoved: function(id, models) { | ||
for(let i = 0, l = models.length; i < l; i++ ) { | ||
if(models[i].id === id) { return false; } | ||
} | ||
return true; | ||
}, | ||
_modelVsMapChildDiff: function(model, mapChild) { | ||
for(let p in model) { | ||
if(model.hasOwnProperty(p)) { | ||
_wasModelUpdated: function wasModelUpdated(modelIdsKey, model) { | ||
return function() { | ||
if(!this.get('map')){ return false; } | ||
const _modelIds = this.get(modelIdsKey); | ||
const mapModel = this.get('map')[model]; | ||
// Model were updated | ||
if(mapModel.length !== _modelIds.length) { return true; } | ||
for(let i = 0, l = mapModel.length; i < l; i++) { | ||
// Compare GMap marker id's to Model markers id's | ||
if(_modelIds.indexOf(mapModel[i].id) === -1) { | ||
return true; // Model were updated | ||
// Only diff one level deep on parent model | ||
if(typeof model[p] !== 'object' && model[p] !== mapChild[p]) { | ||
return true; | ||
} | ||
} | ||
return false; // Model not updated | ||
}; | ||
} | ||
return false; | ||
} | ||
}; |
@@ -10,3 +10,3 @@ /* jshint node: true */ | ||
app.import(app.bowerDirectory + '/gmaps/gmaps.js'); | ||
app.import(app.bowerDirectory + '/gmaps-for-apps/gmaps.js'); | ||
}, | ||
@@ -13,0 +13,0 @@ |
{ | ||
"name": "ember-cli-g-maps", | ||
"version": "0.0.12", | ||
"version": "0.0.13", | ||
"description": "An Ember CLI Addon for map driven applications. Built with GMaps, an open source Google Maps project. Currently in Beta.", | ||
@@ -5,0 +5,0 @@ "directories": { |
148
README.md
@@ -9,3 +9,5 @@ # Ember CLI G-Maps | ||
## Installation ## | ||
Installation | ||
------------ | ||
Requires: Ember-CLI >= 1.13.7 & Ember > 1.13.6 | ||
@@ -41,3 +43,4 @@ | ||
## Currently Supports ## | ||
Currently Supports | ||
------------------- | ||
@@ -47,4 +50,6 @@ - [Polygons](http://hpneo.github.io/gmaps/documentation.html#GMaps-drawPolygon) | ||
- [Circles](http://hpneo.github.io/gmaps/documentation.html#GMaps-drawCircle) | ||
- [Polylines](https://developers.google.com/maps/documentation/javascript/3.exp/reference#CircleOptions#FusionTablesPolylineOptions) | ||
## Usage ## | ||
Usage | ||
------ | ||
@@ -70,2 +75,3 @@ **Simplest Possible G-Map** | ||
``` | ||
**Add Markers** | ||
@@ -82,12 +88,12 @@ ```js | ||
{ | ||
id: 'jdlkfajs22', // Required | ||
lat: 33.516674497188255, // Required | ||
lng: -86.80091857910156, // Required | ||
infoWindow: { | ||
content: '<p>Birmingham</p>', | ||
visible: true | ||
}, | ||
click: function() { | ||
console.log('You clicked me!'); | ||
} | ||
id: 'jdlkfajs22', // Recommended | ||
lat: 33.516674497188255, // Required | ||
lng: -86.80091857910156, // Required | ||
infoWindow: { | ||
content: '<p>Birmingham</p>', | ||
visible: true | ||
}, | ||
click: function() { | ||
console.log('You clicked me!'); | ||
} | ||
} | ||
@@ -115,3 +121,3 @@ ]); | ||
{ | ||
id: 'lka234klafj23', // Required | ||
id: 'lka234klafj23', // Recommended | ||
paths: [ // Required | ||
@@ -134,2 +140,35 @@ [35.0041, -88.1955], // Lat, Lng | ||
**Add Polylines** | ||
```js | ||
export default Ember.Route.extend({ | ||
setupController: function(controller) { | ||
controller.setProperties({ | ||
lat: 32.75494243654723, | ||
lng: -86.8359375, | ||
zoom: 4, | ||
// Must be an Ember Array | ||
polylines: Ember.A([ | ||
{ | ||
id: 'lka234klafj23', // Recommended | ||
strokeColor: 'blue', | ||
strokeOpacity: 1, | ||
strokeWeight: 6, | ||
path: [ // Required | ||
[34.220, -100.7], // Lat, Lng | ||
[33.783, -92.81], // Lat, Lng | ||
[35.946, -94.83], // Lat, Lng | ||
[32.458, -95.71], // Lat, Lng | ||
[33.783, -92.85] // Lat, Lng | ||
] | ||
} | ||
]) | ||
}); | ||
} | ||
}); | ||
``` | ||
```handlebars | ||
{{g-maps ... polylines=polylines}} | ||
``` | ||
**Add Circles** | ||
@@ -146,3 +185,3 @@ ```js | ||
{ | ||
id: 'lfkjasd23faj2f31', // Required | ||
id: 'lfkjasd23faj2f31', // Recommended | ||
lat: 32.75494243654723, // Required | ||
@@ -166,23 +205,23 @@ lng: -86.8359375, // Required | ||
actions: { | ||
onMapClick: function(e) { | ||
console.info('Click coordinate', | ||
e.latLng.A, // Latitude | ||
e.latLng.F // Longitude | ||
); | ||
console.info('Map boundaries', | ||
e.bounds[0], // Top left map coordinate | ||
e.bounds[1], // Top right map coordinate | ||
e.bounds[2], // Bottom left map coordinate | ||
e.bounds[3] // Bottom right map coordinate | ||
); | ||
console.info('Map\'s center', | ||
this.controller.lat, | ||
this.controller.lng | ||
); | ||
e.mapIdle.then(function() { // Promise | ||
console.log('maps done loading tiles and user is not interacting with map'); | ||
}); | ||
e.mapTilesLoaded.then(function() { // Promise | ||
console.log('Map tiles have finished loading'); | ||
}); | ||
onMapEvent: function(e) { | ||
console.info('Click coordinate', | ||
e.latLng.A, // Latitude | ||
e.latLng.F // Longitude | ||
); | ||
console.info('Map boundaries', | ||
e.bounds[0], // Top left map coordinate | ||
e.bounds[1], // Top right map coordinate | ||
e.bounds[2], // Bottom left map coordinate | ||
e.bounds[3] // Bottom right map coordinate | ||
); | ||
console.info('Map\'s center', | ||
this.controller.lat, | ||
this.controller.lng | ||
); | ||
e.mapIdle.then(function() { // Promise | ||
console.log('maps done loading tiles and user is not interacting with map'); | ||
}); | ||
e.mapTilesLoaded.then(function() { // Promise | ||
console.log('Map tiles have finished loading'); | ||
}); | ||
} | ||
@@ -204,3 +243,3 @@ } | ||
lng: -86.8359375, | ||
mapType: 'satellite' // Accepts roadmap, satellite, hybrid, or terrain | ||
mapType: 'satellite' // Accepts 'roadmap', 'satellite', 'hybrid', or 'terrain' | ||
}); | ||
@@ -303,6 +342,6 @@ } | ||
## Planned Features ## | ||
Planned Features | ||
---------------- | ||
- [Overlays](https://developers.google.com/maps/documentation/javascript/3.exp/reference#CircleOptions#MapPanes) | ||
- [Polylines](https://developers.google.com/maps/documentation/javascript/3.exp/reference#CircleOptions#FusionTablesPolylineOptions) | ||
- [Controls](http://hpneo.github.io/gmaps/examples/custom_controls.html) | ||
@@ -312,5 +351,6 @@ - [Layers & KML Layers](https://developers.google.com/maps/documentation/javascript/3.exp/reference#CircleOptions#KmlLayerOptions) | ||
- [Info Windows](https://github.com/huafu/ember-google-map/wiki/Provided-Tools-and-Classes-%28API%29#info-windows) | ||
- Labels (A simple text element) | ||
- Text labels | ||
## Customization ## | ||
Customization | ||
------------- | ||
@@ -326,2 +366,26 @@ In `config/environment.js` | ||
} | ||
``` | ||
``` | ||
Changelog | ||
--------- | ||
0.0.12-beta | ||
----------- | ||
* GMaps-For-Apps.js rendering layer | ||
* Improved Map Child bindings | ||
* No longer requires id property | ||
* Polyline Map Child | ||
* Performant Map destroy | ||
0.0.11-beta | ||
------------ | ||
* Basic Map Component | ||
* Bound MapType | ||
* Map service | ||
* map idle promise | ||
* map tilesLoaded promise | ||
* Marker Map Child | ||
* Circle Map Child | ||
* Polygon Map Child | ||
379
34038
21
677