/webmapper/static/webmapper/core.js
JavaScript | 415 lines | 265 code | 102 blank | 48 comment | 29 complexity | 474e082d338fbe75d36e9779bdd3524f MD5 | raw file
Possible License(s): BSD-3-Clause
- /* webmapper.js */
- window.webmapper = (function(webmapper){
- if(typeof window.console === "undefined"){
- (function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();)b[a]=b[a]||c;})(window.console=window.console||{})
- }
- webmapper.errorHandler = {error:function(){
- // TODO: an error reporting system, display messages to the User
- }
- }
- webmapper.ApiParams = {
- // construct and store url paramters on a per Model/Collection basis
- // helper function to build query param list
- serializeQueryParams: function(pairList){
- var str = []
- _(pairList).each(function(pair){
- str.push(encodeURIComponent(pair[0]) + "=" + encodeURIComponent(pair[1]))
- })
- return str.join("&")
- },
- // models and collections can build up api call params
- // by setting and deleting specific key/value pairs
- setApiParam:function(param, value){
- if(!this._apiParams)
- this._apiParams = []
- this._apiParams.push([param, value])
- },
- url: function(apiParameters){
- var baseUrl = this.urlRoot + (this.id ? this.id + "/" : "")
- var params = []
- if(apiParameters){
- // if we are passed any api parameters, use those
- params = apiParameters
- }
- else{
- // otherwise if we have set any parameters on this model/collection
- // accumulate those and append them to the url
- if(this._apiParams)
- _(this._apiParams).each(function(pair){params.push(pair)})
- if(this.collection && this.collection._apiParams)
- _(this.collection._apiParams).each(function(pair){params.push(pair)})
- }
- if(params.length > 0)
- baseUrl = baseUrl + '?' + this.serializeQueryParams(params)
- return baseUrl
- },
- }
- webmapper.BaseModel = Backbone.Model.extend(_.extend(webmapper.ApiParams,{
- isNew: function(){
- return ( typeof this.id == 'undefined' || this.id == "new")
- }
- }))
- webmapper.BaseCollection = Backbone.Collection.extend(webmapper.ApiParams)
- webmapper.Map = webmapper.BaseModel.extend({
- urlRoot: webmapperConstant['api_maps_url'],
- bindEvents:function(){
- var map = this
- },
- initialize: function(opts){
- this.loading = $.Deferred()
- this.templateStore = $('<div></div>')
- var defaultOptions = {
- fillContainer : false,
- applicationViews : [],
- scrollZoom : false,
- tools : [
- new webmapper.tools.MarkerTool(),
- new webmapper.tools.KmlTool()
- ]
- }
- this.options = _.extend(defaultOptions, opts)
- if(this.options.domId)
- this.baseEl = $('#'+this.options.domId)
- else
- this.baseEl = this.options.baseEl || null
- this.tools = this.options.tools
- this.applicationViews = this.options.applicationViews
- // FIXME, need to override the construtor, pull args out and then
- // pass the rest to initialize, or maybe whitelist model attributes
- this.unset('baseEl',{silent:true})
- this.unset('tools',{silent:true})
- this.unset('applicationViews',{silent:true})
- if(this.isNew() && this.id != "new" )
- this.mapLoadedPromise = this.save()
- else
- this.mapLoadedPromise = this.fetch()
- _.bindAll(this)
- this.bindTools()
- this.bindApplicationViews()
- var map = this
- // bootstrap the application
- $.when(map.mapLoadedPromise).then(function(){
- map.mapPane = new webmapper.MapPane({id: map.get('map_pane_id') || 'new'})
- map.mapPane.map = map
- var mapViewArgs = {model:map}
- if(map.baseEl){
- mapViewArgs.el = map.baseEl
- }
- map.view = new webmapper.MapView(mapViewArgs)
- $.when(map.mapPane.fetch()).then(function(){
- if(map.baseEl){
- map.view.render()
- // create map widget views render and append to map DOM
- map.mapPane.initializeMapPaneView()
- }
- // create geo attrs
- map.kmlLayers = new webmapper.KmlLayerCollection([], {map:map})
- map.points = new webmapper.PointCollection([], {map:map})
- map.polygons = new webmapper.PolygonCollection([], {map:map})
- if(map.isNew()){
- map.loading.resolve()
- map.trigger('loaded')
- return
- }
- map.trigger('prepare-sync', map)
- $.when(map.kmlLayers.fetch(),
- map.points.fetch(),
- map.polygons.fetch())
- .then(function(){
- // tools and application views can bind to various events
- map.trigger('init-ready', map)
- map.trigger('attach-kml', map.kmlLayers)
- map.trigger('attach-points', map.points)
- map.trigger('attach-polygons', map.polygons)
- map.trigger('init-complete', map)
- map.loading.resolve()
- map.trigger('loaded')
- })
- })
- })
- return this
- },
- // Save the Map and MapPane
- create:function(){
-
- var created = $.Deferred()
- var map = this
- if(this.isNew()){
- this.save().done(function(){
- map.mapPane.id = map.get('map_pane_id')
- map.mapPane.fetch().done(function(){
- created.resolve()
- }).fail(function(){
- created.reject("MapPane fetch failed")
- })
- }).fail(function(){
- created.reject('Map create failed')
- })
- return created
- }
- else{
- throw('cannot create an existing map')
- }
- },
- bindTools:function(){
- var map = this
- _(this.tools).each(function(tool){
- tool.bindMap(map)
- })
- },
- bindApplicationViews:function(){
- var map = this
- _(this.applicationViews).each(function(view){
- view.bindMap(map)
- })
- },
- getTemplate:function(templateDomId){
- return webmapperConstant.templates[templateDomId]
- },
- getCurrentPane: function(){
- return this.mapPane
- },
- })
- webmapper.MapView = Backbone.View.extend({
- tagName: 'div',
- className: 'webmapper-map',
- render: function(){
- // build the map dom
- this.$el.css('position','relative')
- return this
- }
- })
- // MapPanes
- // --------
- webmapper.MapPane = webmapper.BaseModel.extend({
- initializeMapPaneView: function(args){
- // Initialize attributes specific to the map.
- var opts = args || {}
- opts.model = this
- // Create the map widget view.
- if(this.get('map_type') == "google")
- this.view = new webmapper.google.MapPaneView(opts)
- else if(this.get('map_type') == "openlayers")
- this.view = new webmapper.openlayers.MapPaneView(opts)
- else
- throw('unknown map type ' + this.get('map_type'))
- // Append the mapPane's rendered el to map DOM fragment.
- $(this.map.view.el).prepend(this.view.render().el)
- // trigger reflow on the map widget
- this.view.reflowSize()
- this.map.on('attach-kml', this.view.attachKML, this)
- this.map.on('attach-points', this.view.attachPoints, this)
- this.map.on('attach-polygons', this.view.attachPolygons, this)
- this.trigger('map-pane-initialized')
- },
- changeMapWidget: function(new_map_type){
- var mapPane = this
- // Supress map_type change event until the various attributes settle.
- mapPane.set({map_type:new_map_type},{silent:true})
- // We can save and reuse el.
- var el = mapPane.view.el
- mapPane.view.removeMapWidget()
- mapPane.initializeMapPaneView({el:el})
- // Impress the server state upon the widget.
- mapPane.view.syncMapWidget()
- // Re-setup tool delegations.
- mapPane.map.bindTools()
- mapPane.map.bindApplicationViews()
- // Our new widget is lacking the canonical layer: reattach the geo-models.
- this.map.trigger('attach-kml', this.map.kmlLayers)
- this.map.trigger('attach-points', this.map.points)
- this.map.trigger('attach-polygons', this.map.polygons)
- // Alert listeners that the map_type has changed.
- mapPane.trigger('change:map_type')
- },
- urlRoot: webmapperConstant['api_map_panes_url'],
- url:function(){
- return this.urlRoot + (this.id ? this.id + "/" : "")
- }
- })
- // MapPane Views
- // -------------
- webmapper.AbstractMapPaneView = Backbone.View.extend({
- // provides custom events:
- // * tiles-loaded
- // This class defines the interface to the concrete Google/Openlayers
- // implementations. Webmapper map-types implement this interface.
- tagName: "div",
- className: "webmapper-mappane",
- initialize: function(opts){
- // if we are in an editing state,
- // set up the appropriate css class
- if(this.model.map.editing)
- $(this.el).addClass('editing')
- },
- // build up the attributes that handle events
- // pass in `translateFunc` to convert the event to an intermediate
- // "webmapper" representation
- _eventAttrConstructor:function(eventName, translateFunc){
- var mapPaneView = this
- return function(){
- var eventArgs = arguments
- mapPaneView.trigger(eventName, translateFunc.apply(mapPaneView, eventArgs))
- }
- },
- render: function(){
- // calls to this should be idempotent across implementations
- this.removeMapWidget()
- var opts = this.model.toJSON()
- this.renderMapWidget()
- this.bindMapWidgetEvents()
- return this
- },
- reflowSize: function(){
- // allow the map to "reflow" in the container div
- if(this.model.map.options.fillContainer){
- this.$el.width(this.$el.parent().width())
- this.$el.height(this.$el.parent().height())
- }
- else{
- this.$el.width(this.model.get('width'))
- this.$el.height(this.model.get('height'))
- }
- this.trigger('reflow')
- },
- remove: function(){
- this.removeMapWidget()
- return Backbone.View.prototype.remove.call(this)
- },
- // Map Panes must implement a bare minimum interface
- paneDiv: function(){
- throw('not implemented')
- },
- renderMapWidget:function(){
- throw('not implemented')
- },
- bindMapWidgetEvents:function(){
- throw('not implemented')
- },
- syncMapWidget:function(){
- throw('not implemented')
- },
- removeMapWidget:function(){
- throw('not implemented')
- },
- getBounds:function(){
- throw('not implemented')
- },
- attachPoints:function(){
- throw('not implemented')
- },
- attachKML:function(){
- throw('not implemented')
- },
- attachPolygons:function(){
- throw('not implemented')
- },
- // Internal APIs must provide a mechanism to propagate widget attrs back
- // to the MapPane model. Something like:
- // * propagateCenter
- // * propagateZoom
- // * propagateMapTypeId
- })
- return webmapper
- })(window.webmapper || {});