From d2dbd68c396c56d1ba305c68b38c91dc41ee0d53 Mon Sep 17 00:00:00 2001 From: Ilya Radchenko Date: Wed, 24 Jun 2015 14:28:12 -0400 Subject: [PATCH 001/120] Fix #512, mousewheel error --- vendor/mwheelIntent.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/mwheelIntent.js b/vendor/mwheelIntent.js index 6035bf6217..ff51ecb157 100644 --- a/vendor/mwheelIntent.js +++ b/vendor/mwheelIntent.js @@ -54,7 +54,7 @@ $.event.special.mwheelIntent = { minDif = 3; }, 1500); e = $.extend({}, e, {type: 'mwheelIntent'}); - return $.event.handle.apply(this, arguments); + return ($.event.dispatch || $.event.handle).apply(this, arguments); } } }; @@ -73,4 +73,4 @@ $(function(){ //assume that document is always scrollable, doesn't hurt if not $(doc).bind('mwheelIntent.mwheelIntentDefault', $.noop); }); -})(jQuery); \ No newline at end of file +})(jQuery); From 06457f78616be7373f5db2b20cdbc4c6b3f530c7 Mon Sep 17 00:00:00 2001 From: Ilya Radchenko Date: Mon, 31 Aug 2015 09:21:51 -0400 Subject: [PATCH 002/120] replace missing layer `getSQL` with `getQuery` `getSQL` didn't exist when I tried this. --- examples/leaflet_hover_features.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/leaflet_hover_features.html b/examples/leaflet_hover_features.html index 612692b9ad..9f29382a14 100644 --- a/examples/leaflet_hover_features.html +++ b/examples/leaflet_hover_features.html @@ -51,7 +51,7 @@ // fetch the geometry var sql = new cartodb.SQL({ user: username, format: 'geojson' }); - sql.execute("select cartodb_id, ST_Simplify(the_geom, 0.1) as the_geom from (" + layer.getSQL() + ") as _wrap").done(function(geojson) { + sql.execute("select cartodb_id, ST_Simplify(the_geom, 0.1) as the_geom from (" + layer.getQuery() + ") as _wrap").done(function(geojson) { var features = geojson.features; for(var i = 0; i < features.length; ++i) { var f = geojson.features[i]; From e9036f88b8b56f72cc5d51e9f1ccbc623dcbfedc Mon Sep 17 00:00:00 2001 From: csobier Date: Tue, 8 Sep 2015 18:37:56 -0400 Subject: [PATCH 003/120] updated getUrl syntax in API.md --- doc/API.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/API.md b/doc/API.md index cc0a496228..5082a85be4 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1182,16 +1182,16 @@ cartodb.Image(vizjson_url) An _Image_ object -#### Image.getURL(_callback(err, url)_) +#### Image.getUrl(_callback(err, url)_) Gets the URL for the image requested. -
Image.getURL
+
Image.getUrl
```javascript From d51a270c4bc81a5cefc7184f28cf10a17c9ecd5c Mon Sep 17 00:00:00 2001 From: Carla Date: Mon, 14 Sep 2015 11:05:05 +0200 Subject: [PATCH 004/120] Small edits --- doc/API.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/API.md b/doc/API.md index b8a02be3b1..698f6fe5b1 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1325,21 +1325,21 @@ cartodb.createVis('map', 'http://examples.cartodb.com/api/v2/viz/ne_10m_populate ``` ### How to set a different host than cartodb.com -cartodb.js by default send all requests to cartodb.com domain but it you are running your own -instance of cartodb you can change the urls. +CartoDB.js sends all requests to the cartodb.com domain by default. If you are running your own +instance of CartoDB you can change the URLs to specify a different host. -The way to do it is using ``sql_api_template`` and ``maps_api_template`` in ``options`` paramater +A different host can be configured by using ``sql_api_template`` and ``maps_api_template`` in the ``options`` parameter for any ``cartodb`` function call. -The format of those templates is like: +The format of these templates is as follows: ```javascript sql_api_template: 'https://{user}.test.com' ``` -cartodb.js will replace ``{user}``. +CartoDB.js will replace ``{user}``. -Notice you don't need to set the path to the endpoint, cartodb.js sets it +Notice that you don't need to set the path to the endpoint, CartoDB.js will set it automatically. ### Bounds wrapper From f5f2bc24b3f901ac90e80fbbd91ba492c4e5a325 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Mon, 14 Sep 2015 13:47:29 +0200 Subject: [PATCH 005/120] Do not rely on object.offsetParent. The map can have a fixed position and the offsetTop can be 0, but there might be some scroll that we need to take into account. Check out this bug for more info: https://github.com/CartoDB/cartodb.js/issues/639 --- src/geo/leaflet/leaflet_cartodb_layergroup.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/geo/leaflet/leaflet_cartodb_layergroup.js b/src/geo/leaflet/leaflet_cartodb_layergroup.js index 052c4cddb5..e65822d3d5 100644 --- a/src/geo/leaflet/leaflet_cartodb_layergroup.js +++ b/src/geo/leaflet/leaflet_cartodb_layergroup.js @@ -277,18 +277,21 @@ L.CartoDBGroupLayerBase = L.TileLayer.extend({ y = o.e.clientY; } - if (obj.offsetParent) { - // Modern browsers + // If the map is fixed at the top of the window, we can't use offsetParent + // cause there might be some scrolling that we need to take into account. + if (obj.offsetParent && obj.offsetTop > 0) { do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); - return map.containerPointToLayerPoint(new L.Point(x - curleft, y - curtop)); + var p = new L.Point( + x - curleft, y - curtop); + return map.containerPointToLayerPoint(p); } else { var rect = obj.getBoundingClientRect(); var p = new L.Point( - (o.e.clientX? o.e.clientX: x) - rect.left - obj.clientLeft - window.scrollX, - (o.e.clientY? o.e.clientY: y) - rect.top - obj.clientTop - window.scrollY); + (o.e.clientX? o.e.clientX: x) - rect.left - obj.clientLeft - window.scrollX, + (o.e.clientY? o.e.clientY: y) - rect.top - obj.clientTop - window.scrollY); return map.containerPointToLayerPoint(p); } } From 6d916ab1b3a2a506eb82e42aa7af09e1db0cc613 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Mon, 14 Sep 2015 14:15:17 +0200 Subject: [PATCH 006/120] pageXOffset and pageYOffset is required for I.E. Some versions of I.E don't support window.scrollX and window.scrollY so we have to use window.pageXOffset and window.pageYOffset. --- src/geo/leaflet/leaflet_cartodb_layergroup.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/geo/leaflet/leaflet_cartodb_layergroup.js b/src/geo/leaflet/leaflet_cartodb_layergroup.js index e65822d3d5..566b90f309 100644 --- a/src/geo/leaflet/leaflet_cartodb_layergroup.js +++ b/src/geo/leaflet/leaflet_cartodb_layergroup.js @@ -289,9 +289,11 @@ L.CartoDBGroupLayerBase = L.TileLayer.extend({ return map.containerPointToLayerPoint(p); } else { var rect = obj.getBoundingClientRect(); + var scrollX = (window.scrollX || window.pageXOffset); + var scrollY = (window.scrollY || window.pageYOffset); var p = new L.Point( - (o.e.clientX? o.e.clientX: x) - rect.left - obj.clientLeft - window.scrollX, - (o.e.clientY? o.e.clientY: y) - rect.top - obj.clientTop - window.scrollY); + (o.e.clientX? o.e.clientX: x) - rect.left - obj.clientLeft - scrollX, + (o.e.clientY? o.e.clientY: y) - rect.top - obj.clientTop - scrollY); return map.containerPointToLayerPoint(p); } } From b65d0a881115ef8767017d2be63851b5600ee002 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Mon, 14 Sep 2015 16:33:34 +0200 Subject: [PATCH 007/120] Fixed issue with info windows and scrolling for GMaps too. --- src/geo/gmaps/gmaps_cartodb_layergroup.js | 39 +++++++++++++------ src/geo/leaflet/leaflet_cartodb_layergroup.js | 20 ++++++---- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/geo/gmaps/gmaps_cartodb_layergroup.js b/src/geo/gmaps/gmaps_cartodb_layergroup.js index 24292b468c..b3e3276c82 100644 --- a/src/geo/gmaps/gmaps_cartodb_layergroup.js +++ b/src/geo/gmaps/gmaps_cartodb_layergroup.js @@ -230,12 +230,12 @@ CartoDBLayerGroupBase.prototype._checkLayer = function() { } CartoDBLayerGroupBase.prototype._findPos = function (map,o) { - var curleft, cartop; - curleft = curtop = 0; + var curleft = 0; + var curtop = 0; var obj = map.getDiv(); var x, y; - if (o.e.changedTouches && o.e.changedTouches.length > 0 && (o.e.changedTouches[0] !== undefined) ) { + if (o.e.changedTouches && o.e.changedTouches.length > 0) { x = o.e.changedTouches[0].clientX + window.scrollX; y = o.e.changedTouches[0].clientY + window.scrollY; } else { @@ -243,14 +243,31 @@ CartoDBLayerGroupBase.prototype._findPos = function (map,o) { y = o.e.clientY; } - do { - curleft += obj.offsetLeft; - curtop += obj.offsetTop; - } while (obj = obj.offsetParent); - return new google.maps.Point( - x - curleft, - y - curtop - ); + // If the map is fixed at the top of the window, we can't use offsetParent + // cause there might be some scrolling that we need to take into account. + if (obj.offsetParent && obj.offsetTop > 0) { + do { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + } while (obj = obj.offsetParent); + var point = this._newPoint( + x - curleft, y - curtop); + } else { + var rect = obj.getBoundingClientRect(); + var scrollX = (window.scrollX || window.pageXOffset); + var scrollY = (window.scrollY || window.pageYOffset); + var point = this._newPoint( + (o.e.clientX? o.e.clientX: x) - rect.left - obj.clientLeft - scrollX, + (o.e.clientY? o.e.clientY: y) - rect.top - obj.clientTop - scrollY); + } + return point; +}; + +/** + * Creates an instance of a google.maps Point + */ +CartoDBLayerGroupBase.prototype._newPoint = function(x, y) { + return new google.maps.Point(x, y); }; CartoDBLayerGroupBase.prototype._manageOffEvents = function(map, o){ diff --git a/src/geo/leaflet/leaflet_cartodb_layergroup.js b/src/geo/leaflet/leaflet_cartodb_layergroup.js index 566b90f309..3c287940a9 100644 --- a/src/geo/leaflet/leaflet_cartodb_layergroup.js +++ b/src/geo/leaflet/leaflet_cartodb_layergroup.js @@ -264,8 +264,9 @@ L.CartoDBGroupLayerBase = L.TileLayer.extend({ * @params {Object} Map object * @params {Object} Wax event object */ - _findPos: function (map,o) { - var curleft = 0, curtop = 0; + _findPos: function (map, o) { + var curleft = 0; + var curtop = 0; var obj = map.getContainer(); var x, y; @@ -284,20 +285,25 @@ L.CartoDBGroupLayerBase = L.TileLayer.extend({ curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); - var p = new L.Point( + var point = this._newPoint( x - curleft, y - curtop); - return map.containerPointToLayerPoint(p); } else { var rect = obj.getBoundingClientRect(); var scrollX = (window.scrollX || window.pageXOffset); var scrollY = (window.scrollY || window.pageYOffset); - var p = new L.Point( + var point = this._newPoint( (o.e.clientX? o.e.clientX: x) - rect.left - obj.clientLeft - scrollX, (o.e.clientY? o.e.clientY: y) - rect.top - obj.clientTop - scrollY); - return map.containerPointToLayerPoint(p); } - } + return map.containerPointToLayerPoint(point); + }, + /** + * Creates an instance of a Leaflet Point + */ + _newPoint: function(x, y) { + return new L.Point(x, y); + } }); L.CartoDBGroupLayer = L.CartoDBGroupLayerBase.extend({ From 08f85c2efc72393c466a8a5116894f5bbb121f59 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Mon, 14 Sep 2015 16:46:47 +0200 Subject: [PATCH 008/120] Added an example of maps with infowindows and different positionings --- ...infowindow-with-different-positioning.html | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 examples/infowindow-with-different-positioning.html diff --git a/examples/infowindow-with-different-positioning.html b/examples/infowindow-with-different-positioning.html new file mode 100644 index 0000000000..0981e6b957 --- /dev/null +++ b/examples/infowindow-with-different-positioning.html @@ -0,0 +1,59 @@ + + + + Infowindow working with different positionings | CartoDB.js + + + + + + + + +
+
⬇ ⬇ ⬇ Scroll down ⬇ ⬇ ⬇
+
+ + + + + + + From 855e5230eea41052af9704fad4eb163970f7beff Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Mon, 14 Sep 2015 18:08:46 +0200 Subject: [PATCH 009/120] Also consider torque layers as data layers. Some visualizations only have torque layers. --- src/api/layers.js | 2 +- test/spec/api/layers.spec.js | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/api/layers.js b/src/api/layers.js index 9bd0d7623d..b9243576fe 100644 --- a/src/api/layers.js +++ b/src/api/layers.js @@ -104,7 +104,7 @@ } layerData = visData.layers[index]; } else { - var DATA_LAYER_TYPES = ['namedmap', 'layergroup']; + var DATA_LAYER_TYPES = ['namedmap', 'layergroup', 'torque']; // Select the first data layer (namedmap or layergroup) layerData = _.find(visData.layers, function(layer){ diff --git a/test/spec/api/layers.spec.js b/test/spec/api/layers.spec.js index 042649467b..cc86e7d464 100644 --- a/test/spec/api/layers.spec.js +++ b/test/spec/api/layers.spec.js @@ -254,12 +254,13 @@ describe('api.layers', function() { }); setTimeout(function() { + expect(layer).toBeDefined(); expect(layer.options.type).toEqual('namedmap'); done(); - }, 100); + }, 0); }); - it("should load the `layergroup` by default", function(done) { + it("should load the `layergroup` layer by default", function(done) { var layer; cartodb.createLayer(map, { @@ -281,9 +282,36 @@ describe('api.layers', function() { }); setTimeout(function() { + expect(layer).toBeDefined(); expect(layer.options.type).toEqual('layergroup'); done(); - }, 100); + }, 0); + }); + + it("should load the `torque` layer by default", function(done) { + var layer; + + cartodb.createLayer(map, { + updated_at: 'jaja', + layers: [ + { type: 'tiled', options: {} }, + { type: 'tiled', options: {} }, + { + type: 'torque', + options: { + 'torque-steps': 3 + } + } + ] + }).done(function(lyr) { + layer = lyr; + }); + + setTimeout(function() { + expect(layer).toBeDefined(); + expect(layer.type).toEqual('torque'); + done(); + }, 0); }); it("should add a torque layer", function(done) { From 7707f4a45eb26ac067dee78a719d6cbf2020cd04 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Tue, 15 Sep 2015 13:26:19 +0200 Subject: [PATCH 010/120] Updated NEWS --- NEWS | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8448a517bc..b8162326e0 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,12 @@ -3.X.X () +3.15.X (XX//YY//ZZZZ) +* +3.15.5 (15//09//2015) +* Fixed infowindows in maps with fixed position [#639](https://github.com/CartoDB/cartodb.js/issues/639) +* Automatically select "torque" layers when no index is specified in cartodb.createLayer [#678](https://github.com/CartoDB/cartodb.js/issues/678) + +3.15.4 (11//09//2015) +* Add checker to fullscreen button when it is rendered in an iframe [#674](https://github.com/CartoDB/cartodb.js/pull/674) 3.15.3 (08//09//2015) * Display custom attribution of layers (#5216). From a63bff7094876de60b36dd3913ae69ffce973813 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Tue, 15 Sep 2015 13:27:25 +0200 Subject: [PATCH 011/120] Files changed for version 3.15.5 --- RELEASING.md | 12 ++++++------ doc/API.md | 4 ++-- package.json | 2 +- src/cartodb.js | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 98f5cf45f5..e20fef4225 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -13,7 +13,7 @@ - Create a new branch to prepare the release: ``` -git flow release start 3.15.4 +git flow release start 3.15.5 ``` - Build CartoDB.js files, choosing the new version: @@ -25,7 +25,7 @@ grunt release - Update the NEWS file and commit the changes. Take into account that new CartoDB.js version will be replaced in ```API.md```, ```RELEASING.md```, ```README.md```, ```package.json```, ```cartodb.js``` and ```examples``` files. ``` -git commit -am "Files changed for version 3.15.4" +git commit -am "Files changed for version 3.15.5" ``` - Release it. @@ -36,8 +36,8 @@ grunt publish - Check if those files have been updated in the CDN: ``` -http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.4/cartodb.js -http://libs.cartocdn.com/cartodb.js/v3/3.15.4/cartodb.js +http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.5/cartodb.js +http://libs.cartocdn.com/cartodb.js/v3/3.15.5/cartodb.js http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.13/cartodb.js http://libs.cartocdn.com/cartodb.js/v3/3.13/cartodb.js ``` @@ -46,7 +46,7 @@ http://libs.cartocdn.com/cartodb.js/v3/3.13/cartodb.js - And to finish: close the release and push it. ``` -git flow release finish 3.15.4 +git flow release finish 3.15.5 git push --all git push --tags ``` @@ -75,7 +75,7 @@ grunt grunt publish ``` -For example, if we are in 3.15.4 and we want to go back to 3.13.4 +For example, if we are in 3.15.5 and we want to go back to 3.13.4 ``` git checkout 3.13.4 diff --git a/doc/API.md b/doc/API.md index b8a02be3b1..d422799556 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1452,10 +1452,10 @@ Anytime you wish to push a stable version of your site to the web though, you ca alert(cartodb.VERSION) ``` -Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.4, the URL would be: +Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.5, the URL would be: ```html - + ``` You can do the same for the CSS documents we provide: diff --git a/package.json b/package.json index ef1abc7e82..624a250bb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cartodb.js", - "version": "3.15.4", + "version": "3.15.5", "description": "CartoDB javascript library", "repository": { "type": "git", diff --git a/src/cartodb.js b/src/cartodb.js index bb418ffcbf..a2b844276e 100644 --- a/src/cartodb.js +++ b/src/cartodb.js @@ -5,7 +5,7 @@ var cdb = root.cdb = {}; - cdb.VERSION = "3.15.4"; + cdb.VERSION = "3.15.5"; cdb.DEBUG = false; cdb.CARTOCSS_VERSIONS = { From fcf3356540d1164eb1d1277cf9fa38c0708e0917 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 16 Sep 2015 18:16:08 +0200 Subject: [PATCH 012/120] Fixed two issues with attribution in Leaflet maps 1. Leaflet maps might have `attributionControl` set to false and CartoDB.js should still handle attributions correctly. 2. Leaflet maps might have some other layers with attributions, and these shouldn't be removed. --- src/geo/leaflet/leaflet.js | 35 +++++++++--- test/spec/geo/leaflet/leaflet.spec.js | 81 ++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 9 deletions(-) diff --git a/src/geo/leaflet/leaflet.js b/src/geo/leaflet/leaflet.js index fa557439d7..a80de2dc72 100644 --- a/src/geo/leaflet/leaflet.js +++ b/src/geo/leaflet/leaflet.js @@ -282,14 +282,35 @@ }, setAttribution: function() { + var attributionControl = this._getAttributionControl(); - // Attributions have already been set but we override them with - // the ones in the map object that are in the right order and include - // the default CartoDB attribution - this.map_leaflet.attributionControl._attributions = {}; - _.each(this.map.get('attribution'), function(attribution){ - this.map_leaflet.attributionControl.addAttribution(attribution); - }.bind(this)); + // Save the attributions that were in the map the first time a new layer + // is added and the attributions of the map have changed + if (!this._originalAttributions) { + this._originalAttributions = Object.keys(attributionControl._attributions); + } + + // Clear the attributions and re-add the original and custom attributions in + // the order we want + attributionControl._attributions = {}; + var newAttributions = this._originalAttributions.concat(this.map.get('attribution')); + _.each(newAttributions, function(attribution) { + attributionControl.addAttribution(attribution); + }); + }, + + _getAttributionControl: function() { + if (this._attributionControl) { + return this._attributionControl; + } + + this._attributionControl = this.map_leaflet.attributionControl; + if (!this._attributionControl) { + this._attributionControl = L.control.attribution({ prefix: '' }); + this.map_leaflet.addControl(this._attributionControl); + } + + return this._attributionControl; }, getSize: function() { diff --git a/test/spec/geo/leaflet/leaflet.spec.js b/test/spec/geo/leaflet/leaflet.spec.js index bf64753e8d..bc3ef1bad8 100644 --- a/test/spec/geo/leaflet/leaflet.spec.js +++ b/test/spec/geo/leaflet/leaflet.spec.js @@ -8,7 +8,6 @@ describe('LeafletMapView', function() { 'height': '200px', 'width': '200px' }); - //$('body').append(container); map = new cdb.geo.Map(); mapView = new cdb.geo.LeafletMapView({ el: container, @@ -188,7 +187,6 @@ describe('LeafletMapView', function() { spyOn(mapView.map_leaflet,'addLayer'); var b = new cdb.geo.TileLayer({urlTemplate: 'test' }); map.addLayer(b, {at: 0}); - expect(mapView.getLayerByCid(layer.cid).options.zIndex).toEqual(1); expect(mapView.getLayerByCid(b.cid).options.zIndex).toEqual(0); @@ -418,5 +416,84 @@ describe('LeafletMapView', function() { }); }); + describe('attributions', function() { + + var container; + + beforeEach(function() { + container = $('
').css({ + 'height': '200px', + 'width': '200px' + }); + }); + + it('should render the right attributions', function() { + var attributions = mapView.$el.find('.leaflet-control-attribution').text(); + expect(attributions).toEqual('CartoDB attribution'); + + layer = new cdb.geo.CartoDBLayer({ + attribution: 'custom attribution' + }); + map.addLayer(layer); + + var attributions = mapView.$el.find('.leaflet-control-attribution').text(); + expect(attributions).toEqual('custom attribution, CartoDB attribution'); + }); + + it('should respect the attribution of existing Leaflet layers', function() { + var leafletMap = new L.Map(container[0], { + center: [43, 0], + zoom: 3 + }); + + // Add a tile layer with some attribution + L.tileLayer('http://tile.stamen.com/toner/{z}/{x}/{y}.png', { + attribution: 'Stamen' + }).addTo(leafletMap); + + mapView = new cdb.geo.LeafletMapView({ + el: container, + map: map, + map_object: leafletMap + }); + + // Add a CartoDB layer with some custom attribution + layer = new cdb.geo.CartoDBLayer({ + attribution: 'custom attribution' + }); + map.addLayer(layer); + + var attributions = mapView.$el.find('.leaflet-control-attribution').text(); + expect(attributions).toEqual('Leaflet | Stamen, custom attribution, CartoDB attribution'); + }); + + it('should render attributions when the Leaflet map has attributionControl disabled', function() { + var leafletMap = new L.Map(container[0], { + center: [43, 0], + zoom: 3, + attributionControl: false + }); + + // Add a tile layer with some attribution + L.tileLayer('http://tile.stamen.com/toner/{z}/{x}/{y}.png', { + attribution: 'Stamen' + }).addTo(leafletMap); + + mapView = new cdb.geo.LeafletMapView({ + el: container, + map: map, + map_object: leafletMap + }); + + // Add a CartoDB layer with some custom attribution + layer = new cdb.geo.CartoDBLayer({ + attribution: 'custom attribution' + }); + map.addLayer(layer); + + var attributions = mapView.$el.find('.leaflet-control-attribution').text(); + expect(attributions).toEqual('Stamen, custom attribution, CartoDB attribution'); + }); + }); }); From 415f889520b2d89ac4c216efc874bbef9efcb493 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Thu, 17 Sep 2015 11:07:44 +0200 Subject: [PATCH 013/120] Files changed for version 3.15.6 --- NEWS | 3 +++ RELEASING.md | 12 ++++++------ doc/API.md | 4 ++-- package.json | 2 +- src/cartodb.js | 2 +- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index b8162326e0..64f4223d14 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ 3.15.X (XX//YY//ZZZZ) * +3.15.6 (17//09//2015) +* Fixed a couple of bugs related with Leaflet attributions [#681](https://github.com/CartoDB/cartodb.js/issues/681) + 3.15.5 (15//09//2015) * Fixed infowindows in maps with fixed position [#639](https://github.com/CartoDB/cartodb.js/issues/639) * Automatically select "torque" layers when no index is specified in cartodb.createLayer [#678](https://github.com/CartoDB/cartodb.js/issues/678) diff --git a/RELEASING.md b/RELEASING.md index e20fef4225..34dbe46ec0 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -13,7 +13,7 @@ - Create a new branch to prepare the release: ``` -git flow release start 3.15.5 +git flow release start 3.15.6 ``` - Build CartoDB.js files, choosing the new version: @@ -25,7 +25,7 @@ grunt release - Update the NEWS file and commit the changes. Take into account that new CartoDB.js version will be replaced in ```API.md```, ```RELEASING.md```, ```README.md```, ```package.json```, ```cartodb.js``` and ```examples``` files. ``` -git commit -am "Files changed for version 3.15.5" +git commit -am "Files changed for version 3.15.6" ``` - Release it. @@ -36,8 +36,8 @@ grunt publish - Check if those files have been updated in the CDN: ``` -http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.5/cartodb.js -http://libs.cartocdn.com/cartodb.js/v3/3.15.5/cartodb.js +http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.6/cartodb.js +http://libs.cartocdn.com/cartodb.js/v3/3.15.6/cartodb.js http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.13/cartodb.js http://libs.cartocdn.com/cartodb.js/v3/3.13/cartodb.js ``` @@ -46,7 +46,7 @@ http://libs.cartocdn.com/cartodb.js/v3/3.13/cartodb.js - And to finish: close the release and push it. ``` -git flow release finish 3.15.5 +git flow release finish 3.15.6 git push --all git push --tags ``` @@ -75,7 +75,7 @@ grunt grunt publish ``` -For example, if we are in 3.15.5 and we want to go back to 3.13.4 +For example, if we are in 3.15.6 and we want to go back to 3.13.4 ``` git checkout 3.13.4 diff --git a/doc/API.md b/doc/API.md index c829d92270..05f2dcd2ec 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1452,10 +1452,10 @@ Anytime you wish to push a stable version of your site to the web though, you ca alert(cartodb.VERSION) ``` -Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.5, the URL would be: +Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.6, the URL would be: ```html - + ``` You can do the same for the CSS documents we provide: diff --git a/package.json b/package.json index 624a250bb6..e2546b44d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cartodb.js", - "version": "3.15.5", + "version": "3.15.6", "description": "CartoDB javascript library", "repository": { "type": "git", diff --git a/src/cartodb.js b/src/cartodb.js index a2b844276e..797134a248 100644 --- a/src/cartodb.js +++ b/src/cartodb.js @@ -5,7 +5,7 @@ var cdb = root.cdb = {}; - cdb.VERSION = "3.15.5"; + cdb.VERSION = "3.15.6"; cdb.DEBUG = false; cdb.CARTOCSS_VERSIONS = { From 6b3409e53e98d6d0c06c7e0c95afd4c2373e1b82 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Thu, 17 Sep 2015 11:20:19 +0200 Subject: [PATCH 014/120] Fixed vesions in RELEASING.md --- RELEASING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 34dbe46ec0..cd9ab4ae4b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -38,8 +38,8 @@ grunt publish ``` http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.6/cartodb.js http://libs.cartocdn.com/cartodb.js/v3/3.15.6/cartodb.js -http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.13/cartodb.js -http://libs.cartocdn.com/cartodb.js/v3/3.13/cartodb.js +http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15/cartodb.js +http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js ``` - Sometimes It takes more than 10 minutes, if it is not updated, execute ```grunt invalidate```. From a2dc77debbb6ca87448f9ddf31938c02e9e0004d Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Thu, 17 Sep 2015 11:55:20 +0200 Subject: [PATCH 015/120] Replace the version of CartoDB.js in bower.json --- grunt/tasks/replace.js | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/grunt/tasks/replace.js b/grunt/tasks/replace.js index 389567e454..4efa939496 100644 --- a/grunt/tasks/replace.js +++ b/grunt/tasks/replace.js @@ -110,28 +110,23 @@ module.exports = { src: ['RELEASING.md'], dest: '' }] - } + }, + + bower: { + options: { + patterns: [{ + match: '/"version": "<%= config.version.bugfixing %>"/g', + replacement: '"version": "<%= grunt.config(\'bump.version\') %>"', + expression: true + }] + }, + files: [{ + expand: true, + flatten: true, + src: ['bower.json'], + dest: '' + }] + }, } } } - - // { - // expand: true, - // cwd: '.', - // src: 'package.json', - // dest: '.' - // }, - // { - // expand: true, - // cwd: 'src/', - // src: 'cartodb.js', - // dest: 'src/' - // }, - - // { - // match: '"version": "<%= config.version.bugfixing %>",', - // replacement: '"version": "<%= grunt.config(\'bump.version\') %>",' - // }, { - // match: "cdb.VERSION = '<%= config.version.bugfixing %>'", - // replacement: "cdb.VERSION = '<%= grunt.config(\'bump.version\') %>'", - // }, From 6feb1dc627261e885060d686f304ea685e772b02 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Thu, 17 Sep 2015 11:56:38 +0200 Subject: [PATCH 016/120] Fixed version of CartoDB.js in bower.json --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 6a1120fd4b..800f9158bc 100644 --- a/bower.json +++ b/bower.json @@ -4,7 +4,7 @@ "cartodb.js", "themes/css/cartodb.css" ], - "version": "3.15.1", + "version": "3.15.6", "homepage": "https://github.com/CartoDB/cartodb.js", "authors": [ "CartoDB " From 7c2e76b5900358a4781a2d7282e0050b1cc3e774 Mon Sep 17 00:00:00 2001 From: xavijam Date: Mon, 21 Sep 2015 16:09:29 +0200 Subject: [PATCH 017/120] If fullscreen is not possible, open the map in a new tab --- src/geo/ui/fullscreen.js | 88 +++++++++++++++------------------------- src/vis/overlays.js | 2 +- 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/src/geo/ui/fullscreen.js b/src/geo/ui/fullscreen.js index 0adb69682f..a831faae71 100644 --- a/src/geo/ui/fullscreen.js +++ b/src/geo/ui/fullscreen.js @@ -14,47 +14,33 @@ cdb.ui.common.FullScreen = cdb.core.View.extend({ className: 'cartodb-fullscreen', events: { - "click a": "_toggleFullScreen" - }, initialize: function() { - _.bindAll(this, 'render'); _.defaults(this.options, this.default_options); - - //this.model = new cdb.core.Model({ - //allowWheelOnFullscreen: false - //}); - this._addWheelEvent(); - }, _addWheelEvent: function() { + var self = this; + var mapView = this.options.mapView; - var self = this; - var mapView = this.options.mapView; - - $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function() { - - if ( !document.fullscreenElement && !document.webkitFullscreenElement && !document.mozFullScreenElement && !document.msFullscreenElement) { - if (self.model.get("allowWheelOnFullscreen")) { - mapView.options.map.set("scrollwheel", false); - } + $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function() { + if (!document.fullscreenElement && !document.webkitFullscreenElement && !document.mozFullScreenElement && !document.msFullscreenElement) { + if (self.model.get("allowWheelOnFullscreen")) { + mapView.options.map.set("scrollwheel", false); } - - mapView.invalidateSize(); - - }); - + } + mapView.invalidateSize(); + }); }, _toggleFullScreen: function(ev) { - - ev.stopPropagation(); - ev.preventDefault(); + if (ev) { + this.killEvent(ev); + } var doc = window.document; var docEl = doc.documentElement; @@ -65,11 +51,9 @@ cdb.ui.common.FullScreen = cdb.core.View.extend({ var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; - var mapView = this.options.mapView; if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { - if (docEl.webkitRequestFullScreen) { // Cartodb.js #361 :: Full screen button not working on Safari 8.0.3 #361 // Safari has a bug that fullScreen doestn't work with Element.ALLOW_KEYBOARD_INPUT); @@ -84,57 +68,49 @@ cdb.ui.common.FullScreen = cdb.core.View.extend({ } if (mapView) { - if (this.model.get("allowWheelOnFullscreen")) { mapView.options.map.set("scrollwheel", true); } - } - } else { - cancelFullScreen.call(doc); - } }, render: function() { - if (this._canFullScreenBeEnabled()) { - var $el = this.$el; - var options = _.extend(this.options); - $el.html(this.options.template(options)); - } else { - cdb.log.info('FullScreen is deprecated on insecure origins. See https://goo.gl/rStTGz for more details.'); + var $el = this.$el; + var options = _.extend( + this.options, + { + mapUrl: location.href || '' + } + ); + $el.html(this.options.template(options)); + + if (!this._canFullScreenBeEnabled()) { + this.undelegateEvents(); + cdb.log.info('FullScreen API is deprecated on insecure origins. See https://goo.gl/rStTGz for more details.'); } return this; }, _canFullScreenBeEnabled: function() { - // If frameElement exists, it means that the map - // is embebed as an iframe so we need to check if - // the parent has a secure protocol - var frameElement = window && window.frameElement; - if (frameElement) { - var parentWindow = this._getFramedWindow(frameElement); - var parentProtocol = parentWindow.location.protocol; - if (parentProtocol.search('https:') !== 0) { + if (this._isInIframe()) { + var parentUrl = document.referrer; + if (parentUrl.search('https:') !== 0) { return false; } } - return true; }, - _getFramedWindow: function(f) { - if (f.parentNode == null) { - f = document.body.appendChild(f); - } - var w = (f.contentWindow || f.contentDocument); - if (w && w.nodeType && w.nodeType==9) { - w = (w.defaultView || w.parentWindow); + _isInIframe: function() { + try { + return window.self !== window.top; + } catch (e) { + return true; } - return w; } }); diff --git a/src/vis/overlays.js b/src/vis/overlays.js index 76f9a78312..7cdb33516d 100644 --- a/src/vis/overlays.js +++ b/src/vis/overlays.js @@ -358,7 +358,7 @@ cdb.vis.Overlay.register('fullscreen', function(data, vis) { options.allowWheelOnFullscreen = false; var template = cdb.core.Template.compile( - data.template || '', + data.template || '', data.templateType || 'mustache' ); From fbf3f58d85c12cd1422e4aacc20957fd866493a8 Mon Sep 17 00:00:00 2001 From: xavijam Date: Mon, 21 Sep 2015 17:24:00 +0200 Subject: [PATCH 018/120] Removed unnecessary element --- src/geo/ui/fullscreen.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/geo/ui/fullscreen.js b/src/geo/ui/fullscreen.js index a831faae71..0106017391 100644 --- a/src/geo/ui/fullscreen.js +++ b/src/geo/ui/fullscreen.js @@ -78,14 +78,13 @@ cdb.ui.common.FullScreen = cdb.core.View.extend({ }, render: function() { - var $el = this.$el; var options = _.extend( this.options, { mapUrl: location.href || '' } ); - $el.html(this.options.template(options)); + this.$el.html(this.options.template(options)); if (!this._canFullScreenBeEnabled()) { this.undelegateEvents(); From 11ee84f433f7a680b8ad80309f907f9718b08895 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Tue, 22 Sep 2015 13:42:57 +0200 Subject: [PATCH 019/120] Updated version in link to CSS in API.md --- doc/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/API.md b/doc/API.md index 05f2dcd2ec..2f6bcd8ff1 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1461,5 +1461,5 @@ Once you know which version of CartoDB.js you're using, you can point your site You can do the same for the CSS documents we provide: ```html - + ``` From d7cf219a6119d01a3e0391488a4887bebaa3830b Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Tue, 22 Sep 2015 13:57:15 +0200 Subject: [PATCH 020/120] Undefine define in the header for all dist files --- grunt/templates/wrapper_header.js | 1 + 1 file changed, 1 insertion(+) diff --git a/grunt/templates/wrapper_header.js b/grunt/templates/wrapper_header.js index 10974ec72a..9c936dc495 100644 --- a/grunt/templates/wrapper_header.js +++ b/grunt/templates/wrapper_header.js @@ -2,6 +2,7 @@ // uncompressed version: cartodb.uncompressed.js // sha: <%= sha %> (function() { + var define; // Undefine define (require.js), see https://github.com/CartoDB/cartodb.js/issues/543 var root = this; if(!<%= load_jquery %>) { From 24cc0699ae6e28c923b6b735692972dd2505075d Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Tue, 22 Sep 2015 14:00:59 +0200 Subject: [PATCH 021/120] Updated NEWS --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 64f4223d14..e90ff4233b 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,5 @@ 3.15.X (XX//YY//ZZZZ) -* +* Undefine `define` so that dependencies don't load via AMD [#543](https://github.com/CartoDB/cartodb.js/issues/543) 3.15.6 (17//09//2015) * Fixed a couple of bugs related with Leaflet attributions [#681](https://github.com/CartoDB/cartodb.js/issues/681) From d92ce2c5124ad50b04025c004e37a6d8268d65de Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Tue, 22 Sep 2015 15:16:35 +0200 Subject: [PATCH 022/120] NEWS -> NEWS.md --- NEWS | 713 ------------------------------------------------ NEWS.md | 819 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 819 insertions(+), 713 deletions(-) delete mode 100644 NEWS create mode 100644 NEWS.md diff --git a/NEWS b/NEWS deleted file mode 100644 index 64f4223d14..0000000000 --- a/NEWS +++ /dev/null @@ -1,713 +0,0 @@ -3.15.X (XX//YY//ZZZZ) -* - -3.15.6 (17//09//2015) -* Fixed a couple of bugs related with Leaflet attributions [#681](https://github.com/CartoDB/cartodb.js/issues/681) - -3.15.5 (15//09//2015) -* Fixed infowindows in maps with fixed position [#639](https://github.com/CartoDB/cartodb.js/issues/639) -* Automatically select "torque" layers when no index is specified in cartodb.createLayer [#678](https://github.com/CartoDB/cartodb.js/issues/678) - -3.15.4 (11//09//2015) -* Add checker to fullscreen button when it is rendered in an iframe [#674](https://github.com/CartoDB/cartodb.js/pull/674) - -3.15.3 (08//09//2015) -* Display custom attribution of layers (#5216). -* Updated grunt-contrib-imagemin package version. - -3.15.2 (01//09//2015) -* Take `visible` attribute into account when determining visibility of layers and serializing maps (#546) -* Only show legends if the layer is visible (#651) -* Extracted pecan code to separate module, https://github.com/CartoDB/pecan/ (#649,#654) -* Search control will show the result of the search with a pin and infowindow (cartodb/#4914). - -3.15 (24//06//2015) -* cartodb.js knows how to work with multiple types of sublayers (#508): - * cartodb.createLayer accepts a `filter` option to specify wich types of layers must - be rendered in the tiles. WARNING: all non-torque layers will be rendered by default. - * cartodb.js uses metadata from Windshaft to determine what layers are present in the - map and specify the layer indexes in the tile URLs. More about this - [here](https://github.com/CartoDB/Windshaft-cartodb/blob/488c2462229474db21ba40b61a93edf83e6493b5/docs/Map-API.md#blending-and-layer-selection) - * New subclasses of SubLayer for different types of sublayers. -* Handle hidden layers properly when fetching attributes from tiler -* Make the torque slider have the correct range every time it changes -* Remove check for http-beginng vizjson addresses -* Use local time in timeslider instead of UTC -* New sublayer.isVisible() function -* `cartodb.createLayer` selects the first data layer instead of assuming that it's in position 1 - -3.14.6 (16//06//2015) -* Use the right indexes when fetching grids and attributes (#518) - -3.14.4 (10//06//2015) -* Do not enable layer interaction if tooltip is empty (#513) -* Replaces minified carto.js with uncompressed version (#516) - -3.14.3 (29//05//2015) -* Hide tag of infowindow covers when the url is invalid. -* Expose legend model in sublayers/layers so that users can customize legends (#480). -* Handle tooltip overflow (#482). -* Only show tooltips when they have fields (#486). -* Updated Torque to 2.11.3 -* Fix scrolling of infowindows with images (#490). -* Fix dropdown bind events not being unbound on clean (#493) - -3.14.2 (06//05//2015) -* Allow to specify a template for the items of a custom legend. -* The NOKIA geocoder doesn't encode the whitespaces anymore. -* Adds documentation for the Static Map API - -3.14.1 (30//04//2015) -* Fixes a bug that prevented setting the maxZoom and minZoom of a map. -* Updates Torque to 2.11.2 - -3.14.0 (23//04//2015) -* Infowindow in anonymous maps are requested by attributes endpoint in maps api so SQL API is not used anymore -* Changed the way remote host is set for maps and sql API. -* Fixed error management when map instanciation fails -* Instead of showing a single date, Torque's timeslider shows the date range that a single step comprises. -* Fixed enabling or disabling the torque loop property not working from cartodb.js -* Allows to specify a step when generating a static map of a Torque layer -* Deprecation warning: - - tiler_host, tiler_prototol, tiler_port, sql_api_domain, sql_api_protocol are deprecated, use sql_api_template and maps_api_template instead. https://github.com/CartoDB/cartodb.js/blob/develop/doc/API.md#how-to-set-a-different-host-than-cartodbcom - -3.13.3 (09//04//2015) -* Fixes default styles for header titles in infowindows. - -3.13.2 (07//04//2015) -* Fix double escaping on infowindows -* Fix a-tag's target attribute not working - -3.13.1 (06//04//2015) -* Allows to request a Static Map of a password protected visualization - -3.13.0 (31//03//2015) -* Breaking Changes - - Sanitize output by default (#2972), see doc change and example below how to override: - - docs: https://github.com/CartoDB/cartodb.js/blob/v3.13.0/doc/API.md#arguments-11 - - example: https://github.com/CartoDB/cartodb.js/blob/v3.13.0/examples/infowindow_with_graph.html - -3.12.14 (30//03//2015) -- Fixes fullscreen button is throwing errors (#412) -- Updates Torque.js to 2.11 - -3.12.13 (18//03//2015) -- Changes how infowindows handle null values (#406) -- Updates the version of wax and upgrades mustache.js to v1.1.0 (403) -- Fixes a bug with fullscreen in Safari (#361) - -3.12.12 (12//03//2015) -- Fixes a bug that prevented generating previews of torque layers with named maps - -3.12.11 (04//03//2015) -- LayerDefinition now trusts the tiler and uses whatever CDN configuration it gets (or nothing, if cdn_url is empty). -- Fixes bootstrap collisions (#87, #107) - -3.12.10 (02//03//2015) -- Don't send the urlTemplate to generate a Static Map if we don't have it. -- Disables the CDN if the server doesn't send us the configuration. - -3.12.9 (26//02//2015) -- Updates Static Map module to use the CDN URL from the layergroup. - -3.12.8 (26//02//2015) -- Allows to override the default use of the bounding box to generate an image, using the center instead. -- Fixes the static map module to avoid using hidden layers to generate images. -- Extracts the CDN host configuration from the vizjson. -- Removes cdbui bower dependency. - -3.12.7 (23//02//2015) -- By default we now serve the Static API images through CartoDB's CDN. - -3.12.6 (23//02//2015) -- Fixes mobile and IE interaction issues (#346, #313, #223, #139). - -3.12.5 (20//02/2015) -- Fixes request to generate an image when the vizjson contains a named map and a torque layer with a named map - -3.12.4 (18//02/2015) -- Fixes leaflet point generation on events when using touch devices - -3.12.3 (17//02/2015) -- Fixes a case were having an empty bbox would end up generatign an erroneous bounding box URL. - -3.12.2 (17//02/2015) -- Fixes error generating a map preview of a visualization with a torque layer. -- Fixed use of https parameter in torque layer -- Fixed change of play/pause state in timeslider -- Fixed legend values named 0 being evaluated as NULL - -3.12.1 (13//02/2015) -- Allows to force the https protocol when requesting a vizjson to generate a static image - -3.12.0 (09//02/2015) -- Added Odyssey support for visualizations -- Adds new API to generate static images (https://github.com/CartoDB/cartodb.js/wiki/CartoDB-Map-API) -- Fixes the hiding of the tile loader in mobile -- Adds heatmap support for torque - -3.11.36 (09//02//2014) -- Fixes slider style problem in narrower devices. - -3.11.35 (06//02//2014) -- re-fixes google maps mobile events - -3.11.34 (06//02//2014) -- Fixes google maps mobile events - -3.11.33 (05//02//2014) -- Fixes tooltip style. - -3.11.32 (29//01//2015) -- Fixed touch events on mobile (Android) - -3.11.31 (23//01//2015) -- #291 - Removes padding and margin reset for webkit browsers - -3.11.30 (13//01//2015) -- #264 - Fix addTo (when the second param specifies index) - -3.11.29 (30//12//2014) -- #257 - Fixes rendering of several bold typefaces - -3.11.28 (19//12//2014) -- #256 - Fixes loader position -- #255 - Adds new fonts for the overlays - -3.11.27 (19//12//2014) -- #245 - Fixed a bug with error messages named map instantiation -- #224 - Public method close infowindow - -3.11.26 (17//12//2014) -- #235 - Allows to use the input fields in fullscreen on Chrome -- #243 - Adds a target="_top" in the overlay links so they work inside iframes -- udpated torque with bugfixes for firefox - -3.11.25 (26//11//2014) -- #211 - Viz made with Torque between 2 different dates shows date + time -- #223 - fixed problem with IE11 touch devices. -- #205 - fixed problem with invalid lat lng object in touch devices. - -3.11.24 (11//11//2014) -- don't render the fullscreen overlay for unsupported versions of IE -- fixed using same callback name when there are more than one layer (#186) -- added new params options to cartodb.createVis(): gmaps_base_type and gmaps_style -- deprecate GMaps support, substitute GMaps basemaps with equivalent ones for Leaflet instead (#188) -- fixes default height for itensity list elements in mobile - -3.11.23 (04//11//2014) -- fixes rendering issue with category legends that contain long names -- adds .toggle() method to layers and sublayers to change their visibility - -3.11.22 (03//11//2014) -- fixes a bug that made the hidden Torque layers visible - -3.11.21 (24//10//2014) -- enabled dynamic_cdn to route layergroup calls through the CDN - -3.11.20 (24//10//2014) -- enabled fixed callback for layergroups and infowindows - -3.11.19 (23//10//2014) -- fixes annotation specs -- adds several methods to set the annotation properties. - -3.11.18 (22//10//2014) -- adds annotation overlays - -3.11.17 (20//10//2014) -- fixes positioning of the search and share overlays on the screen -- fixed compatibility with mootools -- fixes a problem with touch devices using two fingers for zooming. - -3.11.16 (10//10//2014) -- applies the z-index to the text and image overlays - -3.11.15 (07//10//2014) -- fixes a display issue with overlays in desktop. -- fixed compatibility with mootools - -3.11.14 (06//10//2014) -- adds stats_tag for all request in the url -- mobile layout fixes: - - small CSS fixes - - fixes issues activating legends, layer_selectors and search - - setting the force_mobile to false disables the mobile layout - - adds specs - -3.11.13 (29//09//2014) -- fixes the scope of the backdrop element in the CSS file - -3.11.12 (29//09//2014) -- fixes a bug that prevented showing the torque slider - -3.11.11 (29//09//2014) -- fixes a bug that prevented dragging google maps with the mobile layout activated - -3.11.10 (29//09//2014) -- fixes a bug that prevented showing the legend using the createLayer method - -3.11.09 (29//09//2014) -- adds mobile layout - -3.11.08 (21//09//2014) -- updated torque module with speed optimizations - -3.11.07 (15//09/2014) -- Fixed problem breaking words in infowindow content. - -3.11.06 (12//09/2014) -- Fixed problem in infowindow showing horizontal scrollbar when it was not needed -- Fixed creating search overlay - -3.11.05 (20//08/2014) -- Added support for query_wrapper in torque layers - -3.11.04 (12//08/2014) -- Fixes ugly word break in text overlays. -- Updates leaflet to 0.7.3 - -3.11.03 (08//08/2014) -- Fixes rendering issues with webfonts. - -3.11.02 (07//08/2014) -- No longer sets the width to the text overlays. - -3.11.01 (07//08/2014) -- Improves text and image overlay positioning. - -3.11.0 (06//08/2014) -- If available visualization uses layer visibility settings from CartoDB viz.json. -- Map header styles changed. -- Support for new kind of overlays (text and image). - -3.10.2 (11//07/2014) -- Added instanciateCallback to allow to cache instanciation responses -- fixed rendering order in cdb.vis.addInfowindow (#126) -- torque tiles use cdn_url from windshaft - -3.10.1 (09//06//2014) -- Updated torque library -- Fixed showing "no data" on empty tooltips (#122) - -3.10.0 (04//06//2014) -- Fixed problem for already customized infowindows setting width property. - -3.9.08 (03//06//2014) -- New "liquid" infowindow style implemented. - -3.9.07 (03//06//2014) -- Fixed exception on hover for layers without tooltip -- Improved tooltip interaction -- Changed cartocss library to support marker-type "rectangle" -- Fixed setParam when there are no default params (#120) - -3.9.06 (25//05//2014) -- Allowfullscreen parameter added to iframe code - in share dialog. -- Fixes link style in embed header -- Enables custom legends in Torque. - -3.9.05 (19//05//2014) -- Fixed tileJSON method in cdb.Tiles -- Adds support for Markdown in descriptions - -3.9.04 (14//05//2014) -- Added position parameter in Tooltip overlay - -3.9.03 (14//05//2014) -- Added tooltip option in createLayer method - -3.9.02 (14//05//2014) -- Fixes torque width for small screens - -3.9.01 (14//05//2014) -- Fixed regression for mouseover event in layers - -3.9.00 (13//05//2014) -- indents HTML of legends -- fixed getSubLayer in core library -- added tooltip loading from viz.json - -3.8.11 (28//04/2014) -- adds new link to the visualization in the share dialog. - -3.8.10 (21//04/2014) -- fixed problem parsing map viz options when values are not valid -- fixed interaction in IE8 -- getCartoCSS and getSQL raise an exception for named maps -- fixed core library -- added url translation for https for cartodb basemaps - -3.8.09 (04//04/2014) -- fixed map instanciation when named map has no layer information - -3.8.08 (03//04/2014) -- fixed layer visibility - -3.8.07 (03//04/2014) -- fixed attribution position for gmaps -- fixed maps api request when all the layers are hidden -- fixed error in gmaps when tile loading raises an error -- fixed panBy on leaflet when torque layers are used - -3.8.06 (27//03//2014) -- fixed layer interaction is not disabled when sublayer is hidden - -3.8.05 (25//03/2014) -- update torque library -- fixed interaction with naned maps when there is a hidden layer -- added multiple metrics - -3.8.04 (20//03//2014) -- prevent the scrolling of the map when the user scrolls the infowindow content. -- enables the scrollwheel when the user enters in the fullscreen model. -- fixes the embed_map url in the share dialog. -- raised leaflet maxZoom from 18 to 30 -- fixed setting interactivity in private layers should raise an exception (#108) -- added metrics for tile and layergroup loading time - -3.8.03 (15//03//2014) -- fixed addCursorInteraction -- fixed fieldCount when there are no fields in infowindow - -3.8.02 (14//03//2014) -- use cdn_url from tiler requests -- use https to fetch infowindow data when https is used -- changes default target for the fullscreen option in embeds - -3.8.01 (13//03/2014) -- fixed nokia https to http url rewrite - -3.8.00 (11//03//2014) -- Added mouseover and mouseout for layers -- Fixed error in old IE browsers for torque visualizations. -- Changed CartoDB attribution style under google maps. - -3.7.07 (10//03//2014) -- Fixes infowindow placement in fullscreen mode. - -3.7.06 (07//03//2014) -- alternate_names in infowindow was not being honored - -3.7.05 (06//03//2014) -- Added setParams method to layer to support named maps (#106) -- fixed problems with infowindow when there are hidden layers - -3.7.04 (27//02//2014) -- fixed layer update in gmaps -- when jsonp is used errors are not reported to the layer -- updated torque, fix problem with some cartocss options (step) - -3.7.03 (25//02//2014) -- Fixed https in torque tiles - -3.7.02 (25//02//2014) -- Fixed auth_token fetching infowindow attributes -- updated torque library - -3.7.01 (25//02//2014) -- Fixed auth_token in torque layers -- Fixed time slider in torque layers -- Fixed auth_token fetching attributes - -3.7.00 (24//02/2014) -- Added support for named maps -- Added cartodb.noleaflet.js to build (#105) - -3.6.02 (18//02//2014) -- Adds profiling support for plugable backends - -3.6.01 (13//02//2014) -- Fixes a call to window.addEventListener in IE8. -- Adds fullscreen detection. - -3.6.00 (31//01//2014) -- Using Leaflet 0.7.2 -- Adjusts the map header after the device is rotated -- Fixes map header when there's no title & description - -3.5.07 (28//01//2014) -- fixed fetching twice updated_at in torque layers - -3.5.06 (23//01//2014) -- Fixed IE7 - -3.5.05 (14//01//2014) ------ -- Removed animation while dragging a marker under GMaps. -- Added retina icons -- Enable interactivity when tooltip is added fixed #92 #64 -- Fixed torque styles when zoom was used in cartocss - -3.5.04 (20//12//2013) ------ -- Added attribution for torque layers. - -3.5.03 (18//12/2013) ------ -- updates twitter share message for mobile devices - -3.5.02 (17//12/2013) ------ -- improves twitter share message - -3.5.01 (17//12/2013) ------ -- fixes a bug that prevented using the scrolling wheel to zoom in and out - -3.5.00 (16//12/2013) ------ -- improves legends and torque player UI in mobile displays. -- allows passing extra params in the calls to the SQL API. -- changed profiler API. - -3.4.03 (11//12/2013) ------ -- fixes a bug that prevented showing a legend with custom HTML - -3.4.02 (10//12/2013) ------ -- adds new API for legends (documentation coming soon) -- fixes a bug that incorrectly rendered an empty legend -- fixes a bug that prevented showing the layer alias in torque layers -- adds a new time_slider example - -3.4.01 (26//11/2013) ------ -- fixed parsing keyword arguments in cartocss for torque - -3.4.00 (26//11/2013) ------ -- release of Torque Cumulative. -- enables max and min zoom for Google Maps. -- fixed URL of one asset in the examples directory. - -3.3.05 (20//11/2013) ------ -- fixed torque problems with cached sql requests #81 - -3.3.04 (15//11/2013) ------ -- sets maxZoom of GMaps layers to a high value to use the one defined by Google -- update GMaps layers specs - -3.3.03 (15//11/2013) ------ -- fixes a bug that prevented the triggering of callbacks after setting properties to cdb.geo.GMapsBaseLayer. - -3.3.02 (14//11/2013) ------ -- we don't set maxZoom in GMaps layers anymore. -- adds support for WMS layers. - -3.3.01 (14//11/2013) ------ -- added CartoDB logo in torque layers - -3.3.00 (11//11/2013) ------ -- torque support - -3.2.06 (04//11/2013) ------ -- adjusts the max and min zoom for each layer - -3.2.05 (04//11/2013) ------ -- correctly shows false values in the category legend. -- prepares the legends to support images - -3.2.04 (15//10/2013) ------ -- enable image basemaps - -3.2.03 (14//10/2013) ------ -- changed CDN urls - -3.2.02 (10//10/2013) ------ -- fixed click propagation in legends. - -3.2.01 (10//09/2013) ------ -- fixed bug that prevented the use of google charts urls in the infowindow covers. -- fixed geocoder specs - -3.2.00 (09//30/2013) ------ -- ported to leaflet 0.6 #55 - -3.1.14 (09//24/2013) ------ -- fixed problem with IE9 when the map has only one layer - -3.1.13 (09//18/2013) ------ -- new custom infowindow html available for visualization. -- problems editing polygon and linestring geojson. - -3.1.12 (09//11/2013) ------ -- fixed problem when an embed GMaps/Leaflet map is hidden (#70) - -3.1.11 (09//10/2013) ------ -- fixed problem when an embed GMaps map is hidden (#70) - -3.1.10 (09//10/2013) ------ -- fixed problem with infowindow option in createVis (#69) - -3.1.09 (09//06/2013) ------ -- fixed problem when the number of layers is different than the number of legends (refix) - -3.1.08 (09//06/2013) ------ -- fixed problem when the number of layers is different than the number of legends - -3.1.07 (09//03/2013) ------ -- fixed interactiviy in IE9 with more than one layer -- removed extra comma in layer selector (IE fix) - -3.1.06 (09//02/2013) ------ -- fixed #66 layer interactivity was wrong when a layer was hidden - -3.1.05 (08//08/2013) ------ -- Adds addInfowindow and addCursorInteraction -- changes layergroup request to use GET when is possible - -3.1.04 (08//08/2013) ------ -- Adds styles for NoneLegend - -3.1.03 (08//08/2013) ------ -- Prevents showing empty legends. - -3.1.02 (08//07/2013) ------ -- Flips the order of the legends. - -3.1.01 (08//06/2013) ------ -- Fixes the order of the legends. - -3.1.00 (08//06/2013) ------ -- added legends support - -3.0.05 (07//18//2013) ------ -- infowindow templates can be functions - -3.0.04 (07//18//2013) ------ -- fixed IE8 cors checking - -3.0.03 (07//17//2013) ------ -- fixed collision with older jQuery version -- fixed infowindows when there is no interaction enabled when loading from viz.json - -3.0.02 (07//11//2013) ------ -- fixed sublayer_options - -3.0.01 (07//11//2013) ------ -- added sublayer_options -- fixed compatibility with older viz.json - -3.0.00 (07//09//2013) ------ -- release v3 version -- multilayer support -- major refactor, backwards incompatible - -2.0.28 (04//17//2013) ------ -- Fixed infowindow position when a map is in a scroll page. -- Added a new example (scroll_map). - -2.0.27 (04//15//2013) ------ -- Fixed infowindow content (#47). - -2.0.26 (04//15//2013) ------ -- Fixed interaction for IE10 browsers (#43). -- Fixed https option in createLayer (#46). - -2.0.25 (03//22//2013) ------ -- Fixed #37 featureOut is called when the cursor moves between tiles. -- Fixed #38 Infowindow isn't working using 'createVis' function without any parameter. -- Fixed #27 IE styles included in main css file. - -2.0.24 (03//13//2013) ------ -- Added option to control map scrollwheel zoom. -- Loading content in infowindow bug fixed. -- New classes applied to CartoDB map components avoiding other css collisions. - -2.0.23 (03//04//2013) ------ -- Fixed infowindow bug with cover image checking number fields as url. -- Added template_name in the infowindow model for vis.js. - -2.0.22 (03//01//2013) ------ -- Added cartodb.nojquery.js to the cdn. -- Infowindow crops text when it is too large in infowindows headers. -- Infowindow converts links automatically. -- Added retina CartoDB logo. -- Fixed problem with leaflet markers image paths. -- Fixed infowindow option in createVis #31. - -2.0.21 (02//19//2013) ------ -- Fixed problem with interaction in IE9. - -2.0.20 (02//13//2013) ------ -- Fixed problem with setOpacity in IE8. - -2.0.19 (02//13//2013) ------ -- Fixed problem with setOpacity in IE7 and IE8. It replaces leaflet with a custom one. - -2.0.18 (02//12//2013) ------ -- Fixed problem when loading leaflet externally. - -2.0.17 (02//11//2013) ------ -- Fixed problem with hide method on layers for IE8. -- Migrated to leaflet 0.5.1. -- Fixed problem guessing map type in createLayer. -- Fixed showing null values in the infowindow. - -2.0.16 (01//31//2013) ------ -- Added support for new infowindow' theme: 'header with image'. -- Fixed loading more than one viz.json in the same application. -- Documentation fixes. - -2.0.15 (01//14//2013) ------ -- Fixed problem fetching viz.json when createVis and createLayer are called in the same script. - -2.0.14 (01//11//2013) ------ -- Improvements in the documentation. -- Reduced the final file size by 58kb. -- Added cartodb_logo option to remove cartodb logo on visualizations . -- Fixed problem with the map always in fullscreen (#20). -- Fixed bootstrap conflicts (#16). -- Fixed autobounds in the map when user calls to createLayer (#11). diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000000..4270e643ee --- /dev/null +++ b/NEWS.md @@ -0,0 +1,819 @@ +3.15.X (dd/mm/yyyy) +------ +* + +3.15.6 (17/09/2015) +------ +* Fixed a couple of bugs related with Leaflet attributions [#681](https://github.com/CartoDB/cartodb.js/issues/681) + +3.15.5 (15/09/2015) +------ +* Fixed infowindows in maps with fixed position [#639](https://github.com/CartoDB/cartodb.js/issues/639) +* Automatically select "torque" layers when no index is specified in cartodb.createLayer [#678](https://github.com/CartoDB/cartodb.js/issues/678) + +3.15.4 (11/09/2015) +------ +* Add checker to fullscreen button when it is rendered in an iframe [#674](https://github.com/CartoDB/cartodb.js/pull/674) + +3.15.3 (08/09/2015) +------ +* Display custom attribution of layers (#5216). +* Updated grunt-contrib-imagemin package version. + +3.15.2 (01/09/2015) +------ +* Take `visible` attribute into account when determining visibility of layers and serializing maps (#546) +* Only show legends if the layer is visible (#651) +* Extracted pecan code to separate module, https://github.com/CartoDB/pecan/ (#649,#654) +* Search control will show the result of the search with a pin and infowindow (cartodb/#4914). + +3.15.0 (24/06/2015) +------ +* cartodb.js knows how to work with multiple types of sublayers (#508): + * cartodb.createLayer accepts a `filter` option to specify wich types of layers must + be rendered in the tiles. WARNING: all non-torque layers will be rendered by default. + * cartodb.js uses metadata from Windshaft to determine what layers are present in the + map and specify the layer indexes in the tile URLs. More about this + [here](https://github.com/CartoDB/Windshaft-cartodb/blob/488c2462229474db21ba40b61a93edf83e6493b5/docs/Map-API.md#blending-and-layer-selection) + * New subclasses of SubLayer for different types of sublayers. +* Handle hidden layers properly when fetching attributes from tiler +* Make the torque slider have the correct range every time it changes +* Remove check for http-beginng vizjson addresses +* Use local time in timeslider instead of UTC +* New sublayer.isVisible() function +* `cartodb.createLayer` selects the first data layer instead of assuming that it's in position 1 + +3.14.6 (16/06/2015) +------ +* Use the right indexes when fetching grids and attributes (#518) + +3.14.4 (10/06/2015) +------ +* Do not enable layer interaction if tooltip is empty (#513) +* Replaces minified carto.js with uncompressed version (#516) + +3.14.3 (29/05/2015) +------ +* Hide tag of infowindow covers when the url is invalid. +* Expose legend model in sublayers/layers so that users can customize legends (#480). +* Handle tooltip overflow (#482). +* Only show tooltips when they have fields (#486). +* Updated Torque to 2.11.3 +* Fix scrolling of infowindows with images (#490). +* Fix dropdown bind events not being unbound on clean (#493) + +3.14.2 (06/05/2015) +------ +* Allow to specify a template for the items of a custom legend. +* The NOKIA geocoder doesn't encode the whitespaces anymore. +* Adds documentation for the Static Map API + +3.14.1 (30/04/2015) +------ +* Fixes a bug that prevented setting the maxZoom and minZoom of a map. +* Updates Torque to 2.11.2 + +3.14.0 (23/04/2015) +------ +* Infowindow in anonymous maps are requested by attributes endpoint in maps api so SQL API is not used anymore +* Changed the way remote host is set for maps and sql API. +* Fixed error management when map instanciation fails +* Instead of showing a single date, Torque's timeslider shows the date range that a single step comprises. +* Fixed enabling or disabling the torque loop property not working from cartodb.js +* Allows to specify a step when generating a static map of a Torque layer +* Deprecation warning: + - tiler_host, tiler_prototol, tiler_port, sql_api_domain, sql_api_protocol are deprecated, use sql_api_template and maps_api_template instead. https://github.com/CartoDB/cartodb.js/blob/develop/doc/API.md#how-to-set-a-different-host-than-cartodbcom + +3.13.3 (09/04/2015) +------ +* Fixes default styles for header titles in infowindows. + +3.13.2 (07/04/2015) +------ +* Fix double escaping on infowindows +* Fix a-tag's target attribute not working + +3.13.1 (06/04/2015) +------ +* Allows to request a Static Map of a password protected visualization + +3.13.0 (31/03/2015) +------ +* Breaking Changes + - Sanitize output by default (#2972), see doc change and example below how to override: + - docs: https://github.com/CartoDB/cartodb.js/blob/v3.13.0/doc/API.md#arguments-11 + - example: https://github.com/CartoDB/cartodb.js/blob/v3.13.0/examples/infowindow_with_graph.html + +3.12.14 (30/03/2015) +------ +* Fixes fullscreen button is throwing errors (#412) +* Updates Torque.js to 2.11 + +3.12.13 (18/03/2015) +------ +* Changes how infowindows handle null values (#406) +* Updates the version of wax and upgrades mustache.js to v1.1.0 (403) +* Fixes a bug with fullscreen in Safari (#361) + +3.12.12 (12/03/2015) +------ +* Fixes a bug that prevented generating previews of torque layers with named maps + +3.12.11 (04/03/2015) +------ +* LayerDefinition now trusts the tiler and uses whatever CDN configuration it gets (or nothing, if cdn_url is empty). +* Fixes bootstrap collisions (#87, #107) + +3.12.10 (02/03/2015) +------ +* Don't send the urlTemplate to generate a Static Map if we don't have it. +* Disables the CDN if the server doesn't send us the configuration. + +3.12.9 (26/02/2015) +------ +* Updates Static Map module to use the CDN URL from the layergroup. + +3.12.8 (26/02/2015) +------ +* Allows to override the default use of the bounding box to generate an image, using the center instead. +* Fixes the static map module to avoid using hidden layers to generate images. +* Extracts the CDN host configuration from the vizjson. +* Removes cdbui bower dependency. + +3.12.7 (23/02/2015) +------ +* By default we now serve the Static API images through CartoDB's CDN. + +3.12.6 (23/02/2015) +------ +* Fixes mobile and IE interaction issues (#346, #313, #223, #139). + +3.12.5 (20//02/2015) +------ +* Fixes request to generate an image when the vizjson contains a named map and a torque layer with a named map + +3.12.4 (18//02/2015) +------ +* Fixes leaflet point generation on events when using touch devices + +3.12.3 (17//02/2015) +------ +* Fixes a case were having an empty bbox would end up generatign an erroneous bounding box URL. + +3.12.2 (17//02/2015) +------ +* Fixes error generating a map preview of a visualization with a torque layer. +* Fixed use of https parameter in torque layer +* Fixed change of play/pause state in timeslider +* Fixed legend values named 0 being evaluated as NULL + +3.12.1 (13//02/2015) +------ +* Allows to force the https protocol when requesting a vizjson to generate a static image + +3.12.0 (09//02/2015) +------ +* Added Odyssey support for visualizations +* Adds new API to generate static images (https://github.com/CartoDB/cartodb.js/wiki/CartoDB-Map-API) +* Fixes the hiding of the tile loader in mobile +* Adds heatmap support for torque + +3.11.36 (09/02/2014) +------ +* Fixes slider style problem in narrower devices. + +3.11.35 (06/02/2014) +------ +* re-fixes google maps mobile events + +3.11.34 (06/02/2014) +------ +* Fixes google maps mobile events + +3.11.33 (05/02/2014) +------ +* Fixes tooltip style. + +3.11.32 (29/01/2015) +------ +* Fixed touch events on mobile (Android) + +3.11.31 (23/01/2015) +------ +- #291 - Removes padding and margin reset for webkit browsers + +3.11.30 (13/01/2015) +------ +- #264 - Fix addTo (when the second param specifies index) + +3.11.29 (30/12/2014) +------ +- #257 - Fixes rendering of several bold typefaces + +3.11.28 (19/12/2014) +------ +- #256 - Fixes loader position +- #255 - Adds new fonts for the overlays + +3.11.27 (19/12/2014) +------ +* #245 - Fixed a bug with error messages named map instantiation +* #224 - Public method close infowindow + +3.11.26 (17/12/2014) +------ +* #235 - Allows to use the input fields in fullscreen on Chrome +* #243 - Adds a target="_top" in the overlay links so they work inside iframes +* udpated torque with bugfixes for firefox + +3.11.25 (26/11/2014) +------ +* #211 - Viz made with Torque between 2 different dates shows date + time +* #223 - fixed problem with IE11 touch devices. +- #205 - fixed problem with invalid lat lng object in touch devices. + +3.11.24 (11/11/2014) +------ +* don't render the fullscreen overlay for unsupported versions of IE +* fixed using same callback name when there are more than one layer (#186) +* added new params options to cartodb.createVis(): gmaps_base_type and gmaps_style +* deprecate GMaps support, substitute GMaps basemaps with equivalent ones for Leaflet instead (#188) +* fixes default height for itensity list elements in mobile + +3.11.23 (04/11/2014) +------ +* fixes rendering issue with category legends that contain long names +* adds .toggle() method to layers and sublayers to change their visibility + +3.11.22 (03/11/2014) +------ +* fixes a bug that made the hidden Torque layers visible + +3.11.21 (24/10/2014) +------ +* enabled dynamic_cdn to route layergroup calls through the CDN + +3.11.20 (24/10/2014) +------ +* enabled fixed callback for layergroups and infowindows + +3.11.19 (23/10/2014) +------ +* fixes annotation specs +* adds several methods to set the annotation properties. + +3.11.18 (22/10/2014) +------ +* adds annotation overlays + +3.11.17 (20/10/2014) +------ +* fixes positioning of the search and share overlays on the screen +* fixed compatibility with mootools +* fixes a problem with touch devices using two fingers for zooming. + +3.11.16 (10/10/2014) +------ +* applies the z-index to the text and image overlays + +3.11.15 (07/10/2014) +------ +* fixes a display issue with overlays in desktop. +* fixed compatibility with mootools + +3.11.14 (06/10/2014) +------ +* adds stats_tag for all request in the url +* mobile layout fixes: + - small CSS fixes + - fixes issues activating legends, layer_selectors and search + - setting the force_mobile to false disables the mobile layout + - adds specs + +3.11.13 (29/09/2014) +------ +* fixes the scope of the backdrop element in the CSS file + +3.11.12 (29/09/2014) +------ +* fixes a bug that prevented showing the torque slider + +3.11.11 (29/09/2014) +------ +* fixes a bug that prevented dragging google maps with the mobile layout activated + +3.11.10 (29/09/2014) +------ +* fixes a bug that prevented showing the legend using the createLayer method + +3.11.09 (29/09/2014) +------ +* adds mobile layout + +3.11.08 (21/09/2014) +------ +* updated torque module with speed optimizations + +3.11.07 (15//09/2014) +------ +* Fixed problem breaking words in infowindow content. + +3.11.06 (12//09/2014) +------ +* Fixed problem in infowindow showing horizontal scrollbar when it was not needed +* Fixed creating search overlay + +3.11.05 (20//08/2014) +------ +* Added support for query_wrapper in torque layers + +3.11.04 (12//08/2014) +------ +* Fixes ugly word break in text overlays. +* Updates leaflet to 0.7.3 + +3.11.03 (08//08/2014) +------ +* Fixes rendering issues with webfonts. + +3.11.02 (07//08/2014) +------ +* No longer sets the width to the text overlays. + +3.11.01 (07//08/2014) +------ +* Improves text and image overlay positioning. + +3.11.0 (06//08/2014) +------ +* If available visualization uses layer visibility settings from CartoDB viz.json. +* Map header styles changed. +* Support for new kind of overlays (text and image). + +3.10.2 (11//07/2014) +------ +* Added instanciateCallback to allow to cache instanciation responses +* fixed rendering order in cdb.vis.addInfowindow (#126) +* torque tiles use cdn_url from windshaft + +3.10.1 (09/06/2014) +------ +* Updated torque library +* Fixed showing "no data" on empty tooltips (#122) + +3.10.0 (04/06/2014) +------ +* Fixed problem for already customized infowindows setting width property. + +3.9.08 (03/06/2014) +------ +* New "liquid" infowindow style implemented. + +3.9.07 (03/06/2014) +------ +* Fixed exception on hover for layers without tooltip +* Improved tooltip interaction +* Changed cartocss library to support marker-type "rectangle" +* Fixed setParam when there are no default params (#120) + +3.9.06 (25/05/2014) +------ +* Allowfullscreen parameter added to iframe code + in share dialog. +* Fixes link style in embed header +* Enables custom legends in Torque. + +3.9.05 (19/05/2014) +------ +* Fixed tileJSON method in cdb.Tiles +* Adds support for Markdown in descriptions + +3.9.04 (14/05/2014) +------ +* Added position parameter in Tooltip overlay + +3.9.03 (14/05/2014) +------ +* Added tooltip option in createLayer method + +3.9.02 (14/05/2014) +------ +* Fixes torque width for small screens + +3.9.01 (14/05/2014) +------ +* Fixed regression for mouseover event in layers + +3.9.00 (13/05/2014) +------ +* indents HTML of legends +* fixed getSubLayer in core library +* added tooltip loading from viz.json + +3.8.11 (28//04/2014) +------ +* adds new link to the visualization in the share dialog. + +3.8.10 (21//04/2014) +------ +* fixed problem parsing map viz options when values are not valid +* fixed interaction in IE8 +* getCartoCSS and getSQL raise an exception for named maps +* fixed core library +* added url translation for https for cartodb basemaps + +3.8.09 (04//04/2014) +------ +* fixed map instanciation when named map has no layer information + +3.8.08 (03//04/2014) +------ +* fixed layer visibility + +3.8.07 (03//04/2014) +------ +* fixed attribution position for gmaps +* fixed maps api request when all the layers are hidden +* fixed error in gmaps when tile loading raises an error +* fixed panBy on leaflet when torque layers are used + +3.8.06 (27/03/2014) +------ +* fixed layer interaction is not disabled when sublayer is hidden + +3.8.05 (25//03/2014) +------ +* update torque library +* fixed interaction with naned maps when there is a hidden layer +* added multiple metrics + +3.8.04 (20/03/2014) +------ +* prevent the scrolling of the map when the user scrolls the infowindow content. +* enables the scrollwheel when the user enters in the fullscreen model. +* fixes the embed_map url in the share dialog. +* raised leaflet maxZoom from 18 to 30 +* fixed setting interactivity in private layers should raise an exception (#108) +* added metrics for tile and layergroup loading time + +3.8.03 (15/03/2014) +------ +* fixed addCursorInteraction +* fixed fieldCount when there are no fields in infowindow + +3.8.02 (14/03/2014) +------ +* use cdn_url from tiler requests +* use https to fetch infowindow data when https is used +* changes default target for the fullscreen option in embeds + +3.8.01 (13//03/2014) +------ +* fixed nokia https to http url rewrite + +3.8.00 (11/03/2014) +------ +* Added mouseover and mouseout for layers +* Fixed error in old IE browsers for torque visualizations. +* Changed CartoDB attribution style under google maps. + +3.7.07 (10/03/2014) +------ +* Fixes infowindow placement in fullscreen mode. + +3.7.06 (07/03/2014) +------ +* alternate_names in infowindow was not being honored + +3.7.05 (06/03/2014) +------ +* Added setParams method to layer to support named maps (#106) +* fixed problems with infowindow when there are hidden layers + +3.7.04 (27/02/2014) +------ +* fixed layer update in gmaps +* when jsonp is used errors are not reported to the layer +* updated torque, fix problem with some cartocss options (step) + +3.7.03 (25/02/2014) +------ +* Fixed https in torque tiles + +3.7.02 (25/02/2014) +------ +* Fixed auth_token fetching infowindow attributes +* updated torque library + +3.7.01 (25/02/2014) +------ +* Fixed auth_token in torque layers +* Fixed time slider in torque layers +* Fixed auth_token fetching attributes + +3.7.00 (24//02/2014) +------ +* Added support for named maps +* Added cartodb.noleaflet.js to build (#105) + +3.6.02 (18/02/2014) +------ +* Adds profiling support for plugable backends + +3.6.01 (13/02/2014) +------ +* Fixes a call to window.addEventListener in IE8. +* Adds fullscreen detection. + +3.6.00 (31/01/2014) +------ +* Using Leaflet 0.7.2 +* Adjusts the map header after the device is rotated +* Fixes map header when there's no title & description + +3.5.07 (28/01/2014) +------ +* fixed fetching twice updated_at in torque layers + +3.5.06 (23/01/2014) +------ +* Fixed IE7 + +3.5.05 (14/01/2014) +------ +* Removed animation while dragging a marker under GMaps. +* Added retina icons +* Enable interactivity when tooltip is added fixed #92 #64 +* Fixed torque styles when zoom was used in cartocss + +3.5.04 (20/12/2013) +------ +* Added attribution for torque layers. + +3.5.03 (18//12/2013) +------ +* updates twitter share message for mobile devices + +3.5.02 (17//12/2013) +------ +* improves twitter share message + +3.5.01 (17//12/2013) +------ +* fixes a bug that prevented using the scrolling wheel to zoom in and out + +3.5.00 (16//12/2013) +------ +* improves legends and torque player UI in mobile displays. +* allows passing extra params in the calls to the SQL API. +* changed profiler API. + +3.4.03 (11//12/2013) +------ +* fixes a bug that prevented showing a legend with custom HTML + +3.4.02 (10//12/2013) +------ +* adds new API for legends (documentation coming soon) +* fixes a bug that incorrectly rendered an empty legend +* fixes a bug that prevented showing the layer alias in torque layers +* adds a new time_slider example + +3.4.01 (26//11/2013) +------ +* fixed parsing keyword arguments in cartocss for torque + +3.4.00 (26//11/2013) +------ +* release of Torque Cumulative. +* enables max and min zoom for Google Maps. +* fixed URL of one asset in the examples directory. + +3.3.05 (20//11/2013) +------ +* fixed torque problems with cached sql requests #81 + +3.3.04 (15//11/2013) +------ +* sets maxZoom of GMaps layers to a high value to use the one defined by Google +* update GMaps layers specs + +3.3.03 (15//11/2013) +------ +* fixes a bug that prevented the triggering of callbacks after setting properties to cdb.geo.GMapsBaseLayer. + +3.3.02 (14//11/2013) +------ +* we don't set maxZoom in GMaps layers anymore. +* adds support for WMS layers. + +3.3.01 (14//11/2013) +------ +* added CartoDB logo in torque layers + +3.3.00 (11//11/2013) +------ +* torque support + +3.2.06 (04//11/2013) +------ +* adjusts the max and min zoom for each layer + +3.2.05 (04//11/2013) +------ +* correctly shows false values in the category legend. +* prepares the legends to support images + +3.2.04 (15//10/2013) +------ +* enable image basemaps + +3.2.03 (14//10/2013) +------ +* changed CDN urls + +3.2.02 (10//10/2013) +------ +* fixed click propagation in legends. + +3.2.01 (10//09/2013) +------ +* fixed bug that prevented the use of google charts urls in the infowindow covers. +* fixed geocoder specs + +3.2.00 (09//30/2013) +------ +* ported to leaflet 0.6 #55 + +3.1.14 (09//24/2013) +------ +* fixed problem with IE9 when the map has only one layer + +3.1.13 (09//18/2013) +------ +* new custom infowindow html available for visualization. +* problems editing polygon and linestring geojson. + +3.1.12 (09//11/2013) +------ +* fixed problem when an embed GMaps/Leaflet map is hidden (#70) + +3.1.11 (09//10/2013) +------ +* fixed problem when an embed GMaps map is hidden (#70) + +3.1.10 (09//10/2013) +------ +* fixed problem with infowindow option in createVis (#69) + +3.1.09 (09//06/2013) +------ +* fixed problem when the number of layers is different than the number of legends (refix) + +3.1.08 (09//06/2013) +------ +* fixed problem when the number of layers is different than the number of legends + +3.1.07 (09//03/2013) +------ +* fixed interactiviy in IE9 with more than one layer +* removed extra comma in layer selector (IE fix) + +3.1.06 (09//02/2013) +------ +* fixed #66 layer interactivity was wrong when a layer was hidden + +3.1.05 (08//08/2013) +------ +* Adds addInfowindow and addCursorInteraction +* changes layergroup request to use GET when is possible + +3.1.04 (08//08/2013) +------ +* Adds styles for NoneLegend + +3.1.03 (08//08/2013) +------ +* Prevents showing empty legends. + +3.1.02 (08//07/2013) +------ +* Flips the order of the legends. + +3.1.01 (08//06/2013) +------ +* Fixes the order of the legends. + +3.1.00 (08//06/2013) +------ +* added legends support + +3.0.05 (07/18/2013) +------ +* infowindow templates can be functions + +3.0.04 (07/18/2013) +------ +* fixed IE8 cors checking + +3.0.03 (07/17/2013) +------ +* fixed collision with older jQuery version +* fixed infowindows when there is no interaction enabled when loading from viz.json + +3.0.02 (07/11/2013) +------ +* fixed sublayer_options + +3.0.01 (07/11/2013) +------ +* added sublayer_options +* fixed compatibility with older viz.json + +3.0.00 (07/09/2013) +------ +* release v3 version +* multilayer support +* major refactor, backwards incompatible + +2.0.28 (04/17/2013) +------- +* Fixed infowindow position when a map is in a scroll page. +* Added a new example (scroll_map). + +2.0.27 (04/15/2013) +------ +* Fixed infowindow content (#47). + +2.0.26 (04/15/2013) +------ +* Fixed interaction for IE10 browsers (#43). +* Fixed https option in createLayer (#46). + +2.0.25 (03/22/2013) +------ +* Fixed #37 featureOut is called when the cursor moves between tiles. +* Fixed #38 Infowindow isn't working using 'createVis' function without any parameter. +* Fixed #27 IE styles included in main css file. + +2.0.24 (03/13/2013) +------ +* Added option to control map scrollwheel zoom. +* Loading content in infowindow bug fixed. +* New classes applied to CartoDB map components avoiding other css collisions. + +2.0.23 (03/04/2013) +------ +* Fixed infowindow bug with cover image checking number fields as url. +* Added template_name in the infowindow model for vis.js. + +2.0.22 (03/01/2013) +------ +* Added cartodb.nojquery.js to the cdn. +* Infowindow crops text when it is too large in infowindows headers. +* Infowindow converts links automatically. +* Added retina CartoDB logo. +* Fixed problem with leaflet markers image paths. +* Fixed infowindow option in createVis #31. + +2.0.21 (02/19/2013) +------ +* Fixed problem with interaction in IE9. + +2.0.20 (02/13/2013) +------ +* Fixed problem with setOpacity in IE8. + +2.0.19 (02/13/2013) +------ +* Fixed problem with setOpacity in IE7 and IE8. It replaces leaflet with a custom one. + +2.0.18 (02/12/2013) +------ +* Fixed problem when loading leaflet externally. + +2.0.17 (02/11/2013) +------ +* Fixed problem with hide method on layers for IE8. +* Migrated to leaflet 0.5.1. +* Fixed problem guessing map type in createLayer. +* Fixed showing null values in the infowindow. + +2.0.16 (01/31/2013) +------ +* Added support for new infowindow' theme: 'header with image'. +* Fixed loading more than one viz.json in the same application. +* Documentation fixes. + +2.0.15 (01/14/2013) +------ +* Fixed problem fetching viz.json when createVis and createLayer are called in the same script. + +2.0.14 (01/11/2013) +------ +* Improvements in the documentation. +* Reduced the final file size by 58kb. +* Added cartodb_logo option to remove cartodb logo on visualizations . +* Fixed problem with the map always in fullscreen (#20). +* Fixed bootstrap conflicts (#16). +* Fixed autobounds in the map when user calls to createLayer (#11). From 54fc291c5dadb0f8966de91e287f6f0ac387b94b Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Tue, 22 Sep 2015 15:38:33 +0200 Subject: [PATCH 023/120] Removed NEWS --- NEWS | 713 ----------------------------------------------------------- 1 file changed, 713 deletions(-) delete mode 100644 NEWS diff --git a/NEWS b/NEWS deleted file mode 100644 index e90ff4233b..0000000000 --- a/NEWS +++ /dev/null @@ -1,713 +0,0 @@ -3.15.X (XX//YY//ZZZZ) -* Undefine `define` so that dependencies don't load via AMD [#543](https://github.com/CartoDB/cartodb.js/issues/543) - -3.15.6 (17//09//2015) -* Fixed a couple of bugs related with Leaflet attributions [#681](https://github.com/CartoDB/cartodb.js/issues/681) - -3.15.5 (15//09//2015) -* Fixed infowindows in maps with fixed position [#639](https://github.com/CartoDB/cartodb.js/issues/639) -* Automatically select "torque" layers when no index is specified in cartodb.createLayer [#678](https://github.com/CartoDB/cartodb.js/issues/678) - -3.15.4 (11//09//2015) -* Add checker to fullscreen button when it is rendered in an iframe [#674](https://github.com/CartoDB/cartodb.js/pull/674) - -3.15.3 (08//09//2015) -* Display custom attribution of layers (#5216). -* Updated grunt-contrib-imagemin package version. - -3.15.2 (01//09//2015) -* Take `visible` attribute into account when determining visibility of layers and serializing maps (#546) -* Only show legends if the layer is visible (#651) -* Extracted pecan code to separate module, https://github.com/CartoDB/pecan/ (#649,#654) -* Search control will show the result of the search with a pin and infowindow (cartodb/#4914). - -3.15 (24//06//2015) -* cartodb.js knows how to work with multiple types of sublayers (#508): - * cartodb.createLayer accepts a `filter` option to specify wich types of layers must - be rendered in the tiles. WARNING: all non-torque layers will be rendered by default. - * cartodb.js uses metadata from Windshaft to determine what layers are present in the - map and specify the layer indexes in the tile URLs. More about this - [here](https://github.com/CartoDB/Windshaft-cartodb/blob/488c2462229474db21ba40b61a93edf83e6493b5/docs/Map-API.md#blending-and-layer-selection) - * New subclasses of SubLayer for different types of sublayers. -* Handle hidden layers properly when fetching attributes from tiler -* Make the torque slider have the correct range every time it changes -* Remove check for http-beginng vizjson addresses -* Use local time in timeslider instead of UTC -* New sublayer.isVisible() function -* `cartodb.createLayer` selects the first data layer instead of assuming that it's in position 1 - -3.14.6 (16//06//2015) -* Use the right indexes when fetching grids and attributes (#518) - -3.14.4 (10//06//2015) -* Do not enable layer interaction if tooltip is empty (#513) -* Replaces minified carto.js with uncompressed version (#516) - -3.14.3 (29//05//2015) -* Hide tag of infowindow covers when the url is invalid. -* Expose legend model in sublayers/layers so that users can customize legends (#480). -* Handle tooltip overflow (#482). -* Only show tooltips when they have fields (#486). -* Updated Torque to 2.11.3 -* Fix scrolling of infowindows with images (#490). -* Fix dropdown bind events not being unbound on clean (#493) - -3.14.2 (06//05//2015) -* Allow to specify a template for the items of a custom legend. -* The NOKIA geocoder doesn't encode the whitespaces anymore. -* Adds documentation for the Static Map API - -3.14.1 (30//04//2015) -* Fixes a bug that prevented setting the maxZoom and minZoom of a map. -* Updates Torque to 2.11.2 - -3.14.0 (23//04//2015) -* Infowindow in anonymous maps are requested by attributes endpoint in maps api so SQL API is not used anymore -* Changed the way remote host is set for maps and sql API. -* Fixed error management when map instanciation fails -* Instead of showing a single date, Torque's timeslider shows the date range that a single step comprises. -* Fixed enabling or disabling the torque loop property not working from cartodb.js -* Allows to specify a step when generating a static map of a Torque layer -* Deprecation warning: - - tiler_host, tiler_prototol, tiler_port, sql_api_domain, sql_api_protocol are deprecated, use sql_api_template and maps_api_template instead. https://github.com/CartoDB/cartodb.js/blob/develop/doc/API.md#how-to-set-a-different-host-than-cartodbcom - -3.13.3 (09//04//2015) -* Fixes default styles for header titles in infowindows. - -3.13.2 (07//04//2015) -* Fix double escaping on infowindows -* Fix a-tag's target attribute not working - -3.13.1 (06//04//2015) -* Allows to request a Static Map of a password protected visualization - -3.13.0 (31//03//2015) -* Breaking Changes - - Sanitize output by default (#2972), see doc change and example below how to override: - - docs: https://github.com/CartoDB/cartodb.js/blob/v3.13.0/doc/API.md#arguments-11 - - example: https://github.com/CartoDB/cartodb.js/blob/v3.13.0/examples/infowindow_with_graph.html - -3.12.14 (30//03//2015) -- Fixes fullscreen button is throwing errors (#412) -- Updates Torque.js to 2.11 - -3.12.13 (18//03//2015) -- Changes how infowindows handle null values (#406) -- Updates the version of wax and upgrades mustache.js to v1.1.0 (403) -- Fixes a bug with fullscreen in Safari (#361) - -3.12.12 (12//03//2015) -- Fixes a bug that prevented generating previews of torque layers with named maps - -3.12.11 (04//03//2015) -- LayerDefinition now trusts the tiler and uses whatever CDN configuration it gets (or nothing, if cdn_url is empty). -- Fixes bootstrap collisions (#87, #107) - -3.12.10 (02//03//2015) -- Don't send the urlTemplate to generate a Static Map if we don't have it. -- Disables the CDN if the server doesn't send us the configuration. - -3.12.9 (26//02//2015) -- Updates Static Map module to use the CDN URL from the layergroup. - -3.12.8 (26//02//2015) -- Allows to override the default use of the bounding box to generate an image, using the center instead. -- Fixes the static map module to avoid using hidden layers to generate images. -- Extracts the CDN host configuration from the vizjson. -- Removes cdbui bower dependency. - -3.12.7 (23//02//2015) -- By default we now serve the Static API images through CartoDB's CDN. - -3.12.6 (23//02//2015) -- Fixes mobile and IE interaction issues (#346, #313, #223, #139). - -3.12.5 (20//02/2015) -- Fixes request to generate an image when the vizjson contains a named map and a torque layer with a named map - -3.12.4 (18//02/2015) -- Fixes leaflet point generation on events when using touch devices - -3.12.3 (17//02/2015) -- Fixes a case were having an empty bbox would end up generatign an erroneous bounding box URL. - -3.12.2 (17//02/2015) -- Fixes error generating a map preview of a visualization with a torque layer. -- Fixed use of https parameter in torque layer -- Fixed change of play/pause state in timeslider -- Fixed legend values named 0 being evaluated as NULL - -3.12.1 (13//02/2015) -- Allows to force the https protocol when requesting a vizjson to generate a static image - -3.12.0 (09//02/2015) -- Added Odyssey support for visualizations -- Adds new API to generate static images (https://github.com/CartoDB/cartodb.js/wiki/CartoDB-Map-API) -- Fixes the hiding of the tile loader in mobile -- Adds heatmap support for torque - -3.11.36 (09//02//2014) -- Fixes slider style problem in narrower devices. - -3.11.35 (06//02//2014) -- re-fixes google maps mobile events - -3.11.34 (06//02//2014) -- Fixes google maps mobile events - -3.11.33 (05//02//2014) -- Fixes tooltip style. - -3.11.32 (29//01//2015) -- Fixed touch events on mobile (Android) - -3.11.31 (23//01//2015) -- #291 - Removes padding and margin reset for webkit browsers - -3.11.30 (13//01//2015) -- #264 - Fix addTo (when the second param specifies index) - -3.11.29 (30//12//2014) -- #257 - Fixes rendering of several bold typefaces - -3.11.28 (19//12//2014) -- #256 - Fixes loader position -- #255 - Adds new fonts for the overlays - -3.11.27 (19//12//2014) -- #245 - Fixed a bug with error messages named map instantiation -- #224 - Public method close infowindow - -3.11.26 (17//12//2014) -- #235 - Allows to use the input fields in fullscreen on Chrome -- #243 - Adds a target="_top" in the overlay links so they work inside iframes -- udpated torque with bugfixes for firefox - -3.11.25 (26//11//2014) -- #211 - Viz made with Torque between 2 different dates shows date + time -- #223 - fixed problem with IE11 touch devices. -- #205 - fixed problem with invalid lat lng object in touch devices. - -3.11.24 (11//11//2014) -- don't render the fullscreen overlay for unsupported versions of IE -- fixed using same callback name when there are more than one layer (#186) -- added new params options to cartodb.createVis(): gmaps_base_type and gmaps_style -- deprecate GMaps support, substitute GMaps basemaps with equivalent ones for Leaflet instead (#188) -- fixes default height for itensity list elements in mobile - -3.11.23 (04//11//2014) -- fixes rendering issue with category legends that contain long names -- adds .toggle() method to layers and sublayers to change their visibility - -3.11.22 (03//11//2014) -- fixes a bug that made the hidden Torque layers visible - -3.11.21 (24//10//2014) -- enabled dynamic_cdn to route layergroup calls through the CDN - -3.11.20 (24//10//2014) -- enabled fixed callback for layergroups and infowindows - -3.11.19 (23//10//2014) -- fixes annotation specs -- adds several methods to set the annotation properties. - -3.11.18 (22//10//2014) -- adds annotation overlays - -3.11.17 (20//10//2014) -- fixes positioning of the search and share overlays on the screen -- fixed compatibility with mootools -- fixes a problem with touch devices using two fingers for zooming. - -3.11.16 (10//10//2014) -- applies the z-index to the text and image overlays - -3.11.15 (07//10//2014) -- fixes a display issue with overlays in desktop. -- fixed compatibility with mootools - -3.11.14 (06//10//2014) -- adds stats_tag for all request in the url -- mobile layout fixes: - - small CSS fixes - - fixes issues activating legends, layer_selectors and search - - setting the force_mobile to false disables the mobile layout - - adds specs - -3.11.13 (29//09//2014) -- fixes the scope of the backdrop element in the CSS file - -3.11.12 (29//09//2014) -- fixes a bug that prevented showing the torque slider - -3.11.11 (29//09//2014) -- fixes a bug that prevented dragging google maps with the mobile layout activated - -3.11.10 (29//09//2014) -- fixes a bug that prevented showing the legend using the createLayer method - -3.11.09 (29//09//2014) -- adds mobile layout - -3.11.08 (21//09//2014) -- updated torque module with speed optimizations - -3.11.07 (15//09/2014) -- Fixed problem breaking words in infowindow content. - -3.11.06 (12//09/2014) -- Fixed problem in infowindow showing horizontal scrollbar when it was not needed -- Fixed creating search overlay - -3.11.05 (20//08/2014) -- Added support for query_wrapper in torque layers - -3.11.04 (12//08/2014) -- Fixes ugly word break in text overlays. -- Updates leaflet to 0.7.3 - -3.11.03 (08//08/2014) -- Fixes rendering issues with webfonts. - -3.11.02 (07//08/2014) -- No longer sets the width to the text overlays. - -3.11.01 (07//08/2014) -- Improves text and image overlay positioning. - -3.11.0 (06//08/2014) -- If available visualization uses layer visibility settings from CartoDB viz.json. -- Map header styles changed. -- Support for new kind of overlays (text and image). - -3.10.2 (11//07/2014) -- Added instanciateCallback to allow to cache instanciation responses -- fixed rendering order in cdb.vis.addInfowindow (#126) -- torque tiles use cdn_url from windshaft - -3.10.1 (09//06//2014) -- Updated torque library -- Fixed showing "no data" on empty tooltips (#122) - -3.10.0 (04//06//2014) -- Fixed problem for already customized infowindows setting width property. - -3.9.08 (03//06//2014) -- New "liquid" infowindow style implemented. - -3.9.07 (03//06//2014) -- Fixed exception on hover for layers without tooltip -- Improved tooltip interaction -- Changed cartocss library to support marker-type "rectangle" -- Fixed setParam when there are no default params (#120) - -3.9.06 (25//05//2014) -- Allowfullscreen parameter added to iframe code - in share dialog. -- Fixes link style in embed header -- Enables custom legends in Torque. - -3.9.05 (19//05//2014) -- Fixed tileJSON method in cdb.Tiles -- Adds support for Markdown in descriptions - -3.9.04 (14//05//2014) -- Added position parameter in Tooltip overlay - -3.9.03 (14//05//2014) -- Added tooltip option in createLayer method - -3.9.02 (14//05//2014) -- Fixes torque width for small screens - -3.9.01 (14//05//2014) -- Fixed regression for mouseover event in layers - -3.9.00 (13//05//2014) -- indents HTML of legends -- fixed getSubLayer in core library -- added tooltip loading from viz.json - -3.8.11 (28//04/2014) -- adds new link to the visualization in the share dialog. - -3.8.10 (21//04/2014) -- fixed problem parsing map viz options when values are not valid -- fixed interaction in IE8 -- getCartoCSS and getSQL raise an exception for named maps -- fixed core library -- added url translation for https for cartodb basemaps - -3.8.09 (04//04/2014) -- fixed map instanciation when named map has no layer information - -3.8.08 (03//04/2014) -- fixed layer visibility - -3.8.07 (03//04/2014) -- fixed attribution position for gmaps -- fixed maps api request when all the layers are hidden -- fixed error in gmaps when tile loading raises an error -- fixed panBy on leaflet when torque layers are used - -3.8.06 (27//03//2014) -- fixed layer interaction is not disabled when sublayer is hidden - -3.8.05 (25//03/2014) -- update torque library -- fixed interaction with naned maps when there is a hidden layer -- added multiple metrics - -3.8.04 (20//03//2014) -- prevent the scrolling of the map when the user scrolls the infowindow content. -- enables the scrollwheel when the user enters in the fullscreen model. -- fixes the embed_map url in the share dialog. -- raised leaflet maxZoom from 18 to 30 -- fixed setting interactivity in private layers should raise an exception (#108) -- added metrics for tile and layergroup loading time - -3.8.03 (15//03//2014) -- fixed addCursorInteraction -- fixed fieldCount when there are no fields in infowindow - -3.8.02 (14//03//2014) -- use cdn_url from tiler requests -- use https to fetch infowindow data when https is used -- changes default target for the fullscreen option in embeds - -3.8.01 (13//03/2014) -- fixed nokia https to http url rewrite - -3.8.00 (11//03//2014) -- Added mouseover and mouseout for layers -- Fixed error in old IE browsers for torque visualizations. -- Changed CartoDB attribution style under google maps. - -3.7.07 (10//03//2014) -- Fixes infowindow placement in fullscreen mode. - -3.7.06 (07//03//2014) -- alternate_names in infowindow was not being honored - -3.7.05 (06//03//2014) -- Added setParams method to layer to support named maps (#106) -- fixed problems with infowindow when there are hidden layers - -3.7.04 (27//02//2014) -- fixed layer update in gmaps -- when jsonp is used errors are not reported to the layer -- updated torque, fix problem with some cartocss options (step) - -3.7.03 (25//02//2014) -- Fixed https in torque tiles - -3.7.02 (25//02//2014) -- Fixed auth_token fetching infowindow attributes -- updated torque library - -3.7.01 (25//02//2014) -- Fixed auth_token in torque layers -- Fixed time slider in torque layers -- Fixed auth_token fetching attributes - -3.7.00 (24//02/2014) -- Added support for named maps -- Added cartodb.noleaflet.js to build (#105) - -3.6.02 (18//02//2014) -- Adds profiling support for plugable backends - -3.6.01 (13//02//2014) -- Fixes a call to window.addEventListener in IE8. -- Adds fullscreen detection. - -3.6.00 (31//01//2014) -- Using Leaflet 0.7.2 -- Adjusts the map header after the device is rotated -- Fixes map header when there's no title & description - -3.5.07 (28//01//2014) -- fixed fetching twice updated_at in torque layers - -3.5.06 (23//01//2014) -- Fixed IE7 - -3.5.05 (14//01//2014) ------ -- Removed animation while dragging a marker under GMaps. -- Added retina icons -- Enable interactivity when tooltip is added fixed #92 #64 -- Fixed torque styles when zoom was used in cartocss - -3.5.04 (20//12//2013) ------ -- Added attribution for torque layers. - -3.5.03 (18//12/2013) ------ -- updates twitter share message for mobile devices - -3.5.02 (17//12/2013) ------ -- improves twitter share message - -3.5.01 (17//12/2013) ------ -- fixes a bug that prevented using the scrolling wheel to zoom in and out - -3.5.00 (16//12/2013) ------ -- improves legends and torque player UI in mobile displays. -- allows passing extra params in the calls to the SQL API. -- changed profiler API. - -3.4.03 (11//12/2013) ------ -- fixes a bug that prevented showing a legend with custom HTML - -3.4.02 (10//12/2013) ------ -- adds new API for legends (documentation coming soon) -- fixes a bug that incorrectly rendered an empty legend -- fixes a bug that prevented showing the layer alias in torque layers -- adds a new time_slider example - -3.4.01 (26//11/2013) ------ -- fixed parsing keyword arguments in cartocss for torque - -3.4.00 (26//11/2013) ------ -- release of Torque Cumulative. -- enables max and min zoom for Google Maps. -- fixed URL of one asset in the examples directory. - -3.3.05 (20//11/2013) ------ -- fixed torque problems with cached sql requests #81 - -3.3.04 (15//11/2013) ------ -- sets maxZoom of GMaps layers to a high value to use the one defined by Google -- update GMaps layers specs - -3.3.03 (15//11/2013) ------ -- fixes a bug that prevented the triggering of callbacks after setting properties to cdb.geo.GMapsBaseLayer. - -3.3.02 (14//11/2013) ------ -- we don't set maxZoom in GMaps layers anymore. -- adds support for WMS layers. - -3.3.01 (14//11/2013) ------ -- added CartoDB logo in torque layers - -3.3.00 (11//11/2013) ------ -- torque support - -3.2.06 (04//11/2013) ------ -- adjusts the max and min zoom for each layer - -3.2.05 (04//11/2013) ------ -- correctly shows false values in the category legend. -- prepares the legends to support images - -3.2.04 (15//10/2013) ------ -- enable image basemaps - -3.2.03 (14//10/2013) ------ -- changed CDN urls - -3.2.02 (10//10/2013) ------ -- fixed click propagation in legends. - -3.2.01 (10//09/2013) ------ -- fixed bug that prevented the use of google charts urls in the infowindow covers. -- fixed geocoder specs - -3.2.00 (09//30/2013) ------ -- ported to leaflet 0.6 #55 - -3.1.14 (09//24/2013) ------ -- fixed problem with IE9 when the map has only one layer - -3.1.13 (09//18/2013) ------ -- new custom infowindow html available for visualization. -- problems editing polygon and linestring geojson. - -3.1.12 (09//11/2013) ------ -- fixed problem when an embed GMaps/Leaflet map is hidden (#70) - -3.1.11 (09//10/2013) ------ -- fixed problem when an embed GMaps map is hidden (#70) - -3.1.10 (09//10/2013) ------ -- fixed problem with infowindow option in createVis (#69) - -3.1.09 (09//06/2013) ------ -- fixed problem when the number of layers is different than the number of legends (refix) - -3.1.08 (09//06/2013) ------ -- fixed problem when the number of layers is different than the number of legends - -3.1.07 (09//03/2013) ------ -- fixed interactiviy in IE9 with more than one layer -- removed extra comma in layer selector (IE fix) - -3.1.06 (09//02/2013) ------ -- fixed #66 layer interactivity was wrong when a layer was hidden - -3.1.05 (08//08/2013) ------ -- Adds addInfowindow and addCursorInteraction -- changes layergroup request to use GET when is possible - -3.1.04 (08//08/2013) ------ -- Adds styles for NoneLegend - -3.1.03 (08//08/2013) ------ -- Prevents showing empty legends. - -3.1.02 (08//07/2013) ------ -- Flips the order of the legends. - -3.1.01 (08//06/2013) ------ -- Fixes the order of the legends. - -3.1.00 (08//06/2013) ------ -- added legends support - -3.0.05 (07//18//2013) ------ -- infowindow templates can be functions - -3.0.04 (07//18//2013) ------ -- fixed IE8 cors checking - -3.0.03 (07//17//2013) ------ -- fixed collision with older jQuery version -- fixed infowindows when there is no interaction enabled when loading from viz.json - -3.0.02 (07//11//2013) ------ -- fixed sublayer_options - -3.0.01 (07//11//2013) ------ -- added sublayer_options -- fixed compatibility with older viz.json - -3.0.00 (07//09//2013) ------ -- release v3 version -- multilayer support -- major refactor, backwards incompatible - -2.0.28 (04//17//2013) ------ -- Fixed infowindow position when a map is in a scroll page. -- Added a new example (scroll_map). - -2.0.27 (04//15//2013) ------ -- Fixed infowindow content (#47). - -2.0.26 (04//15//2013) ------ -- Fixed interaction for IE10 browsers (#43). -- Fixed https option in createLayer (#46). - -2.0.25 (03//22//2013) ------ -- Fixed #37 featureOut is called when the cursor moves between tiles. -- Fixed #38 Infowindow isn't working using 'createVis' function without any parameter. -- Fixed #27 IE styles included in main css file. - -2.0.24 (03//13//2013) ------ -- Added option to control map scrollwheel zoom. -- Loading content in infowindow bug fixed. -- New classes applied to CartoDB map components avoiding other css collisions. - -2.0.23 (03//04//2013) ------ -- Fixed infowindow bug with cover image checking number fields as url. -- Added template_name in the infowindow model for vis.js. - -2.0.22 (03//01//2013) ------ -- Added cartodb.nojquery.js to the cdn. -- Infowindow crops text when it is too large in infowindows headers. -- Infowindow converts links automatically. -- Added retina CartoDB logo. -- Fixed problem with leaflet markers image paths. -- Fixed infowindow option in createVis #31. - -2.0.21 (02//19//2013) ------ -- Fixed problem with interaction in IE9. - -2.0.20 (02//13//2013) ------ -- Fixed problem with setOpacity in IE8. - -2.0.19 (02//13//2013) ------ -- Fixed problem with setOpacity in IE7 and IE8. It replaces leaflet with a custom one. - -2.0.18 (02//12//2013) ------ -- Fixed problem when loading leaflet externally. - -2.0.17 (02//11//2013) ------ -- Fixed problem with hide method on layers for IE8. -- Migrated to leaflet 0.5.1. -- Fixed problem guessing map type in createLayer. -- Fixed showing null values in the infowindow. - -2.0.16 (01//31//2013) ------ -- Added support for new infowindow' theme: 'header with image'. -- Fixed loading more than one viz.json in the same application. -- Documentation fixes. - -2.0.15 (01//14//2013) ------ -- Fixed problem fetching viz.json when createVis and createLayer are called in the same script. - -2.0.14 (01//11//2013) ------ -- Improvements in the documentation. -- Reduced the final file size by 58kb. -- Added cartodb_logo option to remove cartodb logo on visualizations . -- Fixed problem with the map always in fullscreen (#20). -- Fixed bootstrap conflicts (#16). -- Fixed autobounds in the map when user calls to createLayer (#11). From d04931f368d6f8c20b893e35a9652ffc5d178594 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Tue, 22 Sep 2015 16:05:41 +0200 Subject: [PATCH 024/120] Updated NEWS.md --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6cd8a8b235..4479360487 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ 3.15.X (dd/mm/yyyy) ------ -* Undefine `define` so that dependencies don't load via AMD [#543](https://github.com/CartoDB/cartodb.js/issues/543) +* Undefine `define` so that dependencies aren't loaded via AMD [#543](https://github.com/CartoDB/cartodb.js/issues/543) +* 3.15.6 (17/09/2015) ------ From ad6fb3e45f9ce8f37df840b6703cc7bc162cf872 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 23 Sep 2015 11:08:29 +0200 Subject: [PATCH 025/120] Files changed for version 3.15.7 --- RELEASING.md | 12 ++++++------ bower.json | 2 +- doc/API.md | 6 +++--- package.json | 2 +- src/cartodb.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index cd9ab4ae4b..407cf2ba66 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -13,7 +13,7 @@ - Create a new branch to prepare the release: ``` -git flow release start 3.15.6 +git flow release start 3.15.7 ``` - Build CartoDB.js files, choosing the new version: @@ -25,7 +25,7 @@ grunt release - Update the NEWS file and commit the changes. Take into account that new CartoDB.js version will be replaced in ```API.md```, ```RELEASING.md```, ```README.md```, ```package.json```, ```cartodb.js``` and ```examples``` files. ``` -git commit -am "Files changed for version 3.15.6" +git commit -am "Files changed for version 3.15.7" ``` - Release it. @@ -36,8 +36,8 @@ grunt publish - Check if those files have been updated in the CDN: ``` -http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.6/cartodb.js -http://libs.cartocdn.com/cartodb.js/v3/3.15.6/cartodb.js +http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.7/cartodb.js +http://libs.cartocdn.com/cartodb.js/v3/3.15.7/cartodb.js http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15/cartodb.js http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js ``` @@ -46,7 +46,7 @@ http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js - And to finish: close the release and push it. ``` -git flow release finish 3.15.6 +git flow release finish 3.15.7 git push --all git push --tags ``` @@ -75,7 +75,7 @@ grunt grunt publish ``` -For example, if we are in 3.15.6 and we want to go back to 3.13.4 +For example, if we are in 3.15.7 and we want to go back to 3.13.4 ``` git checkout 3.13.4 diff --git a/bower.json b/bower.json index 800f9158bc..60e4465430 100644 --- a/bower.json +++ b/bower.json @@ -4,7 +4,7 @@ "cartodb.js", "themes/css/cartodb.css" ], - "version": "3.15.6", + "version": "3.15.7", "homepage": "https://github.com/CartoDB/cartodb.js", "authors": [ "CartoDB " diff --git a/doc/API.md b/doc/API.md index 2f6bcd8ff1..6ffd39101e 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1452,14 +1452,14 @@ Anytime you wish to push a stable version of your site to the web though, you ca alert(cartodb.VERSION) ``` -Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.6, the URL would be: +Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.7, the URL would be: ```html - + ``` You can do the same for the CSS documents we provide: ```html - + ``` diff --git a/package.json b/package.json index e2546b44d7..314bc95fe4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cartodb.js", - "version": "3.15.6", + "version": "3.15.7", "description": "CartoDB javascript library", "repository": { "type": "git", diff --git a/src/cartodb.js b/src/cartodb.js index 797134a248..e65d5ab29b 100644 --- a/src/cartodb.js +++ b/src/cartodb.js @@ -5,7 +5,7 @@ var cdb = root.cdb = {}; - cdb.VERSION = "3.15.6"; + cdb.VERSION = "3.15.7"; cdb.DEBUG = false; cdb.CARTOCSS_VERSIONS = { From e1a324bccf8758ae0f0239c156d6d7a4ca6641ef Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 23 Sep 2015 11:17:23 +0200 Subject: [PATCH 026/120] Fixed spec --- test/spec/geo/geocoder.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/geo/geocoder.spec.js b/test/spec/geo/geocoder.spec.js index 28e4e20d35..24691f0012 100644 --- a/test/spec/geo/geocoder.spec.js +++ b/test/spec/geo/geocoder.spec.js @@ -36,7 +36,7 @@ describe('Geocoder', function() { it("we shouldn't get a direction that doesn't exist using NOKIA", function(done) { var data; - cdb.geo.geocoder.NOKIA.geocode('InternationalWadusworldIncorporated', function(d) { + cdb.geo.geocoder.NOKIA.geocode('ASDF1234567890', function(d) { data = d; expect(data.length).toEqual(0); done(); From 005e8bfc4eab858dda717526994aff348ade7abd Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 23 Sep 2015 12:43:37 +0200 Subject: [PATCH 027/120] Updated bower.sh --- bower.sh | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/bower.sh b/bower.sh index 1244dfaef6..71682065a2 100755 --- a/bower.sh +++ b/bower.sh @@ -10,9 +10,8 @@ ORG=CartoDB REPO=cartodb.js-bower # prepare repo folder -if [ -d $REPO ] - then - rm -rf $REPO +if [ -d $REPO ]; then + rm -rf $REPO fi # clone repo @@ -36,13 +35,19 @@ cp -R LICENSE $REPO/LICENSE.md echo "-- Committing and tagging $REPO" cd $REPO git add -A -CARTODBJS_VER=$(git diff bower.json | grep version | cut -d':' -f2 | cut -d'"' -f2 | sort -g -r | head -1) && if [ $(echo $CARTODBJS_VER | wc -m | tr -d ' ') = '1' ]; then echo 'VERSION DID NOT CHANGE'; else git tag -a $CARTODBJS_VER -m "Version $CARTODBJS_VER"; fi -git tag -a $CARTODBJS_VER -m "Version $CARTODBJS_VER" -git commit -m "v$CARTODBJS_VER" -echo "-- Pushing $REPO" -git push -fq origin master -git push -fq origin --tags +NEW_VERSION=$(git diff origin/master bower.json | grep version | cut -d':' -f2 | cut -d'"' -f2 | sort -g -r | head -1) +if [ -z "$NEW_VERSION" ]; then + echo 'VERSION DID NOT CHANGE' +else + echo "-- Tagging $NEW_VERSION" + git tag -a $NEW_VERSION -m "Version $NEW_VERSION"; + git commit -m "v$NEW_VERSION" + + echo "-- Pushing $REPO" + git push -fq origin master + git push -fq origin --tags +fi cd .. From d25ddbdf27ab2e3492029f7bccdb27f66f100b04 Mon Sep 17 00:00:00 2001 From: xavijam Date: Wed, 23 Sep 2015 13:51:55 +0200 Subject: [PATCH 028/120] Removing an unnecessary conditional --- src/geo/ui/fullscreen.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/geo/ui/fullscreen.js b/src/geo/ui/fullscreen.js index 0106017391..1ffde32fa5 100644 --- a/src/geo/ui/fullscreen.js +++ b/src/geo/ui/fullscreen.js @@ -67,10 +67,8 @@ cdb.ui.common.FullScreen = cdb.core.View.extend({ } } - if (mapView) { - if (this.model.get("allowWheelOnFullscreen")) { - mapView.options.map.set("scrollwheel", true); - } + if (mapView && this.model.get("allowWheelOnFullscreen")) { + mapView.map.set("scrollwheel", true); } } else { cancelFullScreen.call(doc); From f0bdd9653fde51147f9a6a158adcf97bed08309b Mon Sep 17 00:00:00 2001 From: xavijam Date: Wed, 30 Sep 2015 11:36:18 +0200 Subject: [PATCH 029/120] Fixing btoa method within cartodb.js utils --- src/core/util.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/util.js b/src/core/util.js index 8e402e1178..9c3d8df18f 100644 --- a/src/core/util.js +++ b/src/core/util.js @@ -12,12 +12,12 @@ cdb.core.util.array2hex = function(byteArr) { return cdb.core.util.btoa(encoded.join('')); }; -cdb.core.util.btoa = function() { +cdb.core.util.btoa = function(data) { if (typeof window['btoa'] == 'function') { - return cdb.core.util.encodeBase64Native; + return cdb.core.util.encodeBase64Native(data); }; - return cdb.core.util.encodeBase64; + return cdb.core.util.encodeBase64(data); }; cdb.core.util.encodeBase64Native = function (input) { @@ -121,4 +121,4 @@ cdb.core.util._inferBrowser = function(ua){ return browser; } -cdb.core.util.browser = cdb.core.util._inferBrowser(); \ No newline at end of file +cdb.core.util.browser = cdb.core.util._inferBrowser(); From 29abbf01b8c03c6f86d0419da97b1423e9269fa6 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 30 Sep 2015 13:44:07 +0200 Subject: [PATCH 030/120] Removed comments and logging from bower.sh --- bower.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/bower.sh b/bower.sh index 71682065a2..f453ebfaac 100755 --- a/bower.sh +++ b/bower.sh @@ -31,8 +31,6 @@ cp -R bower.json $REPO/bower.json cp -R LICENSE $REPO/LICENSE.md -# commit and tag repo -echo "-- Committing and tagging $REPO" cd $REPO git add -A From efb2055a3c032677e3ec70abe73beaaee34c43a1 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 30 Sep 2015 17:09:28 +0200 Subject: [PATCH 031/120] Added some tests --- test/spec/geo/layer_definition.spec.js | 31 ++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/test/spec/geo/layer_definition.spec.js b/test/spec/geo/layer_definition.spec.js index c84f8ea9f5..4123f162c9 100644 --- a/test/spec/geo/layer_definition.spec.js +++ b/test/spec/geo/layer_definition.spec.js @@ -674,17 +674,44 @@ describe("LayerDefinition", function() { expect(ajaxParams.url.indexOf('http://cdn.test.com/api/v1/map?')).toEqual(0); }); - it('should compress the map definition using LZMA', function() { + it("should compress the map definition using LZMA and the browser's btoa function", function() { layerDefinition.options.force_compress = true; + spyOn(window, "btoa"); + + var json = layerDefinition.toJSON(); + json = JSON.stringify({ config: JSON.stringify(json) }); + LZMA.compress(json, 3, function(encoded) { + lzma = cdb.core.util.array2hex(encoded); + + layerDefinition.createMap(callback); + }); + + expect(window.btoa.calls.count()).toEqual(2); + expect(window.btoa.calls.mostRecent().args.length).toEqual(1); + expect(ajaxParams.url.indexOf('lzma=' + lzma)).not.toEqual(-1); + }); + + it("should compress the map definition using LZMA and cdb.core.util.encodeBase64", function() { + layerDefinition.options.force_compress = true; + + var _btoa = window.btoa; + window.btoa = undefined; + spyOn(cdb.core.util, "encodeBase64"); + var json = layerDefinition.toJSON(); json = JSON.stringify({ config: JSON.stringify(json) }); LZMA.compress(json, 3, function(encoded) { lzma = cdb.core.util.array2hex(encoded); + layerDefinition.createMap(callback); }); - expect(ajaxParams.url.indexOf('lzma=' + encodeURIComponent(lzma))).not.toEqual(-1); + expect(cdb.core.util.encodeBase64.calls.count()).toEqual(2); + expect(cdb.core.util.encodeBase64 + expect(ajaxParams.url.indexOf('lzma=' + lzma)).not.toEqual(-1); + + window.btoa = _btoa; }); it('should handle errors returned by the tiler', function() { From bbb511941f7e3e54d213cef9c2860c7a6eb8ee1f Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 30 Sep 2015 17:22:10 +0200 Subject: [PATCH 032/120] Fixed tests --- test/spec/geo/layer_definition.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/geo/layer_definition.spec.js b/test/spec/geo/layer_definition.spec.js index 4123f162c9..099a4169e0 100644 --- a/test/spec/geo/layer_definition.spec.js +++ b/test/spec/geo/layer_definition.spec.js @@ -708,7 +708,7 @@ describe("LayerDefinition", function() { }); expect(cdb.core.util.encodeBase64.calls.count()).toEqual(2); - expect(cdb.core.util.encodeBase64 + expect(cdb.core.util.encodeBase64.calls.mostRecent().args.length).toEqual(1); expect(ajaxParams.url.indexOf('lzma=' + lzma)).not.toEqual(-1); window.btoa = _btoa; From 7c495aa2082090452a54cf492e4f70a588abcb68 Mon Sep 17 00:00:00 2001 From: xavijam Date: Wed, 30 Sep 2015 18:45:50 +0200 Subject: [PATCH 033/120] Adding new function to get maps api host within cdb.config --- src/core/config.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/core/config.js b/src/core/config.js index 1bde3d1a5d..746f31286a 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -44,8 +44,21 @@ getSqlApiUrl: function(version) { version = version || 'v2'; return this.getSqlApiBaseUrl() + "/api/" + version + "/sql"; - } + }, + /** + * returns the maps api host, removing user template + * and the protocol. + * cartodb.com:3333 + */ + getMapsApiHost: function() { + var url; + var mapsApiTemplate = this.get('maps_api_template'); + if (mapsApiTemplate) { + url = mapsApiTemplate.replace(/https?:\/\/{user}\./, ''); + } + return url; + } }); From 4eb21226c1730797e1d35b220908c99844b7fffa Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Thu, 1 Oct 2015 10:29:09 +0200 Subject: [PATCH 034/120] Files changed for version 3.15.8 --- NEWS.md | 9 ++++++++- RELEASING.md | 12 ++++++------ bower.json | 2 +- doc/API.md | 6 +++--- package.json | 2 +- src/cartodb.js | 2 +- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4479360487..20b1953223 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,15 @@ 3.15.X (dd/mm/yyyy) ------ -* Undefine `define` so that dependencies aren't loaded via AMD [#543](https://github.com/CartoDB/cartodb.js/issues/543) * +3.15.8 (01/10/2015) +------ +* Fixed btoa methods in cdb.core.util [#692](https://github.com/CartoDB/cartodb.js/issues/692) + +3.15.7 (23/09/2015) +------ +* Undefine `define` so that dependencies aren't loaded via AMD [#543](https://github.com/CartoDB/cartodb.js/issues/543) + 3.15.6 (17/09/2015) ------ * Fixed a couple of bugs related with Leaflet attributions [#681](https://github.com/CartoDB/cartodb.js/issues/681) diff --git a/RELEASING.md b/RELEASING.md index 407cf2ba66..a9972fd849 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -13,7 +13,7 @@ - Create a new branch to prepare the release: ``` -git flow release start 3.15.7 +git flow release start 3.15.8 ``` - Build CartoDB.js files, choosing the new version: @@ -25,7 +25,7 @@ grunt release - Update the NEWS file and commit the changes. Take into account that new CartoDB.js version will be replaced in ```API.md```, ```RELEASING.md```, ```README.md```, ```package.json```, ```cartodb.js``` and ```examples``` files. ``` -git commit -am "Files changed for version 3.15.7" +git commit -am "Files changed for version 3.15.8" ``` - Release it. @@ -36,8 +36,8 @@ grunt publish - Check if those files have been updated in the CDN: ``` -http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.7/cartodb.js -http://libs.cartocdn.com/cartodb.js/v3/3.15.7/cartodb.js +http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.8/cartodb.js +http://libs.cartocdn.com/cartodb.js/v3/3.15.8/cartodb.js http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15/cartodb.js http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js ``` @@ -46,7 +46,7 @@ http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js - And to finish: close the release and push it. ``` -git flow release finish 3.15.7 +git flow release finish 3.15.8 git push --all git push --tags ``` @@ -75,7 +75,7 @@ grunt grunt publish ``` -For example, if we are in 3.15.7 and we want to go back to 3.13.4 +For example, if we are in 3.15.8 and we want to go back to 3.13.4 ``` git checkout 3.13.4 diff --git a/bower.json b/bower.json index 60e4465430..c9f8add3c4 100644 --- a/bower.json +++ b/bower.json @@ -4,7 +4,7 @@ "cartodb.js", "themes/css/cartodb.css" ], - "version": "3.15.7", + "version": "3.15.8", "homepage": "https://github.com/CartoDB/cartodb.js", "authors": [ "CartoDB " diff --git a/doc/API.md b/doc/API.md index 6ffd39101e..34dc26e4be 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1452,14 +1452,14 @@ Anytime you wish to push a stable version of your site to the web though, you ca alert(cartodb.VERSION) ``` -Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.7, the URL would be: +Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.8, the URL would be: ```html - + ``` You can do the same for the CSS documents we provide: ```html - + ``` diff --git a/package.json b/package.json index 314bc95fe4..4fab1a6afe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cartodb.js", - "version": "3.15.7", + "version": "3.15.8", "description": "CartoDB javascript library", "repository": { "type": "git", diff --git a/src/cartodb.js b/src/cartodb.js index e65d5ab29b..a818311678 100644 --- a/src/cartodb.js +++ b/src/cartodb.js @@ -5,7 +5,7 @@ var cdb = root.cdb = {}; - cdb.VERSION = "3.15.7"; + cdb.VERSION = "3.15.8"; cdb.DEBUG = false; cdb.CARTOCSS_VERSIONS = { From 98c9e39e6324a501b7d8c04ffcc4942227c8db61 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Thu, 1 Oct 2015 15:45:01 +0200 Subject: [PATCH 035/120] Fixed comment --- src/api/sql.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/sql.js b/src/api/sql.js index cf4b729ae7..0776899df2 100644 --- a/src/api/sql.js +++ b/src/api/sql.js @@ -50,7 +50,7 @@ /** * var sql = new SQL('cartodb_username'); - * sql.execute("select * form {table} where id = {id}", { + * sql.execute("select * from {{ table }} where id = {{ id }}", { * table: 'test', * id: '1' * }) From c0e72542badbed9d4a42239f1c3c4ccc5d0da0c2 Mon Sep 17 00:00:00 2001 From: csobier Date: Thu, 8 Oct 2015 12:07:47 -0400 Subject: [PATCH 036/120] updated link for more cartocss information to our own internal content --- doc/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/API.md b/doc/API.md index 5082a85be4..2e1cea3bed 100644 --- a/doc/API.md +++ b/doc/API.md @@ -530,7 +530,7 @@ Adds a new data to the current layer. With this method, data from multiple table } ``` -`sql` and `cartocss` are mandatory. An exception is raised if either of them are not present. If the interactivity is not set, there is no interactivity enabled for that layer (better performance). SQL and CartoCSS syntax should be correct. Look at the documentation for [PostgreSQL](http://www.postgresql.org/docs/9.3/interactive/sql-syntax.html) and [CartoCSS](https://github.com/mapbox/carto/blob/master/docs/latest.md) for more information. There are some restrictions in the SQL queries: +`sql` and `cartocss` are mandatory. An exception is raised if either of them are not present. If the interactivity is not set, there is no interactivity enabled for that layer (better performance). SQL and CartoCSS syntax should be correct. Look at the documentation for [PostgreSQL](http://www.postgresql.org/docs/9.3/interactive/sql-syntax.html) and [CartoCSS](http://docs.cartodb.com/cartodb-editor.html#customizing-maps-with-cartocss) for more information. There are some restrictions in the SQL queries: - Must not write. INSERT, DELETE, UPDATE, ALTER and so on are not allowed (the query will fail) - Must not contain trialing semicolon From 15ee0365ef948a2ec80080a34f6e78c5fb9b24e4 Mon Sep 17 00:00:00 2001 From: xavijam Date: Wed, 14 Oct 2015 10:11:04 +0200 Subject: [PATCH 037/120] Removed background-clip properties from legends --- themes/css/map/cartodb-map-light.css | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/themes/css/map/cartodb-map-light.css b/themes/css/map/cartodb-map-light.css index b3631b94c2..29794e55da 100644 --- a/themes/css/map/cartodb-map-light.css +++ b/themes/css/map/cartodb-map-light.css @@ -1240,10 +1240,6 @@ div.cartodb-legend.choropleth li.graph { -o-border-radius: 3px; border-radius: 3px; - -moz-background-clip: padding; - -webkit-background-clip: padding; - background-clip: padding; - /*box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2);*/ border: 1px solid #b3b3b3; } @@ -1295,10 +1291,6 @@ div.cartodb-legend.density li.graph { -o-border-radius: 3px; border-radius: 3px; - -moz-background-clip: padding; - -webkit-background-clip: padding; - background-clip: padding; - /*box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2);*/ border: 1px solid #b3b3b3; } @@ -1336,10 +1328,6 @@ div.cartodb-legend.intensity li.graph { -o-border-radius: 3px; border-radius: 3px; - -moz-background-clip: padding; - -webkit-background-clip: padding; - background-clip: padding; - /*border: 1px solid #b3b3b3;*/ -webkit-box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); -o-box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.2); @@ -2344,7 +2332,7 @@ div.cartodb-timeslider p { padding: 10px; } -.cartodb-slides-controller .slides-controller-content .prev, +.cartodb-slides-controller .slides-controller-content .prev, .cartodb-slides-controller .slides-controller-content .next { position:relative; } .cartodb-slides-controller .slides-controller-content .prev { display:inline-block; *display:inline; vertical-align:middle; @@ -2365,10 +2353,10 @@ div.cartodb-timeslider p { opacity: .5; } -.cartodb-slides-controller .slides-controller-content .prev:hover, +.cartodb-slides-controller .slides-controller-content .prev:hover, .cartodb-slides-controller .slides-controller-content .next:hover { opacity: .8; } -.cartodb-slides-controller .slides-controller-content .prev:hover, +.cartodb-slides-controller .slides-controller-content .prev:hover, .cartodb-slides-controller .slides-controller-content .next:hover { opacity: .8; } .cartodb-slides-controller .slides-controller-content .prev:after { content: ''; position: absolute; top: -5px; left: 31px; height: 25px; width: 2px; background:#fff; opacity: .5; } From ce36e8008e70bea79fd5fc20f03fcfc28fadcf14 Mon Sep 17 00:00:00 2001 From: xavijam Date: Wed, 7 Oct 2015 13:03:47 +0200 Subject: [PATCH 038/120] Fixed spec for Image --- test/spec/core/image.spec.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test/spec/core/image.spec.js b/test/spec/core/image.spec.js index 5300a27d15..1c2797521d 100644 --- a/test/spec/core/image.spec.js +++ b/test/spec/core/image.spec.js @@ -23,15 +23,11 @@ describe("Image", function() { }); it("should use the basemap defined in the vizjson", function(done) { - var vizjson = "http://documentation.cartodb.com/api/v2/viz/318ab654-c989-11e4-97c6-0e9d821ea90d/viz.json" - var image = cartodb.Image(vizjson).size(640, 480); - - var basemap = { options: { visible: true, type: 'Tiled', urlTemplate: 'https://{s}.maps.nlp.nokia.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/256/png8?lg=eng&token=A7tBPacePg9Mj_zghvKt9Q&app_id=KuYppsdXZznpffJsKT24', subdomains: '1234', name: 'Nokia Day', className: 'nokia_day', attribution: "©2012 Nokia Terms of use" }, infowindow: null, tooltip: null, id: '2c4a8c5e-2ba5-4068-8807-d916a01b48d5', order: 0, parent_id: null, children: [ ], type: 'tiled' } - + var basemapURLTemplate = 'https://{s}.maps.nlp.nokia.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/256/png8?lg=eng&token=A7tBPacePg9Mj_zghvKt9Q&app_id=KuYppsdXZznpffJsKT24'; image.getUrl(function() { - expect(image.imageOptions.basemap).toEqual(basemap); + expect(image.imageOptions.basemap.options.urlTemplate).toEqual(basemapURLTemplate); done(); }); @@ -154,7 +150,7 @@ describe("Image", function() { }); - it("shouldn't use hidden layers to generate the image", function(done) { + it("shouldn't use hidden layers to generate the image", function(done) { var vizjson = "http://documentation.cartodb.com/api/v2/viz/42e98b9a-bcce-11e4-9d68-0e9d821ea90d/viz.json"; From 38839717f2190a44b0f10303afca47030513df0e Mon Sep 17 00:00:00 2001 From: xavijam Date: Wed, 14 Oct 2015 15:59:10 +0200 Subject: [PATCH 039/120] Getting proper layer infowindow data --- src/geo/layer_definition.js | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/geo/layer_definition.js b/src/geo/layer_definition.js index 3286109e80..2901c89c44 100644 --- a/src/geo/layer_definition.js +++ b/src/geo/layer_definition.js @@ -648,22 +648,32 @@ MapBase.prototype = { }, getTooltipData: function(layer) { - var tooltip = this.layers[layer].tooltip; - if (tooltip && tooltip.fields && tooltip.fields.length) { - return tooltip; + var pos = this.getLayerNumberByIndex(layer); + + if (pos >= 0) { + var tooltip = this.layers[pos].tooltip; + if (tooltip && tooltip.fields && tooltip.fields.length) { + return tooltip; + } } + return null; }, getInfowindowData: function(layer) { - var lyr; - var infowindow = this.layers[layer].infowindow; - if (!infowindow && this.options.layer_definition && (lyr = this.options.layer_definition.layers[layer])) { - infowindow = lyr.infowindow; - } - if (infowindow && infowindow.fields && infowindow.fields.length > 0) { - return infowindow; + var pos = this.getLayerNumberByIndex(layer); + + if (pos >= 0) { + var lyr = this.options.layer_definition.layers[pos]; + var infowindow = this.layers[pos].infowindow; + if (!infowindow && this.options.layer_definition && lyr) { + infowindow = lyr.infowindow; + } + if (infowindow && infowindow.fields && infowindow.fields.length > 0) { + return infowindow; + } } + return null; }, From d2d9f152bb980575fff16ab21a3494d329e4ddd6 Mon Sep 17 00:00:00 2001 From: xavijam Date: Wed, 14 Oct 2015 15:59:19 +0200 Subject: [PATCH 040/120] Added example test --- examples/infowindow-data-layer-bug.html | 46 +++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 examples/infowindow-data-layer-bug.html diff --git a/examples/infowindow-data-layer-bug.html b/examples/infowindow-data-layer-bug.html new file mode 100644 index 0000000000..c9791aaff5 --- /dev/null +++ b/examples/infowindow-data-layer-bug.html @@ -0,0 +1,46 @@ + + + + + CartoDB.js load example + + + + + +
+ + + + From e6207a55ff835df3430164d90d1b8ec4b67970f9 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 14 Oct 2015 16:39:30 +0200 Subject: [PATCH 041/120] Use layers instance variable instead of options.layer_definition.layers --- src/geo/layer_definition.js | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/src/geo/layer_definition.js b/src/geo/layer_definition.js index 2901c89c44..5f5984230c 100644 --- a/src/geo/layer_definition.js +++ b/src/geo/layer_definition.js @@ -664,12 +664,8 @@ MapBase.prototype = { var pos = this.getLayerNumberByIndex(layer); if (pos >= 0) { - var lyr = this.options.layer_definition.layers[pos]; var infowindow = this.layers[pos].infowindow; - if (!infowindow && this.options.layer_definition && lyr) { - infowindow = lyr.infowindow; - } - if (infowindow && infowindow.fields && infowindow.fields.length > 0) { + if (infowindow && infowindow.fields && infowindow.fields.length) { return infowindow; } } @@ -678,10 +674,10 @@ MapBase.prototype = { }, containInfowindow: function() { - var layers = this.options.layer_definition.layers; + var layers = this.layers || []; for(var i = 0; i < layers.length; ++i) { var infowindow = layers[i].infowindow; - if (infowindow && infowindow.fields && infowindow.fields.length > 0) { + if (infowindow && infowindow.fields && infowindow.fields.length) { return true; } } @@ -689,7 +685,7 @@ MapBase.prototype = { }, containTooltip: function() { - var layers = this.options.layer_definition.layers; + var layers = this.layers || []; for(var i = 0; i < layers.length; ++i) { var tooltip = layers[i].tooltip; if (tooltip && tooltip.fields && tooltip.fields.length) { @@ -975,28 +971,6 @@ NamedMap.prototype = _.extend({}, MapBase.prototype, { return payload; }, - containInfowindow: function() { - var layers = this.layers || []; - for(var i = 0; i < layers.length; ++i) { - var infowindow = layers[i].infowindow; - if (infowindow && infowindow.fields && infowindow.fields.length > 0) { - return true; - } - } - return false; - }, - - containTooltip: function() { - var layers = this.layers || []; - for(var i = 0; i < layers.length; ++i) { - var tooltip = layers[i].tooltip; - if (tooltip) { - return true; - } - } - return false; - }, - setSQL: function(sql) { throw new Error("SQL is read-only in NamedMaps"); }, From be4ab3bb6c943f7330f5f2f4818c67554c3528f8 Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 14 Oct 2015 17:19:45 +0200 Subject: [PATCH 042/120] Added some tests and removed example --- examples/infowindow-data-layer-bug.html | 46 ---------------- test/spec/geo/layer_definition.spec.js | 72 ++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 47 deletions(-) delete mode 100644 examples/infowindow-data-layer-bug.html diff --git a/examples/infowindow-data-layer-bug.html b/examples/infowindow-data-layer-bug.html deleted file mode 100644 index c9791aaff5..0000000000 --- a/examples/infowindow-data-layer-bug.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CartoDB.js load example - - - - - -
- - - - diff --git a/test/spec/geo/layer_definition.spec.js b/test/spec/geo/layer_definition.spec.js index 099a4169e0..7a8a48f433 100644 --- a/test/spec/geo/layer_definition.spec.js +++ b/test/spec/geo/layer_definition.spec.js @@ -153,7 +153,7 @@ describe("LayerDefinition", function() { it('should add cartodb layers by default or the specified type', function() { layerDefinition.addLayer({ sql : 'b', cartocss: 'b'}); - expect(layerDefinition.getLayer(2).type).toEqual('cartodb') + expect(layerDefinition.getLayer(2).type).toEqual('cartodb'); layerDefinition.addLayer({ type: 'http', urlTemplate: 'urlTemplate' }); @@ -214,6 +214,25 @@ describe("LayerDefinition", function() { expect(tooltip).toEqual({ fields: ['wadus'] }); }); + it ('should handle hidden layers correctly', function() { + layerDefinition.layers = [ + { + visible: false, + tooltip: { + fields: ['invisible'] + } + }, + { + tooltip: { + fields: ['visible'] + } + } + ]; + + var tooltip = layerDefinition.getTooltipData(0); + expect(tooltip).toEqual({ fields: ['visible'] }); + }); + it ('should return NULL if tooltip is not present or does NOT have fields', function() { layerDefinition.layers = [{ tooltip: {} @@ -233,6 +252,57 @@ describe("LayerDefinition", function() { }); }) + describe(".getInfowindowData", function() { + + it ('should return infowindow data if infowindow is present and has fields', function() { + layerDefinition.layers = [{ + infowindow: { + fields: ['wadus'] + } + }]; + + var infowindow = layerDefinition.getInfowindowData(0); + expect(infowindow).toEqual({ fields: ['wadus'] }); + }); + + it ('should handle hidden layers correctly', function() { + layerDefinition.layers = [ + { + visible: false, + infowindow: { + fields: ['invisible'] + } + }, + { + infowindow: { + fields: ['visible'] + } + } + ]; + + var infowindow = layerDefinition.getInfowindowData(0); + expect(infowindow).toEqual({ fields: ['visible'] }); + }); + + it ('should return NULL if infowindow is not present or does NOT have fields', function() { + layerDefinition.layers = [{ + infowindow: {} + }]; + + var infowindow = layerDefinition.getInfowindowData(0); + expect(infowindow).toBeNull() + + layerDefinition.layers = [{ + infowindow: { + fields: [] + } + }]; + + var infowindow = layerDefinition.getInfowindowData(0); + expect(infowindow).toBeNull() + }); + }) + describe('.toJSON', function() { it("should return json spec of visible layers", function() { From df0fcad8f931f82e4e9c2e1a4163d0a079d071aa Mon Sep 17 00:00:00 2001 From: Pablo Alonso Garcia Date: Wed, 14 Oct 2015 17:38:37 +0200 Subject: [PATCH 043/120] Updated NEWS.md --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 20b1953223..3ef5b1254e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ 3.15.X (dd/mm/yyyy) ------ -* +* Fixed broken infowindows/tooltips when some layers were hidden [#5917](https://github.com/CartoDB/cartodb/issues/5917) 3.15.8 (01/10/2015) ------ From cf73b73c817edcfc99b43b00f6bacd5546b14ac4 Mon Sep 17 00:00:00 2001 From: csobier Date: Wed, 14 Oct 2015 15:58:43 -0400 Subject: [PATCH 044/120] added interaction methods for torque layers to the API Methods section --- doc/API.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/doc/API.md b/doc/API.md index 466f7aa479..d257826d29 100644 --- a/doc/API.md +++ b/doc/API.md @@ -389,6 +389,89 @@ Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not } } ``` +##### Interaction Methods for a Torque Layer + +Used to create an animated torque layer with customized settings. + +```javascript + // initialize a torque layer that uses the CartoDB account details and SQL API to pull in data + var torqueLayer = new L.TorqueLayer({ + user : 'viz2', + table : 'ow', + cartocss: CARTOCSS + }); +``` + +`getValueForPos(x, y[, step])` +Description | Allows to get the value for the coordinate (in map reference system) for a concrete step. If a step is not specified, the animation step is used. Use caution, as this method increases CPU usage. It returns the value from the raster data, not the rendered data. +Returns | An object, such as a { bbox:[], value: VALUE } if there is value for the pos, otherwise, it is null. + +`getValueForBBox(xstart, ystart, xend, yend)` +Description | Returns an accumulated numerical value from all the torque areas, within the specified bounds. +Returns | A number. + +`getActivePointsBBox(step)` +Description | Returns the list of bounding boxes active for `step`. +Returns | List of bbox:[]. + +`getValues(step)` +Description | Returns the list of values for the pixels active in `step`. +Returns | List of values. + +`invalidate()` +Description | Forces a reload of the layer data. + +##### Example of Interaction Methods for a Torque Layer +```javascript + - -``` - -[Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/easy.html) - - -## Using the library - -CartoDB.js can be used when you want to embed and use a visualization you have designed using CartoDB's user interface, or to dynamically create visualizations from scratch using your data. If you want to create new maps on your webpage, jump to [Creating a visualization from scratch](#creating-a-visualization-from-scratch). If you already have maps on your webpage and want to add CartoDB visualizations to them, read [Adding CartoDB layers to an existing map](#adding-cartodb-layers-to-an-existing-map). - -You can also use the CartoDB API to create visualizations programmatically. This can be useful when the visualizations react to user interactions. To read more about it jump to [Creating visualizations at runtime](#creating-visualizations-at-runtime). - -We’ve also made it easier than ever for you to build maps using the mapping library of your choice. Whether you are using Leaflet or something else, our CartoDB.js code remains the same. This makes our API documentation simple and straightforward. It also makes it easy for you to remember and be consistent if you develop or maintain multiple maps online. - -To start using CartoDB.js just paste this piece of code within the HEAD tags of your HTML: - -
Linking cartodb.js on your html file
-```html - - -``` - -### Creating a visualization from scratch - -The easiest way to quickly get a CartoDB map onto your webpage. Use this when there is no map in your application and you want to add the visualization to hack over it. With this method, CartoDB.js handles all the details of loading a map interface, basemap, and your CartoDB visualization. - -You can start by giving cartodb.js the DIV ID from your HTML where you want to place your map, and the viz.json URL of your visualization, which you can get from the share window. - -
Simplest way to add your map to a webpage ever!
-```javascript -cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'); -``` - -That’s it! No need to create the map instance, insert controls, or load layers. CartoDB.js takes care of this for you. If you want to modify the result after instantiating your map with this method, take a look at the CartoDB.js API [available methods](#api-methods). For example, you can also use the returned layer to build more functionality (show/hide, click, hover, custom infowindows): - -
Simplest way to add your map to a webpage ever!
-```javascript -cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json') - .done(function(vis, layers) { - // layer 0 is the base layer, layer 1 is cartodb layer - // when setInteraction is disabled featureOver is triggered - layers[1].setInteraction(true); - layers[1].on('featureOver', function(e, latlng, pos, data, layerNumber) { - console.log(e, latlng, pos, data, layerNumber); - }); - - // you can get the native map to work with it - var map = vis.getNativeMap(); - - // now, perform any operations you need, e.g. assuming map is a L.Map object: - // map.setZoom(3); - // map.panTo([50.5, 30.5]); - }); -``` - -### Adding CartoDB layers to an existing map - -In case you already have a map instantiated on your page, you can simply use the [createLayer](#cartodbcreatelayermap-layersource--options--callback) method to add new CartoDB layers to it. This is particullary useful when you have more things on your map apart from CartoDB layers or you have an application where you want to integrate CartoDB layers. - -Below, you have an example using a previously instatiated Leaflet map. - -
Adding cartodb layers to an existing map
-```html -
- - -``` - -[Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/leaflet.html) - -### Creating visualizations at runtime - -All CartoDB services are available through the API, which basically means that you can create a new visualization without doing it before through the CartoDB UI. This is particularly useful when you are modifying the visualization depending on user interactions that change the SQL to get the data or CartoCSS to style it. Although this method requires more programming skills, it provides all the flexibility you might need to create more dynamic visualizations. - -When you create a visualization using the CartoDB website, you automatically get a viz.json URL that defines it. When you want to create the visualization via JavaScript, you don't always have a viz.json. You will need to pass all the required parameters to the library so that it can create the visualization at runtime and display it on your map. It is pretty simple. - -
Creating visualizations at runtime
-```javascript -// create a layer with 1 sublayer -cartodb.createLayer(map, { - user_name: 'mycartodbuser', - type: 'cartodb', - sublayers: [{ - sql: "SELECT * FROM table_name", - cartocss: '#table_name {marker-fill: #F0F0F0;}' - }] -}) -.addTo(map) // add the layer to our map which already contains 1 sublayer -.done(function(layer) { - - // create and add a new sublayer - layer.createSubLayer({ - sql: "SELECT * FROM table_name limit 200", - cartocss: '#table_name {marker-fill: #F0F0F0;}' - }); - - // change the query for the first layer - layer.getSubLayer(0).setSQL("SELECT * FROM table_name limit 10"); -}); -``` - -Want further information? [Check out the complete list of API methods](#api-methods). - - -## Usage examples - -The best way to start learning about the library is by taking a look at some of the examples below: - -+ An easy example using the library - ([view live](http://cartodb.github.com/cartodb.js/examples/easy.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/easy.html)). -+ Leaflet integration - ([view live](http://cartodb.github.com/cartodb.js/examples/leaflet.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/leaflet.html)). -+ Customizing infowindow data - ([view live](http://cartodb.github.com/cartodb.js/examples/custom_infowindow.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/custom_infowindow.html)). -+ An example using a layer selector - ([view live](http://cartodb.github.com/cartodb.js/examples/layer_selector.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/layer_selector.html)). -+ The Hobbit map done with the library - ([view live](http://cartodb.github.com/cartodb.js/examples/TheHobbitLocations/) / [source code](https://github.com/CartoDB/cartodb.js/tree/develop/examples/TheHobbitLocations)). - - -## API methods - -The documentation below refers to CartoDB.js v3. For major changes in the library we will update the documentation here. This documentation is meant to help developers find specific methods from the CartoDB.js library. - -### Visualization - -#### cartodb.createVis(_map_id, vizjson_url[, options] [, callback]_) - -Creates a visualization inside the map_id DOM object. - -
cartodb.createVis
-```javascript -var url = 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'; - -cartodb.createVis('map', url) - .done(function(vis, layers) { - }); -``` - -##### Arguments - -- **map_id**: a DOM object, for example `$('#map')` or a DOM id. -- **vizjson_url**: url of the vizjson object. -- **options**: - - **shareable**: add facebook and twitter share buttons. - - **title**: adds a header with the title of the visualization. - - **description**: adds description to the header (as you set in the UI). - - **search**: adds a search control (default: true). - - **zoomControl**: adds zoom control (default: true). - - **loaderControl**: adds loading control (default: true). - - **center_lat**: latitude where the map is initializated. - - **center_lon**: longitude where the map is initializated. - - **zoom**: initial zoom. - - **cartodb_logo**: default to true, set to false if you want to remove the cartodb logo. - - **infowindow**: set to false if you want to disable the infowindow (enabled by default). - - **time_slider**: show time slider with torque layers (enabled by default) - - **layer_selector**: show layer selector (default: false) - - **legends**: if it's true legends are shown in the map. - - **https**: if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. - - **scrollwheel**: enable/disable the ability of zooming using scrollwheel (default enabled) - - **fullscreen**: if true adds a button to toggle the map fullscreen - - **mobile_layout**: if true enables a custom layout for mobile devices (default: false) - - **force_mobile**: forces enabling/disabling the mobile layout (it has priority over mobile_layout argument) - - **gmaps_base_type**: Use Google Maps as map provider whatever is the one specified in the viz.json". Available types: 'roadmap', 'gray_roadmap', 'dark_roadmap', 'hybrid', 'satellite', 'terrain'. - - **gmaps_style**: Google Maps styled maps. See [documentation](https://developers.google.com/maps/documentation/javascript/styling). - - **no_cdn**: true to disable CDN when fetching tiles -- **callback(vis,layers)**: if a function is specified, it is called once the visualization is created, passing vis and layers as arguments - -##### Returns - -A promise object. You can listen for the following events: - -+ **done**: triggered when the visualization is created, `vis` is passed as the first argument and `layers` is passed as the second argument. Each layer type has different options, see layers section. -+ **error**: triggered when the layer couldn't be created. The error string is the first argument. - -### cartodb.Vis - -#### vis.getLayers() - -Returns an array of layers in the map. The first is the base layer. - -#### vis.addOverlay(_options_) - -Adds an overlay to the map that can be either a zoom control, a tooltip or an infobox. - -##### Arguments - -- **options** - - **layer** layer from the visualization where the overlay should be applied (optional) - - **type** zoom / tooltip / infobox - -If no layer is provided, the overlay will be added to the first layer of the visualization. Extra options are available based on the specific UI component. - -##### Returns - -An overlay object, see [vis.Overlays](#visoverlays). - -#### vis.getOverlay(_type_) - -Returns the first overlay with the specified **type**. - -
vis.getOverlay
-```javascript -var zoom = vis.getOverlay('zoom'); -zoom.clean() // remove it from the screen -``` - -#### vis.getOverlays() - -Returns a list of the overlays that are currently on the screen (see overlays description). - -#### vis.getNativeMap() - -Returns the native map object being used (e.g. a L.Map object for Leaflet). - -#### vis.Overlays - -An overlay is a control shown on top of the map. - -Overlay objects are always created using the **addOverlay** method of a cartodb.Vis object. - -An overlay is internally a [**Backbone.View**](http://backbonejs.org/#View) so if you know how Backbone works you can use it. If you want to use plain DOM objects you can access **overlay.el** (**overlay.$el** for jQuery object). - -#### vis.addInfowindow(_map, layer, fields [, options]_) - -Adds an infowindow to the map controlled by layer events. It enables interaction and overrides the layer interactivity. - -##### Arguments - - - **map**: native map object or leaflet - - **layer**: cartodb layer (or sublayer) - - **fields**: array of column names - -##### Returns - -An infowindow object, see [sublayer.infowindow](#sublayerinfowindow) - -#### cartodb.createLayer(_map, layerSource [, options] [, callback]_) - -With visualizations already created through the CartoDB console, you can simply use the **createLayer** function to add them into your web pages. Unlike **createVis**, this method requires an already activated **map** object and it does not load a basemap for you. - -##### Arguments - -- **map**: Leaflet L.Map object. The map should be initialized before calling this function. - -- **layerSource**: contains information about the layer. It can be specified in 2 ways: - -
Passing the url where the layer data is located
-```javascript -cartodb.createLayer(map, 'http://myserver.com/layerdata.json') -``` - -
passing the data directly
-```javascript -cartodb.createLayer(map, { layermetadata }) -``` - -- **options**: - - **https**: force https - - **refreshTime**: if is set, the layer is refreshed each refreshTime milliseconds. - - **infowindow**: set to false if you want to disable the infowindow (enabled by default). - - **tooltip**: set to false if you want to disable the tooltip (enabled by default). - - **legends**: if it's true legends are shown in the map. - - **time_slider**: show time slider with torque layers (enabled by default) - - **layerIndex**: when the visualization contains more than one layer this index allows you to select - what layer is created. Take into account that `layerIndex == 0` is the base layer and that - all the tiled layers (non animated ones) are merged into a single one. The default value for - this option is 1 (usually tiled layers). - - **filter**: a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. - -- **callback(_layer_)**: if a function is specified, it will be invoked after the layer has been created. The layer will be passed as an argument. - -##### Returns - -A promise object. You can listen for the following events: - -+ **done**: triggered when the layer is created, the layer is passed as first argument. Each layer type has different options, see layers section. -+ **error**: triggered when the layer couldn't be created. The error string is the first argument. - -You can call to `addTo(map[, position])` in the promise so when the layer is ready it will be added to the map. - -##### Example - -
cartodb.createLayer using a url
- -```javascript -var map; -var mapOptions = { - zoom: 5, - center: [43, 0] -}; -map = new L.Map('map', mapOptions); - -cartodb.createLayer(map, 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json') - .addTo(map) - .on('done', function(layer) { - layer - .on('featureOver', function(e, latlng, pos, data) { - console.log(e, latlng, pos, data); - }) - .on('error', function(err) { - console.log('error: ' + err); - }); - }).on('error', function(err) { - console.log("some error occurred: " + err); - }); -``` - -Layer metadata must take one of the following forms: - -#### Standard Layer Source Object (`type: 'cartodb'`) - -Used for most maps with tables that are set to public or public with link. - -```javascript -{ - user_name: 'your_user_name', // Required - type: 'cartodb', // Required - sublayers: [{ - sql: "SELECT * FROM table_name", // Required - cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required - interactivity: "column1, column2, ...", // Optional - }, - { - sql: "SELECT * FROM table_name", // Required - cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required - interactivity: "column1, column2, ...", // Optional - }, - ... - ] -} -``` - -#### Torque Layer Source Object (`type: 'torque'`) - -Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not allow sublayers. - -```javascript -{ - type: 'torque', // Required - order: 1, // Optional - options: { - query: "SQL statement", // Required if table_name is not given - table_name: "table_name", // Required if query is not given - user_name: "your_user_name", // Required - cartocss: "CartoCSS styles" // Required - } -} -``` - -#### Named Maps Layer Source Object (`type: 'namedmap'`) - -Used for making public maps with private data. See [Named Maps](http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps-1) for more information. - - -```javascript -{ - user_name: 'your_user_name', // Required - type: 'namedmap', // Required - named_map: { - name: 'name_of_map', // Required - // Optional - layers: [{ - layer_name: "sublayer0", // Optional - interactivity: "column1, column2, ..." // Optional - }, - { - layer_name: "sublayer1", - interactivity: "column1, column2, ..." - }, - ... - ], - // Optional - params: { - color: "hex_value", - num: 2 - } - } -} -``` - -##### Example - -
cartodb.createLayer combining multiple types of layers and setting a filter
- -```javascript -cartodb.createLayer(map, { - user_name: 'examples', - type: 'cartodb', - sublayers: [ - { - type: "http", - urlTemplate: "http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png", - subdomains: [ "a", "b", "c" ] - }, - { - sql: 'select * from country_boundaries', - cartocss: '#layer { polygon-fill: #F00; polygon-opacity: 0.3; line-color: #F00; }' - }, - ], -}, { filter: ['http', 'mapnik'] }) -``` - -### cartodb.CartoDBLayer - -CartoDBLayer allows you to manage tiled layers from CartoDB. It manages the sublayers. - -#### layer.clear() - -Clears the layer. It should be invoked after removing the layer from the map. - -#### layer.hide() - -Hides the layer from the map. - -#### layer.show() - -Shows the layer in the map if it was previously added. - -#### layer.toggle() - -Toggles the visibility of the layer and returns a boolean that indicates the new status (true if the layer is shown, false if it is hidden) - -#### layer.setOpacity(_opacity_) - -Changes the opacity of the layer. - -##### Arguments - -+ **opacity**: value in range [0, 1] - -#### layer.getSubLayer(_layerIndex_) - -Gets a previously created sublayer. And exception is raised if no sublayer exists. - -##### Arguments - -+ **layerIndex**: 0 based index of the sublayer to get. Should be within [0, getSubLayerCount()) - -##### Returns - -A SubLayer object. - -##### Example - -
layer.getSubLayer
-```javascript -layer.getSubLayer(1).hide(); - -var sublayer = layer.getSubLayer(0); - -sublayer.setSQL('SELECT * FROM table_name limit 10'); -``` - -#### layer.getSubLayerCount() - -Gets the number of sublayers in layer. - -##### Returns - -The number of sublayers. - -##### Example - -
Hide layers using layer.getSubLayerCount
-```javascript -var num_sublayers = layer.getSubLayerCount(); - -for (var i = 0; i < num_sublayers; i++) { - layer.getSubLayer(i).hide(); -} -``` - -#### layer.createSubLayer(_layerDefinition_) - -Adds a new data to the current layer. With this method, data from multiple tables can be easily visualized. New in V3. - -##### Arguments - -- **layerDefinition**: an object with the sql and cartocss that defines the data, should be like: - -
layerDefinition
-```javascript -{ - sql: "SELECT * FROM table_name", - cartocss: "#layer { marker-fill: red; }", - interactivity: 'cartodb_id, area, column' // optional -} -``` - -`sql` and `cartocss` are mandatory. An exception is raised if either of them are not present. If the interactivity is not set, there is no interactivity enabled for that layer (better performance). SQL and CartoCSS syntax should be correct. Look at the documentation for [PostgreSQL](http://www.postgresql.org/docs/9.3/interactive/sql-syntax.html) and [CartoCSS](http://docs.cartodb.com/cartodb-editor.html#customizing-maps-with-cartocss) for more information. There are some restrictions in the SQL queries: - -- Must not write. INSERT, DELETE, UPDATE, ALTER and so on are not allowed (the query will fail) -- Must not contain trialing semicolon - -##### Returns - -A SubLayer object. - -##### Example - -
layer.createSubLayer
-```javascript -cartodb.createLayer(map, 'http://examples.cartodb.com/api/v2/viz/european_countries_e/viz.json', function(layer) { - // add populated places points over the countries layer - layer.createSubLayer({ - sql: 'SELECT * FROM ne_10m_populated_places_simple', - cartocss: '#layer { marker-fill: red; }' - }); -}).addTo(map); -``` - -#### layer.invalidate() - -Refreshes the data. If the data has been changed in the CartoDB server those changes will be displayed. Nothing happens otherwise. Every time a parameter is changed in a sublayer, the layer is refreshed automatically, so there's no need to call this method manually. New in V3. - -#### layer.setAuthToken(_auth_token_) - -Sets the auth token that will be used to create the layer. Only available for private visualizations. An exception is -raised if the layer is not being loaded with HTTPS. See [Named Maps](http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps-1) for more information. - -##### Returns - -The layer itself. - -##### Arguments - -- **auth_token:** string - -#### layer.setParams(_key, value_) - -Sets the configuration of a layer when using [named maps](http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps-1). It can be invoked in different ways: - -
layer.setParams
-```javascript -layer.setParams('test', 10); // sets test = 10 -layer.setParams('test', null); // unset test -layer.setParams({'test': 1, 'color': '#F00'}); // set more than one parameter at once -``` - -##### Arguments - -- **key:** string -- **value:** string or number - -##### Returns - -The layer itself. - -### cartodb.CartoDBLayer.SubLayer - -#### sublayer.set(_layerDefinition_) - -Sets sublayer parameters. Useful when more than one parameter needs to be changed. - -##### Arguments - -- **layerDefinition**: an object with the sql and cartocss that defines the data, like: - -
layerDefinition
-```javascript -{ - sql: "SELECT * FROM table_name", - cartocss: "#layer { marker-fill: red; }", - interactivity: 'cartodb_id, area, column' // optional -} -``` - -##### Returns - -The layer itself. - -##### Example - -
sublayer.set
-```javascript -sublayer.set({ - sql: "SELECT * FROM table_name WHERE cartodb_id < 100", - cartocss: "#layer { marker-fill: red }", - interactivity: "cartodb_id, the_geom, magnitude" -}); -``` - -#### sublayer.get(_attr_) - -Gets the attribute for the sublayer, for example 'sql', 'cartocss'. - -##### Returns - -The requested attribute or undefined if it's not present. - -#### sublayer.remove() - -Removes the sublayer. An exception will be thrown if a method is called and the layer has been removed. - -#### sublayer.show() - -Shows a previously hidden sublayer. The layer is refreshed after calling this function. - -#### sublayer.hide() - -Removes the sublayer from the layer temporarily. The layer is refreshed after calling this function. - -#### sublayer.toggle() - -Toggles the visibility of the sublayer and returns a boolean that indicates the new status (true if the sublayer is visible, false if it is hidden) - -#### sublayer.isVisible() - -It returns `true` if the sublayer is visible. - -### cartodb.CartoDBLayer.CartoDBSubLayer - -#### sublayer.getSQL() - -Shortcut for `get('sql')` - -#### sublayer.getCartoCSS() - -Shortcut for `get('cartocss')` - -#### sublayer.setSQL(sql) - -Shortcut for `set({'sql': 'SELECT * FROM table_name'})` - -#### sublayer.setCartoCSS(css) - -Shortcut for `set({'cartocss': '#layer {...}' })` - -#### sublayer.setInteractivity('cartodb_id, name, ...') - -Shortcut for `set({'interactivity': 'cartodb_id, name, ...' })` - -Sets the columns which data will be available via the interaction with the sublayer. - -#### sublayer.setInteraction(_true_) - -Enables (true) or disables (false) the interaction of the layer. When disabled, **featureOver**, **featureClick**, **featureOut**, **mouseover** and **mouseout** are **not** triggered. - -##### Arguments - -+ **enable**: true if the interaction needs to be enabled. - -#### sublayer.infowindow - -**sublayer.infowindow** is a Backbone model where we modify the parameters of the infowindow. - -##### Attributes - -- **template**: Custom HTML template for the infowindow. You can write simple HTML or use [Mustache templates](http://mustache.github.com/). -- **sanitizeTemplate**: By default all templates are sanitized from unsafe tags/attrs (e.g. ` - - -``` - -[Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/custom_infowindow.html) - -### cartodb.CartoDBLayer.HttpSubLayer - -#### sublayer.setURLTemplate(urlTemplate) - -Shortcut for `set({'urlTemplate': 'http://{s}.example.com/{z}/{x}/{y}.png' })` - -#### sublayer.setSubdomains(subdomains) - -Shortcut for `set({'subdomains': ['a', 'b', '...'] })` - -#### sublayer.setTms(tms) - -Shortcut for `set({'tms': true|false })` - -#### sublayer.getURLTemplate - -Shortcut for `get('urlTemplate')` - -#### sublayer.getSubdomains - -Shortcut for `get('subdomains')` - -#### sublayer.getTms - -Shortcut for `get('tms')` - -#### sublayer.legend - -**sublayer.legend** is a Backbone model with the information about the legend. - -##### Attributes - -- **template**: Custom HTML template for the legend. You can write simple HTML. -- **title**: Title of the legend. -- **show_title**: Set this to `false` if you don't want the title to be displayed. -- **items**: An array with the items that are displayed in the legend. -- **visible**: Set this to `false` if you want to hide the legend. - -## Events - -You can bind custom functions to layer events. This is useful for integrating your website with your maps, adding events for mouseovers and click events. - -### layer - -#### layer.featureOver(_event, latlng, pos, data, layerIndex_) - -Triggered when the user hovers on any feature. - -##### Callback arguments - -- **event**: Browser mouse event object. -- **latlng**: Array with the LatLng ([lat,lng]) where the layer was clicked. -- **pos**: Object with x and y position in the DOM map element. -- **data**: The CartoDB data of the clicked feature with the **interactivity** param. -- **layerIndex**: the layerIndex where the event happened. - -##### Example - -
layer.on
-```javascript -layer.on('featureOver', function(e, latlng, pos, data, subLayerIndex) { - console.log("mouse over polygon with data: " + data); -}); -``` - -#### layer.featureOut(_layerIndex_) - -Triggered when the user hovers out any feature. - -#### layer.featureClick(_event, latlng, pos, data, layerIndex_) - -Triggered when when the user clicks on a feature of a layer. - -##### callback arguments - -Same as `featureOver`. - -#### layer.mouseover() - -Triggered when the mouse enters in **any** feature. Useful to change the cursor while hovering. - -#### layer.mouseout() - -Triggered when the mouse leaves all the features. Useful to revert the cursor after hovering. - -##### Example - -
sublayer.on
-```javascript -layer.on('mouseover', function() { - cursor.set('hand') -}); - -layer.on('mouseout', function() { - cursor.set('auto') -}); -``` - -#### layer.loading() - -Triggered when the layer or any of its sublayers are about to be loaded. This is also triggered when any properties are changed but not yet visible. - -##### Example - -
layer.on
-```javascript -layer.on("loading", function() { - console.log("layer about to load"); -}); -layer.getSubLayer(0).set({ - cartocss: "#export { polygon-opacity: 0; }" -}); -``` - -#### layer.load() - -Triggered when the layer or its sublayers have been loaded. This is also triggered when any properties are changed and visible. - -##### Example - -
layer.on
-```javascript -layer.on("load", function() { - console.log("layer loaded"); -}); -layer.getSubLayer(0).set({ - cartocss: "#export { polygon-opacity: 0; }" -}); -``` - -### subLayer - -#### sublayer.featureOver(_event, latlng, pos, data, layerIndex_) - -Same as `layer.featureOver()` but sublayer specific. - -##### callback arguments - -Same as `layer.featureOver()`. - -#### sublayer.featureClick(_event, latlng, pos, data, layerIndex_) - -Same as `layer.featureClick()` but sublayer specific. - -##### callback arguments - -Same as `layer.featureClick()`. - -#### sublayer.mouseover() - -Same as `layer.mouseover()` but sublayer specific. - -#### sublayer.mouseout() - -Same as `layer.mouseover()` but sublayer specific. - - -## Specific UI functions - -There are a few functions in CartoDB.js for creating, enabling, and disabling pieces of the user interface. - -### cartodb.geo.ui.Tooltip - -Shows a small tooltip on hover: - -
cartodb.geo.ui.Tooltip
-```javascript -var tooltip = vis.addOverlay({ - type: 'tooltip', - template: '

{{variable}}

' // mustache template -}); -``` - -#### cartodb.geo.ui.Tooltip.enable() - -The tooltip is shown when hover on feature when is called. - -#### cartodb.geo.ui.Tooltip.disable() - -The tooltip is not shown when hover on feature. - -### cartodb.geo.ui.InfoBox - -Shows a small box when the user hovers on a map feature. The position is fixed: - -
cartodb.geo.ui.InfoBox
-```javascript -var box = vis.addOverlay({ - type: 'infobox', - template: '

{{name_to_display}}

', - width: 200, // width of the box - position: 'bottom|right' // top, bottom, left and right are available -}); -``` - -#### cartodb.geo.ui.InfoBox.enable() - -The tooltip is shown when hover on feature. - -#### cartodb.geo.ui.InfoBox.disable() - -The tooltip is not shown when hover on feature. - -### cartodb.geo.ui.Zoom - -Shows the zoom control: - -
cartodb.geo.ui.Zoom
-```javascript -vis.addOverlay({ type: 'zoom' }); -``` - -#### cartodb.geo.ui.Zoom.show() - -#### cartodb.geo.ui.Zoom.hide() - - -## Getting data with SQL - -CartoDB offers a powerful SQL API for you to query and retreive data from your CartoDB tables. CartoDB.js offers a simple to use wrapper for sending those requests and using the results. - -### cartodb.SQL - -**cartodb.SQL** is the tool you will use to access data you store in your CartoDB tables. This is a really powerful technique for returning things like: **items closest to a point**, **items ordered by date**, or **GeoJSON vector geometries**. It’s all powered with SQL and our tutorials will show you how easy it is to begin with SQL. - -
cartodb.SQL
-```javascript -var sql = new cartodb.SQL({ user: 'cartodb_user' }); -sql.execute("SELECT * FROM table_name WHERE id > {{id}}", { id: 3 }) - .done(function(data) { - console.log(data.rows); - }) - .error(function(errors) { - // errors contains a list of errors - console.log("errors:" + errors); - }) -``` - -It accepts the following options: - -+ **format**: should be geoJSON. -+ **dp**: float precision. -+ **jsonp**: if jsonp should be used instead of CORS. This param is enabled if the browser does not support CORS. - -These arguments will be applied to all the queries performed by this object. If you want to override them for one query see **execute** options. - -#### sql.execute(_sql [,vars][, options][, callback]_) - -It executes a sql query. - -##### Arguments - -+ **sql**: a string with the sql query to be executed. You can specify template variables like {{variable}} which will be filled with **vars** object. -+ **vars**: a map with the variables to be interpolated in the sql query. -+ **options**: accepts **format**, **dp** and **jsonp**. This object also overrides the params passed to $.ajax. - -##### Returns - -A promise object. You can listen for the following events: - -+ **done**: triggered when the data arrives. -+ **error**: triggered when something failed. - -You can also use done and error methods: - -
sql.execute
-```javascript -sql.execute('SELECT * FROM table_name') - .done(fn) - .error(fnError) -``` - -#### sql.getBounds(_sql [,vars][, options][, callback]_) - -Returns the bounds [ [sw_lat, sw_lon], [ne_lat, ne_lon ] ] for the geometry resulting of specified query. - -
sql.getBounds
-```javascript -sql.getBounds('select * from table').done(function(bounds) { - console.log(bounds); -}); -``` - -##### Arguments - -+ **sql**: a string with the sql query to calculate the bounds from. - -##### Application of getBounds in Leaflet - -You can use the results from `getBounds` to center data on your maps using Leaflet. - -- **getBounds and Leaflet** - -
sql.getBounds
-```javascript -sql.getBounds('select * from table').done(function(bounds) { - map.setBounds(bounds); - // or map.fitBounds(bounds, mapView.getSize()); -}); -``` - -## Static Maps - -Static views of CartoDB maps can be generated using the [Static Maps API](http://docs.cartodb.com/cartodb-platform/maps-api.html#static-maps-api) within CartoDB.js. The map's style, including the zoom and bounding box, follows from what was set in the viz.json file, but you can change the zoom, center, and size of your image with a few lines of code. You can also change your basemap Images can be placed in specified DOM elements on your page, or you can generate a URL for the image. - -### Quick Start - -The easiest way to generate an image is by using the following piece of code, which generates is replaced by an `img` tag once run in an HTML file: - -```javascript - -``` - -#### Result -```html - -``` - -#### cartodb.Image(_layerSource_[, options]) - -##### Arguments - -- **layerSource**: can be either a viz.json object or a [layer source object](http://docs.cartodb.com/cartodb-platform/cartodb-js.html#standard-layer-source-object-type-cartodb) - -##### Options - -Options take the form of a JavaScript object. - -- **options**: - - **basemap**: change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). - - **no_cdn**: Disable CDN usage. Type: Boolean. Default: `false` (use CDN) - - **override_bbox**: Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) - -```javascript - -``` - -##### Returns -An _Image_ object - -### cartodb.Image - -#### Image.size(_width_,_height_) - -Sets the size of the image. - -##### Arguments - -- **width**: the width of the resulting image in pixels -- **height**: the height of the resulting image in pixels - -##### Returns -An _Image_ object - -#### Image.center(_latLng_) - -Sets the center of the map. - -##### Arguments - -- **latLng**: an array of the latitude and longitude of the center of the map. Example: `[40.4378271,-3.6795367]` - -##### Returns - -An _Image_ object - -#### Image.zoom(zoomLevel) - -Sets the zoom level of the static map. Must be used with the option `override_bbox: true` if not using `Image.center` or `Image.bbox`. - -##### Arguments - -- **zoomLevel**: the zoom of the resulting static map. `zoomLevel` must be an integer in the range [0,24]. - -##### Returns - -An _Image_ object - -#### Image.bbox(_boundingBox_) - -If you set `bbox`, `center` and `zoom` will be overridden. - -##### Arguments - -- **boundingBox**: an array of coordinates making up the bounding box for your map. `boundingBox` takes the form: `[sw_lat, sw_lon, ne_lat, ne_lon]`. - -##### Returns - -An _Image_ object - -#### Image.into(HTMLImageElement) - -Inserts the image into the HTML DOM element specified. - -##### Arguments - -- **HTMLImageElement**: the DOM element where your image is to be located. - -##### Returns - -An _Image_ object - -
Image.into
-```javascript -cartodb.Image(vizjson_url).into(document.getElementById('map_preview')) -``` - -#### Image.write(_attributes_) - -Adds an `img` tag in the same place script is executed. It's possible to specify a class name (`class`) and/or an id attribute (`id`) for the resulting image: - -
Image.write
-```javascript - -``` - -##### Arguments - -- **attributes**: - + **class**: the DOM class applied to the resulting `img` tag - + **id**: the DOM id applied to the resulting `img` tag - + **src**: path to a temporary image that acts as a placeholder while the static map is retrieved - -##### Returns - -An _Image_ object - - -#### Image.getUrl(_callback(err, url)_) - -Gets the URL for the image requested. - -
Image.getUrl
-```javascript - -``` - -##### Callback Arguments - -- **err**: error associated with the image request, if any -- **url**: URL of the generated image - -##### Returns - -An _Image_ object - -#### Image.format(_format_) - -Gets the URL for the image requested. - -##### Argument - -- **format**: image format of resulting image. One of `png` (default) or `jpg` (which have a quality of 85 dpi) - -##### Returns - -An _Image_ object - -## Core API functionality - -In case you are not using Leaflet, or you want to implement your own layer object, CartoDB provides a way to get the tiles url for a layer definition. - -If you want to use this functionality, you only need to load cartodb.core.js from our cdn. No CSS is needed: - -
Core API functionallity
-```html - -``` - -An example using this funcionality can be found in a ModestMaps example: [view live](http://cartodb.github.com/cartodb.js/examples/modestmaps.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/modestmaps.html). - -Notice that cartodb.SQL is also included in that JavaScript file - -### cartodb.Tiles - -#### cartodb.Tiles.getTiles(_layerOptions, callback_) - -Fetch the tile template for the layer definition. - -##### Arguments - -+ **layerOptions**: the data that defines the layer. It should contain at least user_name and sublayer list. These are the available options: - -
cartodb.Tiles.getTiles
-```javascript -{ - user_name: 'mycartodbuser', - sublayers: [{ - sql: "SELECT * FROM table_name"; - cartocss: '#layer { marker-fill: #F0F0F0; }' - }], - maps_api_template: 'https://{user}.cartodb.com' // Optional -} -``` - -+ **callback(tilesUrl, error)**: a function that recieves the tiles templates. In case of an error, the first param is null and the second one will be an object with an errors attribute that contains the list of errors. The tilesUrl object contains url template for tiles and interactivity grids: - -
cartodb.Tiles.getTiles
-```javascript -{ - tiles: [ - "http://{s}.cartodb.com/HASH/{z}/{x}/{y}.png", - ... - ], - grids: [ - // for each sublayer there is one entry on this array - [ - "http://{s}.cartodb.com/HASH/0/{z}/{x}/{y}.grid.json" - ], - [ - "http://{s}.cartodb.com/HASH/1/{z}/{x}/{y}.grid.json" - ], - ... - ] -} -``` - -##### Example - -In this example, a layer with one sublayer is created. The sublayer renders all the content from a table. - -
cartodb.Tiles.getTiles
-```javascript -var layerData = { - user_name: 'mycartodbuser', - sublayers: [{ - sql: "SELECT * FROM table_name"; - cartocss: '#layer { marker-fill: #F0F0F0; }' - }] -}; -cartodb.Tiles.getTiles(layerData, function(tiles, err) { - if(tiler == null) { - console.log("error: ", err.errors.join('\n')); - return; - } - console.log("url template is ", tiles.tiles[0]); -} -``` - - -## Versions - -Keep in mind the version of CartoDB.js you are using for development. For any live code, we recommend you to link directly to the tested CartoDB.js version from your development environment. You can check the version of CartoDB.js as follows: - -### cartodb.VERSION - -Returns the version of the library. It should be something like `3.0.1`. - - -## Other important stuff - -CartoDB.js has many great features for you to use in your applications. Let’s take a look at some of the most important ones: - -### Viz JSON support - -The Viz.JSON document tells CartoDB.js all the information about your map, including the style you want to use for your data and the filters you want to apply with SQL. The Viz JSON file is served with each map you create in your CartoDB account. - -Although the Viz JSON file stores all your map settings, all these settings can be easily customized with CartoDB.js. For example, if you want to do something completely different than what you initially designed it for. Loading the Viz JSON is as simple as: - -
Viz JSON support
-```javascript -cartodb.createVis('map', 'http://examples.cartodb.com/api/v2/viz/ne_10m_populated_p_1/viz.json') -``` - -### How to set a different host than cartodb.com -CartoDB.js sends all requests to the cartodb.com domain by default. If you are running your own -instance of CartoDB you can change the URLs to specify a different host. - -A different host can be configured by using ``sql_api_template`` and ``maps_api_template`` in the ``options`` parameter -for any ``cartodb`` function call. - -The format of these templates is as follows: - -```javascript -sql_api_template: 'https://{user}.test.com' -``` - -CartoDB.js will replace ``{user}``. - -Notice that you don't need to set the path to the endpoint, CartoDB.js will set it automatically. - -### Bounds wrapper - -We have added an easy method to get the bounding box for any dataset or filtered query using the CartoDB.js library. The **getBounds** function can be useful for guiding users to the right location on a map or for loading only the right data at the right time based on user actions. - -
Bounds wrapper
-```javascript -var sql = new cartodb.SQL({ user: 'cartodb_user' }); - -sql.getBounds('SELECT * FROM table_name').done(function(bounds) { - console.log(bounds); -}); -``` - -### Event listener support - -CartoDB.js is highly asynchronous. Your application can get on with what it needs to do while the library efficiently does what you request in the background. This is useful for loading maps or getting query results. At the same time, we have made it very simple to add listeners and callbacks to the async portions of the library. - -#### Loading events - -The **createLayer** and **createVis** functions trigger two important events for you to take advantage of. The first one is **done**, which will let your code know that the library has successfully read the information from the Viz JSON and loaded the layer you requested. The second is **error**, which lets you know that something did not go as expected when trying to load the requested layer: - -
Loading events
-```javascript -cartodb.createLayer(map, 'http://examples.cartodb.com/api/v1/viz/0001/viz.json') - .addTo(map) - .on('done', function(layer) { - alert(‘CartoDB layer loaded!’); - }).on('error', function(err) { - alert("some error occurred: " + err); - }); -``` - -#### Active layer events - -The next important set of events for you to use happen on those layers that are already loaded (returned by the **done** event above). Three events are triggered by layers on your webpage, each requires the layer to include an **interactivity** layer. The first event is **featureClick**, which lets you set up events after the user clicks anything that you have mapped. - -
featureClick
-```javascript -layer.on('featureClick', function(e, latlng, pos, data, layer) { - console.log("mouse clicked polygon with data: " + data); -}); -``` - -The second event is the **featureOver** event, which lets you listen for mouse hovers on any feature. Be careful, as these functions can get costly if you have a lot of features on a map. - -
featureOver
-```javascript -layer.on('featureOver', function(e, latlng, pos, data, layer) { - console.log("mouse over polygon with data: " + data); -}); -``` - -Finally, there is the **featureOut** event. This is best used if you do things like highlighting polygons on mouseover and need a way to know when to remove the highlighting after the mouse has left. - -
featureOut
-```javascript -layer.on('featureOut', function(e, latlng, pos, data, layer) { - console.log("mouse left polygon with data: " + data); -}); -``` - -#### Leaflet integration - -If you want to use [Leaflet](http://leafletjs.com) it gets even easier. CartoDB.js handles loading all the necessary libraries for you! Just include CartoDB.js and CartoDB.css in the HEAD of your website and you are ready to go! The CartoDB.css document isn’t mandatory. However, if you are making a map and are not familiar with writing your own CSS for the various needed elements, it can help you jumpstart the process. Using Leaflet is as simple as adding the main JavaScript library: - -
Leaflet integration
-```html - - -``` - -#### HTTPS support - -You can use all the functionality of CartoDB.js with HTTPs support. Be sure to use https when importing both the JS library and the CSS file. You will also need to use HTTPs in the Viz.JSON URL you pass to **createVis**. - -
HTTPS support
-```html -
- - - - - -``` - -#### Persistent version hosting - -We are committed to making sure your website works as intended no matter what changes in the future. We may find more efficient or more useful features to add to the library as time progresses. But we never want to break things you have already developed. For this reason, we make versioned CartoDB.js libraries available to you. The way they function will never unexpectedly change on you. - -We recommend that you always develop against the most recent version of CartoDB.js: - -```html - -``` - -Anytime you wish to push a stable version of your site to the web though, you can find the version of CartoDB.js you are using by looking at the first line of the library or running the following in your code: - -```javascript -alert(cartodb.VERSION) -``` - -Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.8, the URL would be: - -```html - -``` - -You can do the same for the CSS documents we provide: - -```html - -``` +* [Getting started](/CartoDB/cartodb.js/blob/develop/doc/getting_started.md) +* [API methods](/CartoDB/cartodb.js/blob/develop/doc/api_methods.md) +* [Events](/CartoDB/cartodb.js/blob/develop/doc/events.md) +* [Specific UI functions](/CartoDB/cartodb.js/blob/develop/doc/ui_functions.md) +* [Getting data with SQL](/CartoDB/cartodb.js/blob/develop/doc/sql.md) +* [Static Maps](/CartoDB/cartodb.js/blob/develop/doc/static_maps.md) +* [Core API functionality](/CartoDB/cartodb.js/blob/develop/doc/core_api.md) +* [Other important stuff](/CartoDB/cartodb.js/blob/develop/doc/other_stuff.md) diff --git a/doc/api_methods.md b/doc/api_methods.md new file mode 100644 index 0000000000..b0cb56dff7 --- /dev/null +++ b/doc/api_methods.md @@ -0,0 +1,603 @@ +# API methods + +The documentation below refers to CartoDB.js v3. For major changes in the library we will update the documentation here. This documentation is meant to help developers find specific methods from the CartoDB.js library. + +## Visualization + +### cartodb.createVis(_map_id, vizjson_url[, options] [, callback]_) + +Creates a visualization inside the map_id DOM object. + +
cartodb.createVis
+{% highlight javascript %} +var url = 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'; + +cartodb.createVis('map', url) + .done(function(vis, layers) { + }); +{% endhighlight %} + +#### Arguments + +- **map_id**: a DOM object, for example `$('#map')` or a DOM id. +- **vizjson_url**: url of the vizjson object. +- **options**: + - **shareable**: add facebook and twitter share buttons. + - **title**: adds a header with the title of the visualization. + - **description**: adds description to the header (as you set in the UI). + - **search**: adds a search control (default: true). + - **zoomControl**: adds zoom control (default: true). + - **loaderControl**: adds loading control (default: true). + - **center_lat**: latitude where the map is initializated. + - **center_lon**: longitude where the map is initializated. + - **zoom**: initial zoom. + - **cartodb_logo**: default to true, set to false if you want to remove the cartodb logo. + - **infowindow**: set to false if you want to disable the infowindow (enabled by default). + - **time_slider**: show time slider with torque layers (enabled by default) + - **layer_selector**: show layer selector (default: false) + - **legends**: if it's true legends are shown in the map. + - **https**: if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. + - **scrollwheel**: enable/disable the ability of zooming using scrollwheel (default enabled) + - **fullscreen**: if true adds a button to toggle the map fullscreen + - **mobile_layout**: if true enables a custom layout for mobile devices (default: false) + - **force_mobile**: forces enabling/disabling the mobile layout (it has priority over mobile_layout argument) + - **gmaps_base_type**: Use Google Maps as map provider whatever is the one specified in the viz.json". Available types: 'roadmap', 'gray_roadmap', 'dark_roadmap', 'hybrid', 'satellite', 'terrain'. + - **gmaps_style**: Google Maps styled maps. See [documentation](https://developers.google.com/maps/documentation/javascript/styling). + - **no_cdn**: true to disable CDN when fetching tiles +- **callback(vis,layers)**: if a function is specified, it is called once the visualization is created, passing vis and layers as arguments + +#### Returns + +A promise object. You can listen for the following events: + ++ **done**: triggered when the visualization is created, `vis` is passed as the first argument and `layers` is passed as the second argument. Each layer type has different options, see layers section. ++ **error**: triggered when the layer couldn't be created. The error string is the first argument. + +## cartodb.Vis + +### vis.getLayers() + +Returns an array of layers in the map. The first is the base layer. + +### vis.addOverlay(_options_) + +Adds an overlay to the map that can be either a zoom control, a tooltip or an infobox. + +#### Arguments + +- **options** + - **layer** layer from the visualization where the overlay should be applied (optional) + - **type** zoom / tooltip / infobox + +If no layer is provided, the overlay will be added to the first layer of the visualization. Extra options are available based on the specific UI component. + +#### Returns + +An overlay object, see [vis.Overlays](#visoverlays). + +### vis.getOverlay(_type_) + +Returns the first overlay with the specified **type**. + +
vis.getOverlay
+{% highlight javascript %} +var zoom = vis.getOverlay('zoom'); +zoom.clean() // remove it from the screen +{% endhighlight %} + +### vis.getOverlays() + +Returns a list of the overlays that are currently on the screen (see overlays description). + +### vis.getNativeMap() + +Returns the native map object being used (e.g. a L.Map object for Leaflet). + +### vis.Overlays + +An overlay is a control shown on top of the map. + +Overlay objects are always created using the **addOverlay** method of a cartodb.Vis object. + +An overlay is internally a [**Backbone.View**](http://backbonejs.org/#View) so if you know how Backbone works you can use it. If you want to use plain DOM objects you can access **overlay.el** (**overlay.$el** for jQuery object). + +### vis.addInfowindow(_map, layer, fields [, options]_) + +Adds an infowindow to the map controlled by layer events. It enables interaction and overrides the layer interactivity. + +#### Arguments + + - **map**: native map object or leaflet + - **layer**: cartodb layer (or sublayer) + - **fields**: array of column names + +#### Returns + +An infowindow object, see [sublayer.infowindow](#sublayerinfowindow) + +### cartodb.createLayer(_map, layerSource [, options] [, callback]_) + +With visualizations already created through the CartoDB console, you can simply use the **createLayer** function to add them into your web pages. Unlike **createVis**, this method requires an already activated **map** object and it does not load a basemap for you. + +#### Arguments + +- **map**: Leaflet L.Map object. The map should be initialized before calling this function. + +- **layerSource**: contains information about the layer. It can be specified in 2 ways: + +
Passing the url where the layer data is located
+{% highlight javascript %} +cartodb.createLayer(map, 'http://myserver.com/layerdata.json') +{% endhighlight %} + +
passing the data directly
+{% highlight javascript %} +cartodb.createLayer(map, { layermetadata }) +{% endhighlight %} + +- **options**: + - **https**: force https + - **refreshTime**: if is set, the layer is refreshed each refreshTime milliseconds. + - **infowindow**: set to false if you want to disable the infowindow (enabled by default). + - **tooltip**: set to false if you want to disable the tooltip (enabled by default). + - **legends**: if it's true legends are shown in the map. + - **time_slider**: show time slider with torque layers (enabled by default) + - **layerIndex**: when the visualization contains more than one layer this index allows you to select + what layer is created. Take into account that `layerIndex == 0` is the base layer and that + all the tiled layers (non animated ones) are merged into a single one. The default value for + this option is 1 (usually tiled layers). + - **filter**: a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. + +- **callback(_layer_)**: if a function is specified, it will be invoked after the layer has been created. The layer will be passed as an argument. + +#### Returns + +A promise object. You can listen for the following events: + ++ **done**: triggered when the layer is created, the layer is passed as first argument. Each layer type has different options, see layers section. ++ **error**: triggered when the layer couldn't be created. The error string is the first argument. + +You can call to `addTo(map[, position])` in the promise so when the layer is ready it will be added to the map. + +#### Example + +
cartodb.createLayer using a url
+ +{% highlight javascript %} +var map; +var mapOptions = { + zoom: 5, + center: [43, 0] +}; +map = new L.Map('map', mapOptions); + +cartodb.createLayer(map, 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json') + .addTo(map) + .on('done', function(layer) { + layer + .on('featureOver', function(e, latlng, pos, data) { + console.log(e, latlng, pos, data); + }) + .on('error', function(err) { + console.log('error: ' + err); + }); + }).on('error', function(err) { + console.log("some error occurred: " + err); + }); +{% endhighlight %} + +Layer metadata must take one of the following forms: + +### Standard Layer Source Object (`type: 'cartodb'`) + +Used for most maps with tables that are set to public or public with link. + +{% highlight javascript %} +{ + user_name: 'your_user_name', // Required + type: 'cartodb', // Required + sublayers: [{ + sql: "SELECT * FROM table_name", // Required + cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required + interactivity: "column1, column2, ...", // Optional + }, + { + sql: "SELECT * FROM table_name", // Required + cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required + interactivity: "column1, column2, ...", // Optional + }, + ... + ] +} +{% endhighlight %} + +### Torque Layer Source Object (`type: 'torque'`) + +Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not allow sublayers. + +{% highlight javascript %} +{ + type: 'torque', // Required + order: 1, // Optional + options: { + query: "SQL statement", // Required if table_name is not given + table_name: "table_name", // Required if query is not given + user_name: "your_user_name", // Required + cartocss: "CartoCSS styles" // Required + } +} +{% endhighlight %} + +### Named Maps Layer Source Object (`type: 'namedmap'`) + +Used for making public maps with private data. See [Named Maps](http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps-1) for more information. + + +{% highlight javascript %} +{ + user_name: 'your_user_name', // Required + type: 'namedmap', // Required + named_map: { + name: 'name_of_map', // Required + // Optional + layers: [{ + layer_name: "sublayer0", // Optional + interactivity: "column1, column2, ..." // Optional + }, + { + layer_name: "sublayer1", + interactivity: "column1, column2, ..." + }, + ... + ], + // Optional + params: { + color: "hex_value", + num: 2 + } + } +} +{% endhighlight %} + +#### Example + +
cartodb.createLayer combining multiple types of layers and setting a filter
+ +{% highlight javascript %} +cartodb.createLayer(map, { + user_name: 'examples', + type: 'cartodb', + sublayers: [ + { + type: "http", + urlTemplate: "http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png", + subdomains: [ "a", "b", "c" ] + }, + { + sql: 'select * from country_boundaries', + cartocss: '#layer { polygon-fill: #F00; polygon-opacity: 0.3; line-color: #F00; }' + }, + ], +}, { filter: ['http', 'mapnik'] }) +{% endhighlight %} + +## cartodb.CartoDBLayer + +CartoDBLayer allows you to manage tiled layers from CartoDB. It manages the sublayers. + +### layer.clear() + +Clears the layer. It should be invoked after removing the layer from the map. + +### layer.hide() + +Hides the layer from the map. + +### layer.show() + +Shows the layer in the map if it was previously added. + +### layer.toggle() + +Toggles the visibility of the layer and returns a boolean that indicates the new status (true if the layer is shown, false if it is hidden) + +### layer.setOpacity(_opacity_) + +Changes the opacity of the layer. + +#### Arguments + ++ **opacity**: value in range [0, 1] + +### layer.getSubLayer(_layerIndex_) + +Gets a previously created sublayer. And exception is raised if no sublayer exists. + +#### Arguments + ++ **layerIndex**: 0 based index of the sublayer to get. Should be within [0, getSubLayerCount()) + +#### Returns + +A SubLayer object. + +#### Example + +
layer.getSubLayer
+{% highlight javascript %} +layer.getSubLayer(1).hide(); + +var sublayer = layer.getSubLayer(0); + +sublayer.setSQL('SELECT * FROM table_name limit 10'); +{% endhighlight %} + +### layer.getSubLayerCount() + +Gets the number of sublayers in layer. + +#### Returns + +The number of sublayers. + +#### Example + +
Hide layers using layer.getSubLayerCount
+{% highlight javascript %} +var num_sublayers = layer.getSubLayerCount(); + +for (var i = 0; i < num_sublayers; i++) { + layer.getSubLayer(i).hide(); +} +{% endhighlight %} + +### layer.createSubLayer(_layerDefinition_) + +Adds a new data to the current layer. With this method, data from multiple tables can be easily visualized. New in V3. + +#### Arguments + +- **layerDefinition**: an object with the sql and cartocss that defines the data, should be like: + +
layerDefinition
+{% highlight javascript %} +{ + sql: "SELECT * FROM table_name", + cartocss: "#layer { marker-fill: red; }", + interactivity: 'cartodb_id, area, column' // optional +} +{% endhighlight %} + +`sql` and `cartocss` are mandatory. An exception is raised if either of them are not present. If the interactivity is not set, there is no interactivity enabled for that layer (better performance). SQL and CartoCSS syntax should be correct. Look at the documentation for [PostgreSQL](http://www.postgresql.org/docs/9.3/interactive/sql-syntax.html) and [CartoCSS](http://docs.cartodb.com/cartodb-editor.html#customizing-maps-with-cartocss) for more information. There are some restrictions in the SQL queries: + +- Must not write. INSERT, DELETE, UPDATE, ALTER and so on are not allowed (the query will fail) +- Must not contain trialing semicolon + +#### Returns + +A SubLayer object. + +#### Example + +
layer.createSubLayer
+{% highlight javascript %} +cartodb.createLayer(map, 'http://examples.cartodb.com/api/v2/viz/european_countries_e/viz.json', function(layer) { + // add populated places points over the countries layer + layer.createSubLayer({ + sql: 'SELECT * FROM ne_10m_populated_places_simple', + cartocss: '#layer { marker-fill: red; }' + }); +}).addTo(map); +{% endhighlight %} + +### layer.invalidate() + +Refreshes the data. If the data has been changed in the CartoDB server those changes will be displayed. Nothing happens otherwise. Every time a parameter is changed in a sublayer, the layer is refreshed automatically, so there's no need to call this method manually. New in V3. + +### layer.setAuthToken(_auth_token_) + +Sets the auth token that will be used to create the layer. Only available for private visualizations. An exception is +raised if the layer is not being loaded with HTTPS. See [Named Maps](http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps-1) for more information. + +#### Returns + +The layer itself. + +#### Arguments + +- **auth_token:** string + +### layer.setParams(_key, value_) + +Sets the configuration of a layer when using [named maps](http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps-1). It can be invoked in different ways: + +
layer.setParams
+{% highlight javascript %} +layer.setParams('test', 10); // sets test = 10 +layer.setParams('test', null); // unset test +layer.setParams({'test': 1, 'color': '#F00'}); // set more than one parameter at once +{% endhighlight %} + +#### Arguments + +- **key:** string +- **value:** string or number + +#### Returns + +The layer itself. + +## cartodb.CartoDBLayer.SubLayer + +### sublayer.set(_layerDefinition_) + +Sets sublayer parameters. Useful when more than one parameter needs to be changed. + +#### Arguments + +- **layerDefinition**: an object with the sql and cartocss that defines the data, like: + +
layerDefinition
+{% highlight javascript %} +{ + sql: "SELECT * FROM table_name", + cartocss: "#layer { marker-fill: red; }", + interactivity: 'cartodb_id, area, column' // optional +} +{% endhighlight %} + +#### Returns + +The layer itself. + +#### Example + +
sublayer.set
+{% highlight javascript %} +sublayer.set({ + sql: "SELECT * FROM table_name WHERE cartodb_id < 100", + cartocss: "#layer { marker-fill: red }", + interactivity: "cartodb_id, the_geom, magnitude" +}); +{% endhighlight %} + +### sublayer.get(_attr_) + +Gets the attribute for the sublayer, for example 'sql', 'cartocss'. + +#### Returns + +The requested attribute or undefined if it's not present. + +### sublayer.remove() + +Removes the sublayer. An exception will be thrown if a method is called and the layer has been removed. + +### sublayer.show() + +Shows a previously hidden sublayer. The layer is refreshed after calling this function. + +### sublayer.hide() + +Removes the sublayer from the layer temporarily. The layer is refreshed after calling this function. + +### sublayer.toggle() + +Toggles the visibility of the sublayer and returns a boolean that indicates the new status (true if the sublayer is visible, false if it is hidden) + +### sublayer.isVisible() + +It returns `true` if the sublayer is visible. + +## cartodb.CartoDBLayer.CartoDBSubLayer + +### sublayer.getSQL() + +Shortcut for `get('sql')` + +### sublayer.getCartoCSS() + +Shortcut for `get('cartocss')` + +### sublayer.setSQL(sql) + +Shortcut for `set({'sql': 'SELECT * FROM table_name'})` + +### sublayer.setCartoCSS(css) + +Shortcut for `set({'cartocss': '#layer {...}' })` + +### sublayer.setInteractivity('cartodb_id, name, ...') + +Shortcut for `set({'interactivity': 'cartodb_id, name, ...' })` + +Sets the columns which data will be available via the interaction with the sublayer. + +### sublayer.setInteraction(_true_) + +Enables (true) or disables (false) the interaction of the layer. When disabled, **featureOver**, **featureClick**, **featureOut**, **mouseover** and **mouseout** are **not** triggered. + +#### Arguments + ++ **enable**: true if the interaction needs to be enabled. + +### sublayer.infowindow + +**sublayer.infowindow** is a Backbone model where we modify the parameters of the infowindow. + +#### Attributes + +- **template**: Custom HTML template for the infowindow. You can write simple HTML or use [Mustache templates](http://mustache.github.com/). +- **sanitizeTemplate**: By default all templates are sanitized from unsafe tags/attrs (e.g. ` + + +{% endhighlight %} + +[Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/custom_infowindow.html) + +## cartodb.CartoDBLayer.HttpSubLayer + +### sublayer.setURLTemplate(urlTemplate) + +Shortcut for `set({'urlTemplate': 'http://{s}.example.com/{z}/{x}/{y}.png' })` + +### sublayer.setSubdomains(subdomains) + +Shortcut for `set({'subdomains': ['a', 'b', '...'] })` + +### sublayer.setTms(tms) + +Shortcut for `set({'tms': true|false })` + +### sublayer.getURLTemplate + +Shortcut for `get('urlTemplate')` + +### sublayer.getSubdomains + +Shortcut for `get('subdomains')` + +### sublayer.getTms + +Shortcut for `get('tms')` + +### sublayer.legend + +**sublayer.legend** is a Backbone model with the information about the legend. + +#### Attributes + +- **template**: Custom HTML template for the legend. You can write simple HTML. +- **title**: Title of the legend. +- **show_title**: Set this to `false` if you don't want the title to be displayed. +- **items**: An array with the items that are displayed in the legend. +- **visible**: Set this to `false` if you want to hide the legend. diff --git a/doc/core_api.md b/doc/core_api.md new file mode 100644 index 0000000000..535666ef4e --- /dev/null +++ b/doc/core_api.md @@ -0,0 +1,80 @@ +# Core API functionality + +In case you are not using Leaflet, or you want to implement your own layer object, CartoDB provides a way to get the tiles url for a layer definition. + +If you want to use this functionality, you only need to load cartodb.core.js from our cdn. No CSS is needed: + +
Core API functionallity
+{% highlight html %} + +{% endhighlight %} + +An example using this funcionality can be found in a ModestMaps example: [view live](http://cartodb.github.com/cartodb.js/examples/modestmaps.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/modestmaps.html). + +Notice that cartodb.SQL is also included in that JavaScript file + +## cartodb.Tiles + +### cartodb.Tiles.getTiles(_layerOptions, callback_) + +Fetch the tile template for the layer definition. + +#### Arguments + ++ **layerOptions**: the data that defines the layer. It should contain at least user_name and sublayer list. These are the available options: + +
cartodb.Tiles.getTiles
+{% highlight javascript %} +{ + user_name: 'mycartodbuser', + sublayers: [{ + sql: "SELECT * FROM table_name"; + cartocss: '#layer { marker-fill: #F0F0F0; }' + }], + maps_api_template: 'https://{user}.cartodb.com' // Optional +} +{% endhighlight %} + ++ **callback(tilesUrl, error)**: a function that recieves the tiles templates. In case of an error, the first param is null and the second one will be an object with an errors attribute that contains the list of errors. The tilesUrl object contains url template for tiles and interactivity grids: + +
cartodb.Tiles.getTiles
+{% highlight javascript %} +{ + tiles: [ + "http://{s}.cartodb.com/HASH/{z}/{x}/{y}.png", + ... + ], + grids: [ + // for each sublayer there is one entry on this array + [ + "http://{s}.cartodb.com/HASH/0/{z}/{x}/{y}.grid.json" + ], + [ + "http://{s}.cartodb.com/HASH/1/{z}/{x}/{y}.grid.json" + ], + ... + ] +} +{% endhighlight %} + +#### Example + +In this example, a layer with one sublayer is created. The sublayer renders all the content from a table. + +
cartodb.Tiles.getTiles
+{% highlight javascript %} +var layerData = { + user_name: 'mycartodbuser', + sublayers: [{ + sql: "SELECT * FROM table_name"; + cartocss: '#layer { marker-fill: #F0F0F0; }' + }] +}; +cartodb.Tiles.getTiles(layerData, function(tiles, err) { + if(tiler == null) { + console.log("error: ", err.errors.join('\n')); + return; + } + console.log("url template is ", tiles.tiles[0]); +} +{% endhighlight %} \ No newline at end of file diff --git a/doc/events.md b/doc/events.md new file mode 100644 index 0000000000..9792d2f7eb --- /dev/null +++ b/doc/events.md @@ -0,0 +1,262 @@ +# Events + +You can bind custom functions to layer events. This is useful for integrating your website with your maps, adding events for mouseovers and click events. + +## layer + +### layer.featureOver(_event, latlng, pos, data, layerIndex_) + +Triggered when the user hovers on any feature. + +#### Callback arguments + +- **event**: Browser mouse event object. +- **latlng**: Array with the LatLng ([lat,lng]) where the layer was clicked. +- **pos**: Object with x and y position in the DOM map element. +- **data**: The CartoDB data of the clicked feature with the **interactivity** param. +- **layerIndex**: the layerIndex where the event happened. + +#### Example + +
layer.on
+{% highlight javascript %} +layer.on('featureOver', function(e, latlng, pos, data, subLayerIndex) { + console.log("mouse over polygon with data: " + data); +}); +{% endhighlight %} + +### layer.featureOut(_layerIndex_) + +Triggered when the user hovers out any feature. + +### layer.featureClick(_event, latlng, pos, data, layerIndex_) + +Triggered when when the user clicks on a feature of a layer. + +#### callback arguments + +Same as `featureOver`. + +### layer.mouseover() + +Triggered when the mouse enters in **any** feature. Useful to change the cursor while hovering. + +### layer.mouseout() + +Triggered when the mouse leaves all the features. Useful to revert the cursor after hovering. + +#### Example + +
sublayer.on
+{% highlight javascript %} +layer.on('mouseover', function() { + cursor.set('hand') +}); + +layer.on('mouseout', function() { + cursor.set('auto') +}); +{% endhighlight %} + +### layer.loading() + +Triggered when the layer or any of its sublayers are about to be loaded. This is also triggered when any properties are changed but not yet visible. + +#### Example + +
layer.on
+{% highlight javascript %} +layer.on("loading", function() { + console.log("layer about to load"); +}); +layer.getSubLayer(0).set({ + cartocss: "#export { polygon-opacity: 0; }" +}); +{% endhighlight %} + +### layer.load() + +Triggered when the layer or its sublayers have been loaded. This is also triggered when any properties are changed and visible. + +#### Example + +
layer.on
+{% highlight javascript %} +layer.on("load", function() { + console.log("layer loaded"); +}); +layer.getSubLayer(0).set({ + cartocss: "#export { polygon-opacity: 0; }" +}); +{% endhighlight %} + +## subLayer + +### sublayer.featureOver(_event, latlng, pos, data, layerIndex_) + +Same as `layer.featureOver()` but sublayer specific. + +#### callback arguments + +Same as `layer.featureOver()`. + +### sublayer.featureClick(_event, latlng, pos, data, layerIndex_) + +Same as `layer.featureClick()` but sublayer specific. + +#### callback arguments + +Same as `layer.featureClick()`. + +### sublayer.mouseover() + +Same as `layer.mouseover()` but sublayer specific. + +### sublayer.mouseout() + +Same as `layer.mouseover()` but sublayer specific. + + +# Specific UI functions + +There are a few functions in CartoDB.js for creating, enabling, and disabling pieces of the user interface. + +## cartodb.geo.ui.Tooltip + +Shows a small tooltip on hover: + +
cartodb.geo.ui.Tooltip
+{% highlight javascript %} +var tooltip = vis.addOverlay({ + type: 'tooltip', + template: '

{% raw %}{{variable}}{% endraw %}

' // mustache template +}); +{% endhighlight %} + +### cartodb.geo.ui.Tooltip.enable() + +The tooltip is shown when hover on feature when is called. + +### cartodb.geo.ui.Tooltip.disable() + +The tooltip is not shown when hover on feature. + +## cartodb.geo.ui.InfoBox + +Shows a small box when the user hovers on a map feature. The position is fixed: + +
cartodb.geo.ui.InfoBox
+{% highlight javascript %} +var box = vis.addOverlay({ + type: 'infobox', + template: '

{% raw %}{{name_to_display}}{% endraw %}

', + width: 200, // width of the box + position: 'bottom|right' // top, bottom, left and right are available +}); +{% endhighlight %} + +### cartodb.geo.ui.InfoBox.enable() + +The tooltip is shown when hover on feature. + +### cartodb.geo.ui.InfoBox.disable() + +The tooltip is not shown when hover on feature. + +## cartodb.geo.ui.Zoom + +Shows the zoom control: + +
cartodb.geo.ui.Zoom
+{% highlight javascript %} +vis.addOverlay({ type: 'zoom' }); +{% endhighlight %} + +### cartodb.geo.ui.Zoom.show() + +### cartodb.geo.ui.Zoom.hide() + + +# Getting data with SQL + +CartoDB offers a powerful SQL API for you to query and retreive data from your CartoDB tables. CartoDB.js offers a simple to use wrapper for sending those requests and using the results. + +## cartodb.SQL + +**cartodb.SQL** is the tool you will use to access data you store in your CartoDB tables. This is a really powerful technique for returning things like: **items closest to a point**, **items ordered by date**, or **GeoJSON vector geometries**. It’s all powered with SQL and our tutorials will show you how easy it is to begin with SQL. + +
cartodb.SQL
+{% highlight javascript %} +var sql = new cartodb.SQL({ user: 'cartodb_user' }); +sql.execute("SELECT * FROM table_name WHERE id > {% raw %}{{id}}{% endraw %}", { id: 3 }) + .done(function(data) { + console.log(data.rows); + }) + .error(function(errors) { + // errors contains a list of errors + console.log("errors:" + errors); + }) +{% endhighlight %} + +It accepts the following options: + ++ **format**: should be geoJSON. ++ **dp**: float precision. ++ **jsonp**: if jsonp should be used instead of CORS. This param is enabled if the browser does not support CORS. + +These arguments will be applied to all the queries performed by this object. If you want to override them for one query see **execute** options. + +### sql.execute(_sql [,vars][, options][, callback]_) + +It executes a sql query. + +#### Arguments + ++ **sql**: a string with the sql query to be executed. You can specify template variables like {% raw %}{{variable}}{% endraw %} which will be filled with **vars** object. ++ **vars**: a map with the variables to be interpolated in the sql query. ++ **options**: accepts **format**, **dp** and **jsonp**. This object also overrides the params passed to $.ajax. + +#### Returns + +A promise object. You can listen for the following events: + ++ **done**: triggered when the data arrives. ++ **error**: triggered when something failed. + +You can also use done and error methods: + +
sql.execute
+{% highlight javascript %} +sql.execute('SELECT * FROM table_name') + .done(fn) + .error(fnError) +{% endhighlight %} + +### sql.getBounds(_sql [,vars][, options][, callback]_) + +Returns the bounds [ [sw_lat, sw_lon], [ne_lat, ne_lon ] ] for the geometry resulting of specified query. + +
sql.getBounds
+{% highlight javascript %} +sql.getBounds('select * from table').done(function(bounds) { + console.log(bounds); +}); +{% endhighlight %} + +#### Arguments + ++ **sql**: a string with the sql query to calculate the bounds from. + +#### Application of getBounds in Leaflet + +You can use the results from `getBounds` to center data on your maps using Leaflet. + +- **getBounds and Leaflet** + +
sql.getBounds
+{% highlight javascript %} +sql.getBounds('select * from table').done(function(bounds) { + map.setBounds(bounds); + // or map.fitBounds(bounds, mapView.getSize()); +}); +{% endhighlight %} diff --git a/doc/getting_started.md b/doc/getting_started.md new file mode 100644 index 0000000000..635582f318 --- /dev/null +++ b/doc/getting_started.md @@ -0,0 +1,148 @@ +# Getting started + +The simplest way to use a visualization created in CartoDB on an external site is as follows: + +{% highlight html %} + +... +
+... + + +{% endhighlight %} + +[Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/easy.html) + + +## Using the library + +CartoDB.js can be used when you want to embed and use a visualization you have designed using CartoDB's user interface, or to dynamically create visualizations from scratch using your data. If you want to create new maps on your webpage, jump to [Creating a visualization from scratch](#creating-a-visualization-from-scratch). If you already have maps on your webpage and want to add CartoDB visualizations to them, read [Adding CartoDB layers to an existing map](#adding-cartodb-layers-to-an-existing-map). + +You can also use the CartoDB API to create visualizations programmatically. This can be useful when the visualizations react to user interactions. To read more about it jump to [Creating visualizations at runtime](#creating-visualizations-at-runtime). + +We’ve also made it easier than ever for you to build maps using the mapping library of your choice. Whether you are using Leaflet or something else, our CartoDB.js code remains the same. This makes our API documentation simple and straightforward. It also makes it easy for you to remember and be consistent if you develop or maintain multiple maps online. + +To start using CartoDB.js just paste this piece of code within the HEAD tags of your HTML: + +
Linking cartodb.js on your html file
+{% highlight html %} + + +{% endhighlight %} + +### Creating a visualization from scratch + +The easiest way to quickly get a CartoDB map onto your webpage. Use this when there is no map in your application and you want to add the visualization to hack over it. With this method, CartoDB.js handles all the details of loading a map interface, basemap, and your CartoDB visualization. + +You can start by giving cartodb.js the DIV ID from your HTML where you want to place your map, and the viz.json URL of your visualization, which you can get from the share window. + +
Simplest way to add your map to a webpage ever!
+{% highlight javascript %} +cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'); +{% endhighlight %} + +That’s it! No need to create the map instance, insert controls, or load layers. CartoDB.js takes care of this for you. If you want to modify the result after instantiating your map with this method, take a look at the CartoDB.js API [available methods](#api-methods). For example, you can also use the returned layer to build more functionality (show/hide, click, hover, custom infowindows): + +
Simplest way to add your map to a webpage ever!
+{% highlight javascript %} +cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json') + .done(function(vis, layers) { + // layer 0 is the base layer, layer 1 is cartodb layer + // when setInteraction is disabled featureOver is triggered + layers[1].setInteraction(true); + layers[1].on('featureOver', function(e, latlng, pos, data, layerNumber) { + console.log(e, latlng, pos, data, layerNumber); + }); + + // you can get the native map to work with it + var map = vis.getNativeMap(); + + // now, perform any operations you need, e.g. assuming map is a L.Map object: + // map.setZoom(3); + // map.panTo([50.5, 30.5]); + }); +{% endhighlight %} + +### Adding CartoDB layers to an existing map + +In case you already have a map instantiated on your page, you can simply use the [createLayer](#cartodbcreatelayermap-layersource--options--callback) method to add new CartoDB layers to it. This is particullary useful when you have more things on your map apart from CartoDB layers or you have an application where you want to integrate CartoDB layers. + +Below, you have an example using a previously instatiated Leaflet map. + +
Adding cartodb layers to an existing map
+{% highlight html %} +
+ + +{% endhighlight %} + +[Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/leaflet.html) + +### Creating visualizations at runtime + +All CartoDB services are available through the API, which basically means that you can create a new visualization without doing it before through the CartoDB UI. This is particularly useful when you are modifying the visualization depending on user interactions that change the SQL to get the data or CartoCSS to style it. Although this method requires more programming skills, it provides all the flexibility you might need to create more dynamic visualizations. + +When you create a visualization using the CartoDB website, you automatically get a viz.json URL that defines it. When you want to create the visualization via JavaScript, you don't always have a viz.json. You will need to pass all the required parameters to the library so that it can create the visualization at runtime and display it on your map. It is pretty simple. + +
Creating visualizations at runtime
+{% highlight javascript %} +// create a layer with 1 sublayer +cartodb.createLayer(map, { + user_name: 'mycartodbuser', + type: 'cartodb', + sublayers: [{ + sql: "SELECT * FROM table_name", + cartocss: '#table_name {marker-fill: #F0F0F0;}' + }] +}) +.addTo(map) // add the layer to our map which already contains 1 sublayer +.done(function(layer) { + + // create and add a new sublayer + layer.createSubLayer({ + sql: "SELECT * FROM table_name limit 200", + cartocss: '#table_name {marker-fill: #F0F0F0;}' + }); + + // change the query for the first layer + layer.getSubLayer(0).setSQL("SELECT * FROM table_name limit 10"); +}); +{% endhighlight %} + +Want further information? [Check out the complete list of API methods](#api-methods). + + +## Usage examples + +The best way to start learning about the library is by taking a look at some of the examples below: + ++ An easy example using the library - ([view live](http://cartodb.github.com/cartodb.js/examples/easy.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/easy.html)). ++ Leaflet integration - ([view live](http://cartodb.github.com/cartodb.js/examples/leaflet.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/leaflet.html)). ++ Customizing infowindow data - ([view live](http://cartodb.github.com/cartodb.js/examples/custom_infowindow.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/custom_infowindow.html)). ++ An example using a layer selector - ([view live](http://cartodb.github.com/cartodb.js/examples/layer_selector.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/layer_selector.html)). ++ The Hobbit map done with the library - ([view live](http://cartodb.github.com/cartodb.js/examples/TheHobbitLocations/) / [source code](https://github.com/CartoDB/cartodb.js/tree/develop/examples/TheHobbitLocations)). diff --git a/doc/other_stuff.md b/doc/other_stuff.md new file mode 100644 index 0000000000..c3e7e9e0ab --- /dev/null +++ b/doc/other_stuff.md @@ -0,0 +1,162 @@ +# Other important stuff + +CartoDB.js has many great features for you to use in your applications. Let’s take a look at some of the most important ones: + +## Viz JSON support + +The Viz.JSON document tells CartoDB.js all the information about your map, including the style you want to use for your data and the filters you want to apply with SQL. The Viz JSON file is served with each map you create in your CartoDB account. + +Although the Viz JSON file stores all your map settings, all these settings can be easily customized with CartoDB.js. For example, if you want to do something completely different than what you initially designed it for. Loading the Viz JSON is as simple as: + +
Viz JSON support
+{% highlight javascript %} +cartodb.createVis('map', 'http://examples.cartodb.com/api/v2/viz/ne_10m_populated_p_1/viz.json') +{% endhighlight %} + +## How to set a different host than cartodb.com +CartoDB.js sends all requests to the cartodb.com domain by default. If you are running your own +instance of CartoDB you can change the URLs to specify a different host. + +A different host can be configured by using ``sql_api_template`` and ``maps_api_template`` in the ``options`` parameter +for any ``cartodb`` function call. + +The format of these templates is as follows: + +{% highlight javascript %} +sql_api_template: 'https://{user}.test.com' +{% endhighlight %} + +CartoDB.js will replace ``{user}``. + +Notice that you don't need to set the path to the endpoint, CartoDB.js will set it automatically. + +## Bounds wrapper + +We have added an easy method to get the bounding box for any dataset or filtered query using the CartoDB.js library. The **getBounds** function can be useful for guiding users to the right location on a map or for loading only the right data at the right time based on user actions. + +
Bounds wrapper
+{% highlight javascript %} +var sql = new cartodb.SQL({ user: 'cartodb_user' }); + +sql.getBounds('SELECT * FROM table_name').done(function(bounds) { + console.log(bounds); +}); +{% endhighlight %} + +## Event listener support + +CartoDB.js is highly asynchronous. Your application can get on with what it needs to do while the library efficiently does what you request in the background. This is useful for loading maps or getting query results. At the same time, we have made it very simple to add listeners and callbacks to the async portions of the library. + +### Loading events + +The **createLayer** and **createVis** functions trigger two important events for you to take advantage of. The first one is **done**, which will let your code know that the library has successfully read the information from the Viz JSON and loaded the layer you requested. The second is **error**, which lets you know that something did not go as expected when trying to load the requested layer: + +
Loading events
+{% highlight javascript %} +cartodb.createLayer(map, 'http://examples.cartodb.com/api/v1/viz/0001/viz.json') + .addTo(map) + .on('done', function(layer) { + alert(‘CartoDB layer loaded!’); + }).on('error', function(err) { + alert("some error occurred: " + err); + }); +{% endhighlight %} + +### Active layer events + +The next important set of events for you to use happen on those layers that are already loaded (returned by the **done** event above). Three events are triggered by layers on your webpage, each requires the layer to include an **interactivity** layer. The first event is **featureClick**, which lets you set up events after the user clicks anything that you have mapped. + +
featureClick
+{% highlight javascript %} +layer.on('featureClick', function(e, latlng, pos, data, layer) { + console.log("mouse clicked polygon with data: " + data); +}); +{% endhighlight %} + +The second event is the **featureOver** event, which lets you listen for mouse hovers on any feature. Be careful, as these functions can get costly if you have a lot of features on a map. + +
featureOver
+{% highlight javascript %} +layer.on('featureOver', function(e, latlng, pos, data, layer) { + console.log("mouse over polygon with data: " + data); +}); +{% endhighlight %} + +Finally, there is the **featureOut** event. This is best used if you do things like highlighting polygons on mouseover and need a way to know when to remove the highlighting after the mouse has left. + +
featureOut
+{% highlight javascript %} +layer.on('featureOut', function(e, latlng, pos, data, layer) { + console.log("mouse left polygon with data: " + data); +}); +{% endhighlight %} + +### Leaflet integration + +If you want to use [Leaflet](http://leafletjs.com) it gets even easier. CartoDB.js handles loading all the necessary libraries for you! Just include CartoDB.js and CartoDB.css in the HEAD of your website and you are ready to go! The CartoDB.css document isn’t mandatory. However, if you are making a map and are not familiar with writing your own CSS for the various needed elements, it can help you jumpstart the process. Using Leaflet is as simple as adding the main JavaScript library: + +
Leaflet integration
+{% highlight html %} + + +{% endhighlight %} + +### HTTPS support + +You can use all the functionality of CartoDB.js with HTTPs support. Be sure to use https when importing both the JS library and the CSS file. You will also need to use HTTPs in the Viz.JSON URL you pass to **createVis**. + +
HTTPS support
+{% highlight html %} +
+ + + + + +{% endhighlight %} + +### Persistent version hosting + +We are committed to making sure your website works as intended no matter what changes in the future. We may find more efficient or more useful features to add to the library as time progresses. But we never want to break things you have already developed. For this reason, we make versioned CartoDB.js libraries available to you. The way they function will never unexpectedly change on you. + +We recommend that you always develop against the most recent version of CartoDB.js: + +{% highlight html %} + +{% endhighlight %} + +Anytime you wish to push a stable version of your site to the web though, you can find the version of CartoDB.js you are using by looking at the first line of the library or running the following in your code: + +{% highlight javascript %} +alert(cartodb.VERSION) +{% endhighlight %} + +Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.8, the URL would be: + +{% highlight html %} + +{% endhighlight %} + +You can do the same for the CSS documents we provide: + +{% highlight html %} + +{% endhighlight %} + +## Versions + +Keep in mind the version of CartoDB.js you are using for development. For any live code, we recommend you to link directly to the tested CartoDB.js version from your development environment. You can check the version of CartoDB.js as follows: + +### cartodb.VERSION + +Returns the version of the library. It should be something like `3.0.1`. diff --git a/doc/sql.md b/doc/sql.md new file mode 100644 index 0000000000..c787d067c0 --- /dev/null +++ b/doc/sql.md @@ -0,0 +1,83 @@ +# Getting data with SQL + +CartoDB offers a powerful SQL API for you to query and retreive data from your CartoDB tables. CartoDB.js offers a simple to use wrapper for sending those requests and using the results. + +## cartodb.SQL + +**cartodb.SQL** is the tool you will use to access data you store in your CartoDB tables. This is a really powerful technique for returning things like: **items closest to a point**, **items ordered by date**, or **GeoJSON vector geometries**. It’s all powered with SQL and our tutorials will show you how easy it is to begin with SQL. + +
cartodb.SQL
+{% highlight javascript %} +var sql = new cartodb.SQL({ user: 'cartodb_user' }); +sql.execute("SELECT * FROM table_name WHERE id > {% raw %}{{id}}{% endraw %}", { id: 3 }) + .done(function(data) { + console.log(data.rows); + }) + .error(function(errors) { + // errors contains a list of errors + console.log("errors:" + errors); + }) +{% endhighlight %} + +It accepts the following options: + ++ **format**: should be geoJSON. ++ **dp**: float precision. ++ **jsonp**: if jsonp should be used instead of CORS. This param is enabled if the browser does not support CORS. + +These arguments will be applied to all the queries performed by this object. If you want to override them for one query see **execute** options. + +### sql.execute(_sql [,vars][, options][, callback]_) + +It executes a sql query. + +#### Arguments + ++ **sql**: a string with the sql query to be executed. You can specify template variables like {% raw %}{{variable}}{% endraw %} which will be filled with **vars** object. ++ **vars**: a map with the variables to be interpolated in the sql query. ++ **options**: accepts **format**, **dp** and **jsonp**. This object also overrides the params passed to $.ajax. + +#### Returns + +A promise object. You can listen for the following events: + ++ **done**: triggered when the data arrives. ++ **error**: triggered when something failed. + +You can also use done and error methods: + +
sql.execute
+{% highlight javascript %} +sql.execute('SELECT * FROM table_name') + .done(fn) + .error(fnError) +{% endhighlight %} + +### sql.getBounds(_sql [,vars][, options][, callback]_) + +Returns the bounds [ [sw_lat, sw_lon], [ne_lat, ne_lon ] ] for the geometry resulting of specified query. + +
sql.getBounds
+{% highlight javascript %} +sql.getBounds('select * from table').done(function(bounds) { + console.log(bounds); +}); +{% endhighlight %} + +#### Arguments + ++ **sql**: a string with the sql query to calculate the bounds from. + +#### Application of getBounds in Leaflet + +You can use the results from `getBounds` to center data on your maps using Leaflet. + +- **getBounds and Leaflet** + +
sql.getBounds
+{% highlight javascript %} +sql.getBounds('select * from table').done(function(bounds) { + map.setBounds(bounds); + // or map.fitBounds(bounds, mapView.getSize()); +}); +{% endhighlight %} diff --git a/doc/static_maps.md b/doc/static_maps.md new file mode 100644 index 0000000000..97c9db28af --- /dev/null +++ b/doc/static_maps.md @@ -0,0 +1,190 @@ +# Static Maps + +Static views of CartoDB maps can be generated using the [Static Maps API](http://docs.cartodb.com/cartodb-platform/maps-api.html#static-maps-api) within CartoDB.js. The map's style, including the zoom and bounding box, follows from what was set in the viz.json file, but you can change the zoom, center, and size of your image with a few lines of code. You can also change your basemap Images can be placed in specified DOM elements on your page, or you can generate a URL for the image. + +## Quick Start + +The easiest way to generate an image is by using the following piece of code, which generates is replaced by an `img` tag once run in an HTML file: + +{% highlight javascript %} + +{% endhighlight %} + +### Result +{% highlight html %} + +{% endhighlight %} + +### cartodb.Image(_layerSource_[, options]) + +#### Arguments + +- **layerSource**: can be either a viz.json object or a [layer source object](http://docs.cartodb.com/cartodb-platform/cartodb-js.html#standard-layer-source-object-type-cartodb) + +#### Options + +Options take the form of a JavaScript object. + +- **options**: + - **basemap**: change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). + - **no_cdn**: Disable CDN usage. Type: Boolean. Default: `false` (use CDN) + - **override_bbox**: Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) + +{% highlight javascript %} + +{% endhighlight %} + +#### Returns +An _Image_ object + +## cartodb.Image + +### Image.size(_width_,_height_) + +Sets the size of the image. + +#### Arguments + +- **width**: the width of the resulting image in pixels +- **height**: the height of the resulting image in pixels + +#### Returns +An _Image_ object + +### Image.center(_latLng_) + +Sets the center of the map. + +#### Arguments + +- **latLng**: an array of the latitude and longitude of the center of the map. Example: `[40.4378271,-3.6795367]` + +#### Returns + +An _Image_ object + +### Image.zoom(zoomLevel) + +Sets the zoom level of the static map. Must be used with the option `override_bbox: true` if not using `Image.center` or `Image.bbox`. + +#### Arguments + +- **zoomLevel**: the zoom of the resulting static map. `zoomLevel` must be an integer in the range [0,24]. + +#### Returns + +An _Image_ object + +### Image.bbox(_boundingBox_) + +If you set `bbox`, `center` and `zoom` will be overridden. + +#### Arguments + +- **boundingBox**: an array of coordinates making up the bounding box for your map. `boundingBox` takes the form: `[sw_lat, sw_lon, ne_lat, ne_lon]`. + +#### Returns + +An _Image_ object + +### Image.into(HTMLImageElement) + +Inserts the image into the HTML DOM element specified. + +#### Arguments + +- **HTMLImageElement**: the DOM element where your image is to be located. + +#### Returns + +An _Image_ object + +
Image.into
+{% highlight javascript %} +cartodb.Image(vizjson_url).into(document.getElementById('map_preview')) +{% endhighlight %} + +### Image.write(_attributes_) + +Adds an `img` tag in the same place script is executed. It's possible to specify a class name (`class`) and/or an id attribute (`id`) for the resulting image: + +
Image.write
+{% highlight javascript %} + +{% endhighlight %} + +#### Arguments + +- **attributes**: + + **class**: the DOM class applied to the resulting `img` tag + + **id**: the DOM id applied to the resulting `img` tag + + **src**: path to a temporary image that acts as a placeholder while the static map is retrieved + +#### Returns + +An _Image_ object + + +### Image.getUrl(_callback(err, url)_) + +Gets the URL for the image requested. + +
Image.getUrl
+{% highlight javascript %} + +{% endhighlight %} + +#### Callback Arguments + +- **err**: error associated with the image request, if any +- **url**: URL of the generated image + +#### Returns + +An _Image_ object + +### Image.format(_format_) + +Gets the URL for the image requested. + +#### Argument + +- **format**: image format of resulting image. One of `png` (default) or `jpg` (which have a quality of 85 dpi) + +#### Returns + +An _Image_ object \ No newline at end of file diff --git a/doc/thumbnail.png b/doc/thumbnail.png deleted file mode 100644 index 0e36f1b942026f5c66e411251697ead9b97ac157..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130369 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

?K^(45vG4GxCEocu2~g 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.geo.ui.Tooltip

+{% highlight javascript %} +var tooltip = vis.addOverlay({ + type: 'tooltip', + template: '

{% raw %}{{variable}}{% endraw %}

' // mustache template +}); +{% endhighlight %} + +### cartodb.geo.ui.Tooltip.enable() + +The tooltip is shown when hover on feature when is called. + +### cartodb.geo.ui.Tooltip.disable() + +The tooltip is not shown when hover on feature. + +## cartodb.geo.ui.InfoBox + +Shows a small box when the user hovers on a map feature. The position is fixed: + +
cartodb.geo.ui.InfoBox
+{% highlight javascript %} +var box = vis.addOverlay({ + type: 'infobox', + template: '

{% raw %}{{name_to_display}}{% endraw %}

', + width: 200, // width of the box + position: 'bottom|right' // top, bottom, left and right are available +}); +{% endhighlight %} + +### cartodb.geo.ui.InfoBox.enable() + +The tooltip is shown when hover on feature. + +### cartodb.geo.ui.InfoBox.disable() + +The tooltip is not shown when hover on feature. + +## cartodb.geo.ui.Zoom + +Shows the zoom control: + +
cartodb.geo.ui.Zoom
+{% highlight javascript %} +vis.addOverlay({ type: 'zoom' }); +{% endhighlight %} + +### cartodb.geo.ui.Zoom.show() + +### cartodb.geo.ui.Zoom.hide() From 581dd968cfe4c5e24d23c23321d16326c17b7597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Wed, 21 Oct 2015 19:12:55 +0200 Subject: [PATCH 047/120] remove bounds wrapper from other stuff --- doc/other_stuff.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/doc/other_stuff.md b/doc/other_stuff.md index c3e7e9e0ab..bd9c95f288 100644 --- a/doc/other_stuff.md +++ b/doc/other_stuff.md @@ -30,19 +30,6 @@ CartoDB.js will replace ``{user}``. Notice that you don't need to set the path to the endpoint, CartoDB.js will set it automatically. -## Bounds wrapper - -We have added an easy method to get the bounding box for any dataset or filtered query using the CartoDB.js library. The **getBounds** function can be useful for guiding users to the right location on a map or for loading only the right data at the right time based on user actions. - -
Bounds wrapper
-{% highlight javascript %} -var sql = new cartodb.SQL({ user: 'cartodb_user' }); - -sql.getBounds('SELECT * FROM table_name').done(function(bounds) { - console.log(bounds); -}); -{% endhighlight %} - ## Event listener support CartoDB.js is highly asynchronous. Your application can get on with what it needs to do while the library efficiently does what you request in the background. This is useful for loading maps or getting query results. At the same time, we have made it very simple to add listeners and callbacks to the async portions of the library. From f88711b983d366f69b0036c3b8c6063b77fced5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Wed, 21 Oct 2015 19:25:16 +0200 Subject: [PATCH 048/120] new docs: remove old build files --- doc/Makefile | 12 --------- doc/doc_template.html | 63 ------------------------------------------- doc/style.css | 61 ----------------------------------------- 3 files changed, 136 deletions(-) delete mode 100644 doc/Makefile delete mode 100644 doc/doc_template.html delete mode 100644 doc/style.css diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index fa3d6f42f2..0000000000 --- a/doc/Makefile +++ /dev/null @@ -1,12 +0,0 @@ - -api.html: API.md - markdown2 --extras fenced-code-blocks API.md > _api.html - python -c "from string import Template;open('api.html', 'w').write( Template(open('doc_template.html').read()).substitute(content=open('_api.html').read()))" - - -all: api.html - -clean: - rm -rf api.html - -.PHONY: clean diff --git a/doc/doc_template.html b/doc/doc_template.html deleted file mode 100644 index 0271d51ec5..0000000000 --- a/doc/doc_template.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - $content - - - diff --git a/doc/style.css b/doc/style.css deleted file mode 100644 index 122b4294b9..0000000000 --- a/doc/style.css +++ /dev/null @@ -1,61 +0,0 @@ -.hll { background-color: #ffffcc } -.c { color: #408080; font-style: italic } /* Comment */ -.err { border: 1px solid #FF0000 } /* Error */ -.k { color: #008000; font-weight: bold } /* Keyword */ -.o { color: #666666 } /* Operator */ -.cm { color: #408080; font-style: italic } /* Comment.Multiline */ -.cp { color: #BC7A00 } /* Comment.Preproc */ -.c1 { color: #408080; font-style: italic } /* Comment.Single */ -.cs { color: #408080; font-style: italic } /* Comment.Special */ -.gd { color: #A00000 } /* Generic.Deleted */ -.ge { font-style: italic } /* Generic.Emph */ -.gr { color: #FF0000 } /* Generic.Error */ -.gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.gi { color: #00A000 } /* Generic.Inserted */ -.go { color: #808080 } /* Generic.Output */ -.gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.gs { font-weight: bold } /* Generic.Strong */ -.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.gt { color: #0040D0 } /* Generic.Traceback */ -.kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.kp { color: #008000 } /* Keyword.Pseudo */ -.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.kt { color: #B00040 } /* Keyword.Type */ -.m { color: #666666 } /* Literal.Number */ -.s { color: #BA2121 } /* Literal.String */ -.na { color: #7D9029 } /* Name.Attribute */ -.nb { color: #008000 } /* Name.Builtin */ -.nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.no { color: #880000 } /* Name.Constant */ -.nd { color: #AA22FF } /* Name.Decorator */ -.ni { color: #999999; font-weight: bold } /* Name.Entity */ -.ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.nf { color: #0000FF } /* Name.Function */ -.nl { color: #A0A000 } /* Name.Label */ -.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.nt { color: #008000; font-weight: bold } /* Name.Tag */ -.nv { color: #19177C } /* Name.Variable */ -.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.w { color: #bbbbbb } /* Text.Whitespace */ -.mf { color: #666666 } /* Literal.Number.Float */ -.mh { color: #666666 } /* Literal.Number.Hex */ -.mi { color: #666666 } /* Literal.Number.Integer */ -.mo { color: #666666 } /* Literal.Number.Oct */ -.sb { color: #BA2121 } /* Literal.String.Backtick */ -.sc { color: #BA2121 } /* Literal.String.Char */ -.sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.s2 { color: #BA2121 } /* Literal.String.Double */ -.se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.sh { color: #BA2121 } /* Literal.String.Heredoc */ -.si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.sx { color: #008000 } /* Literal.String.Other */ -.sr { color: #BB6688 } /* Literal.String.Regex */ -.s1 { color: #BA2121 } /* Literal.String.Single */ -.ss { color: #19177C } /* Literal.String.Symbol */ -.bp { color: #008000 } /* Name.Builtin.Pseudo */ -.vc { color: #19177C } /* Name.Variable.Class */ -.vg { color: #19177C } /* Name.Variable.Global */ -.vi { color: #19177C } /* Name.Variable.Instance */ -.il { color: #666666 } /* Literal.Number.Integer.Long */ From 0e0fb5fd696d2f52da91076f7970cb17a1bab5cd Mon Sep 17 00:00:00 2001 From: csobier Date: Wed, 21 Oct 2015 14:18:51 -0400 Subject: [PATCH 049/120] fixed broken table --- doc/API.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/API.md b/doc/API.md index d257826d29..8409094e4a 100644 --- a/doc/API.md +++ b/doc/API.md @@ -402,23 +402,37 @@ Used to create an animated torque layer with customized settings. }); ``` + `getValueForPos(x, y[, step])` + +--- | --- Description | Allows to get the value for the coordinate (in map reference system) for a concrete step. If a step is not specified, the animation step is used. Use caution, as this method increases CPU usage. It returns the value from the raster data, not the rendered data. Returns | An object, such as a { bbox:[], value: VALUE } if there is value for the pos, otherwise, it is null. + `getValueForBBox(xstart, ystart, xend, yend)` + +--- | --- Description | Returns an accumulated numerical value from all the torque areas, within the specified bounds. Returns | A number. + `getActivePointsBBox(step)` + +--- | --- Description | Returns the list of bounding boxes active for `step`. Returns | List of bbox:[]. + `getValues(step)` + +--- | --- Description | Returns the list of values for the pixels active in `step`. Returns | List of values. `invalidate()` + +--- | --- Description | Forces a reload of the layer data. ##### Example of Interaction Methods for a Torque Layer From f5a30950ea788fd1c2dc21fae77a8cd8345b0d08 Mon Sep 17 00:00:00 2001 From: csobier Date: Wed, 21 Oct 2015 14:55:42 -0400 Subject: [PATCH 050/120] fixed darn table --- doc/API.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/API.md b/doc/API.md index 8409094e4a..fc43c09103 100644 --- a/doc/API.md +++ b/doc/API.md @@ -435,6 +435,7 @@ Returns | List of values. --- | --- Description | Forces a reload of the layer data. + ##### Example of Interaction Methods for a Torque Layer ```javascript -{% endhighlight %} +``` [Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/custom_infowindow.html) diff --git a/doc/core_api.md b/doc/core_api.md index 535666ef4e..99928b02d3 100644 --- a/doc/core_api.md +++ b/doc/core_api.md @@ -5,9 +5,9 @@ In case you are not using Leaflet, or you want to implement your own layer objec If you want to use this functionality, you only need to load cartodb.core.js from our cdn. No CSS is needed:
Core API functionallity
-{% highlight html %} +```html -{% endhighlight %} +``` An example using this funcionality can be found in a ModestMaps example: [view live](http://cartodb.github.com/cartodb.js/examples/modestmaps.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/modestmaps.html). @@ -19,12 +19,12 @@ Notice that cartodb.SQL is also included in that JavaScript file Fetch the tile template for the layer definition. -#### Arguments +### Arguments + **layerOptions**: the data that defines the layer. It should contain at least user_name and sublayer list. These are the available options:
cartodb.Tiles.getTiles
-{% highlight javascript %} +```javascript { user_name: 'mycartodbuser', sublayers: [{ @@ -33,12 +33,12 @@ Fetch the tile template for the layer definition. }], maps_api_template: 'https://{user}.cartodb.com' // Optional } -{% endhighlight %} +``` + **callback(tilesUrl, error)**: a function that recieves the tiles templates. In case of an error, the first param is null and the second one will be an object with an errors attribute that contains the list of errors. The tilesUrl object contains url template for tiles and interactivity grids:
cartodb.Tiles.getTiles
-{% highlight javascript %} +```javascript { tiles: [ "http://{s}.cartodb.com/HASH/{z}/{x}/{y}.png", @@ -55,14 +55,14 @@ Fetch the tile template for the layer definition. ... ] } -{% endhighlight %} +``` -#### Example +### Example In this example, a layer with one sublayer is created. The sublayer renders all the content from a table.
cartodb.Tiles.getTiles
-{% highlight javascript %} +```javascript var layerData = { user_name: 'mycartodbuser', sublayers: [{ @@ -77,4 +77,4 @@ cartodb.Tiles.getTiles(layerData, function(tiles, err) { } console.log("url template is ", tiles.tiles[0]); } -{% endhighlight %} \ No newline at end of file +``` diff --git a/doc/events.md b/doc/events.md index 9792d2f7eb..b18580d9fb 100644 --- a/doc/events.md +++ b/doc/events.md @@ -19,11 +19,11 @@ Triggered when the user hovers on any feature. #### Example
layer.on
-{% highlight javascript %} +```javascript layer.on('featureOver', function(e, latlng, pos, data, subLayerIndex) { console.log("mouse over polygon with data: " + data); }); -{% endhighlight %} +``` ### layer.featureOut(_layerIndex_) @@ -48,7 +48,7 @@ Triggered when the mouse leaves all the features. Useful to revert the cursor af #### Example
sublayer.on
-{% highlight javascript %} +```javascript layer.on('mouseover', function() { cursor.set('hand') }); @@ -56,7 +56,7 @@ layer.on('mouseover', function() { layer.on('mouseout', function() { cursor.set('auto') }); -{% endhighlight %} +``` ### layer.loading() @@ -65,14 +65,14 @@ Triggered when the layer or any of its sublayers are about to be loaded. This is #### Example
layer.on
-{% highlight javascript %} +```javascript layer.on("loading", function() { console.log("layer about to load"); }); layer.getSubLayer(0).set({ cartocss: "#export { polygon-opacity: 0; }" }); -{% endhighlight %} +``` ### layer.load() @@ -81,14 +81,14 @@ Triggered when the layer or its sublayers have been loaded. This is also trigger #### Example
layer.on
-{% highlight javascript %} +```javascript layer.on("load", function() { console.log("layer loaded"); }); layer.getSubLayer(0).set({ cartocss: "#export { polygon-opacity: 0; }" }); -{% endhighlight %} +``` ## subLayer @@ -115,148 +115,3 @@ Same as `layer.mouseover()` but sublayer specific. ### sublayer.mouseout() Same as `layer.mouseover()` but sublayer specific. - - -# Specific UI functions - -There are a few functions in CartoDB.js for creating, enabling, and disabling pieces of the user interface. - -## cartodb.geo.ui.Tooltip - -Shows a small tooltip on hover: - -
cartodb.geo.ui.Tooltip
-{% highlight javascript %} -var tooltip = vis.addOverlay({ - type: 'tooltip', - template: '

{% raw %}{{variable}}{% endraw %}

' // mustache template -}); -{% endhighlight %} - -### cartodb.geo.ui.Tooltip.enable() - -The tooltip is shown when hover on feature when is called. - -### cartodb.geo.ui.Tooltip.disable() - -The tooltip is not shown when hover on feature. - -## cartodb.geo.ui.InfoBox - -Shows a small box when the user hovers on a map feature. The position is fixed: - -
cartodb.geo.ui.InfoBox
-{% highlight javascript %} -var box = vis.addOverlay({ - type: 'infobox', - template: '

{% raw %}{{name_to_display}}{% endraw %}

', - width: 200, // width of the box - position: 'bottom|right' // top, bottom, left and right are available -}); -{% endhighlight %} - -### cartodb.geo.ui.InfoBox.enable() - -The tooltip is shown when hover on feature. - -### cartodb.geo.ui.InfoBox.disable() - -The tooltip is not shown when hover on feature. - -## cartodb.geo.ui.Zoom - -Shows the zoom control: - -
cartodb.geo.ui.Zoom
-{% highlight javascript %} -vis.addOverlay({ type: 'zoom' }); -{% endhighlight %} - -### cartodb.geo.ui.Zoom.show() - -### cartodb.geo.ui.Zoom.hide() - - -# Getting data with SQL - -CartoDB offers a powerful SQL API for you to query and retreive data from your CartoDB tables. CartoDB.js offers a simple to use wrapper for sending those requests and using the results. - -## cartodb.SQL - -**cartodb.SQL** is the tool you will use to access data you store in your CartoDB tables. This is a really powerful technique for returning things like: **items closest to a point**, **items ordered by date**, or **GeoJSON vector geometries**. It’s all powered with SQL and our tutorials will show you how easy it is to begin with SQL. - -
cartodb.SQL
-{% highlight javascript %} -var sql = new cartodb.SQL({ user: 'cartodb_user' }); -sql.execute("SELECT * FROM table_name WHERE id > {% raw %}{{id}}{% endraw %}", { id: 3 }) - .done(function(data) { - console.log(data.rows); - }) - .error(function(errors) { - // errors contains a list of errors - console.log("errors:" + errors); - }) -{% endhighlight %} - -It accepts the following options: - -+ **format**: should be geoJSON. -+ **dp**: float precision. -+ **jsonp**: if jsonp should be used instead of CORS. This param is enabled if the browser does not support CORS. - -These arguments will be applied to all the queries performed by this object. If you want to override them for one query see **execute** options. - -### sql.execute(_sql [,vars][, options][, callback]_) - -It executes a sql query. - -#### Arguments - -+ **sql**: a string with the sql query to be executed. You can specify template variables like {% raw %}{{variable}}{% endraw %} which will be filled with **vars** object. -+ **vars**: a map with the variables to be interpolated in the sql query. -+ **options**: accepts **format**, **dp** and **jsonp**. This object also overrides the params passed to $.ajax. - -#### Returns - -A promise object. You can listen for the following events: - -+ **done**: triggered when the data arrives. -+ **error**: triggered when something failed. - -You can also use done and error methods: - -
sql.execute
-{% highlight javascript %} -sql.execute('SELECT * FROM table_name') - .done(fn) - .error(fnError) -{% endhighlight %} - -### sql.getBounds(_sql [,vars][, options][, callback]_) - -Returns the bounds [ [sw_lat, sw_lon], [ne_lat, ne_lon ] ] for the geometry resulting of specified query. - -
sql.getBounds
-{% highlight javascript %} -sql.getBounds('select * from table').done(function(bounds) { - console.log(bounds); -}); -{% endhighlight %} - -#### Arguments - -+ **sql**: a string with the sql query to calculate the bounds from. - -#### Application of getBounds in Leaflet - -You can use the results from `getBounds` to center data on your maps using Leaflet. - -- **getBounds and Leaflet** - -
sql.getBounds
-{% highlight javascript %} -sql.getBounds('select * from table').done(function(bounds) { - map.setBounds(bounds); - // or map.fitBounds(bounds, mapView.getSize()); -}); -{% endhighlight %} diff --git a/doc/getting_started.md b/doc/getting_started.md index 635582f318..5e2fab2215 100644 --- a/doc/getting_started.md +++ b/doc/getting_started.md @@ -2,11 +2,16 @@ The simplest way to use a visualization created in CartoDB on an external site is as follows: -{% highlight html %} +
Create a simple visualization
+```html + ... +
+ ... + -{% endhighlight %} +``` [Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/easy.html) @@ -36,10 +41,10 @@ We’ve also made it easier than ever for you to build maps using the mapping li To start using CartoDB.js just paste this piece of code within the HEAD tags of your HTML:
Linking cartodb.js on your html file
-{% highlight html %} +```html -{% endhighlight %} +``` ### Creating a visualization from scratch @@ -48,14 +53,14 @@ The easiest way to quickly get a CartoDB map onto your webpage. Use this when th You can start by giving cartodb.js the DIV ID from your HTML where you want to place your map, and the viz.json URL of your visualization, which you can get from the share window.
Simplest way to add your map to a webpage ever!
-{% highlight javascript %} +```javascript cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'); -{% endhighlight %} +``` That’s it! No need to create the map instance, insert controls, or load layers. CartoDB.js takes care of this for you. If you want to modify the result after instantiating your map with this method, take a look at the CartoDB.js API [available methods](#api-methods). For example, you can also use the returned layer to build more functionality (show/hide, click, hover, custom infowindows):
Simplest way to add your map to a webpage ever!
-{% highlight javascript %} +```javascript cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json') .done(function(vis, layers) { // layer 0 is the base layer, layer 1 is cartodb layer @@ -72,7 +77,7 @@ cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e // map.setZoom(3); // map.panTo([50.5, 30.5]); }); -{% endhighlight %} +``` ### Adding CartoDB layers to an existing map @@ -81,7 +86,7 @@ In case you already have a map instantiated on your page, you can simply use the Below, you have an example using a previously instatiated Leaflet map.
Adding cartodb layers to an existing map
-{% highlight html %} +```html
-{% endhighlight %} +``` [Grab the complete example source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/leaflet.html) @@ -110,7 +115,7 @@ All CartoDB services are available through the API, which basically means that y When you create a visualization using the CartoDB website, you automatically get a viz.json URL that defines it. When you want to create the visualization via JavaScript, you don't always have a viz.json. You will need to pass all the required parameters to the library so that it can create the visualization at runtime and display it on your map. It is pretty simple.
Creating visualizations at runtime
-{% highlight javascript %} +```javascript // create a layer with 1 sublayer cartodb.createLayer(map, { user_name: 'mycartodbuser', @@ -132,7 +137,7 @@ cartodb.createLayer(map, { // change the query for the first layer layer.getSubLayer(0).setSQL("SELECT * FROM table_name limit 10"); }); -{% endhighlight %} +``` Want further information? [Check out the complete list of API methods](#api-methods). diff --git a/doc/other_stuff.md b/doc/other_stuff.md index bd9c95f288..4949a19910 100644 --- a/doc/other_stuff.md +++ b/doc/other_stuff.md @@ -9,9 +9,9 @@ The Viz.JSON document tells CartoDB.js all the information about your map, inclu Although the Viz JSON file stores all your map settings, all these settings can be easily customized with CartoDB.js. For example, if you want to do something completely different than what you initially designed it for. Loading the Viz JSON is as simple as:
Viz JSON support
-{% highlight javascript %} +```javascript cartodb.createVis('map', 'http://examples.cartodb.com/api/v2/viz/ne_10m_populated_p_1/viz.json') -{% endhighlight %} +``` ## How to set a different host than cartodb.com CartoDB.js sends all requests to the cartodb.com domain by default. If you are running your own @@ -22,14 +22,27 @@ for any ``cartodb`` function call. The format of these templates is as follows: -{% highlight javascript %} +```javascript sql_api_template: 'https://{user}.test.com' -{% endhighlight %} +``` CartoDB.js will replace ``{user}``. Notice that you don't need to set the path to the endpoint, CartoDB.js will set it automatically. +## Bounds wrapper + +We have added an easy method to get the bounding box for any dataset or filtered query using the CartoDB.js library. The **getBounds** function can be useful for guiding users to the right location on a map or for loading only the right data at the right time based on user actions. + +
Bounds wrapper
+```javascript +var sql = new cartodb.SQL({ user: 'cartodb_user' }); + +sql.getBounds('SELECT * FROM table_name').done(function(bounds) { + console.log(bounds); +}); +``` + ## Event listener support CartoDB.js is highly asynchronous. Your application can get on with what it needs to do while the library efficiently does what you request in the background. This is useful for loading maps or getting query results. At the same time, we have made it very simple to add listeners and callbacks to the async portions of the library. @@ -39,7 +52,7 @@ CartoDB.js is highly asynchronous. Your application can get on with what it need The **createLayer** and **createVis** functions trigger two important events for you to take advantage of. The first one is **done**, which will let your code know that the library has successfully read the information from the Viz JSON and loaded the layer you requested. The second is **error**, which lets you know that something did not go as expected when trying to load the requested layer:
Loading events
-{% highlight javascript %} +```javascript cartodb.createLayer(map, 'http://examples.cartodb.com/api/v1/viz/0001/viz.json') .addTo(map) .on('done', function(layer) { @@ -47,53 +60,53 @@ cartodb.createLayer(map, 'http://examples.cartodb.com/api/v1/viz/0001/viz.json') }).on('error', function(err) { alert("some error occurred: " + err); }); -{% endhighlight %} +``` ### Active layer events The next important set of events for you to use happen on those layers that are already loaded (returned by the **done** event above). Three events are triggered by layers on your webpage, each requires the layer to include an **interactivity** layer. The first event is **featureClick**, which lets you set up events after the user clicks anything that you have mapped.
featureClick
-{% highlight javascript %} +```javascript layer.on('featureClick', function(e, latlng, pos, data, layer) { console.log("mouse clicked polygon with data: " + data); }); -{% endhighlight %} +``` The second event is the **featureOver** event, which lets you listen for mouse hovers on any feature. Be careful, as these functions can get costly if you have a lot of features on a map.
featureOver
-{% highlight javascript %} +```javascript layer.on('featureOver', function(e, latlng, pos, data, layer) { console.log("mouse over polygon with data: " + data); }); -{% endhighlight %} +``` Finally, there is the **featureOut** event. This is best used if you do things like highlighting polygons on mouseover and need a way to know when to remove the highlighting after the mouse has left.
featureOut
-{% highlight javascript %} +```javascript layer.on('featureOut', function(e, latlng, pos, data, layer) { console.log("mouse left polygon with data: " + data); }); -{% endhighlight %} +``` ### Leaflet integration If you want to use [Leaflet](http://leafletjs.com) it gets even easier. CartoDB.js handles loading all the necessary libraries for you! Just include CartoDB.js and CartoDB.css in the HEAD of your website and you are ready to go! The CartoDB.css document isn’t mandatory. However, if you are making a map and are not familiar with writing your own CSS for the various needed elements, it can help you jumpstart the process. Using Leaflet is as simple as adding the main JavaScript library:
Leaflet integration
-{% highlight html %} +```html -{% endhighlight %} +``` ### HTTPS support You can use all the functionality of CartoDB.js with HTTPs support. Be sure to use https when importing both the JS library and the CSS file. You will also need to use HTTPs in the Viz.JSON URL you pass to **createVis**.
HTTPS support
-{% highlight html %} +```html
@@ -110,7 +123,7 @@ You can use all the functionality of CartoDB.js with HTTPs support. Be sure to u alert("some error occurred: " + err); }); -{% endhighlight %} +``` ### Persistent version hosting @@ -118,32 +131,24 @@ We are committed to making sure your website works as intended no matter what ch We recommend that you always develop against the most recent version of CartoDB.js: -{% highlight html %} +```html -{% endhighlight %} +``` Anytime you wish to push a stable version of your site to the web though, you can find the version of CartoDB.js you are using by looking at the first line of the library or running the following in your code: -{% highlight javascript %} +```javascript alert(cartodb.VERSION) -{% endhighlight %} +``` Once you know which version of CartoDB.js you're using, you can point your site to that release. If the current version of CartoDB.js is 3.15.8, the URL would be: -{% highlight html %} +```html -{% endhighlight %} +``` You can do the same for the CSS documents we provide: -{% highlight html %} +```html -{% endhighlight %} - -## Versions - -Keep in mind the version of CartoDB.js you are using for development. For any live code, we recommend you to link directly to the tested CartoDB.js version from your development environment. You can check the version of CartoDB.js as follows: - -### cartodb.VERSION - -Returns the version of the library. It should be something like `3.0.1`. +``` diff --git a/doc/sql.md b/doc/sql.md index c787d067c0..beec1b616f 100644 --- a/doc/sql.md +++ b/doc/sql.md @@ -7,9 +7,9 @@ CartoDB offers a powerful SQL API for you to query and retreive data from your C **cartodb.SQL** is the tool you will use to access data you store in your CartoDB tables. This is a really powerful technique for returning things like: **items closest to a point**, **items ordered by date**, or **GeoJSON vector geometries**. It’s all powered with SQL and our tutorials will show you how easy it is to begin with SQL.
cartodb.SQL
-{% highlight javascript %} +```javascript var sql = new cartodb.SQL({ user: 'cartodb_user' }); -sql.execute("SELECT * FROM table_name WHERE id > {% raw %}{{id}}{% endraw %}", { id: 3 }) +sql.execute("SELECT * FROM table_name WHERE id > {{id}}", { id: 3 }) .done(function(data) { console.log(data.rows); }) @@ -17,7 +17,7 @@ sql.execute("SELECT * FROM table_name WHERE id > {% raw %}{{id}}{% endraw %}", { // errors contains a list of errors console.log("errors:" + errors); }) -{% endhighlight %} +``` It accepts the following options: @@ -33,7 +33,7 @@ It executes a sql query. #### Arguments -+ **sql**: a string with the sql query to be executed. You can specify template variables like {% raw %}{{variable}}{% endraw %} which will be filled with **vars** object. ++ **sql**: a string with the sql query to be executed. You can specify template variables like {{variable}} which will be filled with **vars** object. + **vars**: a map with the variables to be interpolated in the sql query. + **options**: accepts **format**, **dp** and **jsonp**. This object also overrides the params passed to $.ajax. @@ -47,22 +47,22 @@ A promise object. You can listen for the following events: You can also use done and error methods:
sql.execute
-{% highlight javascript %} +```javascript sql.execute('SELECT * FROM table_name') .done(fn) .error(fnError) -{% endhighlight %} +``` ### sql.getBounds(_sql [,vars][, options][, callback]_) Returns the bounds [ [sw_lat, sw_lon], [ne_lat, ne_lon ] ] for the geometry resulting of specified query.
sql.getBounds
-{% highlight javascript %} +```javascript sql.getBounds('select * from table').done(function(bounds) { console.log(bounds); }); -{% endhighlight %} +``` #### Arguments @@ -75,9 +75,9 @@ You can use the results from `getBounds` to center data on your maps using Leafl - **getBounds and Leaflet**
sql.getBounds
-{% highlight javascript %} +```javascript sql.getBounds('select * from table').done(function(bounds) { map.setBounds(bounds); // or map.fitBounds(bounds, mapView.getSize()); }); -{% endhighlight %} +``` diff --git a/doc/static_maps.md b/doc/static_maps.md index 97c9db28af..fe468dc5bb 100644 --- a/doc/static_maps.md +++ b/doc/static_maps.md @@ -6,7 +6,7 @@ Static views of CartoDB maps can be generated using the [Static Maps API](http:/ The easiest way to generate an image is by using the following piece of code, which generates is replaced by an `img` tag once run in an HTML file: -{% highlight javascript %} +```javascript -{% endhighlight %} +``` ### Result -{% highlight html %} +```html -{% endhighlight %} +``` ### cartodb.Image(_layerSource_[, options]) @@ -38,7 +38,7 @@ Options take the form of a JavaScript object. - **no_cdn**: Disable CDN usage. Type: Boolean. Default: `false` (use CDN) - **override_bbox**: Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) -{% highlight javascript %} +```javascript -{% endhighlight %} +``` #### Returns An _Image_ object @@ -122,16 +122,16 @@ Inserts the image into the HTML DOM element specified. An _Image_ object
Image.into
-{% highlight javascript %} +```javascript cartodb.Image(vizjson_url).into(document.getElementById('map_preview')) -{% endhighlight %} +``` ### Image.write(_attributes_) Adds an `img` tag in the same place script is executed. It's possible to specify a class name (`class`) and/or an id attribute (`id`) for the resulting image:
Image.write
-{% highlight javascript %} +```javascript -{% endhighlight %} +``` #### Arguments @@ -157,8 +157,8 @@ An _Image_ object Gets the URL for the image requested. -
Image.getUrl
-{% highlight javascript %} +
Image.getUrl
+```javascript -{% endhighlight %} +``` #### Callback Arguments @@ -187,4 +187,4 @@ Gets the URL for the image requested. #### Returns -An _Image_ object \ No newline at end of file +An _Image_ object diff --git a/doc/ui_functions.md b/doc/ui_functions.md index ff00f1708c..9bb53e3258 100644 --- a/doc/ui_functions.md +++ b/doc/ui_functions.md @@ -7,12 +7,12 @@ There are a few functions in CartoDB.js for creating, enabling, and disabling pi Shows a small tooltip on hover:
cartodb.geo.ui.Tooltip
-{% highlight javascript %} +```javascript var tooltip = vis.addOverlay({ type: 'tooltip', - template: '

{% raw %}{{variable}}{% endraw %}

' // mustache template + template: '

{{variable}}

' // mustache template }); -{% endhighlight %} +``` ### cartodb.geo.ui.Tooltip.enable() @@ -27,14 +27,14 @@ The tooltip is not shown when hover on feature. Shows a small box when the user hovers on a map feature. The position is fixed:
cartodb.geo.ui.InfoBox
-{% highlight javascript %} +```javascript var box = vis.addOverlay({ type: 'infobox', - template: '

{% raw %}{{name_to_display}}{% endraw %}

', + template: '

{{name_to_display}}

', width: 200, // width of the box position: 'bottom|right' // top, bottom, left and right are available }); -{% endhighlight %} +``` ### cartodb.geo.ui.InfoBox.enable() @@ -49,9 +49,9 @@ The tooltip is not shown when hover on feature. Shows the zoom control:
cartodb.geo.ui.Zoom
-{% highlight javascript %} +```javascript vis.addOverlay({ type: 'zoom' }); -{% endhighlight %} +``` ### cartodb.geo.ui.Zoom.show() diff --git a/doc/versions.md b/doc/versions.md new file mode 100644 index 0000000000..fb54077734 --- /dev/null +++ b/doc/versions.md @@ -0,0 +1,7 @@ +# Versions + +Keep in mind the version of CartoDB.js you are using for development. For any live code, we recommend you to link directly to the tested CartoDB.js version from your development environment. You can check the version of CartoDB.js as follows: + +### cartodb.VERSION + +Returns the version of the library. It should be something like `3.0.1`. From 02871e7ccf9b31c5682cacb8441a81637117cb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Thu, 22 Oct 2015 14:40:54 +0200 Subject: [PATCH 053/120] newdocs: rm quickstart --- doc/quickstart.md | 61 ----------------------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 doc/quickstart.md diff --git a/doc/quickstart.md b/doc/quickstart.md deleted file mode 100644 index cb308360c4..0000000000 --- a/doc/quickstart.md +++ /dev/null @@ -1,61 +0,0 @@ - - -# cartodb js framework quick start - -This is a little doc with the basis of cartodb js framework (TM), in other words, all you need to know to start workig with it wihout doint it too much wrong. - -## general info - - The framework(TM) is built on top of Backbone.js (so you can use jQuery and underscore everywhere). - - we use jasmine for testing - - cdb is the namespace, so all the components should be inside it, i.e cbd.geo.Map. (look into cartodb.js) - - code style guide: https://github.com/Vizzuality/cartodb/wiki/Javascript-style-guide - -## folders (will be changed soon) - - cartodb.js: this file contains the scopes for all the app, should be included first - - core - - geo - - lib - - demos: place where to put demos for a component. It is recomended to test the components isolated - - test - -## core - -this contains all the base classes, used in all the project: - - - config: all the app config goes here. Accesible by cdb.config - - logging: never ever use console.log, use cbd.log.info, cdb.log.error and cdb.debug. error call will generate an error You can check errors with cbd.errors singleton (it is a backbone.Collection) - - view: DO NOT USE Backbone.View, use cbd.core.View instead. It tracks zombie views and make removing views safe. More later - - templates - - -## general guidelines for views - - - never ever change a view directly. For example a button that changes the state should change the model and the view should change when the models triggers the change event. - - - call clean when your view will not be used anymore - - - link model events in this way, even if you have binded the method. Notice the 3rd parameter (this) - - this.model.bind('change', this.callback, this); - - when you have to unlink the view from the model you can do: - - this.model.unbind(null, null, this); - - - if your view has model and listen events from it, add to realted models (will be free when you call clean): - - // inside the view - this.add_related_model(this.whatevermodel); - -## how to use view - -## info - - - read backbone annotated source: http://backbonejs.org/docs/backbone.html - - - - - - - From c24fef42385b386bb8057683e8c34dc66644bb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Mon, 26 Oct 2015 15:02:22 +0100 Subject: [PATCH 054/120] docs: remove duplicate titles --- doc/getting_started.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/doc/getting_started.md b/doc/getting_started.md index 5e2fab2215..f8277cd8cf 100644 --- a/doc/getting_started.md +++ b/doc/getting_started.md @@ -2,16 +2,11 @@ The simplest way to use a visualization created in CartoDB on an external site is as follows: -
Create a simple visualization
```html - ... -
- ... - @@ -52,14 +46,12 @@ The easiest way to quickly get a CartoDB map onto your webpage. Use this when th You can start by giving cartodb.js the DIV ID from your HTML where you want to place your map, and the viz.json URL of your visualization, which you can get from the share window. -
Simplest way to add your map to a webpage ever!
```javascript cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'); ``` That’s it! No need to create the map instance, insert controls, or load layers. CartoDB.js takes care of this for you. If you want to modify the result after instantiating your map with this method, take a look at the CartoDB.js API [available methods](#api-methods). For example, you can also use the returned layer to build more functionality (show/hide, click, hover, custom infowindows): -
Simplest way to add your map to a webpage ever!
```javascript cartodb.createVis('map', 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json') .done(function(vis, layers) { @@ -85,7 +77,6 @@ In case you already have a map instantiated on your page, you can simply use the Below, you have an example using a previously instatiated Leaflet map. -
Adding cartodb layers to an existing map
```html
@@ -114,7 +105,6 @@ All CartoDB services are available through the API, which basically means that y When you create a visualization using the CartoDB website, you automatically get a viz.json URL that defines it. When you want to create the visualization via JavaScript, you don't always have a viz.json. You will need to pass all the required parameters to the library so that it can create the visualization at runtime and display it on your map. It is pretty simple. -
Creating visualizations at runtime
```javascript // create a layer with 1 sublayer cartodb.createLayer(map, { From 4ddecb4505d630e98622d8d6856cc909510e9445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Mon, 26 Oct 2015 18:29:12 +0100 Subject: [PATCH 055/120] docs: api methods --- doc/api_methods.md | 123 +++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index dddc644079..f2b6d42088 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -2,13 +2,14 @@ The documentation below refers to CartoDB.js v3. For major changes in the library we will update the documentation here. This documentation is meant to help developers find specific methods from the CartoDB.js library. -## Visualization +## cartodb.createVis ### cartodb.createVis(_map_id, vizjson_url[, options] [, callback]_) Creates a visualization inside the map_id DOM object. -
cartodb.createVis
+#### Call + ```javascript var url = 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'; @@ -19,39 +20,43 @@ cartodb.createVis('map', url) #### Arguments -- **map_id**: a DOM object, for example `$('#map')` or a DOM id. -- **vizjson_url**: url of the vizjson object. -- **options**: - - **shareable**: add facebook and twitter share buttons. - - **title**: adds a header with the title of the visualization. - - **description**: adds description to the header (as you set in the UI). - - **search**: adds a search control (default: true). - - **zoomControl**: adds zoom control (default: true). - - **loaderControl**: adds loading control (default: true). - - **center_lat**: latitude where the map is initializated. - - **center_lon**: longitude where the map is initializated. - - **zoom**: initial zoom. - - **cartodb_logo**: default to true, set to false if you want to remove the cartodb logo. - - **infowindow**: set to false if you want to disable the infowindow (enabled by default). - - **time_slider**: show time slider with torque layers (enabled by default) - - **layer_selector**: show layer selector (default: false) - - **legends**: if it's true legends are shown in the map. - - **https**: if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. - - **scrollwheel**: enable/disable the ability of zooming using scrollwheel (default enabled) - - **fullscreen**: if true adds a button to toggle the map fullscreen - - **mobile_layout**: if true enables a custom layout for mobile devices (default: false) - - **force_mobile**: forces enabling/disabling the mobile layout (it has priority over mobile_layout argument) - - **gmaps_base_type**: Use Google Maps as map provider whatever is the one specified in the viz.json". Available types: 'roadmap', 'gray_roadmap', 'dark_roadmap', 'hybrid', 'satellite', 'terrain'. - - **gmaps_style**: Google Maps styled maps. See [documentation](https://developers.google.com/maps/documentation/javascript/styling). - - **no_cdn**: true to disable CDN when fetching tiles -- **callback(vis,layers)**: if a function is specified, it is called once the visualization is created, passing vis and layers as arguments +Arguments | Description +--- | --- +map_id | a DOM object, for example `$('#map')` or a DOM id. +vizjson_url | url of the vizjson object. +options | + shareable | add facebook and twitter share buttons. + title | adds a header with the title of the visualization. + description | adds description to the header (as you set in the UI). + search | adds a search control (default: true). + zoomControl | adds zoom control (default: true). + loaderControl | adds loading control (default: true). + center_lat | latitude where the map is initializated. + center_lon | longitude where the map is initializated. + zoom | initial zoom. + cartodb_logo | default to true, set to false if you want to remove the cartodb logo. + infowindow | set to false if you want to disable the infowindow (enabled by default). + time_slider | show time slider with torque layers (enabled by default). + layer_selector | show layer selector (default: false). + legends | if it's true legends are shown in the map. + https | if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. + scrollwheel | enable/disable the ability of zooming using scrollwheel (default enabled) + fullscreen | if true adds a button to toggle the map fullscreen + mobile_layout | if true enables a custom layout for mobile devices (default: false) + force_mobile | forces enabling/disabling the mobile layout (it has priority over mobile_layout argument) + gmaps_base_type | Use Google Maps as map provider whatever is the one specified in the viz.json". Available types: 'roadmap', 'gray_roadmap', 'dark_roadmap', 'hybrid', 'satellite', 'terrain'. + gmaps_style | Google Maps styled maps. See [documentation](https://developers.google.com/maps/documentation/javascript/styling). + no_cdn | true to disable CDN when fetching tiles +callback(vis,layers) | if a function is specified, it is called once the visualization is created, passing vis and layers as arguments #### Returns A promise object. You can listen for the following events: -+ **done**: triggered when the visualization is created, `vis` is passed as the first argument and `layers` is passed as the second argument. Each layer type has different options, see layers section. -+ **error**: triggered when the layer couldn't be created. The error string is the first argument. +Event | Description +--- | --- +done | triggered when the visualization is created, `vis` is passed as the first argument and `layers` is passed as the second argument. Each layer type has different options, see layers section. +error | triggered when the layer couldn't be created. The error string is the first argument. ## cartodb.Vis @@ -65,9 +70,10 @@ Adds an overlay to the map that can be either a zoom control, a tooltip or an in #### Arguments -- **options** - - **layer** layer from the visualization where the overlay should be applied (optional) - - **type** zoom / tooltip / infobox +Option | Description +--- | --- +layer | layer from the visualization where the overlay should be applied (optional) +type | zoom / tooltip / infobox If no layer is provided, the overlay will be added to the first layer of the visualization. Extra options are available based on the specific UI component. @@ -79,7 +85,8 @@ An overlay object, see [vis.Overlays](#visoverlays). Returns the first overlay with the specified **type**. -
vis.getOverlay
+#### Call + ```javascript var zoom = vis.getOverlay('zoom'); zoom.clean() // remove it from the screen @@ -91,7 +98,7 @@ Returns a list of the overlays that are currently on the screen (see overlays de ### vis.getNativeMap() -Returns the native map object being used (e.g. a L.Map object for Leaflet). +Returns the native map object being used (e.g. a `L.Map` object for Leaflet). ### vis.Overlays @@ -107,9 +114,11 @@ Adds an infowindow to the map controlled by layer events. It enables interaction #### Arguments - - **map**: native map object or leaflet - - **layer**: cartodb layer (or sublayer) - - **fields**: array of column names +Option | Description +--- | --- +map | native map object or leaflet +layer | cartodb layer (or sublayer) +fields | array of column names #### Returns @@ -121,35 +130,31 @@ With visualizations already created through the CartoDB console, you can simply #### Arguments -- **map**: Leaflet L.Map object. The map should be initialized before calling this function. - -- **layerSource**: contains information about the layer. It can be specified in 2 ways: - -
Passing the url where the layer data is located
+Arguments | Description +--- | --- +map | Leaflet `L.Map` object. The map should be initialized before calling this function. +layerSource | contains information about the layer. It can be specified in 2 ways +options | + https | force https + refreshTime | if is set, the layer is refreshed each refreshTime milliseconds. + infowindow | set to false if you want to disable the infowindow (enabled by default). + tooltip | set to false if you want to disable the tooltip (enabled by default). + legends | if it's true legends are shown in the map. + time_slider | show time slider with torque layers (enabled by default) + layerIndex | when the visualization contains more than one layer this index allows you to select what layer is created. Take into account that `layerIndex == 0` is the base layer and that all the tiled layers (non animated ones) are merged into a single one. The default value for this option is 1 (usually tiled layers). + filter | a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. +callback(_layer_) | if a function is specified, it will be invoked after the layer has been created. The layer will be passed as an argument. + +#### Passing the url where the layer data is located ```javascript cartodb.createLayer(map, 'http://myserver.com/layerdata.json') ``` -
passing the data directly
+#### Passing the data directly ```javascript cartodb.createLayer(map, { layermetadata }) ``` -- **options**: - - **https**: force https - - **refreshTime**: if is set, the layer is refreshed each refreshTime milliseconds. - - **infowindow**: set to false if you want to disable the infowindow (enabled by default). - - **tooltip**: set to false if you want to disable the tooltip (enabled by default). - - **legends**: if it's true legends are shown in the map. - - **time_slider**: show time slider with torque layers (enabled by default) - - **layerIndex**: when the visualization contains more than one layer this index allows you to select - what layer is created. Take into account that `layerIndex == 0` is the base layer and that - all the tiled layers (non animated ones) are merged into a single one. The default value for - this option is 1 (usually tiled layers). - - **filter**: a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. - -- **callback(_layer_)**: if a function is specified, it will be invoked after the layer has been created. The layer will be passed as an argument. - #### Returns A promise object. You can listen for the following events: From 5e7efc75c680e0e2c5dd0ead640bd77af261d481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Mon, 26 Oct 2015 18:34:19 +0100 Subject: [PATCH 056/120] docs: api methods --- doc/api_methods.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index f2b6d42088..116b5ec6d6 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -104,9 +104,9 @@ Returns the native map object being used (e.g. a `L.Map` object for Leaflet). An overlay is a control shown on top of the map. -Overlay objects are always created using the **addOverlay** method of a cartodb.Vis object. +Overlay objects are always created using the `addOverlay` method of a `cartodb.Vis object. -An overlay is internally a [**Backbone.View**](http://backbonejs.org/#View) so if you know how Backbone works you can use it. If you want to use plain DOM objects you can access **overlay.el** (**overlay.$el** for jQuery object). +An overlay is internally a [Backbone.View](http://backbonejs.org/#View) so if you know how Backbone works you can use it. If you want to use plain DOM objects you can access `overlay.el` (`overlay.$el` for jQuery object). ### vis.addInfowindow(_map, layer, fields [, options]_) @@ -126,7 +126,7 @@ An infowindow object, see [sublayer.infowindow](#sublayerinfowindow) ### cartodb.createLayer(_map, layerSource [, options] [, callback]_) -With visualizations already created through the CartoDB console, you can simply use the **createLayer** function to add them into your web pages. Unlike **createVis**, this method requires an already activated **map** object and it does not load a basemap for you. +With visualizations already created through the CartoDB console, you can simply use the `createLayer` function to add them into your web pages. Unlike `createVis`, this method requires an already activated `map` object and it does not load a basemap for you. #### Arguments From c750e5094d7347d2a60dc257458d6c2bf114f6e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Mon, 26 Oct 2015 18:40:03 +0100 Subject: [PATCH 057/120] docs: api methods --- doc/api_methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index 116b5ec6d6..49f14547f0 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -104,7 +104,7 @@ Returns the native map object being used (e.g. a `L.Map` object for Leaflet). An overlay is a control shown on top of the map. -Overlay objects are always created using the `addOverlay` method of a `cartodb.Vis object. +Overlay objects are always created using the `addOverlay` method of a `cartodb.Vis` object. An overlay is internally a [Backbone.View](http://backbonejs.org/#View) so if you know how Backbone works you can use it. If you want to use plain DOM objects you can access `overlay.el` (`overlay.$el` for jQuery object). From cb2980b7cf6ee4807c417bfa2f195fe1e471c051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Mon, 26 Oct 2015 18:54:29 +0100 Subject: [PATCH 058/120] docs: until cartodb.CartoDBLayer --- doc/api_methods.md | 175 +++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 87 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index 49f14547f0..49dee3f7f2 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -159,8 +159,10 @@ cartodb.createLayer(map, { layermetadata }) A promise object. You can listen for the following events: -+ **done**: triggered when the layer is created, the layer is passed as first argument. Each layer type has different options, see layers section. -+ **error**: triggered when the layer couldn't be created. The error string is the first argument. +Events | Description +--- | --- +done | triggered when the layer is created, the layer is passed as first argument. Each layer type has different options, see layers section. +error | triggered when the layer couldn't be created. The error string is the first argument. You can call to `addTo(map[, position])` in the promise so when the layer is ready it will be added to the map. @@ -193,7 +195,7 @@ cartodb.createLayer(map, 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e Layer metadata must take one of the following forms: -### Standard Layer Source Object (`type: 'cartodb'`) +### Standard Layer Source Object (_type: 'cartodb'_) Used for most maps with tables that are set to public or public with link. @@ -202,21 +204,21 @@ Used for most maps with tables that are set to public or public with link. user_name: 'your_user_name', // Required type: 'cartodb', // Required sublayers: [{ - sql: "SELECT * FROM table_name", // Required - cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required - interactivity: "column1, column2, ...", // Optional + sql: "SELECT * FROM table_name", // Required + cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required + interactivity: "column1, column2, ...", // Optional }, { - sql: "SELECT * FROM table_name", // Required - cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required - interactivity: "column1, column2, ...", // Optional + sql: "SELECT * FROM table_name", // Required + cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required + interactivity: "column1, column2, ...", // Optional }, ... ] } ``` -### Torque Layer Source Object (`type: 'torque'`) +### Torque Layer Source Object (_type: 'torque'_) Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not allow sublayers. @@ -238,122 +240,121 @@ Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not Used to create an animated torque layer with customized settings. ```javascript - // initialize a torque layer that uses the CartoDB account details and SQL API to pull in data - var torqueLayer = new L.TorqueLayer({ - user : 'viz2', - table : 'ow', - cartocss: CARTOCSS - }); +// initialize a torque layer that uses the CartoDB account details and SQL API to pull in data +var torqueLayer = new L.TorqueLayer({ + user : 'viz2', + table : 'ow', + cartocss: CARTOCSS +}); ``` - -`getValueForPos(x, y[, step])` +`getValueForPos(x, y[, step])` | --- | --- Description | Allows to get the value for the coordinate (in map reference system) for a concrete step. If a step is not specified, the animation step is used. Use caution, as this method increases CPU usage. It returns the value from the raster data, not the rendered data. Returns | An object, such as a { bbox:[], value: VALUE } if there is value for the pos, otherwise, it is null. -`getValueForBBox(xstart, ystart, xend, yend)` +`getValueForBBox(xstart, ystart, xend, yend)` | --- | --- Description | Returns an accumulated numerical value from all the torque areas, within the specified bounds. Returns | A number. -`getActivePointsBBox(step)` +`getActivePointsBBox(step)` | --- | --- Description | Returns the list of bounding boxes active for `step`. Returns | List of bbox:[]. -`getValues(step)` +`getValues(step)` | --- | --- Description | Returns the list of values for the pixels active in `step`. Returns | List of values. -`invalidate()` +`invalidate()` | --- | --- Description | Forces a reload of the layer data. #### Example of Interaction Methods for a Torque Layer + ```javascript ``` -### Result +#### Result + ```html ``` @@ -27,27 +28,24 @@ cartodb.Image(vizjson_url) #### Arguments -- **layerSource**: can be either a viz.json object or a [layer source object](http://docs.cartodb.com/cartodb-platform/cartodb-js.html#standard-layer-source-object-type-cartodb) - -#### Options - -Options take the form of a JavaScript object. - -- **options**: - - **basemap**: change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). - - **no_cdn**: Disable CDN usage. Type: Boolean. Default: `false` (use CDN) - - **override_bbox**: Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) +Arguments | Description +--- | --- +layerSource | can be either a `viz.json` object or a [layer source object](/cartodb-platform/cartodb-js/#standard-layer-source-object-type-cartodb) +options | +basemap | change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). +no_cdn | Disable CDN usage. Type: Boolean. Default: `false` (use CDN) +override_bbox | Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) ```javascript ``` #### Callback Arguments -- **err**: error associated with the image request, if any -- **url**: URL of the generated image +Arguments | Description +--- | --- +err | error associated with the image request, if any. +url | URL of the generated image. #### Returns -An _Image_ object +An `Image` object ### Image.format(_format_) Gets the URL for the image requested. -#### Argument +#### Arguments -- **format**: image format of resulting image. One of `png` (default) or `jpg` (which have a quality of 85 dpi) +Arguments | Description +--- | --- +format | image format of resulting image. One of `png` (default) or `jpg` (which have a quality of 85 dpi) #### Returns -An _Image_ object +An `Image` object From 5fe5a2698b26e9b06c324e1b701372c122940d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 12:52:39 +0100 Subject: [PATCH 066/120] docs: static maps --- doc/static_maps.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/static_maps.md b/doc/static_maps.md index ec51a73d12..169a94d478 100644 --- a/doc/static_maps.md +++ b/doc/static_maps.md @@ -24,7 +24,7 @@ cartodb.Image(vizjson_url) ``` -### cartodb.Image(_layerSource_[, options]) +### cartodb.Image(_layerSource[, options]_) #### Arguments @@ -32,9 +32,11 @@ Arguments | Description --- | --- layerSource | can be either a `viz.json` object or a [layer source object](/cartodb-platform/cartodb-js/#standard-layer-source-object-type-cartodb) options | -basemap | change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). -no_cdn | Disable CDN usage. Type: Boolean. Default: `false` (use CDN) -override_bbox | Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) + basemap | change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). + no_cdn | Disable CDN usage. Type: Boolean. Default: `false` (use CDN) + override_bbox | Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) + +#### Example ```javascript ``` -#### Arguments - -Arguments | Description ---- | --- -class | the DOM class applied to the resulting `img` tag. -id | the DOM id applied to the resulting `img` tag. -src | path to a temporary image that acts as a placeholder while the static map is retrieved. - #### Returns An `Image` object @@ -173,6 +172,13 @@ An `Image` object Gets the URL for the image requested. +#### Callback Arguments + +Arguments | Description +--- | --- +err | error associated with the image request, if any. +url | URL of the generated image. + #### Example ```javascript @@ -185,13 +191,6 @@ cartodb.Image(vizjson_url) ``` -#### Callback Arguments - -Arguments | Description ---- | --- -err | error associated with the image request, if any. -url | URL of the generated image. - #### Returns An `Image` object From 3f5ffa20e42e8c952e7a284db4424b73581689bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 17:13:27 +0100 Subject: [PATCH 069/120] docs: test pipes in table --- doc/api_methods.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index 6bc133e291..9a6faf9976 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -25,8 +25,8 @@ Arguments | Description map_id | a DOM object, for example `$('#map')` or a DOM id. vizjson_url | url of the vizjson object. options | - shareable | add facebook and twitter share buttons. - title | adds a header with the title of the visualization. +|_ shareable | add facebook and twitter share buttons. +|_ title | adds a header with the title of the visualization. description | adds description to the header (as you set in the UI). search | adds a search control (default: true). zoomControl | adds zoom control (default: true). From d8df3be8e6aa7b083cf922f6782f45bc54f5d8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 17:24:52 +0100 Subject: [PATCH 070/120] docs: pipes --- doc/api_methods.md | 56 +++++++++++++++++++++++----------------------- doc/static_maps.md | 6 ++--- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index 9a6faf9976..962a0c909d 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -27,26 +27,26 @@ vizjson_url | url of the vizjson object. options | |_ shareable | add facebook and twitter share buttons. |_ title | adds a header with the title of the visualization. - description | adds description to the header (as you set in the UI). - search | adds a search control (default: true). - zoomControl | adds zoom control (default: true). - loaderControl | adds loading control (default: true). - center_lat | latitude where the map is initializated. - center_lon | longitude where the map is initializated. - zoom | initial zoom. - cartodb_logo | default to true, set to false if you want to remove the cartodb logo. - infowindow | set to false if you want to disable the infowindow (enabled by default). - time_slider | show time slider with torque layers (enabled by default). - layer_selector | show layer selector (default: false). - legends | if it's true legends are shown in the map. - https | if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. - scrollwheel | enable/disable the ability of zooming using scrollwheel (default enabled) - fullscreen | if true adds a button to toggle the map fullscreen - mobile_layout | if true enables a custom layout for mobile devices (default: false) - force_mobile | forces enabling/disabling the mobile layout (it has priority over mobile_layout argument) - gmaps_base_type | Use Google Maps as map provider whatever is the one specified in the viz.json". Available types: 'roadmap', 'gray_roadmap', 'dark_roadmap', 'hybrid', 'satellite', 'terrain'. - gmaps_style | Google Maps styled maps. See [documentation](https://developers.google.com/maps/documentation/javascript/styling). - no_cdn | true to disable CDN when fetching tiles +|_ description | adds description to the header (as you set in the UI). +|_ search | adds a search control (default: true). +|_ zoomControl | adds zoom control (default: true). +|_ loaderControl | adds loading control (default: true). +|_ center_lat | latitude where the map is initializated. +|_ center_lon | longitude where the map is initializated. +|_ zoom | initial zoom. +|_ cartodb_logo | default to true, set to false if you want to remove the cartodb logo. +|_ infowindow | set to false if you want to disable the infowindow (enabled by default). +|_ time_slider | show time slider with torque layers (enabled by default). +|_ layer_selector | show layer selector (default: false). +|_ legends | if it's true legends are shown in the map. +|_ https | if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. +|_ scrollwheel | enable/disable the ability of zooming using scrollwheel (default enabled) +|_ fullscreen | if true adds a button to toggle the map fullscreen +|_ mobile_layout | if true enables a custom layout for mobile devices (default: false) +|_ force_mobile | forces enabling/disabling the mobile layout (it has priority over mobile_layout argument) +|_ gmaps_base_type | Use Google Maps as map provider whatever is the one specified in the viz.json". Available types: 'roadmap', 'gray_roadmap', 'dark_roadmap', 'hybrid', 'satellite', 'terrain'. +|_ gmaps_style | Google Maps styled maps. See [documentation](https://developers.google.com/maps/documentation/javascript/styling). +|_ no_cdn | true to disable CDN when fetching tiles callback(vis,layers) | if a function is specified, it is called once the visualization is created, passing vis and layers as arguments #### Returns @@ -135,14 +135,14 @@ Arguments | Description map | Leaflet `L.Map` object. The map should be initialized before calling this function. layerSource | contains information about the layer. It can be specified in 2 ways options | - https | force https - refreshTime | if is set, the layer is refreshed each refreshTime milliseconds. - infowindow | set to false if you want to disable the infowindow (enabled by default). - tooltip | set to false if you want to disable the tooltip (enabled by default). - legends | if it's true legends are shown in the map. - time_slider | show time slider with torque layers (enabled by default) - layerIndex | when the visualization contains more than one layer this index allows you to select what layer is created. Take into account that `layerIndex == 0` is the base layer and that all the tiled layers (non animated ones) are merged into a single one. The default value for this option is 1 (usually tiled layers). - filter | a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. +|_ https | force https +|_ refreshTime | if is set, the layer is refreshed each refreshTime milliseconds. +|_ infowindow | set to false if you want to disable the infowindow (enabled by default). +|_ tooltip | set to false if you want to disable the tooltip (enabled by default). +|_ legends | if it's true legends are shown in the map. +|_ time_slider | show time slider with torque layers (enabled by default) +|_ layerIndex | when the visualization contains more than one layer this index allows you to select what layer is created. Take into account that `layerIndex == 0` is the base layer and that all the tiled layers (non animated ones) are merged into a single one. The default value for this option is 1 (usually tiled layers). +|_ filter | a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. callback(_layer_) | if a function is specified, it will be invoked after the layer has been created. The layer will be passed as an argument. #### Passing the url where the layer data is located diff --git a/doc/static_maps.md b/doc/static_maps.md index 9cb165e4d1..46bb2b6387 100644 --- a/doc/static_maps.md +++ b/doc/static_maps.md @@ -32,9 +32,9 @@ Arguments | Description --- | --- layerSource | can be either a `viz.json` object or a [layer source object](/cartodb-platform/cartodb-js/#standard-layer-source-object-type-cartodb) options | - basemap | change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). - no_cdn | Disable CDN usage. Type: Boolean. Default: `false` (use CDN) - override_bbox | Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) +|_ basemap | change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). +|_ no_cdn | Disable CDN usage. Type: Boolean. Default: `false` (use CDN) +|_ override_bbox | Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) #### Example From 8417c12b71f95b3edb5b5f38c8f1d9673c89c72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 17:46:18 +0100 Subject: [PATCH 071/120] docs: fix link --- doc/static_maps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/static_maps.md b/doc/static_maps.md index 46bb2b6387..9489ebc870 100644 --- a/doc/static_maps.md +++ b/doc/static_maps.md @@ -30,7 +30,7 @@ cartodb.Image(vizjson_url) Arguments | Description --- | --- -layerSource | can be either a `viz.json` object or a [layer source object](/cartodb-platform/cartodb-js/#standard-layer-source-object-type-cartodb) +layerSource | can be either a `viz.json` object or a [layer source object](/cartodb-platform/cartodb-js/api-methods/#standard-layer-source-object-type-cartodb) options | |_ basemap | change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). |_ no_cdn | Disable CDN usage. Type: Boolean. Default: `false` (use CDN) From d69d9f256de1403c95208a2145d8285b4c90433d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 18:29:07 +0100 Subject: [PATCH 072/120] docs: test concat tables --- doc/static_maps.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/static_maps.md b/doc/static_maps.md index 9489ebc870..edcc4df9b0 100644 --- a/doc/static_maps.md +++ b/doc/static_maps.md @@ -31,7 +31,9 @@ cartodb.Image(vizjson_url) Arguments | Description --- | --- layerSource | can be either a `viz.json` object or a [layer source object](/cartodb-platform/cartodb-js/api-methods/#standard-layer-source-object-type-cartodb) + options | +--- | --- |_ basemap | change the basemap specified in the layer definition. Type: Object defining base map properties (see example below). |_ no_cdn | Disable CDN usage. Type: Boolean. Default: `false` (use CDN) |_ override_bbox | Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) From e767188a07382465af2222a5dd9a67a1dd3390a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 19:19:35 +0100 Subject: [PATCH 073/120] new docs: cartodb.js core_api --- doc/core_api.md | 53 ++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/doc/core_api.md b/doc/core_api.md index 99928b02d3..9327867f61 100644 --- a/doc/core_api.md +++ b/doc/core_api.md @@ -4,7 +4,6 @@ In case you are not using Leaflet, or you want to implement your own layer objec If you want to use this functionality, you only need to load cartodb.core.js from our cdn. No CSS is needed: -
Core API functionallity
```html ``` @@ -19,25 +18,44 @@ Notice that cartodb.SQL is also included in that JavaScript file Fetch the tile template for the layer definition. -### Arguments +#### Arguments -+ **layerOptions**: the data that defines the layer. It should contain at least user_name and sublayer list. These are the available options: +Arguments | Description +--- | --- +layerOptions | the data that defines the layer. It should contain at least `user_name` and `sublayers` list. + +options | +--- | --- +|_user_name | +|_sublayers | +|_maps_api_template | +callback(tilesUrl, error) | a function that recieves the tiles templates. In case of an error, the first param is null and the second one will be an object with an errors attribute that contains the list of errors. + +#### Example + +In this example, a layer with one sublayer is created. The sublayer renders all the content from a table.
cartodb.Tiles.getTiles
```javascript -{ +var layerData = { user_name: 'mycartodbuser', sublayers: [{ sql: "SELECT * FROM table_name"; cartocss: '#layer { marker-fill: #F0F0F0; }' }], maps_api_template: 'https://{user}.cartodb.com' // Optional +}; +cartodb.Tiles.getTiles(layerData, function(tilesUrl, error) { + if (tilesUrl == null) { + console.log("error: ", error.errors.join('\n')); + return; + } + console.log("url template is ", tilesUrl.tiles[0]); } ``` -+ **callback(tilesUrl, error)**: a function that recieves the tiles templates. In case of an error, the first param is null and the second one will be an object with an errors attribute that contains the list of errors. The tilesUrl object contains url template for tiles and interactivity grids: +The `tilesUrl` object contains url template for tiles and interactivity grids: -
cartodb.Tiles.getTiles
```javascript { tiles: [ @@ -55,26 +73,3 @@ Fetch the tile template for the layer definition. ... ] } -``` - -### Example - -In this example, a layer with one sublayer is created. The sublayer renders all the content from a table. - -
cartodb.Tiles.getTiles
-```javascript -var layerData = { - user_name: 'mycartodbuser', - sublayers: [{ - sql: "SELECT * FROM table_name"; - cartocss: '#layer { marker-fill: #F0F0F0; }' - }] -}; -cartodb.Tiles.getTiles(layerData, function(tiles, err) { - if(tiler == null) { - console.log("error: ", err.errors.join('\n')); - return; - } - console.log("url template is ", tiles.tiles[0]); -} -``` From bf8f5059fe8d4d9f713cba40fc8cca99f0e1f7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 19:22:33 +0100 Subject: [PATCH 074/120] new docs: cartodb.js core_api --- doc/core_api.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/core_api.md b/doc/core_api.md index 9327867f61..476ed8d826 100644 --- a/doc/core_api.md +++ b/doc/core_api.md @@ -26,16 +26,15 @@ layerOptions | the data that defines the layer. It should contain at least `user options | --- | --- -|_user_name | -|_sublayers | -|_maps_api_template | +|_ user_name | +|_ sublayers | +|_ maps_api_template | callback(tilesUrl, error) | a function that recieves the tiles templates. In case of an error, the first param is null and the second one will be an object with an errors attribute that contains the list of errors. #### Example In this example, a layer with one sublayer is created. The sublayer renders all the content from a table. -
cartodb.Tiles.getTiles
```javascript var layerData = { user_name: 'mycartodbuser', From 1ef9cd75a56ea0d5b30d5e1d8b7327c9a87ca0e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 19:28:42 +0100 Subject: [PATCH 075/120] new docs: cartodb.js core_api --- doc/core_api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/core_api.md b/doc/core_api.md index 476ed8d826..c4877012c7 100644 --- a/doc/core_api.md +++ b/doc/core_api.md @@ -72,3 +72,4 @@ The `tilesUrl` object contains url template for tiles and interactivity grids: ... ] } +``` From 58ca8fec58960b7a69f6575c2c5306e0fa17dc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 19:30:52 +0100 Subject: [PATCH 076/120] new docs: cartodb.js core_api --- doc/core_api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/core_api.md b/doc/core_api.md index c4877012c7..cfe89658a3 100644 --- a/doc/core_api.md +++ b/doc/core_api.md @@ -10,7 +10,7 @@ If you want to use this functionality, you only need to load cartodb.core.js fro An example using this funcionality can be found in a ModestMaps example: [view live](http://cartodb.github.com/cartodb.js/examples/modestmaps.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/modestmaps.html). -Notice that cartodb.SQL is also included in that JavaScript file +Notice that `cartodb.SQL` is also included in that JavaScript file ## cartodb.Tiles From 06603cfb839a54af704df98953322ac748d1f2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 19:34:04 +0100 Subject: [PATCH 077/120] new docs: cartodb.js other_stuff --- doc/other_stuff.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/doc/other_stuff.md b/doc/other_stuff.md index 4949a19910..00fe9f120e 100644 --- a/doc/other_stuff.md +++ b/doc/other_stuff.md @@ -8,12 +8,12 @@ The Viz.JSON document tells CartoDB.js all the information about your map, inclu Although the Viz JSON file stores all your map settings, all these settings can be easily customized with CartoDB.js. For example, if you want to do something completely different than what you initially designed it for. Loading the Viz JSON is as simple as: -
Viz JSON support
```javascript cartodb.createVis('map', 'http://examples.cartodb.com/api/v2/viz/ne_10m_populated_p_1/viz.json') ``` ## How to set a different host than cartodb.com + CartoDB.js sends all requests to the cartodb.com domain by default. If you are running your own instance of CartoDB you can change the URLs to specify a different host. @@ -34,7 +34,6 @@ Notice that you don't need to set the path to the endpoint, CartoDB.js will set We have added an easy method to get the bounding box for any dataset or filtered query using the CartoDB.js library. The **getBounds** function can be useful for guiding users to the right location on a map or for loading only the right data at the right time based on user actions. -
Bounds wrapper
```javascript var sql = new cartodb.SQL({ user: 'cartodb_user' }); @@ -51,7 +50,6 @@ CartoDB.js is highly asynchronous. Your application can get on with what it need The **createLayer** and **createVis** functions trigger two important events for you to take advantage of. The first one is **done**, which will let your code know that the library has successfully read the information from the Viz JSON and loaded the layer you requested. The second is **error**, which lets you know that something did not go as expected when trying to load the requested layer: -
Loading events
```javascript cartodb.createLayer(map, 'http://examples.cartodb.com/api/v1/viz/0001/viz.json') .addTo(map) @@ -66,7 +64,6 @@ cartodb.createLayer(map, 'http://examples.cartodb.com/api/v1/viz/0001/viz.json') The next important set of events for you to use happen on those layers that are already loaded (returned by the **done** event above). Three events are triggered by layers on your webpage, each requires the layer to include an **interactivity** layer. The first event is **featureClick**, which lets you set up events after the user clicks anything that you have mapped. -
featureClick
```javascript layer.on('featureClick', function(e, latlng, pos, data, layer) { console.log("mouse clicked polygon with data: " + data); @@ -75,7 +72,6 @@ layer.on('featureClick', function(e, latlng, pos, data, layer) { The second event is the **featureOver** event, which lets you listen for mouse hovers on any feature. Be careful, as these functions can get costly if you have a lot of features on a map. -
featureOver
```javascript layer.on('featureOver', function(e, latlng, pos, data, layer) { console.log("mouse over polygon with data: " + data); @@ -84,28 +80,25 @@ layer.on('featureOver', function(e, latlng, pos, data, layer) { Finally, there is the **featureOut** event. This is best used if you do things like highlighting polygons on mouseover and need a way to know when to remove the highlighting after the mouse has left. -
featureOut
```javascript layer.on('featureOut', function(e, latlng, pos, data, layer) { console.log("mouse left polygon with data: " + data); }); ``` -### Leaflet integration +## Leaflet integration If you want to use [Leaflet](http://leafletjs.com) it gets even easier. CartoDB.js handles loading all the necessary libraries for you! Just include CartoDB.js and CartoDB.css in the HEAD of your website and you are ready to go! The CartoDB.css document isn’t mandatory. However, if you are making a map and are not familiar with writing your own CSS for the various needed elements, it can help you jumpstart the process. Using Leaflet is as simple as adding the main JavaScript library: -
Leaflet integration
```html ``` -### HTTPS support +## HTTPS support You can use all the functionality of CartoDB.js with HTTPs support. Be sure to use https when importing both the JS library and the CSS file. You will also need to use HTTPs in the Viz.JSON URL you pass to **createVis**. -
HTTPS support
```html
@@ -125,7 +118,7 @@ You can use all the functionality of CartoDB.js with HTTPs support. Be sure to u ``` -### Persistent version hosting +## Persistent version hosting We are committed to making sure your website works as intended no matter what changes in the future. We may find more efficient or more useful features to add to the library as time progresses. But we never want to break things you have already developed. For this reason, we make versioned CartoDB.js libraries available to you. The way they function will never unexpectedly change on you. From 870053e58cb69dccd894106f4193d4367e0a77ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 27 Oct 2015 19:38:15 +0100 Subject: [PATCH 078/120] new docs: api_methods --- doc/api_methods.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index 962a0c909d..cf9a88efd9 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -24,7 +24,8 @@ Arguments | Description --- | --- map_id | a DOM object, for example `$('#map')` or a DOM id. vizjson_url | url of the vizjson object. -options | + +options | |_ shareable | add facebook and twitter share buttons. |_ title | adds a header with the title of the visualization. |_ description | adds description to the header (as you set in the UI). @@ -134,7 +135,8 @@ Arguments | Description --- | --- map | Leaflet `L.Map` object. The map should be initialized before calling this function. layerSource | contains information about the layer. It can be specified in 2 ways -options | + +options | |_ https | force https |_ refreshTime | if is set, the layer is refreshed each refreshTime milliseconds. |_ infowindow | set to false if you want to disable the infowindow (enabled by default). From 07660f7c965804daf5f5263f4d32c10b8acfa26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Wed, 28 Oct 2015 12:27:28 +0100 Subject: [PATCH 079/120] new docs: api_methods --- doc/api_methods.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/api_methods.md b/doc/api_methods.md index cf9a88efd9..ec93ad1b0c 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -26,6 +26,7 @@ map_id | a DOM object, for example `$('#map')` or a DOM id. vizjson_url | url of the vizjson object. options | +--- | --- |_ shareable | add facebook and twitter share buttons. |_ title | adds a header with the title of the visualization. |_ description | adds description to the header (as you set in the UI). @@ -137,6 +138,7 @@ map | Leaflet `L.Map` object. The map should be initialized before calling this layerSource | contains information about the layer. It can be specified in 2 ways options | +--- | --- |_ https | force https |_ refreshTime | if is set, the layer is refreshed each refreshTime milliseconds. |_ infowindow | set to false if you want to disable the infowindow (enabled by default). From 4c4ab9d2130b1a03753035004078a95e9ca1f59f Mon Sep 17 00:00:00 2001 From: Ilya Radchenko Date: Wed, 28 Oct 2015 11:04:52 -0400 Subject: [PATCH 080/120] Allow pure custom Legends This allows for legends that are purely custom (no data, just a template) to be rendered, otherwise you get a "The legend is empty". This check happens here: https://github.com/CartoDB/cartodb.js/blob/master/src/geo/ui/legend.js#L1062 --- src/geo/ui/legend.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/geo/ui/legend.js b/src/geo/ui/legend.js index 6e273f94cf..96c54dab06 100644 --- a/src/geo/ui/legend.js +++ b/src/geo/ui/legend.js @@ -1099,7 +1099,8 @@ cdb.geo.ui.Legend.Custom = cdb.geo.ui.CustomLegend.extend({ type: this.type, title: this.options.title, show_title: this.options.title ? true : false, - items: this.items.models + items: this.items.models, + template: this.options.template }); this._bindModel(); From a8984be847b6c1c843b258fba382f75e80a43bf2 Mon Sep 17 00:00:00 2001 From: acanimal Date: Thu, 29 Oct 2015 21:28:41 +0100 Subject: [PATCH 081/120] Removed unused callback parameter. Fixed missed coma issue --- src/geo/ui/infowindow.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/geo/ui/infowindow.js b/src/geo/ui/infowindow.js index 0a27b75eb5..59a8b7b466 100644 --- a/src/geo/ui/infowindow.js +++ b/src/geo/ui/infowindow.js @@ -807,7 +807,7 @@ cdb.geo.ui.Infowindow = cdb.core.View.extend({ if(this.isHidden()) return; var - offset = this.model.get("offset") + offset = this.model.get("offset"), pos = this.mapView.latLonToPixel(this.model.get("latlng")), x = this.$el.position().left, y = this.$el.position().top, @@ -823,7 +823,7 @@ cdb.geo.ui.Infowindow = cdb.core.View.extend({ /** * Adjust pan to show correctly the infowindow */ - adjustPan: function (callback) { + adjustPan: function () { var offset = this.model.get("offset"); if (!this.model.get("autoPan") || this.isHidden()) { return; } From 7d0dd8a797fd6155c3ea77536eeabce1d4c52d09 Mon Sep 17 00:00:00 2001 From: acanimal Date: Thu, 29 Oct 2015 21:54:34 +0100 Subject: [PATCH 082/120] Change show/hide efect to use visibility property instead display --- src/geo/ui/infowindow.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/geo/ui/infowindow.js b/src/geo/ui/infowindow.js index 59a8b7b466..99afc9ce10 100644 --- a/src/geo/ui/infowindow.js +++ b/src/geo/ui/infowindow.js @@ -767,7 +767,8 @@ cdb.geo.ui.Infowindow = cdb.core.View.extend({ if (!cdb.core.util.ie || (cdb.core.util.browser.ie && cdb.core.util.browser.ie.version > 8)) { this.$el.css({ 'marginBottom':'-10px', - 'display':'block', + 'display': 'block', + 'visibility':'visible', opacity:0 }); @@ -793,7 +794,7 @@ cdb.geo.ui.Infowindow = cdb.core.View.extend({ opacity: "0", display: "block" }, 180, function() { - self.$el.css({display: "none"}); + self.$el.css({visibility: "hidden"}); }); } else { this.$el.hide(); From c3fba3fe1b96fa1821cef18c74ee132cece8a0bd Mon Sep 17 00:00:00 2001 From: Antonio Santiago Date: Wed, 4 Nov 2015 10:50:35 +0100 Subject: [PATCH 083/120] Added documentation about the no_cdn option used in the createLayer method --- doc/api_methods.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/api_methods.md b/doc/api_methods.md index dddc644079..f251b165fc 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -9,6 +9,7 @@ The documentation below refers to CartoDB.js v3. For major changes in the librar Creates a visualization inside the map_id DOM object.
cartodb.createVis
+ ```javascript var url = 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'; @@ -80,6 +81,7 @@ An overlay object, see [vis.Overlays](#visoverlays). Returns the first overlay with the specified **type**.
vis.getOverlay
+ ```javascript var zoom = vis.getOverlay('zoom'); zoom.clean() // remove it from the screen @@ -126,11 +128,13 @@ With visualizations already created through the CartoDB console, you can simply - **layerSource**: contains information about the layer. It can be specified in 2 ways:
Passing the url where the layer data is located
+ ```javascript cartodb.createLayer(map, 'http://myserver.com/layerdata.json') ```
passing the data directly
+ ```javascript cartodb.createLayer(map, { layermetadata }) ``` @@ -147,6 +151,7 @@ cartodb.createLayer(map, { layermetadata }) all the tiled layers (non animated ones) are merged into a single one. The default value for this option is 1 (usually tiled layers). - **filter**: a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. + - **no_cdn**: true to disable CDN when fetching tiles - **callback(_layer_)**: if a function is specified, it will be invoked after the layer has been created. The layer will be passed as an argument. @@ -419,6 +424,7 @@ A SubLayer object. #### Example
layer.getSubLayer
+ ```javascript layer.getSubLayer(1).hide(); @@ -438,6 +444,7 @@ The number of sublayers. #### Example
Hide layers using layer.getSubLayerCount
+ ```javascript var num_sublayers = layer.getSubLayerCount(); @@ -455,6 +462,7 @@ Adds a new data to the current layer. With this method, data from multiple table - **layerDefinition**: an object with the sql and cartocss that defines the data, should be like:
layerDefinition
+ ```javascript { sql: "SELECT * FROM table_name", @@ -475,6 +483,7 @@ A SubLayer object. #### Example
layer.createSubLayer
+ ```javascript cartodb.createLayer(map, 'http://examples.cartodb.com/api/v2/viz/european_countries_e/viz.json', function(layer) { // add populated places points over the countries layer @@ -507,6 +516,7 @@ The layer itself. Sets the configuration of a layer when using [named maps](http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps-1). It can be invoked in different ways:
layer.setParams
+ ```javascript layer.setParams('test', 10); // sets test = 10 layer.setParams('test', null); // unset test @@ -533,6 +543,7 @@ Sets sublayer parameters. Useful when more than one parameter needs to be change - **layerDefinition**: an object with the sql and cartocss that defines the data, like:
layerDefinition
+ ```javascript { sql: "SELECT * FROM table_name", @@ -548,6 +559,7 @@ The layer itself. #### Example
sublayer.set
+ ```javascript sublayer.set({ sql: "SELECT * FROM table_name WHERE cartodb_id < 100", @@ -629,6 +641,7 @@ to skip sanitization, or a function to provide your own sanitization (e.g. `func - **maxHeight**: Max height of the scrolled content (value must be a number).
sublayer.infowindow.set
+ ```html
From e4cfe325bf16f558b84991d140133e144efb2ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Wed, 4 Nov 2015 12:29:28 +0100 Subject: [PATCH 084/120] new docs: api_methods formatting --- doc/api_methods.md | 86 ++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index ec93ad1b0c..57fd351bc3 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -8,19 +8,9 @@ The documentation below refers to CartoDB.js v3. For major changes in the librar Creates a visualization inside the map_id DOM object. -#### Call - -```javascript -var url = 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'; - -cartodb.createVis('map', url) - .done(function(vis, layers) { - }); -``` - #### Arguments -Arguments | Description +Name |Description --- | --- map_id | a DOM object, for example `$('#map')` or a DOM id. vizjson_url | url of the vizjson object. @@ -60,6 +50,18 @@ Event | Description done | triggered when the visualization is created, `vis` is passed as the first argument and `layers` is passed as the second argument. Each layer type has different options, see layers section. error | triggered when the layer couldn't be created. The error string is the first argument. +#### Example + +```javascript +var url = 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e7c1-11e2-806b-5404a6a683d5/viz.json'; + +cartodb.createVis('map', url) + .done(function(vis, layers) { + }); +``` + +--- + ## cartodb.Vis ### vis.getLayers() @@ -87,7 +89,7 @@ An overlay object, see [vis.Overlays](#visoverlays). Returns the first overlay with the specified **type**. -#### Call +#### Example ```javascript var zoom = vis.getOverlay('zoom'); @@ -132,7 +134,7 @@ With visualizations already created through the CartoDB console, you can simply #### Arguments -Arguments | Description +Name |Description --- | --- map | Leaflet `L.Map` object. The map should be initialized before calling this function. layerSource | contains information about the layer. It can be specified in 2 ways @@ -386,6 +388,8 @@ cartodb.createLayer(map, { }, { filter: ['http', 'mapnik'] }) ``` +--- + ## cartodb.CartoDBLayer CartoDBLayer allows you to manage tiled layers from CartoDB. It manages the sublayers. @@ -412,7 +416,7 @@ Changes the opacity of the layer. #### Arguments -Arguments | Description +Name |Description --- | --- opacity | value in range [0, 1] @@ -422,7 +426,7 @@ Gets a previously created sublayer. And exception is raised if no sublayer exist #### Arguments -Arguments | Description +Name |Description --- | --- layerIndex | 0 based index of the sublayer to get. Should be within [0, getSubLayerCount()) @@ -466,7 +470,7 @@ Adds a new data to the current layer. With this method, data from multiple table #### Arguments -Arguments | Description +Name |Description --- | --- layerDefinition | an object with the sql and cartocss that defines the data, should be like @@ -508,16 +512,15 @@ Refreshes the data. If the data has been changed in the CartoDB server those cha Sets the auth token that will be used to create the layer. Only available for private visualizations. An exception is raised if the layer is not being loaded with HTTPS. See [Named Maps](http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps-1) for more information. -#### Returns - -The layer itself. - #### Arguments -Arguments | Description +Name |Description --- | --- auth_token | string +#### Returns + +The layer itself. ### layer.setParams(_key, value_) @@ -525,15 +528,7 @@ Sets the configuration of a layer when using [named maps](/cartodb-platform/maps #### Arguments -```javascript -layer.setParams('test', 10); // sets test = 10 -layer.setParams('test', null); // unset test -layer.setParams({'test': 1, 'color': '#F00'}); // set more than one parameter at once -``` - -#### Arguments - -Arguments | Description +Name |Description --- | --- key | string value | string or number @@ -542,6 +537,16 @@ value | string or number The layer itself. +#### Example + +```javascript +layer.setParams('test', 10); // sets test = 10 +layer.setParams('test', null); // unset test +layer.setParams({'test': 1, 'color': '#F00'}); // set more than one parameter at once +``` + +--- + ## cartodb.CartoDBLayer.SubLayer ### sublayer.set(_layerDefinition_) @@ -550,21 +555,10 @@ Sets sublayer parameters. Useful when more than one parameter needs to be change #### Arguments -Arguments | Description +Name |Description --- | --- layerDefinition | an object with the sql and cartocss that defines the data - -#### layerDefinition - -```javascript -{ - sql: "SELECT * FROM table_name", - cartocss: "#layer { marker-fill: red; }", - interactivity: 'cartodb_id, area, column' // optional -} -``` - #### Returns The layer itself. @@ -637,7 +631,7 @@ Enables (`true`) or disables (`false`) the interaction of the layer. When disabl #### Arguments -Arguments | Description +Name |Description --- | --- enable | `true` if the interaction needs to be enabled. @@ -647,7 +641,7 @@ enable | `true` if the interaction needs to be enabled. #### Attributes -Attributes | Description +Name | Description --- | --- template | Custom HTML template for the infowindow. You can write simple HTML or use [Mustache templates](http://mustache.github.com/). sanitizeTemplate | By default all templates are sanitized from unsafe tags/attrs (e.g. ` ``` +--- + ## HTTPS support You can use all the functionality of CartoDB.js with HTTPs support. Be sure to use https when importing both the JS library and the CSS file. You will also need to use HTTPs in the Viz.JSON URL you pass to **createVis**. @@ -118,6 +128,8 @@ You can use all the functionality of CartoDB.js with HTTPs support. Be sure to u ``` +--- + ## Persistent version hosting We are committed to making sure your website works as intended no matter what changes in the future. We may find more efficient or more useful features to add to the library as time progresses. But we never want to break things you have already developed. For this reason, we make versioned CartoDB.js libraries available to you. The way they function will never unexpectedly change on you. diff --git a/doc/sql.md b/doc/sql.md index b06be622a9..d15972c905 100644 --- a/doc/sql.md +++ b/doc/sql.md @@ -6,6 +6,16 @@ CartoDB offers a powerful SQL API for you to query and retreive data from your C `cartodb.SQL` is the tool you will use to access data you store in your CartoDB tables. This is a really powerful technique for returning things like: **items closest to a point**, **items ordered by date**, or **GeoJSON vector geometries**. It’s all powered with SQL and our tutorials will show you how easy it is to begin with SQL. +#### Arguments + +Name | Description +--- | --- +format | should be GeoJSON. +dp | float precision. +jsonp | if jsonp should be used instead of CORS. This param is enabled if the browser does not support CORS. + +These arguments will be applied to all the queries performed by this object. If you want to override them for one query see **execute** options. + #### Example ```javascript @@ -20,23 +30,13 @@ sql.execute("SELECT * FROM table_name WHERE id > {{id}}", { id: 3 }) }) ``` -It accepts the following options: - -Options | Description ---- | --- -format | should be GeoJSON. -dp | float precision. -jsonp | if jsonp should be used instead of CORS. This param is enabled if the browser does not support CORS. - -These arguments will be applied to all the queries performed by this object. If you want to override them for one query see **execute** options. - ### sql.execute(_sql [,vars][, options][, callback]_) It executes a sql query. #### Arguments -Arguments | Description +Name |Description --- | --- sql | a string with the sql query to be executed. You can specify template variables like {{variable}} which will be filled with `vars` object. vars | a map with the variables to be interpolated in the sql query. @@ -65,6 +65,12 @@ sql.execute('SELECT * FROM table_name') Returns the bounds `[ [sw_lat, sw_lon], [ne_lat, ne_lon ] ]` for the geometry resulting of specified query. +#### Arguments + +Name |Description +--- | --- +sql | a string with the sql query to calculate the bounds from. + #### Example ```javascript @@ -73,12 +79,6 @@ sql.getBounds('select * from table').done(function(bounds) { }); ``` -#### Arguments - -Arguments | Description ---- | --- -sql | a string with the sql query to calculate the bounds from. - #### getBounds and Leaflet You can use the results from `getBounds` to center data on your maps using Leaflet. diff --git a/doc/static_maps.md b/doc/static_maps.md index edcc4df9b0..c0d4f176ef 100644 --- a/doc/static_maps.md +++ b/doc/static_maps.md @@ -28,7 +28,7 @@ cartodb.Image(vizjson_url) #### Arguments -Arguments | Description +Name |Description --- | --- layerSource | can be either a `viz.json` object or a [layer source object](/cartodb-platform/cartodb-js/api-methods/#standard-layer-source-object-type-cartodb) @@ -38,6 +38,10 @@ options | |_ no_cdn | Disable CDN usage. Type: Boolean. Default: `false` (use CDN) |_ override_bbox | Override default of using the bounding box of the visualization. This is needed to use `Image.center` and `Image.zoom`. Type: Boolean. Default: `false` (use bounding box) +#### Returns + +An `Image` object + #### Example ```javascript @@ -58,10 +62,7 @@ cartodb.Image(vizjson_url, {basemap: basemap}) ``` -#### Returns - -An `Image` object - +--- ## cartodb.Image @@ -71,7 +72,7 @@ Sets the size of the image. #### Arguments -Arguments | Description +Name |Description --- | --- width | the width of the resulting image in pixels height | the height of the resulting image in pixels @@ -86,7 +87,7 @@ Sets the center of the map. #### Arguments -Arguments | Description +Name |Description --- | --- latLng | an array of the latitude and longitude of the center of the map. Example: `[40.4378271, -3.6795367]` @@ -100,7 +101,7 @@ Sets the zoom level of the static map. Must be used with the option `override_bb #### Arguments -Arguments | Description +Name |Description --- | --- zoomLevel | the zoom of the resulting static map. `zoomLevel` must be an integer in the range [0,24]. @@ -114,7 +115,7 @@ If you set `bbox`, `center` and `zoom` will be overridden. #### Arguments -Arguments | Description +Name |Description --- | --- boundingBox | an array of coordinates making up the bounding box for your map. `boundingBox` takes the form: `[sw_lat, sw_lon, ne_lat, ne_lon]`. @@ -128,32 +129,36 @@ Inserts the image into the HTML DOM element specified. #### Arguments -Arguments | Description +Name |Description --- | --- HTMLImageElement | the DOM element where your image is to be located. +#### Returns + +An `Image` object + #### Example ```javascript cartodb.Image(vizjson_url).into(document.getElementById('map_preview')) ``` -#### Returns - -An `Image` object - ### Image.write(_attributes_) Adds an `img` tag in the same place script is executed. It's possible to specify a class name (`class`) and/or an id attribute (`id`) for the resulting image: #### Arguments -Arguments | Description +Name |Description --- | --- class | the DOM class applied to the resulting `img` tag. id | the DOM id applied to the resulting `img` tag. src | path to a temporary image that acts as a placeholder while the static map is retrieved. +#### Returns + +An `Image` object + #### Example ```javascript @@ -166,21 +171,21 @@ cartodb.Image(vizjson_url) ``` -#### Returns - -An `Image` object - ### Image.getUrl(_callback(err, url)_) Gets the URL for the image requested. #### Callback Arguments -Arguments | Description +Name |Description --- | --- err | error associated with the image request, if any. url | URL of the generated image. +#### Returns + +An `Image` object + #### Example ```javascript @@ -193,17 +198,13 @@ cartodb.Image(vizjson_url) ``` -#### Returns - -An `Image` object - ### Image.format(_format_) Gets the URL for the image requested. #### Arguments -Arguments | Description +Name |Description --- | --- format | image format of resulting image. One of `png` (default) or `jpg` (which have a quality of 85 dpi) diff --git a/doc/ui_functions.md b/doc/ui_functions.md index b66eebe321..d479abd2e6 100644 --- a/doc/ui_functions.md +++ b/doc/ui_functions.md @@ -21,6 +21,7 @@ The tooltip is shown when hover on feature when is called. The tooltip is not shown when hover on feature. +--- ## cartodb.geo.ui.InfoBox @@ -43,6 +44,7 @@ The tooltip is shown when hover on feature. The tooltip is not shown when hover on feature. +--- ## cartodb.geo.ui.Zoom From e6d3c4acdfa7357d882247c092713c882f005f77 Mon Sep 17 00:00:00 2001 From: csobier Date: Thu, 5 Nov 2015 13:05:13 -0500 Subject: [PATCH 086/120] removed links to out-of-date The Hobbit --- doc/getting_started.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/getting_started.md b/doc/getting_started.md index 7b563f8d9c..80c1ed20c1 100644 --- a/doc/getting_started.md +++ b/doc/getting_started.md @@ -140,4 +140,3 @@ The best way to start learning about the library is by taking a look at some of + Leaflet integration - ([view live](http://cartodb.github.com/cartodb.js/examples/leaflet.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/leaflet.html)). + Customizing infowindow data - ([view live](http://cartodb.github.com/cartodb.js/examples/custom_infowindow.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/custom_infowindow.html)). + An example using a layer selector - ([view live](http://cartodb.github.com/cartodb.js/examples/layer_selector.html) / [source code](https://github.com/CartoDB/cartodb.js/blob/develop/examples/layer_selector.html)). -+ The Hobbit map done with the library - ([view live](http://cartodb.github.com/cartodb.js/examples/TheHobbitLocations/) / [source code](https://github.com/CartoDB/cartodb.js/tree/develop/examples/TheHobbitLocations)). From 6eda7673a64a0720ff883699646c292b87587956 Mon Sep 17 00:00:00 2001 From: Antonio Santiago Date: Tue, 10 Nov 2015 17:40:26 +0100 Subject: [PATCH 087/120] Stack legend visible by default --- themes/css/map/cartodb-map-light.css | 1 - 1 file changed, 1 deletion(-) diff --git a/themes/css/map/cartodb-map-light.css b/themes/css/map/cartodb-map-light.css index 29794e55da..c50e67d06b 100644 --- a/themes/css/map/cartodb-map-light.css +++ b/themes/css/map/cartodb-map-light.css @@ -984,7 +984,6 @@ div.cartodb-legend-stack { position:absolute; bottom: 35px; right: 20px; - display:none; webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 4px 2px; From 4646856f959c66381a7ae9bf4820c6c1bf7760aa Mon Sep 17 00:00:00 2001 From: Antonio Santiago Date: Wed, 11 Nov 2015 15:56:51 +0100 Subject: [PATCH 088/120] Added new property on cdb.geo.Map model class --- src/geo/gmaps/gmaps.js | 4 + src/geo/leaflet/leaflet.js | 7 ++ src/geo/map.js | 1 + src/vis/vis.js | 9 ++ test/spec/geo/leaflet/leaflet.spec.js | 6 +- test/spec/vis/vis.spec.js | 137 +++++++++++++++++++++++++- 6 files changed, 157 insertions(+), 7 deletions(-) diff --git a/src/geo/gmaps/gmaps.js b/src/geo/gmaps/gmaps.js index 77082feea6..2eb8127f21 100644 --- a/src/geo/gmaps/gmaps.js +++ b/src/geo/gmaps/gmaps.js @@ -45,7 +45,11 @@ if(typeof(google) != "undefined" && typeof(google.maps) != "undefined") { minZoom: this.map.get('minZoom'), maxZoom: this.map.get('maxZoom'), disableDefaultUI: true, + // Set scrollwheel options scrollwheel: this.map.get("scrollwheel"), + // Allow dragging (and double click zoom) + draggable: this.map.get("drag"), + disableDoubleClickZoom: this.map.get("drag"), mapTypeControl:false, mapTypeId: google.maps.MapTypeId.ROADMAP, backgroundColor: 'white', diff --git a/src/geo/leaflet/leaflet.js b/src/geo/leaflet/leaflet.js index a80de2dc72..a7a476290a 100644 --- a/src/geo/leaflet/leaflet.js +++ b/src/geo/leaflet/leaflet.js @@ -42,8 +42,15 @@ // remove the "powered by leaflet" this.map_leaflet.attributionControl.setPrefix(''); + // Disable scrollwheel if (this.map.get("scrollwheel") == false) this.map_leaflet.scrollWheelZoom.disable(); + // Disable keyboard if (this.map.get("keyboard") == false) this.map_leaflet.keyboard.disable(); + // Disable dragging (also doubleClickZoom) + if (this.map.get("drag") == false) { + this.map_leaflet.dragging.disable(); + this.map_leaflet.doubleClickZoom.disable(); + } } else { diff --git a/src/geo/map.js b/src/geo/map.js index b820563266..65e8a98209 100644 --- a/src/geo/map.js +++ b/src/geo/map.js @@ -281,6 +281,7 @@ cdb.geo.Map = cdb.core.Model.extend({ minZoom: 0, maxZoom: 40, scrollwheel: true, + drag: true, keyboard: true, provider: 'leaflet' }, diff --git a/src/vis/vis.js b/src/vis/vis.js index 9a41505dc2..259a8a5af0 100644 --- a/src/vis/vis.js +++ b/src/vis/vis.js @@ -336,6 +336,14 @@ var Vis = cdb.core.View.extend({ var scrollwheel = (options.scrollwheel === undefined) ? data.scrollwheel : options.scrollwheel; var slides_controller = (options.slides_controller === undefined) ? data.slides_controller : options.slides_controller; + // Do not allow pan map if zoom overlay and scrollwheel are disabled + // Check if zoom overlay is present. + var hasZoomOverlay = _.isObject(_.find(data.overlays, function(overlay) { + return overlay.type == "zoom" + })); + + var allowDragging = hasZoomOverlay || scrollwheel; + // map data.maxZoom || (data.maxZoom = 20); data.minZoom || (data.minZoom = 0); @@ -372,6 +380,7 @@ var Vis = cdb.core.View.extend({ minZoom: data.minZoom, legends: data.legends, scrollwheel: scrollwheel, + drag: allowDragging, provider: data.map_provider }; diff --git a/test/spec/geo/leaflet/leaflet.spec.js b/test/spec/geo/leaflet/leaflet.spec.js index bc3ef1bad8..b38d738fd9 100644 --- a/test/spec/geo/leaflet/leaflet.spec.js +++ b/test/spec/geo/leaflet/leaflet.spec.js @@ -139,7 +139,7 @@ describe('LeafletMapView', function() { }); it("should create the cartodb logo", function(done) { - layer = new cdb.geo.CartoDBLayer({ + layer = new cdb.geo.CartoDBLayer({ table_name: "INVENTADO", user_name: 'test', tile_style: 'test' @@ -154,7 +154,7 @@ describe('LeafletMapView', function() { }); it("should not add the cartodb logo when cartodb_logo = false", function(done) { - layer = new cdb.geo.CartoDBLayer({ + layer = new cdb.geo.CartoDBLayer({ table_name: "INVENTADO", user_name: 'test', tile_style: 'test', @@ -495,5 +495,5 @@ describe('LeafletMapView', function() { expect(attributions).toEqual('Stamen, custom attribution, CartoDB attribution'); }); }); -}); +}); diff --git a/test/spec/vis/vis.spec.js b/test/spec/vis/vis.spec.js index e4dd2985e7..e0afe284e7 100644 --- a/test/spec/vis/vis.spec.js +++ b/test/spec/vis/vis.spec.js @@ -1,7 +1,6 @@ describe("Overlay", function() { - it("should register and create a type", function() { var _data; cdb.vis.Overlay.register('test', function(data) { @@ -89,7 +88,7 @@ describe("Vis", function() { expect(this.mapConfig.center[0]).not.toEqual(43.3); expect(this.mapConfig.center[1]).not.toEqual("ham"); }) - + it("should parse bounds values if they are correct", function() { this.container = $('
').css('height', '200px'); var opts = { @@ -410,7 +409,7 @@ describe("Vis", function() { ]; this.vis.load(this.mapConfig); - + setTimeout(function() { var scripts = document.getElementsByTagName('script'), torqueRe = /\/cartodb\.mod\.torque\.js/; @@ -438,11 +437,141 @@ describe("Vis", function() { }; layers = null; - + this.vis.load(this.mapConfig, opts); expect(this.vis.map.layers.at(0).get('type')).toEqual('GMapsBase'); }); + describe("dragging option", function() { + + it("should be enabled with zoom overlay and scrollwheel enabled", function(done) { + var container = $('
').css('height', '200px'); + var vis = new cdb.vis.Vis({el: container}); + + var mapConfig = { + updated_at: 'cachebuster', + title: "irrelevant", + url: "http://cartodb.com", + center: [40.044, -101.95], + bounding_box_sw: [20, -140], + bounding_box_ne: [ 55, -50], + zoom: 4, + bounds: [[1, 2],[3, 4]], + scrollwheel: true, + overlays: [ + { + type: "zoom", + order: 6, + options: { + x: 20, + y: 20, + display: true + }, + template: "" + } + ], + }; + + vis.load(mapConfig); + + setTimeout(function () { + expect(vis.map.get('drag')).toEqual(true); + done(); + }, 500); + + }); + + it("should be enabled with zoom overlay and scrollwheel disabled", function(done) { + var container = $('
').css('height', '200px'); + var vis = new cdb.vis.Vis({el: container}); + + var mapConfig = { + updated_at: 'cachebuster', + title: "irrelevant", + url: "http://cartodb.com", + center: [40.044, -101.95], + bounding_box_sw: [20, -140], + bounding_box_ne: [ 55, -50], + zoom: 4, + bounds: [[1, 2],[3, 4]], + scrollwheel: false, + overlays: [ + { + type: "zoom", + order: 6, + options: { + x: 20, + y: 20, + display: true + }, + template: "" + } + ], + }; + + vis.load(mapConfig); + + setTimeout(function () { + expect(vis.map.get('drag')).toEqual(true); + done(); + }, 500); + + }); + + it("should be enabled without zoom overlay and scrollwheel enabled", function(done) { + var container = $('
').css('height', '200px'); + var vis = new cdb.vis.Vis({el: container}); + + var mapConfig = { + updated_at: 'cachebuster', + title: "irrelevant", + url: "http://cartodb.com", + center: [40.044, -101.95], + bounding_box_sw: [20, -140], + bounding_box_ne: [ 55, -50], + zoom: 4, + bounds: [[1, 2],[3, 4]], + scrollwheel: true, + overlays: [], + }; + + vis.load(mapConfig); + + setTimeout(function () { + expect(vis.map.get('drag')).toEqual(true); + done(); + }, 500); + + }); + + it("should be disabled without zoom overlay and scrollwheel disabled", function(done) { + var container = $('
').css('height', '200px'); + var vis = new cdb.vis.Vis({el: container}); + + var mapConfig = { + updated_at: 'cachebuster', + title: "irrelevant", + url: "http://cartodb.com", + center: [40.044, -101.95], + bounding_box_sw: [20, -140], + bounding_box_ne: [ 55, -50], + zoom: 4, + bounds: [[1, 2],[3, 4]], + scrollwheel: false, + overlays: [], + }; + + vis.load(mapConfig); + + setTimeout(function () { + expect(vis.map.get('drag')).toEqual(false); + done(); + }, 500); + + }); + + }); + describe("Legends", function() { it('should only display legends for visible layers', function() { From 37409b9a7e78cb58ead1fd013a44b91f57b06ba6 Mon Sep 17 00:00:00 2001 From: Antonio Santiago Date: Wed, 11 Nov 2015 16:16:39 +0100 Subject: [PATCH 089/120] New sample showing draggin disable capability --- examples/map_dragging_disabled.html | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 examples/map_dragging_disabled.html diff --git a/examples/map_dragging_disabled.html b/examples/map_dragging_disabled.html new file mode 100644 index 0000000000..1e44b2a197 --- /dev/null +++ b/examples/map_dragging_disabled.html @@ -0,0 +1,41 @@ + + + + Map with dragging disabled | CartoDB.js + + + + + + + + +
+ + + + + + + From c844bd03a28db74177823a56b5a462d3704194b8 Mon Sep 17 00:00:00 2001 From: tibomahe Date: Thu, 12 Nov 2015 09:42:32 +0100 Subject: [PATCH 090/120] Remove capitalize in !important statements --- themes/css/map/cartodb-map-light.css | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/themes/css/map/cartodb-map-light.css b/themes/css/map/cartodb-map-light.css index c50e67d06b..d2c8e37a7a 100644 --- a/themes/css/map/cartodb-map-light.css +++ b/themes/css/map/cartodb-map-light.css @@ -299,8 +299,8 @@ only screen and ( min-resolution: 2dppx) { width:80%; } div.cartodb-zoom a { - background:url('../img/other@2x.png') no-repeat 0 0!Important; - background-size: 113px 34px!Important; + background:url('../img/other@2x.png') no-repeat 0 0!important; + background-size: 113px 34px!important; } div.cartodb-zoom a.zoom_in { background-position: -68px 9px!important @@ -309,31 +309,31 @@ only screen and ( min-resolution: 2dppx) { background-position:-94px 10px!important; } div.cartodb-header div.social a.facebook { - background:url('../img/other@2x.png') no-repeat 0 0!Important; - background-size: 113px 34px!Important; + background:url('../img/other@2x.png') no-repeat 0 0!important; + background-size: 113px 34px!important; } div.cartodb-header div.social a.twitter { - background:url('../img/other@2x.png') no-repeat -26px 0!Important; - background-size: 113px 34px!Important; + background:url('../img/other@2x.png') no-repeat -26px 0!important; + background-size: 113px 34px!important; } div.cartodb-searchbox span.loader { - background: url('../img/loader@2x.gif') no-repeat center center white!Important; - background-size: 16px 16px!Important; + background: url('../img/loader@2x.gif') no-repeat center center white!important; + background-size: 16px 16px!important; } div.cartodb-mobile .aside div.cartodb-searchbox span.loader { - background: url('../img/dark_loader@2x.gif') no-repeat center center #292929!Important; - background-size: 16px 16px!Important; + background: url('../img/dark_loader@2x.gif') no-repeat center center #292929!important; + background-size: 16px 16px!important; } div.cartodb-tiles-loader div.loader { - background: url('../img/loader@2x.gif') no-repeat center center white!Important; - background-size: 16px 16px!Important; + background: url('../img/loader@2x.gif') no-repeat center center white!important; + background-size: 16px 16px!important; } div.cartodb-searchbox input.submit { - background:url('../img/other@2x.png') no-repeat -56px 0!Important; - background-size: 113px 34px!Important; + background:url('../img/other@2x.png') no-repeat -56px 0!important; + background-size: 113px 34px!important; } .cartodb-mobile .aside .cartodb-searchbox input.submit { - background:url('../img/mobile_zoom.png') no-repeat 0 0!Important; + background:url('../img/mobile_zoom.png') no-repeat 0 0!important; } .cartodb-mobile div.cartodb-slides-controller div.slides-controller-content a.prev { background: url('../img/slide_left@2x.png') no-repeat; From c97753cfa2307167798716e8a4ee732e714a4d3a Mon Sep 17 00:00:00 2001 From: Antonio Santiago Date: Thu, 12 Nov 2015 12:50:52 +0100 Subject: [PATCH 091/120] Added samples --- test/spec/vis/vis.spec.js | 76 +++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/test/spec/vis/vis.spec.js b/test/spec/vis/vis.spec.js index e0afe284e7..334de0369d 100644 --- a/test/spec/vis/vis.spec.js +++ b/test/spec/vis/vis.spec.js @@ -472,12 +472,11 @@ describe("Vis", function() { ], }; - vis.load(mapConfig); - - setTimeout(function () { - expect(vis.map.get('drag')).toEqual(true); - done(); - }, 500); + vis.load(mapConfig) + .done(function() { + expect(vis.map.get('drag')).toBeTruthy(); + done(); + }); }); @@ -509,12 +508,11 @@ describe("Vis", function() { ], }; - vis.load(mapConfig); - - setTimeout(function () { - expect(vis.map.get('drag')).toEqual(true); - done(); - }, 500); + vis.load(mapConfig) + .done(function() { + expect(vis.map.get('drag')).toBeTruthy(); + done(); + }); }); @@ -535,12 +533,11 @@ describe("Vis", function() { overlays: [], }; - vis.load(mapConfig); - - setTimeout(function () { - expect(vis.map.get('drag')).toEqual(true); - done(); - }, 500); + vis.load(mapConfig) + .done(function() { + expect(vis.map.get('drag')).toBeTruthy(); + done(); + }); }); @@ -561,13 +558,46 @@ describe("Vis", function() { overlays: [], }; - vis.load(mapConfig); + vis.load(mapConfig) + .done(function() { + expect(vis.map.get('drag')).toBeFalsy(); + done(); + }); + + }); - setTimeout(function () { - expect(vis.map.get('drag')).toEqual(false); - done(); - }, 500); + it("should disable leaflet dragging and double click zooming when the map has drag disabled", function() { + var container = $('
').css({ + 'height': '200px', + 'width': '200px' + }); + var map = new cdb.geo.Map({ + drag: false + }); + var mapView = new cdb.geo.LeafletMapView({ + el: container, + map: map + }); + + expect(mapView.map_leaflet.dragging.enabled()).toBeFalsy(); + expect(mapView.map_leaflet.doubleClickZoom.enabled()).toBeFalsy(); + }); + + it("should disable gmaps dragging and double click zooming when the map has drag disabled", function() { + var container = $('
').css({ + 'height': '200px', + 'width': '200px' + }); + var map = new cdb.geo.Map({ + drag: false + }); + var mapView = new cdb.geo.GoogleMapsMapView({ + el: container, + map: map + }); + expect(mapView.map_googlemaps.get('draggable')).toBeFalsy(); + expect(mapView.map_googlemaps.get('disableDoubleClickZoom')).toBeFalsy(); }); }); From 675e2b579eb64f81bbcd41da10e61fdc148f9847 Mon Sep 17 00:00:00 2001 From: Antonio Santiago Date: Thu, 12 Nov 2015 13:07:17 +0100 Subject: [PATCH 092/120] Moved tests from vis spec to lefalte and gmaps --- test/spec/geo/gmaps/gmaps.spec.js | 20 +++++++- test/spec/geo/leaflet/leaflet.spec.js | 17 ++++++ test/spec/vis/vis.spec.js | 74 +++++---------------------- 3 files changed, 48 insertions(+), 63 deletions(-) diff --git a/test/spec/geo/gmaps/gmaps.spec.js b/test/spec/geo/gmaps/gmaps.spec.js index c64275915f..ca753081a3 100644 --- a/test/spec/geo/gmaps/gmaps.spec.js +++ b/test/spec/geo/gmaps/gmaps.spec.js @@ -133,7 +133,7 @@ layer_definition: { version: '1.0.0', layers: [{ - type: 'cartodb', + type: 'cartodb', options: { sql: "select * from european_countries_export", cartocss: '#layer { polygon-fill: #000; polygon-opacity: 0.8;}', @@ -234,4 +234,22 @@ done(); }, 2000); }); + + it("should disable gmaps dragging and double click zooming when the map has drag disabled", function() { + var container = $('
').css({ + 'height': '200px', + 'width': '200px' + }); + var map = new cdb.geo.Map({ + drag: false + }); + var mapView = new cdb.geo.GoogleMapsMapView({ + el: container, + map: map + }); + + expect(mapView.map_googlemaps.get('draggable')).toBeFalsy(); + expect(mapView.map_googlemaps.get('disableDoubleClickZoom')).toBeFalsy(); + }); + }); diff --git a/test/spec/geo/leaflet/leaflet.spec.js b/test/spec/geo/leaflet/leaflet.spec.js index b38d738fd9..f60803d277 100644 --- a/test/spec/geo/leaflet/leaflet.spec.js +++ b/test/spec/geo/leaflet/leaflet.spec.js @@ -496,4 +496,21 @@ describe('LeafletMapView', function() { }); }); + it("should disable leaflet dragging and double click zooming when the map has drag disabled", function() { + var container = $('
').css({ + 'height': '200px', + 'width': '200px' + }); + var map = new cdb.geo.Map({ + drag: false + }); + var mapView = new cdb.geo.LeafletMapView({ + el: container, + map: map + }); + + expect(mapView.map_leaflet.dragging.enabled()).toBeFalsy(); + expect(mapView.map_leaflet.doubleClickZoom.enabled()).toBeFalsy(); + }); + }); diff --git a/test/spec/vis/vis.spec.js b/test/spec/vis/vis.spec.js index 334de0369d..0d83974602 100644 --- a/test/spec/vis/vis.spec.js +++ b/test/spec/vis/vis.spec.js @@ -444,7 +444,7 @@ describe("Vis", function() { describe("dragging option", function() { - it("should be enabled with zoom overlay and scrollwheel enabled", function(done) { + it("should be enabled with zoom overlay and scrollwheel enabled", function() { var container = $('
').css('height', '200px'); var vis = new cdb.vis.Vis({el: container}); @@ -472,15 +472,11 @@ describe("Vis", function() { ], }; - vis.load(mapConfig) - .done(function() { - expect(vis.map.get('drag')).toBeTruthy(); - done(); - }); - + vis.load(mapConfig); + expect(vis.map.get('drag')).toBeTruthy(); }); - it("should be enabled with zoom overlay and scrollwheel disabled", function(done) { + it("should be enabled with zoom overlay and scrollwheel disabled", function() { var container = $('
').css('height', '200px'); var vis = new cdb.vis.Vis({el: container}); @@ -508,15 +504,11 @@ describe("Vis", function() { ], }; - vis.load(mapConfig) - .done(function() { - expect(vis.map.get('drag')).toBeTruthy(); - done(); - }); - + vis.load(mapConfig); + expect(vis.map.get('drag')).toBeTruthy(); }); - it("should be enabled without zoom overlay and scrollwheel enabled", function(done) { + it("should be enabled without zoom overlay and scrollwheel enabled", function() { var container = $('
').css('height', '200px'); var vis = new cdb.vis.Vis({el: container}); @@ -533,15 +525,11 @@ describe("Vis", function() { overlays: [], }; - vis.load(mapConfig) - .done(function() { - expect(vis.map.get('drag')).toBeTruthy(); - done(); - }); - + vis.load(mapConfig); + expect(vis.map.get('drag')).toBeTruthy(); }); - it("should be disabled without zoom overlay and scrollwheel disabled", function(done) { + it("should be disabled without zoom overlay and scrollwheel disabled", function() { var container = $('
').css('height', '200px'); var vis = new cdb.vis.Vis({el: container}); @@ -558,46 +546,8 @@ describe("Vis", function() { overlays: [], }; - vis.load(mapConfig) - .done(function() { - expect(vis.map.get('drag')).toBeFalsy(); - done(); - }); - - }); - - it("should disable leaflet dragging and double click zooming when the map has drag disabled", function() { - var container = $('
').css({ - 'height': '200px', - 'width': '200px' - }); - var map = new cdb.geo.Map({ - drag: false - }); - var mapView = new cdb.geo.LeafletMapView({ - el: container, - map: map - }); - - expect(mapView.map_leaflet.dragging.enabled()).toBeFalsy(); - expect(mapView.map_leaflet.doubleClickZoom.enabled()).toBeFalsy(); - }); - - it("should disable gmaps dragging and double click zooming when the map has drag disabled", function() { - var container = $('
').css({ - 'height': '200px', - 'width': '200px' - }); - var map = new cdb.geo.Map({ - drag: false - }); - var mapView = new cdb.geo.GoogleMapsMapView({ - el: container, - map: map - }); - - expect(mapView.map_googlemaps.get('draggable')).toBeFalsy(); - expect(mapView.map_googlemaps.get('disableDoubleClickZoom')).toBeFalsy(); + vis.load(mapConfig); + expect(vis.map.get('drag')).toBeFalsy(); }); }); From c5a4913dfeedd427d795acfa0ccd0f1886b08124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Fri, 20 Nov 2015 12:48:44 +0100 Subject: [PATCH 093/120] Bump example version to 3.15 --- examples/tutorials/tutorial-google-driving.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/tutorials/tutorial-google-driving.html b/examples/tutorials/tutorial-google-driving.html index 5a283c15df..1f5fffb593 100644 --- a/examples/tutorials/tutorial-google-driving.html +++ b/examples/tutorials/tutorial-google-driving.html @@ -13,16 +13,16 @@ } - +
- + - + - \ No newline at end of file + From 7dd351eb663ceb4bf91a43a35f4dfc16b36af343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Fri, 20 Nov 2015 12:57:11 +0100 Subject: [PATCH 094/120] Update tutorial-google-driving-2.html --- examples/tutorials/tutorial-google-driving-2.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/tutorials/tutorial-google-driving-2.html b/examples/tutorials/tutorial-google-driving-2.html index 601ea66019..a70bf0295b 100644 --- a/examples/tutorials/tutorial-google-driving-2.html +++ b/examples/tutorials/tutorial-google-driving-2.html @@ -13,7 +13,7 @@ } - +
@@ -22,7 +22,7 @@ - + - \ No newline at end of file + From a65d1dc55ce029d1937a0f8e515c05a803d83768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Fri, 20 Nov 2015 16:17:30 +0100 Subject: [PATCH 095/120] Layer Source Object --- doc/api_methods.md | 55 +++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index e88a8310ed..ada5e46004 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -202,32 +202,13 @@ cartodb.createLayer(map, 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e Layer metadata must take one of the following forms: +## Layer Source Object + ### Standard Layer Source Object (_type: 'cartodb'_) Used for most maps with tables that are set to public or public with link. -```javascript -{ - user_name: 'your_user_name', // Required - type: 'cartodb', // Required - sublayers: [{ - sql: "SELECT * FROM table_name", // Required - cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required - interactivity: "column1, column2, ...", // Optional - }, - { - sql: "SELECT * FROM table_name", // Required - cartocss: '#table_name {marker-fill: #F0F0F0;}', // Required - interactivity: "column1, column2, ...", // Optional - }, - ... - ] -} -``` - -### Torque Layer Source Object (_type: 'torque'_) - -Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not allow sublayers. +#### Example ```javascript { @@ -242,9 +223,11 @@ Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not } ``` -#### Interaction Methods for a Torque Layer +### Torque Layer Source Object (_type: 'torque'_) -Used to create an animated torque layer with customized settings. +Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not allow sublayers. + +#### Example ```javascript // initialize a torque layer that uses the CartoDB account details and SQL API to pull in data @@ -255,36 +238,40 @@ var torqueLayer = new L.TorqueLayer({ }); ``` -`getValueForPos(x, y[, step])` | +### Interaction Methods for a Torque Layer + +Used to create an animated torque layer with customized settings. + +#### getValueForPos(_x, y[, step]_) + --- | --- Description | Allows to get the value for the coordinate (in map reference system) for a concrete step. If a step is not specified, the animation step is used. Use caution, as this method increases CPU usage. It returns the value from the raster data, not the rendered data. Returns | An object, such as a { bbox:[], value: VALUE } if there is value for the pos, otherwise, it is null. +#### getValueForBBox(_xstart, ystart, xend, yend_) -`getValueForBBox(xstart, ystart, xend, yend)` | --- | --- Description | Returns an accumulated numerical value from all the torque areas, within the specified bounds. Returns | A number. +#### getActivePointsBBox(_step_) -`getActivePointsBBox(step)` | --- | --- Description | Returns the list of bounding boxes active for `step`. Returns | List of bbox:[]. +#### getValues(_step_) -`getValues(step)` | --- | --- Description | Returns the list of values for the pixels active in `step`. Returns | List of values. +#### invalidate() -`invalidate()` | --- | --- Description | Forces a reload of the layer data. - -#### Example of Interaction Methods for a Torque Layer +#### Example ```javascript - + - + ', null)).toEqual(''); }); }); + + describe('common XSS attacks', function() { + + var attacks = [ + ''); + expect(view.$el.html()).toEqual('no '); }); it('should allow to override sanitization', function() { @@ -487,7 +487,7 @@ describe("cdb.geo.ui.infowindow", function() { view.model.set('sanitizeTemplate', undefined); view.render(); - expect(view.$el.html()).toEqual('no '); + expect(view.$el.html()).toEqual('no '); }); }); diff --git a/vendor/html-css-sanitizer-bundle.js b/vendor/html-css-sanitizer-bundle.js index e646ef6b84..a7063feb96 100644 --- a/vendor/html-css-sanitizer-bundle.js +++ b/vendor/html-css-sanitizer-bundle.js @@ -3,20 +3,22 @@ // https://code.google.com/p/google-caja/wiki/JsHtmlSanitizer // // Steps to rebuild this file: -// $ svn checkout http://google-caja.googlecode.com/svn/trunk/ google-caja +// $ git clone https://github.com/google/caja.git google-caja // $ cd google-caja // $ ant // $ cp ant-lib/com/google/caja/plugin/html-css-sanitizer-bundle.js /path/to/cartodb.js/vendor/ // // Additional changes after the built file above: // - Added: This header -// - Modified: `sanitizeAttribs` at end, to allow "data-*"" attributes (lines ~4750-4760) -// - changed policy for a::target attribute to be allowed (html4.ATTRIBS: { 'a::target': ... changed value from 10 to 0) +// - Modified: `iframe` element. Mark it as unsafe changing code from 4 to 16. (line: 3543) +// - Modified: `sanitizeAttribs` at end, to allow "data-*"" attributes (lines ~4755-4761) +// - changed policy for a::target attribute to be allowed (html4.ATTRIBS: { 'a::target': ... changed value from 10 to 0) (line 3211) // ------------------------------------------------------------------------------------------------------------------- + /* Copyright Google Inc. * Licensed under the Apache Licence Version 2.0 - * Autogenerated at Mon Mar 23 15:26:16 CET 2015 + * Autogenerated at Wed Nov 25 17:35:12 CET 2015 * \@overrides window * \@provides cssSchema, CSS_PROP_BIT_QUANTITY, CSS_PROP_BIT_HASH_VALUE, CSS_PROP_BIT_NEGATIVE_QUANTITY, CSS_PROP_BIT_QSTRING, CSS_PROP_BIT_URL, CSS_PROP_BIT_UNRESERVED_WORD, CSS_PROP_BIT_UNICODE_RANGE, CSS_PROP_BIT_GLOBAL_NAME, CSS_PROP_BIT_PROPERTY_NAME */ /** @@ -3135,7 +3137,7 @@ if (typeof window !== 'undefined') { ; // Copyright Google Inc. // Licensed under the Apache Licence Version 2.0 -// Autogenerated at Mon Mar 23 15:26:17 CET 2015 +// Autogenerated at Wed Nov 25 17:35:12 CET 2015 // @overrides window // @provides html4 var html4 = {}; @@ -3350,6 +3352,7 @@ html4.ATTRIBS = { 'meter::low': 0, 'meter::max': 0, 'meter::min': 0, + 'meter::optimum': 0, 'meter::value': 0, 'ol::compact': 0, 'ol::reversed': 0, @@ -3537,7 +3540,7 @@ html4.ELEMENTS = { 'hr': 2, 'html': 305, 'i': 0, - 'iframe': 4, + 'iframe': 16, 'img': 2, 'input': 2, 'ins': 0, From 91b665136f5550a34a28a64b1fdaa943921fba6f Mon Sep 17 00:00:00 2001 From: xavijam Date: Thu, 3 Dec 2015 13:26:16 +0100 Subject: [PATCH 100/120] Enable drag for visualization when device is mobile --- src/vis/vis.js | 37 ++++++---- test/spec/vis/vis.spec.js | 138 ++++++++++++++++---------------------- 2 files changed, 81 insertions(+), 94 deletions(-) diff --git a/src/vis/vis.js b/src/vis/vis.js index 259a8a5af0..7b4375d464 100644 --- a/src/vis/vis.js +++ b/src/vis/vis.js @@ -158,7 +158,7 @@ var Vis = cdb.core.View.extend({ legends: legends }); - if (!this.mobile_enabled) { + if (!this.isMobileEnabled) { this.mapView.addOverlay(this.legends); } }, @@ -336,13 +336,18 @@ var Vis = cdb.core.View.extend({ var scrollwheel = (options.scrollwheel === undefined) ? data.scrollwheel : options.scrollwheel; var slides_controller = (options.slides_controller === undefined) ? data.slides_controller : options.slides_controller; - // Do not allow pan map if zoom overlay and scrollwheel are disabled + // Do not allow pan map if zoom overlay and scrollwheel are disabled unless + // mobile view is enabled + var isMobileDevice = this.isMobileDevice(); // Check if zoom overlay is present. var hasZoomOverlay = _.isObject(_.find(data.overlays, function(overlay) { return overlay.type == "zoom" })); var allowDragging = hasZoomOverlay || scrollwheel; + if (isMobileDevice) { + allowDragging = true; + } // map data.maxZoom || (data.maxZoom = 20); @@ -528,7 +533,7 @@ var Vis = cdb.core.View.extend({ this.torqueLayer.bind('change:time', function(s) { this.trigger('change:step', this.torqueLayer, this.torqueLayer.getStep()); }, this); - if (!this.mobile_enabled && this.torqueLayer) { + if (!this.isMobileEnabled && this.torqueLayer) { this.addTimeSlider(this.torqueLayer); } } @@ -691,7 +696,7 @@ var Vis = cdb.core.View.extend({ _createOverlays: function(overlays, vis_data, options) { // if there's no header overlay, we need to explicitly create the slide controller - if ((options["slides_controller"] || options["slides_controller"] === undefined) && !this.mobile_enabled && !_.find(overlays, function(o) { return o.type === 'header' && o.options.display; })) { + if ((options["slides_controller"] || options["slides_controller"] === undefined) && !this.isMobileEnabled && !_.find(overlays, function(o) { return o.type === 'header' && o.options.display; })) { this._addSlideController(vis_data); } @@ -699,7 +704,7 @@ var Vis = cdb.core.View.extend({ var type = data.type; // We don't render certain overlays if we are in mobile - if (this.mobile_enabled && (type === "zoom" || type === "header" || type === "loader")) return; + if (this.isMobileEnabled && (type === "zoom" || type === "header" || type === "loader")) return; // IE<10 doesn't support the Fullscreen API if (type === 'fullscreen' && cdb.core.util.browser.ie && cdb.core.util.browser.ie.version <= 10) return; @@ -725,7 +730,7 @@ var Vis = cdb.core.View.extend({ var opt = data.options; - if (!this.mobile_enabled) { + if (!this.isMobileEnabled) { if (type == 'share' && options["shareable"] || type == 'share' && overlay.model.get("display") && options["shareable"] == undefined) overlay.show(); if (type == 'layer_selector' && options[type] || type == 'layer_selector' && overlay.model.get("display") && options[type] == undefined) overlay.show(); @@ -788,7 +793,7 @@ var Vis = cdb.core.View.extend({ var layers; var layer = data.layers[1]; - if (this.mobile_enabled) { + if (this.isMobileEnabled) { if (options && options.legends === undefined) { options.legends = this.legends ? true : false; @@ -947,10 +952,12 @@ var Vis = cdb.core.View.extend({ this.gmaps_style = opt.gmaps_style; } - this.mobile = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); - this.mobile_enabled = (opt.mobile_layout && this.mobile) || opt.force_mobile; + this.mobile = this.isMobileDevice(); + this.isMobileEnabled = (opt.mobile_layout && this.mobile) || opt.force_mobile; - if (opt.force_mobile === false || opt.force_mobile === "false") this.mobile_enabled = false; + if (opt.force_mobile === false || opt.force_mobile === "false") { + this.isMobileEnabled = false; + } if (!opt.title) { vizjson.title = null; @@ -972,7 +979,7 @@ var Vis = cdb.core.View.extend({ opt.search = opt.searchControl; } - if (!this.mobile_enabled && opt.search) { + if (!this.isMobileEnabled && opt.search) { if (!search_overlay('search')) { vizjson.overlays.push({ type: "search", @@ -1010,7 +1017,7 @@ var Vis = cdb.core.View.extend({ } } - if (opt.shareable && !this.mobile_enabled) { + if (opt.shareable && !this.isMobileEnabled) { if (!search_overlay('share')) { vizjson.overlays.push({ type: "share", @@ -1021,7 +1028,7 @@ var Vis = cdb.core.View.extend({ } // We remove certain overlays in mobile devices - if (this.mobile_enabled) { + if (this.isMobileEnabled) { remove_overlay('logo'); remove_overlay('share'); } @@ -1396,6 +1403,10 @@ var Vis = cdb.core.View.extend({ } }, 150); + }, + + isMobileDevice: function() { + return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } }, { diff --git a/test/spec/vis/vis.spec.js b/test/spec/vis/vis.spec.js index 0d83974602..6e7a0e6daf 100644 --- a/test/spec/vis/vis.spec.js +++ b/test/spec/vis/vis.spec.js @@ -19,6 +19,7 @@ describe("Overlay", function() { describe("Vis", function() { beforeEach(function(){ + this.container = $('
').css('height', '200px'); this.mapConfig = { updated_at: 'cachebuster', @@ -35,10 +36,11 @@ describe("Vis", function() { }; this.vis = new cdb.vis.Vis({el: this.container}); + spyOn(this.vis, 'isMobileDevice').and.returnValue(false); this.vis.load(this.mapConfig); }) - it("should insert default max and minZoom values when not provided", function() { + it("should insert default max and minZoom values when not provided", function() { expect(this.vis.mapView.map_leaflet.options.maxZoom).toEqual(20); expect(this.vis.mapView.map_leaflet.options.minZoom).toEqual(0); }); @@ -444,43 +446,55 @@ describe("Vis", function() { describe("dragging option", function() { - it("should be enabled with zoom overlay and scrollwheel enabled", function() { + beforeEach(function() { var container = $('
').css('height', '200px'); - var vis = new cdb.vis.Vis({el: container}); - - var mapConfig = { - updated_at: 'cachebuster', - title: "irrelevant", - url: "http://cartodb.com", - center: [40.044, -101.95], - bounding_box_sw: [20, -140], - bounding_box_ne: [ 55, -50], - zoom: 4, - bounds: [[1, 2],[3, 4]], - scrollwheel: true, - overlays: [ - { - type: "zoom", - order: 6, - options: { - x: 20, - y: 20, - display: true - }, - template: "" - } - ], - }; + this.vis = new cdb.vis.Vis({el: container}); + }); - vis.load(mapConfig); - expect(vis.map.get('drag')).toBeTruthy(); + it("should be enabled with zoom overlay and scrollwheel enabled", function() { + var mapConfig = mapConfigCustom(true, true); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); }); it("should be enabled with zoom overlay and scrollwheel disabled", function() { - var container = $('
').css('height', '200px'); - var vis = new cdb.vis.Vis({el: container}); + var mapConfig = mapConfigCustom(false, true); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); + }); + + it("should be enabled without zoom overlay and scrollwheel enabled", function() { + var mapConfig = mapConfigCustom(true, false); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); + }); + + it("should be disabled without zoom overlay and scrollwheel disabled", function() { + var mapConfig = mapConfigCustom(false, false); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeFalsy(); + }); + + describe('over mobile', function() { + beforeEach(function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(true); + }); + + it("should be enabled if scrollwheel is enabled (no matter zoom overlay) but device is mobile", function() { + var mapConfig = mapConfigCustom(true, false); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); + }); + + it("should be enabled if scrollwheel is disabled (no matter zoom overlay) but device is mobile", function() { + var mapConfig = mapConfigCustom(false, false); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); + }); + }); - var mapConfig = { + function mapConfigCustom(scrollwheel, zoom) { + var obj = { updated_at: 'cachebuster', title: "irrelevant", url: "http://cartodb.com", @@ -490,7 +504,13 @@ describe("Vis", function() { zoom: 4, bounds: [[1, 2],[3, 4]], scrollwheel: false, - overlays: [ + overlays: [] + }; + if (scrollwheel) { + obj.scrollwheel = true; + } + if (zoom) { + obj.overlays = [ { type: "zoom", order: 6, @@ -501,54 +521,10 @@ describe("Vis", function() { }, template: "" } - ], - }; - - vis.load(mapConfig); - expect(vis.map.get('drag')).toBeTruthy(); - }); - - it("should be enabled without zoom overlay and scrollwheel enabled", function() { - var container = $('
').css('height', '200px'); - var vis = new cdb.vis.Vis({el: container}); - - var mapConfig = { - updated_at: 'cachebuster', - title: "irrelevant", - url: "http://cartodb.com", - center: [40.044, -101.95], - bounding_box_sw: [20, -140], - bounding_box_ne: [ 55, -50], - zoom: 4, - bounds: [[1, 2],[3, 4]], - scrollwheel: true, - overlays: [], - }; - - vis.load(mapConfig); - expect(vis.map.get('drag')).toBeTruthy(); - }); - - it("should be disabled without zoom overlay and scrollwheel disabled", function() { - var container = $('
').css('height', '200px'); - var vis = new cdb.vis.Vis({el: container}); - - var mapConfig = { - updated_at: 'cachebuster', - title: "irrelevant", - url: "http://cartodb.com", - center: [40.044, -101.95], - bounding_box_sw: [20, -140], - bounding_box_ne: [ 55, -50], - zoom: 4, - bounds: [[1, 2],[3, 4]], - scrollwheel: false, - overlays: [], - }; - - vis.load(mapConfig); - expect(vis.map.get('drag')).toBeFalsy(); - }); + ]; + } + return obj; + } }); From 512193c160bfaebc75378d7954707afae9b99341 Mon Sep 17 00:00:00 2001 From: xavijam Date: Thu, 3 Dec 2015 13:26:44 +0100 Subject: [PATCH 101/120] Fixed package imagemin version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4fab1a6afe..6396f30cbc 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "grunt-contrib-csslint": "~0.2.0", "grunt-contrib-cssmin": "~0.7.0", "grunt-contrib-htmlmin": "~0.1.3", - "grunt-contrib-imagemin": "~0.9.4", + "grunt-contrib-imagemin": "~1.0.0", "grunt-contrib-jshint": "~0.8.0", "grunt-contrib-uglify": "~0.3.0", "grunt-replace": "0.6.2", From 43dce9ad2300cb64bb019e94be9a82040283d1e8 Mon Sep 17 00:00:00 2001 From: xavijam Date: Thu, 3 Dec 2015 14:15:03 +0100 Subject: [PATCH 102/120] Improving viz tests --- test/spec/vis/vis.spec.js | 44 +++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/test/spec/vis/vis.spec.js b/test/spec/vis/vis.spec.js index 6e7a0e6daf..45086ace94 100644 --- a/test/spec/vis/vis.spec.js +++ b/test/spec/vis/vis.spec.js @@ -452,25 +452,37 @@ describe("Vis", function() { }); it("should be enabled with zoom overlay and scrollwheel enabled", function() { - var mapConfig = mapConfigCustom(true, true); + var mapConfig = createMapConfig({ + scrollwheel: true, + zoom: true + }); this.vis.load(mapConfig); expect(this.vis.map.get('drag')).toBeTruthy(); }); it("should be enabled with zoom overlay and scrollwheel disabled", function() { - var mapConfig = mapConfigCustom(false, true); + var mapConfig = createMapConfig({ + scrollwheel: false, + zoom: true + }); this.vis.load(mapConfig); expect(this.vis.map.get('drag')).toBeTruthy(); }); it("should be enabled without zoom overlay and scrollwheel enabled", function() { - var mapConfig = mapConfigCustom(true, false); + var mapConfig = createMapConfig({ + scrollwheel: true, + zoom: false + }); this.vis.load(mapConfig); expect(this.vis.map.get('drag')).toBeTruthy(); }); it("should be disabled without zoom overlay and scrollwheel disabled", function() { - var mapConfig = mapConfigCustom(false, false); + var mapConfig = createMapConfig({ + scrollwheel: false, + zoom: false + }); this.vis.load(mapConfig); expect(this.vis.map.get('drag')).toBeFalsy(); }); @@ -481,20 +493,26 @@ describe("Vis", function() { }); it("should be enabled if scrollwheel is enabled (no matter zoom overlay) but device is mobile", function() { - var mapConfig = mapConfigCustom(true, false); + var mapConfig = createMapConfig({ + scrollwheel: true, + zoom: false + }); this.vis.load(mapConfig); expect(this.vis.map.get('drag')).toBeTruthy(); }); it("should be enabled if scrollwheel is disabled (no matter zoom overlay) but device is mobile", function() { - var mapConfig = mapConfigCustom(false, false); + var mapConfig = createMapConfig({ + scrollwheel: false, + zoom: false + }); this.vis.load(mapConfig); expect(this.vis.map.get('drag')).toBeTruthy(); }); }); - function mapConfigCustom(scrollwheel, zoom) { - var obj = { + function createMapConfig(obj) { + var r = { updated_at: 'cachebuster', title: "irrelevant", url: "http://cartodb.com", @@ -506,11 +524,11 @@ describe("Vis", function() { scrollwheel: false, overlays: [] }; - if (scrollwheel) { - obj.scrollwheel = true; + if (obj.scrollwheel) { + r.scrollwheel = true; } - if (zoom) { - obj.overlays = [ + if (obj.zoom) { + r.overlays = [ { type: "zoom", order: 6, @@ -523,7 +541,7 @@ describe("Vis", function() { } ]; } - return obj; + return r; } }); From 3531c0816e4e4953f17cbd5dda1a515847abeb19 Mon Sep 17 00:00:00 2001 From: xavijam Date: Thu, 3 Dec 2015 14:15:41 +0100 Subject: [PATCH 103/120] Changed way to check if map can drag or not --- src/vis/vis.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vis/vis.js b/src/vis/vis.js index 7b4375d464..e6ffb64bd9 100644 --- a/src/vis/vis.js +++ b/src/vis/vis.js @@ -344,10 +344,7 @@ var Vis = cdb.core.View.extend({ return overlay.type == "zoom" })); - var allowDragging = hasZoomOverlay || scrollwheel; - if (isMobileDevice) { - allowDragging = true; - } + var allowDragging = isMobileDevice || hasZoomOverlay || scrollwheel; // map data.maxZoom || (data.maxZoom = 20); From 3e73728e3388325f0ea5f563e6e803987a303512 Mon Sep 17 00:00:00 2001 From: xavijam Date: Thu, 3 Dec 2015 14:40:34 +0100 Subject: [PATCH 104/120] Adding to NEWS --- NEWS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 20b1953223..43343989fd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ 3.15.X (dd/mm/yyyy) ------ -* +* When scrollwheel and zoom are disabled, map panning is disabled unless device is mobile. 3.15.8 (01/10/2015) ------ @@ -8,11 +8,11 @@ 3.15.7 (23/09/2015) ------ -* Undefine `define` so that dependencies aren't loaded via AMD [#543](https://github.com/CartoDB/cartodb.js/issues/543) +* Undefined `define` so that dependencies aren't loaded via AMD [#543](https://github.com/CartoDB/cartodb.js/issues/543) 3.15.6 (17/09/2015) ------ -* Fixed a couple of bugs related with Leaflet attributions [#681](https://github.com/CartoDB/cartodb.js/issues/681) +* Fixed a couple of bugs related with Leaflet attributions [#681](https://github.com/CartoDB/cartodb.js/issues/681) 3.15.5 (15/09/2015) ------ From e959d6d4f1d74c38b47d87ec86ecf8c212d17163 Mon Sep 17 00:00:00 2001 From: xavijam Date: Thu, 3 Dec 2015 17:23:35 +0100 Subject: [PATCH 105/120] Changing specs under Vis class --- test/spec/vis/vis.spec.js | 63 +++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/test/spec/vis/vis.spec.js b/test/spec/vis/vis.spec.js index 45086ace94..1758df77e7 100644 --- a/test/spec/vis/vis.spec.js +++ b/test/spec/vis/vis.spec.js @@ -36,7 +36,6 @@ describe("Vis", function() { }; this.vis = new cdb.vis.Vis({el: this.container}); - spyOn(this.vis, 'isMobileDevice').and.returnValue(false); this.vis.load(this.mapConfig); }) @@ -451,7 +450,8 @@ describe("Vis", function() { this.vis = new cdb.vis.Vis({el: container}); }); - it("should be enabled with zoom overlay and scrollwheel enabled", function() { + it("should be enabled with zoom overlay, scrollwheel enabled and not under a mobile device", function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(false); var mapConfig = createMapConfig({ scrollwheel: true, zoom: true @@ -460,7 +460,8 @@ describe("Vis", function() { expect(this.vis.map.get('drag')).toBeTruthy(); }); - it("should be enabled with zoom overlay and scrollwheel disabled", function() { + it("should be enabled with zoom overlay, scrollwheel disabled and not under a mobile device", function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(false); var mapConfig = createMapConfig({ scrollwheel: false, zoom: true @@ -469,7 +470,8 @@ describe("Vis", function() { expect(this.vis.map.get('drag')).toBeTruthy(); }); - it("should be enabled without zoom overlay and scrollwheel enabled", function() { + it("should be enabled without zoom overlay, scrollwheel enabled and not under mobile device", function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(false); var mapConfig = createMapConfig({ scrollwheel: true, zoom: false @@ -478,7 +480,8 @@ describe("Vis", function() { expect(this.vis.map.get('drag')).toBeTruthy(); }); - it("should be disabled without zoom overlay and scrollwheel disabled", function() { + it("should be disabled without zoom overlay, scrollwheel disabled and not under mobile device", function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(false); var mapConfig = createMapConfig({ scrollwheel: false, zoom: false @@ -487,28 +490,44 @@ describe("Vis", function() { expect(this.vis.map.get('drag')).toBeFalsy(); }); - describe('over mobile', function() { - beforeEach(function() { - spyOn(this.vis, 'isMobileDevice').and.returnValue(true); + it("should be enabled without zoom, scrollwheel is enabled and under mobile device", function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(true); + var mapConfig = createMapConfig({ + scrollwheel: true, + zoom: false }); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); + }); - it("should be enabled if scrollwheel is enabled (no matter zoom overlay) but device is mobile", function() { - var mapConfig = createMapConfig({ - scrollwheel: true, - zoom: false - }); - this.vis.load(mapConfig); - expect(this.vis.map.get('drag')).toBeTruthy(); + it("should be enabled without zoom, scrollwheel disabled and under mobile device", function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(true); + var mapConfig = createMapConfig({ + scrollwheel: false, + zoom: false }); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); + }); - it("should be enabled if scrollwheel is disabled (no matter zoom overlay) but device is mobile", function() { - var mapConfig = createMapConfig({ - scrollwheel: false, - zoom: false - }); - this.vis.load(mapConfig); - expect(this.vis.map.get('drag')).toBeTruthy(); + it("should be enabled with zoom, scrollwheel disabled and under mobile device", function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(true); + var mapConfig = createMapConfig({ + scrollwheel: false, + zoom: true }); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); + }); + + it("should be enabled if all parameters are enabled or present", function() { + spyOn(this.vis, 'isMobileDevice').and.returnValue(true); + var mapConfig = createMapConfig({ + scrollwheel: true, + zoom: true + }); + this.vis.load(mapConfig); + expect(this.vis.map.get('drag')).toBeTruthy(); }); function createMapConfig(obj) { From 132ba8b867e825f835f9220fe37043bc24c57e1c Mon Sep 17 00:00:00 2001 From: xavijam Date: Mon, 7 Dec 2015 17:49:22 +0100 Subject: [PATCH 106/120] Fixing double click over google maps when drag is enabled --- src/geo/gmaps/gmaps.js | 2 +- test/spec/geo/gmaps/gmaps.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/geo/gmaps/gmaps.js b/src/geo/gmaps/gmaps.js index 2eb8127f21..841456f5b2 100644 --- a/src/geo/gmaps/gmaps.js +++ b/src/geo/gmaps/gmaps.js @@ -49,7 +49,7 @@ if(typeof(google) != "undefined" && typeof(google.maps) != "undefined") { scrollwheel: this.map.get("scrollwheel"), // Allow dragging (and double click zoom) draggable: this.map.get("drag"), - disableDoubleClickZoom: this.map.get("drag"), + disableDoubleClickZoom: !this.map.get("drag"), mapTypeControl:false, mapTypeId: google.maps.MapTypeId.ROADMAP, backgroundColor: 'white', diff --git a/test/spec/geo/gmaps/gmaps.spec.js b/test/spec/geo/gmaps/gmaps.spec.js index ca753081a3..8a632596e6 100644 --- a/test/spec/geo/gmaps/gmaps.spec.js +++ b/test/spec/geo/gmaps/gmaps.spec.js @@ -249,7 +249,7 @@ }); expect(mapView.map_googlemaps.get('draggable')).toBeFalsy(); - expect(mapView.map_googlemaps.get('disableDoubleClickZoom')).toBeFalsy(); + expect(mapView.map_googlemaps.get('disableDoubleClickZoom')).toBeTruthy(); }); }); From ab059bdcda834864f7f764b9d3a1b586ee8d0e88 Mon Sep 17 00:00:00 2001 From: acanimal Date: Wed, 9 Dec 2015 14:56:11 +0100 Subject: [PATCH 107/120] Added more sanitize cases --- test/spec/core/sanitize.spec.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/spec/core/sanitize.spec.js b/test/spec/core/sanitize.spec.js index 0589efa5c4..ba3d3d9f67 100644 --- a/test/spec/core/sanitize.spec.js +++ b/test/spec/core/sanitize.spec.js @@ -46,7 +46,11 @@ describe("core.core.sanitize", function() { var attacks = [ '", + '">', + '">' ]; it('should avoid `' + attacks[0] + '`', function() { @@ -57,6 +61,22 @@ describe("core.core.sanitize", function() { expect(cdb.core.sanitize.html(attacks[1])).toEqual(''); }); + it('should avoid `' + attacks[2] + '`', function() { + expect(cdb.core.sanitize.html(attacks[2])).toEqual(''); + }); + + it('should avoid `' + attacks[3] + '`', function() { + expect(cdb.core.sanitize.html(attacks[3])).toEqual(''); + }); + + it('should avoid `' + attacks[4] + '`', function() { + expect(cdb.core.sanitize.html(attacks[4])).toEqual('">'); + }); + + it('should avoid `' + attacks[5] + '`', function() { + expect(cdb.core.sanitize.html(attacks[5])).toEqual('">'); + }); + }); }); From 7fba9958bf65a008f76e023bae2295662d136698 Mon Sep 17 00:00:00 2001 From: csobier Date: Tue, 15 Dec 2015 11:10:59 -0500 Subject: [PATCH 108/120] trying to see if timeslider updates appear in expose torquejs WIP branch --- doc/api_methods.md | 114 +-------------------------------------------- 1 file changed, 2 insertions(+), 112 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index 57fd351bc3..02e0ed2344 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -28,7 +28,7 @@ options | |_ zoom | initial zoom. |_ cartodb_logo | default to true, set to false if you want to remove the cartodb logo. |_ infowindow | set to false if you want to disable the infowindow (enabled by default). -|_ time_slider | show time slider with torque layers (enabled by default). +|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example.

. For details about customizing the time slider, see the [Torque.js](/cartodb-platform/torque/torqueslidertimevalue/) documentation. |_ layer_selector | show layer selector (default: false). |_ legends | if it's true legends are shown in the map. |_ https | if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. @@ -146,7 +146,7 @@ options | |_ infowindow | set to false if you want to disable the infowindow (enabled by default). |_ tooltip | set to false if you want to disable the tooltip (enabled by default). |_ legends | if it's true legends are shown in the map. -|_ time_slider | show time slider with torque layers (enabled by default) +|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example.

. For details about customizing the time slider, see the [Torque.js](/cartodb-platform/torque/torqueslidertimevalue/) documentation. |_ layerIndex | when the visualization contains more than one layer this index allows you to select what layer is created. Take into account that `layerIndex == 0` is the base layer and that all the tiled layers (non animated ones) are merged into a single one. The default value for this option is 1 (usually tiled layers). |_ filter | a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. callback(_layer_) | if a function is specified, it will be invoked after the layer has been created. The layer will be passed as an argument. @@ -224,117 +224,8 @@ Used for most maps with tables that are set to public or public with link. } ``` -### Torque Layer Source Object (_type: 'torque'_) -Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not allow sublayers. -```javascript -{ - type: 'torque', // Required - order: 1, // Optional - options: { - query: "SQL statement", // Required if table_name is not given - table_name: "table_name", // Required if query is not given - user_name: "your_user_name", // Required - cartocss: "CartoCSS styles" // Required - } -} -``` - -#### Interaction Methods for a Torque Layer - -Used to create an animated torque layer with customized settings. - -```javascript -// initialize a torque layer that uses the CartoDB account details and SQL API to pull in data -var torqueLayer = new L.TorqueLayer({ - user : 'viz2', - table : 'ow', - cartocss: CARTOCSS -}); -``` - -`getValueForPos(x, y[, step])` | ---- | --- -Description | Allows to get the value for the coordinate (in map reference system) for a concrete step. If a step is not specified, the animation step is used. Use caution, as this method increases CPU usage. It returns the value from the raster data, not the rendered data. -Returns | An object, such as a { bbox:[], value: VALUE } if there is value for the pos, otherwise, it is null. - - -`getValueForBBox(xstart, ystart, xend, yend)` | ---- | --- -Description | Returns an accumulated numerical value from all the torque areas, within the specified bounds. -Returns | A number. - - -`getActivePointsBBox(step)` | ---- | --- -Description | Returns the list of bounding boxes active for `step`. -Returns | List of bbox:[]. - - -`getValues(step)` | ---- | --- -Description | Returns the list of values for the pixels active in `step`. -Returns | List of values. - - -`invalidate()` | ---- | --- -Description | Forces a reload of the layer data. - - -#### Example of Interaction Methods for a Torque Layer - -```javascript - - - + + + + + + + + + + + From 9a175634160c6ce81fb5eb6ea83d4f3240507324 Mon Sep 17 00:00:00 2001 From: Jorge Sanz Date: Wed, 30 Dec 2015 00:04:50 +0100 Subject: [PATCH 112/120] Undo sorry for this mess, undoing :( --- ...gmaps_driving_directions_plus_sql_api.html | 134 +++++++----------- 1 file changed, 49 insertions(+), 85 deletions(-) diff --git a/examples/gmaps/gmaps_driving_directions_plus_sql_api.html b/examples/gmaps/gmaps_driving_directions_plus_sql_api.html index fc67b6ba0f..dff7cad3ba 100644 --- a/examples/gmaps/gmaps_driving_directions_plus_sql_api.html +++ b/examples/gmaps/gmaps_driving_directions_plus_sql_api.html @@ -1,11 +1,10 @@ - Named Maps Tutorial | CartoDB + Driving directions to clicked point | CartoDB.js - - +
+ - - - - - - - - - + + - + var request = { + origin : school, + destination : exploratorium, + travelMode : google.maps.TravelMode.DRIVING + }; + directionsService.route(request, function(response, status) { + if (status == google.maps.DirectionsStatus.OK) { + directionsDisplay.setDirections(response); + } + }); + }); + }); + } + window.onload = main; + From 1c2fc4dcdf5d2f839bdfe47fa291666085e570b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Wed, 30 Dec 2015 15:16:09 +0100 Subject: [PATCH 113/120] split layer source object --- doc/api_methods.md | 179 +------------------------------------ doc/layer_source_object.md | 177 ++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 178 deletions(-) create mode 100644 doc/layer_source_object.md diff --git a/doc/api_methods.md b/doc/api_methods.md index ada5e46004..e875dfa303 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -200,185 +200,8 @@ cartodb.createLayer(map, 'http://documentation.cartodb.com/api/v2/viz/2b13c956-e }); ``` -Layer metadata must take one of the following forms: +Layer metadata must take one of the forms of the [Layer Source Object](http://docs.cartodb.com/cartodb-platform/cartodb-js/layer-source-object/). -## Layer Source Object - -### Standard Layer Source Object (_type: 'cartodb'_) - -Used for most maps with tables that are set to public or public with link. - -#### Example - -```javascript -{ - type: 'torque', // Required - order: 1, // Optional - options: { - query: "SQL statement", // Required if table_name is not given - table_name: "table_name", // Required if query is not given - user_name: "your_user_name", // Required - cartocss: "CartoCSS styles" // Required - } -} -``` - -### Torque Layer Source Object (_type: 'torque'_) - -Used for [Torque maps](https://github.com/CartoDB/torque). Note that it does not allow sublayers. - -#### Example - -```javascript -// initialize a torque layer that uses the CartoDB account details and SQL API to pull in data -var torqueLayer = new L.TorqueLayer({ - user : 'viz2', - table : 'ow', - cartocss: CARTOCSS -}); -``` - -### Interaction Methods for a Torque Layer - -Used to create an animated torque layer with customized settings. - -#### getValueForPos(_x, y[, step]_) - ---- | --- -Description | Allows to get the value for the coordinate (in map reference system) for a concrete step. If a step is not specified, the animation step is used. Use caution, as this method increases CPU usage. It returns the value from the raster data, not the rendered data. -Returns | An object, such as a { bbox:[], value: VALUE } if there is value for the pos, otherwise, it is null. - -#### getValueForBBox(_xstart, ystart, xend, yend_) - ---- | --- -Description | Returns an accumulated numerical value from all the torque areas, within the specified bounds. -Returns | A number. - -#### getActivePointsBBox(_step_) - ---- | --- -Description | Returns the list of bounding boxes active for `step`. -Returns | List of bbox:[]. - -#### getValues(_step_) - ---- | --- -Description | Returns the list of values for the pixels active in `step`. -Returns | List of values. - -#### invalidate() - ---- | --- -Description | Forces a reload of the layer data. - -#### Example - -```javascript - + + + + From b8f008027295f764996eca5988bc49cd98e62875 Mon Sep 17 00:00:00 2001 From: csobier Date: Thu, 7 Jan 2016 14:38:23 -0500 Subject: [PATCH 116/120] changed type from torque to cartodb for layer source object --- doc/layer_source_object.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/layer_source_object.md b/doc/layer_source_object.md index 266237e35e..739b81ad70 100644 --- a/doc/layer_source_object.md +++ b/doc/layer_source_object.md @@ -8,7 +8,7 @@ Used for most maps with tables that are set to public or public with link. ```javascript { - type: 'torque', // Required + type: 'cartodb', // Required order: 1, // Optional options: { query: "SQL statement", // Required if table_name is not given From d6c2230ce83337fc4d6cc9e74953cfa3c3282cec Mon Sep 17 00:00:00 2001 From: csobier Date: Fri, 8 Jan 2016 15:25:54 -0500 Subject: [PATCH 117/120] added link to Releases folder instead of specific version number --- doc/api_methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index e875dfa303..c4e2a9a6e9 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -1,6 +1,6 @@ # API methods -The documentation below refers to CartoDB.js v3. For major changes in the library we will update the documentation here. This documentation is meant to help developers find specific methods from the CartoDB.js library. +This documentation is intended for developers and describes specific methods from the [latest version](https://github.com/CartoDB/cartodb.js/releases) of the CartoDB.js library. ## cartodb.createVis From 155dfe490c101e0e5f782658bd3ce5c866d55947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 19 Jan 2016 11:38:39 +0100 Subject: [PATCH 118/120] missing torqueslidertimevalue --- doc/api_methods.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index 281e721b73..6af3b43617 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -28,7 +28,7 @@ options | |_ zoom | initial zoom. |_ cartodb_logo | default to true, set to false if you want to remove the cartodb logo. |_ infowindow | set to false if you want to disable the infowindow (enabled by default). -|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example.

For details about customizing the time slider, see the [Torque.js](/cartodb-platform/torque/torqueslidertimevalue/) documentation. +|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example. |_ layer_selector | show layer selector (default: false). |_ legends | if it's true legends are shown in the map. |_ https | if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. @@ -146,7 +146,7 @@ options | |_ infowindow | set to false if you want to disable the infowindow (enabled by default). |_ tooltip | set to false if you want to disable the tooltip (enabled by default). |_ legends | if it's true legends are shown in the map. -|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example.

For details about customizing the time slider, see the [Torque.js](/cartodb-platform/torque/torqueslidertimevalue/) documentation. +|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example. |_ layerIndex | when the visualization contains more than one layer this index allows you to select what layer is created. Take into account that `layerIndex == 0` is the base layer and that all the tiled layers (non animated ones) are merged into a single one. The default value for this option is 1 (usually tiled layers). |_ filter | a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. |_ no_cdn | true to disable CDN when fetching tiles From 4d5dc34f4b050c583245c2a6dee864cbc1077164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Matall=C3=ADn?= Date: Tue, 19 Jan 2016 12:17:39 +0100 Subject: [PATCH 119/120] Update api_methods.md revert https://github.com/CartoDB/cartodb.js/commit/155dfe490c101e0e5f782658bd3ce5c866d55947 --- doc/api_methods.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api_methods.md b/doc/api_methods.md index 6af3b43617..1a2d2586fd 100644 --- a/doc/api_methods.md +++ b/doc/api_methods.md @@ -28,7 +28,7 @@ options | |_ zoom | initial zoom. |_ cartodb_logo | default to true, set to false if you want to remove the cartodb logo. |_ infowindow | set to false if you want to disable the infowindow (enabled by default). -|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example. +|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example.

For details about customizing the time slider, see the [Torque.js](http://docs.cartodb.com/cartodb-platform/torque/torque-time-slider/) documentation. |_ layer_selector | show layer selector (default: false). |_ legends | if it's true legends are shown in the map. |_ https | if true, it makes sure that basemaps are converted to https when possible. If explicitly false, converts https maps to http when possible. If undefined, the basemap template is left as declared at `urlTemplate` in the viz.json. @@ -146,7 +146,7 @@ options | |_ infowindow | set to false if you want to disable the infowindow (enabled by default). |_ tooltip | set to false if you want to disable the tooltip (enabled by default). |_ legends | if it's true legends are shown in the map. -|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example. +|_ time_slider | show an animated time slider with Torque layers. This option is enabled by default, as shown with `time_slider: true` value. To disable the time slider, use `time_slider: false`. See [No Torque Time Slider - Example Code](http://bl.ocks.org/michellechandra/081ca7160a8c782266d2) for an example.

For details about customizing the time slider, see the [Torque.js](http://docs.cartodb.com/cartodb-platform/torque/torque-time-slider/) documentation. |_ layerIndex | when the visualization contains more than one layer this index allows you to select what layer is created. Take into account that `layerIndex == 0` is the base layer and that all the tiled layers (non animated ones) are merged into a single one. The default value for this option is 1 (usually tiled layers). |_ filter | a string or array of strings to specify the type(s) of sublayers that will be rendered (eg: `['http', 'mapnik']`). All non-torque layers (http and mapnik) will be rendered if this option is not present. |_ no_cdn | true to disable CDN when fetching tiles From a722872a6d774f6b357c4199572b7ad9f2bc88ac Mon Sep 17 00:00:00 2001 From: xavijam Date: Mon, 1 Feb 2016 11:59:53 +0100 Subject: [PATCH 120/120] Files changed for version 3.15.9 --- NEWS.md | 2 +- RELEASING.md | 12 ++++++------ bower.json | 2 +- examples/tutorial-google-driving-2.html | 2 +- examples/tutorial-google-driving.html | 4 ++-- package.json | 2 +- src/cartodb.js | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/NEWS.md b/NEWS.md index 43343989fd..5c30874463 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -3.15.X (dd/mm/yyyy) +3.15.9 (01/02/2016) ------ * When scrollwheel and zoom are disabled, map panning is disabled unless device is mobile. diff --git a/RELEASING.md b/RELEASING.md index a9972fd849..6030adf0aa 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -13,7 +13,7 @@ - Create a new branch to prepare the release: ``` -git flow release start 3.15.8 +git flow release start 3.15.9 ``` - Build CartoDB.js files, choosing the new version: @@ -25,7 +25,7 @@ grunt release - Update the NEWS file and commit the changes. Take into account that new CartoDB.js version will be replaced in ```API.md```, ```RELEASING.md```, ```README.md```, ```package.json```, ```cartodb.js``` and ```examples``` files. ``` -git commit -am "Files changed for version 3.15.8" +git commit -am "Files changed for version 3.15.9" ``` - Release it. @@ -36,8 +36,8 @@ grunt publish - Check if those files have been updated in the CDN: ``` -http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.8/cartodb.js -http://libs.cartocdn.com/cartodb.js/v3/3.15.8/cartodb.js +http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15.9/cartodb.js +http://libs.cartocdn.com/cartodb.js/v3/3.15.9/cartodb.js http://libs.cartocdn.com.s3.amazonaws.com/cartodb.js/v3/3.15/cartodb.js http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js ``` @@ -46,7 +46,7 @@ http://libs.cartocdn.com/cartodb.js/v3/3.15/cartodb.js - And to finish: close the release and push it. ``` -git flow release finish 3.15.8 +git flow release finish 3.15.9 git push --all git push --tags ``` @@ -75,7 +75,7 @@ grunt grunt publish ``` -For example, if we are in 3.15.8 and we want to go back to 3.13.4 +For example, if we are in 3.15.9 and we want to go back to 3.13.4 ``` git checkout 3.13.4 diff --git a/bower.json b/bower.json index c9f8add3c4..b4d3b14905 100644 --- a/bower.json +++ b/bower.json @@ -4,7 +4,7 @@ "cartodb.js", "themes/css/cartodb.css" ], - "version": "3.15.8", + "version": "3.15.9", "homepage": "https://github.com/CartoDB/cartodb.js", "authors": [ "CartoDB " diff --git a/examples/tutorial-google-driving-2.html b/examples/tutorial-google-driving-2.html index 3c6a196205..a70bf0295b 100644 --- a/examples/tutorial-google-driving-2.html +++ b/examples/tutorial-google-driving-2.html @@ -72,4 +72,4 @@ window.onload = main; - \ No newline at end of file + diff --git a/examples/tutorial-google-driving.html b/examples/tutorial-google-driving.html index e8e6db43ef..1f5fffb593 100644 --- a/examples/tutorial-google-driving.html +++ b/examples/tutorial-google-driving.html @@ -19,7 +19,7 @@
- + @@ -90,4 +90,4 @@ - \ No newline at end of file + diff --git a/package.json b/package.json index 6396f30cbc..39f8022e2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cartodb.js", - "version": "3.15.8", + "version": "3.15.9", "description": "CartoDB javascript library", "repository": { "type": "git", diff --git a/src/cartodb.js b/src/cartodb.js index a818311678..e69800f8b1 100644 --- a/src/cartodb.js +++ b/src/cartodb.js @@ -5,7 +5,7 @@ var cdb = root.cdb = {}; - cdb.VERSION = "3.15.8"; + cdb.VERSION = "3.15.9"; cdb.DEBUG = false; cdb.CARTOCSS_VERSIONS = {