Compare commits

...

101 Commits

Author SHA1 Message Date
Daniel García Aubert
85881d99dd Release 0.15.1-cdb5 2018-11-20 17:47:20 +01:00
Daniel G. Aubert
d3a5c97143
Merge pull request #50 from CartoDB/nodejs-10
Drop support for oldest Node.js versions and support latest LTS releases
2018-11-20 17:45:16 +01:00
Daniel García Aubert
8ed3ad15c1 Add CHANGELOG.carto.md 2018-11-20 17:36:46 +01:00
Daniel García Aubert
72f79efbdd Prepare next release version 2018-11-20 17:36:30 +01:00
Daniel García Aubert
161516b721 Support package-lock.json 2018-10-29 12:47:17 +01:00
Daniel García Aubert
e48d5ff993 Drop support for oldest Node.js versions and support latest LTS releases 2018-10-29 12:37:47 +01:00
IagoLast
31abb8bee0 0.15.1 2018-05-10 16:07:00 +02:00
IagoLast
e2177cf443 Add release scripts 2018-05-10 16:06:56 +02:00
Carlos Matallín
664b45cdde
Merge pull request #47 from CartoDB/shield_placement_type
adds shield-placement-type to torque-reference
2018-02-23 11:05:33 +01:00
Alegoiko
368dad0d11 adds shield-placement-type to torque-reference 2018-02-20 12:01:45 +01:00
David M
4dfe5361b3 Merge pull request #45 from CartoDB/ampersand-fix
Fix for ampersand on values (JS only)
2017-10-20 15:08:27 +02:00
David Manzanares
3596991362 Fix for ampersand on values (JS only) 2017-10-20 12:45:03 +02:00
David M
bf4c67034a Merge pull request #44 from CartoDB/js-ampersand-fix
Fix ampersand escaping on JS values
2017-10-10 18:20:57 +02:00
David Manzanares
ad5d919139 Use regular expression to replace all occurrences 2017-10-10 18:12:01 +02:00
David Manzanares
9fb894181d Fix replacement 2017-10-10 18:10:40 +02:00
David Manzanares
ffdf2d3c9b Fix ampersand escaping on JS values 2017-10-10 18:00:47 +02:00
David M
cbe66020f9 Merge pull request #42 from CartoDB/dasharrayJSsupport
Cover 'numbers' type, like in line-dasharray
2017-09-01 17:33:15 +02:00
David Manzanares
056081ace6 Cover 'numbers' type, like in line-dasharray 2017-09-01 13:30:19 +02:00
Pablo Alonso
a48ba58cae Merge pull request #41 from CartoDB/carto-40
Ignore zoom for the filtered field when the value is the default one.
2017-08-23 13:26:00 +02:00
Pablo Alonso Garcia
9f67f1bdde Dist files 2017-08-23 13:25:35 +02:00
IagoLast
55c0786130 Use tree.Zoom.all instead custom constant 2017-08-21 17:40:04 +02:00
Pablo Alonso Garcia
fef8cb369f Minor copy changes 2017-08-21 17:35:08 +02:00
IagoLast
d35e54859a Ignore zoom for the filtered field when the value is the default one. 2017-08-21 16:33:05 +02:00
IagoLast
daafa9fbf2 Merge pull request #39 from CartoDB/expose-filtered-field
Expose filtered field
2017-08-18 16:46:13 +02:00
IagoLast
34799db0cc Boy scout rule 2017-08-18 15:59:04 +02:00
IagoLast
41133a65ad Make test compatible with an old node version 2017-08-18 13:21:22 +02:00
IagoLast
1016f6870c Refactor filtered field code 2017-08-18 13:10:15 +02:00
IagoLast
72f93abf16 Expose filtered field 2017-08-18 12:58:01 +02:00
IagoLast
a3538d9007 Add filtered field tests 2017-08-18 12:57:45 +02:00
IagoLast
66cc146a02 Merge pull request #38 from CartoDB/use-hexa-instead-octal
Use hexadecimal values instead octal
2017-08-01 11:04:01 +02:00
IagoLast
aab27e6be8 Use hexadecimal values instead octal
Strict mode in ECMAScript 5 forbids octal syntax. This octal literals
are breaking the webpack compilation in upper packages like tangram-cartocss.

Mapbox have already [fixed this](https://github.com/mapbox/carto/blob/master/lib/carto/index.js#L104)

This PR changes the octal literals and uses hexadecimal ones.
2017-07-31 18:02:13 +02:00
IagoLast
cea1e7c620 Merge pull request #37 from CartoDB/36-remove-console-log
Remove console.log from tests
2017-07-31 17:58:14 +02:00
IagoLast
f8a9995050 Add a tdd option into the package.json scripts
This option includes a env variable to control the verbosity of the test logs.
2017-07-28 12:01:37 +02:00
IagoLast
0de8b82ff9 Remove console.log from tests 2017-07-26 13:08:05 +02:00
Francisco López
7d00bfdf23 Merge pull request #35 from CartoDB/v-release
Bump version
2017-06-15 15:30:33 +02:00
Francisco López
3db2fa322b Bump version 2017-06-15 14:54:29 +02:00
Francisco López
f4a2605a23 Release dist files 2017-05-19 13:57:16 +02:00
Francisco López
d95967247d Merge branch 'master' of github.com:CartoDB/carto 2017-05-19 13:56:36 +02:00
Francisco López
da8707a17f Release changes! 2017-05-19 13:56:26 +02:00
Francisco López
3f9f6ef40d Merge pull request #27 from CartoDB/reference-errors
[WIP] Reference errors (attempt 2)
2017-05-19 13:55:39 +02:00
Francisco López
b051ae284e Remove throw check 2017-05-19 13:51:27 +02:00
Francisco López
ea880ccf7b Check throw 2017-05-19 12:24:50 +02:00
Francisco López
ab412165b2 Set correct indentation 2017-05-19 12:21:00 +02:00
Francisco López
7974fb0a05 Remove editor.test.js 2017-05-19 12:20:22 +02:00
Francisco López
59c0208caa Fix tests.
Now we don't run the parser so there is no errors, we can't look for errors.
2017-04-17 16:00:07 +02:00
Francisco López
9b5fb6a408 Make the error flag more strict 2017-04-17 15:48:55 +02:00
Daniel García Aubert
945f5efb74 Release 0.15.1-cdb3 2017-03-07 11:10:29 +01:00
Daniel
decdcc5d46 Merge pull request #30 from CartoDB/support-multiple-values
Support multiple values/operands
2017-03-07 11:03:23 +01:00
Daniel García Aubert
34bd0a045d Merge branch 'master' into support-multiple-values 2017-03-07 10:46:59 +01:00
Daniel
ed819c3b51 Merge pull request #29 from CartoDB/24-reference-error
Support `=~` operator
2017-03-07 10:43:04 +01:00
Daniel García Aubert
a42afef5a8 Add assertions 2017-03-06 19:20:01 +01:00
Daniel García Aubert
410ecfd3c7 Use constructor for test validation 2017-03-06 12:04:29 +01:00
Daniel García Aubert
8c75b4b0c6 Remove spaces 2017-03-06 11:59:41 +01:00
Daniel García Aubert
fd94fbd2e6 Support numbers for '=~' operator 2017-03-06 11:57:07 +01:00
Daniel García Aubert
5c4bed9593 Keep spaces 2017-03-06 11:38:59 +01:00
Raul Ochoa
2c092f6b39 Support multiple values/operands 2017-03-05 23:31:32 +01:00
Daniel García Aubert
cc2104eb49 Fix typo 2017-03-03 15:35:57 +01:00
Daniel García Aubert
c780998dc8 Prevent TypeError when parsing '=~' operator 2017-03-03 15:27:42 +01:00
Daniel García Aubert
0063ddba7f Support '=~' operator 2017-03-03 14:36:14 +01:00
Francisco López
510847a3b2 Make the validation in the correct place 2017-02-03 16:49:30 +01:00
Raul Ochoa
0c1990f655 Merge branch 'master' into reference-errors 2017-02-01 12:25:57 +01:00
Raul Ochoa
7315428079 Adds test to validate supported features are validated and work 2017-02-01 12:15:46 +01:00
Raul Ochoa
515fbd0991 Only set previous reference if it existed 2017-02-01 11:23:39 +01:00
Raul Ochoa
975abe9b5c Set tree data just before render happens 2017-02-01 11:16:03 +01:00
Raul Ochoa
b6186d884c Merge pull request #26 from CartoDB/revert-21-reference-errors
Revert "Throw an error when not valid ccss"
2017-02-01 11:12:22 +01:00
Raul Ochoa
e6ba32bc07 Revert "Throw an error when not valid ccss" 2017-02-01 10:44:42 +01:00
Raul Ochoa
fd4caf7595 Merge pull request #21 from CartoDB/reference-errors
Throw an error when not valid ccss
2017-01-31 15:36:38 +01:00
Raul Ochoa
45e59f6a5b Revert changes not needed for PR 2017-01-31 15:23:18 +01:00
Raul Ochoa
dfecbbb976 Revert changes in rendering_js test 2017-01-31 15:22:13 +01:00
Raul Ochoa
50fa97564e Strict parser mode.
When options.strict is set to true, it throws parse errors associated to invalid rules,
or in general rules not compliant with the given reference.
2017-01-31 15:19:47 +01:00
Raul Ochoa
8f9982c313 Fix reference to make test pass due to valid error message 2017-01-31 14:50:56 +01:00
Raul Ochoa
01c6f0c6e5 Validate the expected error message, make test to fail 2017-01-31 14:50:09 +01:00
Raul Ochoa
7e02aac641 Reference options from a valid object 2017-01-31 11:31:11 +01:00
María Checa
4f792fbead Merge pull request #25 from CartoDB/allow_expression_in_polygon_pattern_file
Added expression property to `polygon-pattern-file` and `line-pattern-file`
2017-01-19 18:18:05 +01:00
María Checa
9e12a3b0d8 Added expression property to line-pattern-file 2017-01-19 16:44:49 +01:00
María Checa
09d6384b1f Added expression property to polygon-pattern-file 2017-01-19 16:39:10 +01:00
Raul Ochoa
11ffba0a8e Isolate reference as options test 2017-01-17 17:34:28 +01:00
Francisco López
199d41f20d Throw an error when not valid ccss 2017-01-11 11:39:33 +01:00
Raul Ochoa
e931f91475 Merge pull request #20 from CartoDB/more-expressions
More expressions
2017-01-04 17:32:21 +01:00
Raul Ochoa
11d597e733 Accept expressions in marker file 2017-01-03 19:03:39 +01:00
Raul Ochoa
1960aca276 Accept expresions in polygon fill 2017-01-03 19:03:10 +01:00
Raul Ochoa
f17aea8657 Merge pull request #18 from CartoDB/turbo-carto-fn-support
Adds support for other turbo-carto functions
2016-07-20 14:32:34 +02:00
Raul Ochoa
dfaed546e0 Remove only from test 2016-07-20 13:19:00 +02:00
Raul Ochoa
803f0c0a49 Adds support for other turbo-carto functions 2016-07-20 13:17:10 +02:00
Raul Ochoa
0d6f9d4634 Ignore idea based configurations 2016-07-20 13:08:41 +02:00
javi santana
c042733845 Merge pull request #17 from CartoDB/update_reference
adds expression: true for attributes supported by turbo
2016-06-01 14:37:37 +02:00
javi
1fc486b1b9 adds expression: true for attributes supported by turbo 2016-06-01 14:31:14 +02:00
Buti
b50ee48386 Merge pull request #16 from CartoDB/turbo
adds parser support for arrays and also updates reference to add "ram…
2016-05-31 15:12:06 +02:00
javi
cf5886579f adds parser support for arrays and also updates reference to add "ramp" function 2016-05-31 12:40:31 +02:00
Buti
72d005a082 Merge pull request #15 from CartoDB/fix-parser-underscore-template
Fix parser's use of underscore's template
2016-05-30 16:45:18 +02:00
nobuti
9e8c90b6f9 Bumped version of underscore.
Updated some method calls.
Dist generation files.
2016-05-30 16:06:04 +02:00
nobuti
d3e23dcb5d Fix parser's use of underscore's template method to work with modern use of it. 2016-05-30 14:05:42 +02:00
Raul Ochoa
176886f1ad Use build status from cartodb repo 2016-04-27 15:37:35 +02:00
Raul Ochoa
673cf38121 Add test for non-dot notation filters 2016-04-27 15:23:01 +02:00
Raul Ochoa
860bc0adeb Merge pull request #14 from CartoDB/filter-avoid-dot-notation
Avoid using dot notation to allow complex keys
2016-04-27 15:12:51 +02:00
Raul Ochoa
be56e24d9a Avoid using dot notation to allow complex keys, e.g., mapnik::geometry_type 2016-04-26 18:19:53 +02:00
javi santana
27850ed122 Merge pull request #12 from CartoDB/fixed_strings_with_quote
fixed cartocss with single quoted strings
2016-02-24 12:28:45 +01:00
javi
0d2dddf978 fixed cartocss with single quoted strings 2016-02-24 12:16:29 +01:00
Raul Ochoa
fba91a0633 Merge pull request #11 from CartoDB/missed_small_changes
Missed changes
2015-12-14 17:44:59 +01:00
Young Hahn
1612b5a8b7 Drop xml2js and thus mml2json.
(cherry picked from commit bde0d0e2ab)
2015-11-30 16:14:53 -08:00
Paul Norman
4f13aabb6c Update less link
Update Less page link to match the current layout
(cherry picked from commit 73e6726e089ff47fae5115183aa3776918946ab7)
2015-11-30 13:56:53 -08:00
23 changed files with 3906 additions and 894 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
test/rendering/layers/
test/rendering/cache/
test/rendering-mss/npm-debug.log
.idea/

View File

@ -1,7 +1,9 @@
language: node_js
node_js:
- "0.11"
- "0.10"
- '6'
- '8'
- '10'
script:
- npm test

9
CHANGELOG.carto.md Normal file
View File

@ -0,0 +1,9 @@
## CARTO's Changelog
## 0.15.1-cdb5
2018-11-20
* Support Node.js 6, 8 and, 10
* Drop support for Node.js 0.10 and 0.11
* Add package-lock.json
* Add CHANGELOG.carto.md

View File

@ -1,6 +1,6 @@
# CartoCSS
[![Build Status](https://secure.travis-ci.org/mapbox/carto.png)](http://travis-ci.org/mapbox/carto)
[![Build Status](https://travis-ci.org/CartoDB/carto.png?branch=master)](https://travis-ci.org/CartoDB/carto)
Is as stylesheet renderer for javascript, It's an evolution of the Mapnik renderer from Mapbox.
Please, see original [Mapbox repo](http://github.com/mapbox/carto) for more information and credits

View File

@ -1,69 +0,0 @@
#!/usr/bin/env node
var xml2js = require('xml2js'),
fs = require('fs');
if (!process.argv[2]) {
console.log('Please specify a XML file.');
process.exit(1);
}
fs.readFile(process.argv[2], 'utf-8', function(err, data) {
if (err) throw err;
// Replace entities.
var entities = {};
var match = data.match(/<!ENTITY([^>]|"([^"]|\\")*")+>/g)
if (match != null) {
match.forEach(function(entity) {
var parts = entity.match(/^<!ENTITY\s+(\w+)\s+"(.+)">$/);
entities['&' + parts[1] + ';'] = parts[2];
});
}
data = data.replace(/&\w+;/g, function(entity) {
return entities[entity];
});
function addAttributes(obj) {
if (obj['$']) for (var key in obj['$']) obj[key] = obj['$'][key];
delete obj['$'];
return obj;
}
function simplifyExternal(obj) {
if (obj.src) return obj.src;
else return obj;
}
var parser = new xml2js.Parser({
explicitRoot: false,
explicitArray: false
});
parser.addListener('end', function(json) {
console.log(JSON.stringify(json, function(key, value) {
if (!key) {
return addAttributes(value);
}
else if (key === 'Stylesheet') {
if (Array.isArray(value)) return value.map(addAttributes).map(simplifyExternal);
else return [ simplifyExternal(addAttributes(value)) ];
}
else if (key === 'Layer' || key === 'Stylesheet') {
if (Array.isArray(value)) return value.map(addAttributes);
else return [ addAttributes(value) ];
}
else if (key === 'Datasource') {
value = addAttributes(value);
value.Parameter.forEach(function(parameter) {
value[parameter['$'].name] = parameter['_'];
});
delete value.Parameter;
return value;
}
else {
return value;
}
}, 4));
});
parser.parseString(data);
});

8
dist/carto.js vendored

File diff suppressed because one or more lines are too long

View File

@ -268,7 +268,7 @@ var carto = {
if (typeof(extract[2]) === 'string') {
error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
}
error = options.indent + error.join('\n' + options.indent) + '\033[0m\n';
error = options.indent + error.join('\n' + options.indent) + '\x1B[0m\n';
message = options.indent + message + stylize(ctx.message, 'red');
if (ctx.filename) (message += stylize(' in ', 'red') + ctx.filename);
@ -326,12 +326,12 @@ function stylize(str, style) {
'red' : [31, 39],
'grey' : [90, 39]
};
return '\033[' + styles[style][0] + 'm' + str +
'\033[' + styles[style][1] + 'm';
return '\x1B[' + styles[style][0] + 'm' + str +
'\x1B[' + styles[style][1] + 'm';
}
}).call(this,require('_process'),"/lib/carto")
},{"../../package.json":44,"./functions":1,"./parser":3,"./renderer":4,"./renderer_js":5,"./torque-reference":6,"./tree":7,"./tree/call":8,"./tree/color":9,"./tree/comment":10,"./tree/definition":11,"./tree/dimension":12,"./tree/element":13,"./tree/expression":14,"./tree/field":15,"./tree/filter":16,"./tree/filterset":17,"./tree/fontset":18,"./tree/frame_offset":19,"./tree/imagefilter":20,"./tree/invalid":21,"./tree/keyword":22,"./tree/layer":23,"./tree/literal":24,"./tree/operation":25,"./tree/quoted":26,"./tree/reference":27,"./tree/rule":28,"./tree/ruleset":29,"./tree/selector":30,"./tree/style":31,"./tree/url":32,"./tree/value":33,"./tree/variable":34,"./tree/zoom":35,"_process":40,"fs":36,"path":39,"util":42}],3:[function(require,module,exports){
},{"../../package.json":44,"./functions":1,"./parser":3,"./renderer":4,"./renderer_js":5,"./torque-reference":6,"./tree":7,"./tree/call":8,"./tree/color":9,"./tree/comment":10,"./tree/definition":11,"./tree/dimension":12,"./tree/element":13,"./tree/expression":14,"./tree/field":15,"./tree/filter":16,"./tree/filterset":17,"./tree/fontset":18,"./tree/frame_offset":19,"./tree/imagefilter":20,"./tree/invalid":21,"./tree/keyword":22,"./tree/layer":23,"./tree/literal":24,"./tree/operation":25,"./tree/quoted":26,"./tree/reference":27,"./tree/rule":28,"./tree/ruleset":29,"./tree/selector":30,"./tree/style":31,"./tree/url":32,"./tree/value":33,"./tree/variable":34,"./tree/zoom":35,"_process":40,"fs":37,"path":39,"util":43}],3:[function(require,module,exports){
(function (global){
var carto = exports,
tree = require('./tree'),
@ -449,8 +449,9 @@ carto.Parser = function Parser(env) {
// - `index`: Char. index where the error occurred.
function makeError(err) {
var einput;
var errorTemplate;
_(err).defaults({
_.defaults(err, {
index: furthest,
filename: env.filename,
message: 'Parse error.',
@ -468,8 +469,8 @@ carto.Parser = function Parser(env) {
for (var n = err.index; n >= 0 && einput.charAt(n) !== '\n'; n--) {
err.column++;
}
return new Error(_('<%=filename%>:<%=line%>:<%=column%> <%=message%>').template(err));
errorTemplate = _.template('<%=filename%>:<%=line%>:<%=column%> <%=message%>');
return new Error(errorTemplate(err));
}
this.env = env = env || {};
@ -811,6 +812,7 @@ carto.Parser = function Parser(env) {
return new tree.Dimension(value[1], value[2], memo);
}
}
},
// The variable part of a variable definition.
@ -1059,10 +1061,22 @@ carto.Parser = function Parser(env) {
},
// A sub-expression, contained by parenthensis
sub: function() {
var e;
var e, expressions = [];
if ($('(') && (e = $(this.expression)) && $(')')) {
return e;
if ($('(')) {
while (e = $(this.expression)) {
expressions.push(e);
if (! $(',')) { break; }
}
$(')');
}
if (expressions.length > 1) {
return new tree.Value(expressions.map(function(e) {
return e.value[0];
}));
} else if (expressions.length === 1) {
return new tree.Value(expressions);
}
},
// This is a misnomer because it actually handles multiplication
@ -1136,7 +1150,7 @@ carto.Renderer = function Renderer(env, options) {
carto.Renderer.prototype.renderMSS = function render(data) {
// effects is a container for side-effects, which currently
// are limited to FontSets.
var env = _(this.env).defaults({
var env = _.defaults(this.env, {
benchmark: false,
validation_data: false,
effects: []
@ -1190,7 +1204,7 @@ carto.Renderer.prototype.renderMSS = function render(data) {
carto.Renderer.prototype.render = function render(m) {
// effects is a container for side-effects, which currently
// are limited to FontSets.
var env = _(this.env).defaults({
var env = _.defaults(this.env, {
benchmark: false,
validation_data: false,
effects: [],
@ -1204,14 +1218,14 @@ carto.Renderer.prototype.render = function render(m) {
var output = [];
// Transform stylesheets into definitions.
var definitions = _(m.Stylesheet).chain()
var definitions = _.chain(m.Stylesheet)
.map(function(s) {
if (typeof s == 'string') {
throw new Error("Stylesheet object is expected not a string: '" + s + "'");
}
// Passing the environment from stylesheet to stylesheet,
// allows frames and effects to be maintained.
env = _(env).extend({filename:s.id});
env = _.extend(env, {filename:s.id});
var time = +new Date(),
root = (carto.Parser(env)).parse(s.data);
@ -1272,7 +1286,7 @@ carto.Renderer.prototype.render = function render(m) {
if (env.errors) throw env.errors;
// Pass TileJSON and other custom parameters through to Mapnik XML.
var parameters = _(m).reduce(function(memo, v, k) {
var parameters = _.reduce(m, function(memo, v, k) {
if (!v && v !== 0) return memo;
switch (k) {
@ -1325,7 +1339,7 @@ carto.Renderer.prototype.render = function render(m) {
'\n</Parameters>\n'
);
var properties = _(map_properties).map(function(v) { return ' ' + v; }).join('');
var properties = _.map(map_properties, function(v) { return ' ' + v; }).join('');
output.unshift(
'<?xml version="1.0" ' +
@ -1583,7 +1597,7 @@ CartoCSS.Layer.prototype = {
},
/**
* return the symbolizers that need to be rendered with
* return the symbolizers that need to be rendered with
* this style. The order is the rendering order.
* @returns a list with 3 possible values 'line', 'marker', 'polygon'
*/
@ -1625,7 +1639,7 @@ CartoCSS.Layer.prototype = {
//
// given a geoemtry type returns the transformed one acording the CartoCSS
// For points there are two kind of types: point and sprite, the first one
// For points there are two kind of types: point and sprite, the first one
// is a circle, second one is an image sprite
//
// the other geometry types are the same than geojson (polygon, linestring...)
@ -1728,12 +1742,15 @@ CartoCSS.prototype = {
var layer = layers[key] = (layers[key] || {
symbolizers: []
});
for(var u = 0; u<def.rules.length; u++){
if(def.rules[u].name === "marker-file" || def.rules[u].name === "point-file"){
var value = def.rules[u].value.value[0].value[0].value.value;
this.imageURLs.push(value);
var rule = def.rules[u];
if(rule.name === "marker-file" || rule.name === "point-file"){
var value = rule.value.value[0].value[0].value.value;
this.imageURLs.push(value);
}
}
}
layer.frames = [];
layer.zoom = tree.Zoom.all;
var props = def.toJS(parse_env);
@ -1752,6 +1769,8 @@ CartoCSS.prototype = {
// serach the max index to know rendering order
lyr.index = _.max(props[v].map(function(a) { return a.index; }).concat(lyr.index));
lyr.constant = !_.any(props[v].map(function(a) { return !a.constant; }));
// True when the property is filtered.
lyr.filtered = props[v][0].filtered;
}
}
@ -1761,6 +1780,14 @@ CartoCSS.prototype = {
var done = {};
for(var i = 0; i < defs.length; ++i) {
var def = defs[i];
if (this.options.strict) {
def.toXML(parse_env, {});
if (parse_env.errors.message) {
throw new Error(parse_env.errors.message);
}
}
var k = defKey(def);
var layer = layers[k];
if(!done[k]) {
@ -1797,12 +1824,13 @@ CartoCSS.prototype = {
carto.RendererJS = function (options) {
this.options = options || {};
this.options.mapnik_version = this.options.mapnik_version || 'latest';
this.reference = this.options.reference || require('./torque-reference').version.latest;
this.options.strict = this.options.hasOwnProperty('strict') ? this.options.strict : false;
};
// Prepare a javascript object which contains the layers
carto.RendererJS.prototype.render = function render(cartocss, callback) {
var reference = require('./torque-reference');
tree.Reference.setData(reference.version.latest);
tree.Reference.setData(this.reference);
return new CartoCSS(cartocss, this.options);
}
@ -1842,7 +1870,10 @@ var _mapnik_reference_latest = {
["x-gradient", 0],
["y-gradient", 0],
["invert", 0],
["sharpen", 0]
["sharpen", 0],
["colorize-alpha", -1],
["color-to-alpha", 1],
["scale-hsla", 8]
],
"doc": "A list of image filters."
},
@ -1977,7 +2008,20 @@ var _mapnik_reference_latest = {
["x-gradient", 0],
["y-gradient", 0],
["invert", 0],
["sharpen", 0]
["sharpen", 0],
["colorize-alpha", -1],
["color-to-alpha", 1],
["scale-hsla", 8],
["buckets", -1],
["category", -1],
["equal", -1],
["headtails", -1],
["jenks", -1],
["quantiles", -1],
["cartocolor", -1],
["colorbrewer", -1],
["range", -1],
["ramp", -1]
],
"doc": "A list of image filters."
},
@ -2104,14 +2148,16 @@ var _mapnik_reference_latest = {
"type": "color",
"default-value": "rgba(128,128,128,1)",
"default-meaning": "gray and fully opaque (alpha = 1), same as rgb(128,128,128)",
"doc": "Fill color to assign to a polygon"
"doc": "Fill color to assign to a polygon",
"expression": true
},
"fill-opacity": {
"css": "polygon-opacity",
"type": "float",
"doc": "The opacity of the polygon",
"default-value": 1,
"default-meaning": "opaque"
"default-meaning": "opaque",
"expression": true
},
"gamma": {
"css": "polygon-gamma",
@ -2212,13 +2258,15 @@ var _mapnik_reference_latest = {
"default-value": "rgba(0,0,0,1)",
"type": "color",
"default-meaning": "black and fully opaque (alpha = 1), same as rgb(0,0,0)",
"doc": "The color of a drawn line"
"doc": "The color of a drawn line",
"expression": true
},
"stroke-width": {
"css": "line-width",
"default-value": 1,
"type": "float",
"doc": "The width of a line in pixels"
"doc": "The width of a line in pixels",
"expression": true
},
"stroke-opacity": {
"css": "line-opacity",
@ -2383,7 +2431,8 @@ var _mapnik_reference_latest = {
"doc": "An SVG file that this marker shows at each placement. If no file is given, the marker will show an ellipse.",
"default-value": "",
"default-meaning": "An ellipse or circle, if width equals height",
"type": "uri"
"type": "uri",
"expression": true
},
"opacity": {
"css": "marker-opacity",
@ -2467,7 +2516,8 @@ var _mapnik_reference_latest = {
"css": "marker-fill",
"default-value": "blue",
"doc": "The color of the area of the marker.",
"type": "color"
"type": "color",
"expression": true
},
"allow-overlap": {
"css": "marker-allow-overlap",
@ -2842,7 +2892,8 @@ var _mapnik_reference_latest = {
"type": "uri",
"default-value": "none",
"required": true,
"doc": "An image file to be repeated and warped along a line"
"doc": "An image file to be repeated and warped along a line",
"expression": true
},
"clip": {
"css": "line-pattern-clip",
@ -2922,7 +2973,8 @@ var _mapnik_reference_latest = {
"type": "uri",
"default-value": "none",
"required": true,
"doc": "Image to use as a repeated pattern fill within a polygon"
"doc": "Image to use as a repeated pattern fill within a polygon",
"expression": true
},
"alignment": {
"css": "polygon-pattern-alignment",
@ -4179,48 +4231,37 @@ tree.Definition.prototype.toXML = function(env, existing) {
tree.Definition.prototype.toJS = function(env) {
var shaderAttrs = {};
// merge conditions from filters with zoom condition of the
// definition
var zoom = "(" + this.zoom + " & (1 << ctx.zoom))";
var frame_offset = this.frame_offset;
var _if = this.filters.toJS(env);
var filters = [zoom];
if(_if) filters.push(_if);
if(frame_offset) filters.push('ctx["frame-offset"] === ' + frame_offset);
_if = filters.join(" && ");
_.each(this.rules, function(rule) {
if(rule instanceof tree.Rule) {
shaderAttrs[rule.name] = shaderAttrs[rule.name] || [];
var zoomFilter = "(" + this.zoom + " & (1 << ctx.zoom))";
var filters = [zoomFilter];
var originalFilters = this.filters.toJS(env);
// Ignore default zoom for filtering (https://github.com/CartoDB/carto/issues/40)
var zoomFiltered = this.zoom !== tree.Zoom.all;
if (originalFilters) {
filters.push(originalFilters);
}
var r = {
index: rule.index,
symbolizer: rule.symbolizer
};
if (frame_offset) {
filters.push('ctx["frame-offset"] === ' + frame_offset);
}
if (_if) {
r.js = "if(" + _if + "){" + rule.value.toJS(env) + "}"
} else {
r.js = rule.value.toJS(env);
}
_.each(this.rules, function (rule) {
var exportedRule = {};
r.constant = rule.value.ev(env).is !== 'field';
r.filtered = !!_if;
shaderAttrs[rule.name].push(r);
} else {
throw new Error("Ruleset not supported");
//if (rule instanceof tree.Ruleset) {
//var sh = rule.toJS(env);
//for(var v in sh) {
//shaderAttrs[v] = shaderAttrs[v] || [];
//for(var attr in sh[v]) {
//shaderAttrs[v].push(sh[v][attr]);
//}
//}
//}
if (!rule instanceof tree.Rule) {
throw new Error("Ruleset not supported");
}
exportedRule.index = rule.index;
exportedRule.symbolizer = rule.symbolizer;
exportedRule.js = "if(" + filters.join(" && ") + "){" + rule.value.toJS(env) + "}";
exportedRule.constant = rule.value.ev(env).is !== 'field';
exportedRule.filtered = zoomFiltered || (originalFilters !== '');
shaderAttrs[rule.name] = shaderAttrs[rule.name] || [];
shaderAttrs[rule.name].push(exportedRule);
});
return shaderAttrs;
};
@ -4228,7 +4269,7 @@ tree.Definition.prototype.toJS = function(env) {
})(require('../tree'));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../tree":7,"assert":37,"underscore":undefined}],12:[function(require,module,exports){
},{"../tree":7,"assert":36,"underscore":undefined}],12:[function(require,module,exports){
(function (global){
(function(tree) {
var _ = global._ || require('underscore');
@ -4576,7 +4617,10 @@ tree.Filterset.prototype.toJS = function(env) {
val = filter._val.toString(true);
}
var attrs = "data";
return attrs + "." + filter.key.value + " " + op + " " + (val.is === 'string' ? "'"+ val +"'" : val);
if (op === '=~') {
return "(" + attrs + "['" + filter.key.value + "'] + '').match(" + (val.is === 'string' ? "'" + val.toString().replace(/'/g, "\\'") + "'" : val) + ")";
}
return attrs + "['" + filter.key.value + "'] " + op + " " + (val.is === 'string' ? "'" + val.toString().replace(/'/g, "\\'") + "'" : val);
}).join(' && ');
};
@ -5295,7 +5339,7 @@ tree.Reference = ref;
})(require('../tree'));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../tree":7,"mapnik-reference":43,"underscore":undefined}],28:[function(require,module,exports){
},{"../tree":7,"mapnik-reference":38,"underscore":undefined}],28:[function(require,module,exports){
(function(tree) {
// a rule is a single property and value combination, or variable
// name and value combination, like
@ -5756,7 +5800,14 @@ tree.Value.prototype = {
v = "'" + v + "'";
} else if (val.is === 'field') {
// replace [variable] by ctx['variable']
v = v.replace(/\[(.*)\]/g, "data['$1']");
v = v.replace(/\[([^\]]*)\]/g, function(matched) {
return matched.replace(/\[(.*)\]/g, "data['$1']");
});
}else if (val.is === 'call') {
v = JSON.stringify({
name: val.name,
args: val.args
})
}
return "_value = " + v + ";";
}
@ -5929,8 +5980,6 @@ tree.Zoom.prototype.toString = function() {
};
},{"../tree":7}],36:[function(require,module,exports){
},{}],37:[function(require,module,exports){
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
//
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
@ -6292,32 +6341,38 @@ var objectKeys = Object.keys || function (obj) {
return keys;
};
},{"util/":42}],38:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{"util/":43}],37:[function(require,module,exports){
},{}],39:[function(require,module,exports){
},{}],38:[function(require,module,exports){
(function (__dirname){
var fs = require('fs'),
path = require('path'),
existsSync = require('fs').existsSync || require('path').existsSync;
// Load all stated versions into the module exports
module.exports.version = {};
var refs = [
'2.0.0',
'2.0.1',
'2.0.2',
'2.1.0',
'2.1.1',
'2.2.0',
'2.3.0',
'3.0.0'
];
refs.map(function(version) {
module.exports.version[version] = require(path.join(__dirname, version, 'reference.json'));
var ds_path = path.join(__dirname, version, 'datasources.json');
if (existsSync(ds_path)) {
module.exports.version[version].datasources = require(ds_path).datasources;
}
});
}).call(this,"/node_modules/mapnik-reference")
},{"fs":37,"path":39}],39:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
@ -6634,13 +6689,38 @@ process.chdir = function (dir) {
};
},{}],41:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{}],42:[function(require,module,exports){
module.exports = function isBuffer(arg) {
return arg && typeof arg === 'object'
&& typeof arg.copy === 'function'
&& typeof arg.fill === 'function'
&& typeof arg.readUInt8 === 'function';
}
},{}],42:[function(require,module,exports){
},{}],43:[function(require,module,exports){
(function (process,global){
// Copyright Joyent, Inc. and other Node contributors.
//
@ -7230,39 +7310,10 @@ function hasOwnProperty(obj, prop) {
}
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":41,"_process":40,"inherits":38}],43:[function(require,module,exports){
(function (__dirname){
var fs = require('fs'),
path = require('path'),
existsSync = require('fs').existsSync || require('path').existsSync;
// Load all stated versions into the module exports
module.exports.version = {};
var refs = [
'2.0.0',
'2.0.1',
'2.0.2',
'2.1.0',
'2.1.1',
'2.2.0',
'2.3.0',
'3.0.0'
];
refs.map(function(version) {
module.exports.version[version] = require(path.join(__dirname, version, 'reference.json'));
var ds_path = path.join(__dirname, version, 'datasources.json');
if (existsSync(ds_path)) {
module.exports.version[version].datasources = require(ds_path).datasources;
}
});
}).call(this,"/node_modules/mapnik-reference")
},{"fs":36,"path":39}],44:[function(require,module,exports){
},{"./support/isBuffer":42,"_process":40,"inherits":41}],44:[function(require,module,exports){
module.exports={
"name": "carto",
"version": "0.15.1",
"version": "0.15.1-cdb4",
"description": "CartoCSS Stylesheet Compiler",
"url": "https://github.com/cartodb/carto",
"repository": {
@ -7299,7 +7350,7 @@ module.exports={
"node": ">=0.4.x"
},
"dependencies": {
"underscore": "~1.6.0",
"underscore": "1.8.3",
"mapnik-reference": "~6.0.2",
"optimist": "~0.6.0"
},
@ -7315,6 +7366,7 @@ module.exports={
"scripts": {
"pretest": "npm install",
"test": "mocha -R spec",
"tdd" : "env HIDE_LOGS=true mocha -w -R spec",
"coverage": "istanbul cover ./node_modules/.bin/_mocha && coveralls < ./coverage/lcov.info"
}
}

View File

@ -23,7 +23,7 @@ line-color: yellow;
}
```
Especially of note is the support for hsl, which can be [easier to reason about than rgb()](http://mothereffinghsl.com/). Carto also includes several color functions [borrowed from less](http://lesscss.org/#-color-functions):
Especially of note is the support for hsl, which can be [easier to reason about than rgb()](http://mothereffinghsl.com/). Carto also includes several color operation functions [borrowed from less](http://lesscss.org/functions/#color-operations):
``` css
// lighten and darken colors

View File

@ -53,7 +53,7 @@ var carto = {
if (typeof(extract[2]) === 'string') {
error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
}
error = options.indent + error.join('\n' + options.indent) + '\033[0m\n';
error = options.indent + error.join('\n' + options.indent) + '\x1B[0m\n';
message = options.indent + message + stylize(ctx.message, 'red');
if (ctx.filename) (message += stylize(' in ', 'red') + ctx.filename);
@ -111,6 +111,6 @@ function stylize(str, style) {
'red' : [31, 39],
'grey' : [90, 39]
};
return '\033[' + styles[style][0] + 'm' + str +
'\033[' + styles[style][1] + 'm';
return '\x1B[' + styles[style][0] + 'm' + str +
'\x1B[' + styles[style][1] + 'm';
}

View File

@ -114,8 +114,9 @@ carto.Parser = function Parser(env) {
// - `index`: Char. index where the error occurred.
function makeError(err) {
var einput;
var errorTemplate;
_(err).defaults({
_.defaults(err, {
index: furthest,
filename: env.filename,
message: 'Parse error.',
@ -133,8 +134,8 @@ carto.Parser = function Parser(env) {
for (var n = err.index; n >= 0 && einput.charAt(n) !== '\n'; n--) {
err.column++;
}
return new Error(_('<%=filename%>:<%=line%>:<%=column%> <%=message%>').template(err));
errorTemplate = _.template('<%=filename%>:<%=line%>:<%=column%> <%=message%>');
return new Error(errorTemplate(err));
}
this.env = env = env || {};
@ -476,6 +477,7 @@ carto.Parser = function Parser(env) {
return new tree.Dimension(value[1], value[2], memo);
}
}
},
// The variable part of a variable definition.
@ -724,10 +726,22 @@ carto.Parser = function Parser(env) {
},
// A sub-expression, contained by parenthensis
sub: function() {
var e;
var e, expressions = [];
if ($('(') && (e = $(this.expression)) && $(')')) {
return e;
if ($('(')) {
while (e = $(this.expression)) {
expressions.push(e);
if (! $(',')) { break; }
}
$(')');
}
if (expressions.length > 1) {
return new tree.Value(expressions.map(function(e) {
return e.value[0];
}));
} else if (expressions.length === 1) {
return new tree.Value(expressions);
}
},
// This is a misnomer because it actually handles multiplication

View File

@ -16,7 +16,7 @@ carto.Renderer = function Renderer(env, options) {
carto.Renderer.prototype.renderMSS = function render(data) {
// effects is a container for side-effects, which currently
// are limited to FontSets.
var env = _(this.env).defaults({
var env = _.defaults(this.env, {
benchmark: false,
validation_data: false,
effects: []
@ -70,7 +70,7 @@ carto.Renderer.prototype.renderMSS = function render(data) {
carto.Renderer.prototype.render = function render(m) {
// effects is a container for side-effects, which currently
// are limited to FontSets.
var env = _(this.env).defaults({
var env = _.defaults(this.env, {
benchmark: false,
validation_data: false,
effects: [],
@ -84,14 +84,14 @@ carto.Renderer.prototype.render = function render(m) {
var output = [];
// Transform stylesheets into definitions.
var definitions = _(m.Stylesheet).chain()
var definitions = _.chain(m.Stylesheet)
.map(function(s) {
if (typeof s == 'string') {
throw new Error("Stylesheet object is expected not a string: '" + s + "'");
}
// Passing the environment from stylesheet to stylesheet,
// allows frames and effects to be maintained.
env = _(env).extend({filename:s.id});
env = _.extend(env, {filename:s.id});
var time = +new Date(),
root = (carto.Parser(env)).parse(s.data);
@ -152,7 +152,7 @@ carto.Renderer.prototype.render = function render(m) {
if (env.errors) throw env.errors;
// Pass TileJSON and other custom parameters through to Mapnik XML.
var parameters = _(m).reduce(function(memo, v, k) {
var parameters = _.reduce(m, function(memo, v, k) {
if (!v && v !== 0) return memo;
switch (k) {
@ -205,7 +205,7 @@ carto.Renderer.prototype.render = function render(m) {
'\n</Parameters>\n'
);
var properties = _(map_properties).map(function(v) { return ' ' + v; }).join('');
var properties = _.map(map_properties, function(v) { return ' ' + v; }).join('');
output.unshift(
'<?xml version="1.0" ' +

View File

@ -57,7 +57,7 @@ CartoCSS.Layer.prototype = {
},
/**
* return the symbolizers that need to be rendered with
* return the symbolizers that need to be rendered with
* this style. The order is the rendering order.
* @returns a list with 3 possible values 'line', 'marker', 'polygon'
*/
@ -99,7 +99,7 @@ CartoCSS.Layer.prototype = {
//
// given a geoemtry type returns the transformed one acording the CartoCSS
// For points there are two kind of types: point and sprite, the first one
// For points there are two kind of types: point and sprite, the first one
// is a circle, second one is an image sprite
//
// the other geometry types are the same than geojson (polygon, linestring...)
@ -202,12 +202,15 @@ CartoCSS.prototype = {
var layer = layers[key] = (layers[key] || {
symbolizers: []
});
for(var u = 0; u<def.rules.length; u++){
if(def.rules[u].name === "marker-file" || def.rules[u].name === "point-file"){
var value = def.rules[u].value.value[0].value[0].value.value;
this.imageURLs.push(value);
var rule = def.rules[u];
if(rule.name === "marker-file" || rule.name === "point-file"){
var value = rule.value.value[0].value[0].value.value;
this.imageURLs.push(value);
}
}
}
layer.frames = [];
layer.zoom = tree.Zoom.all;
var props = def.toJS(parse_env);
@ -226,6 +229,8 @@ CartoCSS.prototype = {
// serach the max index to know rendering order
lyr.index = _.max(props[v].map(function(a) { return a.index; }).concat(lyr.index));
lyr.constant = !_.any(props[v].map(function(a) { return !a.constant; }));
// True when the property is filtered.
lyr.filtered = props[v][0].filtered;
}
}
@ -235,6 +240,14 @@ CartoCSS.prototype = {
var done = {};
for(var i = 0; i < defs.length; ++i) {
var def = defs[i];
if (this.options.strict) {
def.toXML(parse_env, {});
if (parse_env.errors.message) {
throw new Error(parse_env.errors.message);
}
}
var k = defKey(def);
var layer = layers[k];
if(!done[k]) {
@ -271,12 +284,13 @@ CartoCSS.prototype = {
carto.RendererJS = function (options) {
this.options = options || {};
this.options.mapnik_version = this.options.mapnik_version || 'latest';
this.reference = this.options.reference || require('./torque-reference').version.latest;
this.options.strict = this.options.hasOwnProperty('strict') ? this.options.strict : false;
};
// Prepare a javascript object which contains the layers
carto.RendererJS.prototype.render = function render(cartocss, callback) {
var reference = require('./torque-reference');
tree.Reference.setData(reference.version.latest);
tree.Reference.setData(this.reference);
return new CartoCSS(cartocss, this.options);
}

View File

@ -166,7 +166,17 @@ var _mapnik_reference_latest = {
["sharpen", 0],
["colorize-alpha", -1],
["color-to-alpha", 1],
["scale-hsla", 8]
["scale-hsla", 8],
["buckets", -1],
["category", -1],
["equal", -1],
["headtails", -1],
["jenks", -1],
["quantiles", -1],
["cartocolor", -1],
["colorbrewer", -1],
["range", -1],
["ramp", -1]
],
"doc": "A list of image filters."
},
@ -293,14 +303,16 @@ var _mapnik_reference_latest = {
"type": "color",
"default-value": "rgba(128,128,128,1)",
"default-meaning": "gray and fully opaque (alpha = 1), same as rgb(128,128,128)",
"doc": "Fill color to assign to a polygon"
"doc": "Fill color to assign to a polygon",
"expression": true
},
"fill-opacity": {
"css": "polygon-opacity",
"type": "float",
"doc": "The opacity of the polygon",
"default-value": 1,
"default-meaning": "opaque"
"default-meaning": "opaque",
"expression": true
},
"gamma": {
"css": "polygon-gamma",
@ -401,13 +413,15 @@ var _mapnik_reference_latest = {
"default-value": "rgba(0,0,0,1)",
"type": "color",
"default-meaning": "black and fully opaque (alpha = 1), same as rgb(0,0,0)",
"doc": "The color of a drawn line"
"doc": "The color of a drawn line",
"expression": true
},
"stroke-width": {
"css": "line-width",
"default-value": 1,
"type": "float",
"doc": "The width of a line in pixels"
"doc": "The width of a line in pixels",
"expression": true
},
"stroke-opacity": {
"css": "line-opacity",
@ -572,7 +586,8 @@ var _mapnik_reference_latest = {
"doc": "An SVG file that this marker shows at each placement. If no file is given, the marker will show an ellipse.",
"default-value": "",
"default-meaning": "An ellipse or circle, if width equals height",
"type": "uri"
"type": "uri",
"expression": true
},
"opacity": {
"css": "marker-opacity",
@ -656,7 +671,8 @@ var _mapnik_reference_latest = {
"css": "marker-fill",
"default-value": "blue",
"doc": "The color of the area of the marker.",
"type": "color"
"type": "color",
"expression": true
},
"allow-overlap": {
"css": "marker-allow-overlap",
@ -822,6 +838,18 @@ var _mapnik_reference_latest = {
"default-value": "point",
"doc": "How this shield should be placed. Point placement attempts to place it on top of points, line places along lines multiple times per feature, vertex places on the vertexes of polygons, and interior attempts to place inside of polygons."
},
"placement-type": {
"css": "shield-placement-type",
"doc": "Re-position and/or re-size shield to avoid overlaps. \"simple\" for basic algorithm (using shield-placements string,) \"dummy\" to turn this feature off.",
"type": [
"dummy",
"simple",
"list"
],
"expression":true,
"default-meaning": "Alternative placements will not be enabled.",
"default-value": "dummy"
},
"avoid-edges": {
"css": "shield-avoid-edges",
"doc": "Tell positioning algorithm to avoid labeling near intersection edges.",
@ -1031,7 +1059,8 @@ var _mapnik_reference_latest = {
"type": "uri",
"default-value": "none",
"required": true,
"doc": "An image file to be repeated and warped along a line"
"doc": "An image file to be repeated and warped along a line",
"expression": true
},
"clip": {
"css": "line-pattern-clip",
@ -1111,7 +1140,8 @@ var _mapnik_reference_latest = {
"type": "uri",
"default-value": "none",
"required": true,
"doc": "Image to use as a repeated pattern fill within a polygon"
"doc": "Image to use as a repeated pattern fill within a polygon",
"expression": true
},
"alignment": {
"css": "polygon-pattern-alignment",

View File

@ -210,48 +210,37 @@ tree.Definition.prototype.toXML = function(env, existing) {
tree.Definition.prototype.toJS = function(env) {
var shaderAttrs = {};
// merge conditions from filters with zoom condition of the
// definition
var zoom = "(" + this.zoom + " & (1 << ctx.zoom))";
var frame_offset = this.frame_offset;
var _if = this.filters.toJS(env);
var filters = [zoom];
if(_if) filters.push(_if);
if(frame_offset) filters.push('ctx["frame-offset"] === ' + frame_offset);
_if = filters.join(" && ");
_.each(this.rules, function(rule) {
if(rule instanceof tree.Rule) {
shaderAttrs[rule.name] = shaderAttrs[rule.name] || [];
var zoomFilter = "(" + this.zoom + " & (1 << ctx.zoom))";
var filters = [zoomFilter];
var originalFilters = this.filters.toJS(env);
// Ignore default zoom for filtering (https://github.com/CartoDB/carto/issues/40)
var zoomFiltered = this.zoom !== tree.Zoom.all;
if (originalFilters) {
filters.push(originalFilters);
}
var r = {
index: rule.index,
symbolizer: rule.symbolizer
};
if (frame_offset) {
filters.push('ctx["frame-offset"] === ' + frame_offset);
}
if (_if) {
r.js = "if(" + _if + "){" + rule.value.toJS(env) + "}"
} else {
r.js = rule.value.toJS(env);
}
_.each(this.rules, function (rule) {
var exportedRule = {};
r.constant = rule.value.ev(env).is !== 'field';
r.filtered = !!_if;
shaderAttrs[rule.name].push(r);
} else {
throw new Error("Ruleset not supported");
//if (rule instanceof tree.Ruleset) {
//var sh = rule.toJS(env);
//for(var v in sh) {
//shaderAttrs[v] = shaderAttrs[v] || [];
//for(var attr in sh[v]) {
//shaderAttrs[v].push(sh[v][attr]);
//}
//}
//}
if (!rule instanceof tree.Rule) {
throw new Error("Ruleset not supported");
}
exportedRule.index = rule.index;
exportedRule.symbolizer = rule.symbolizer;
exportedRule.js = "if(" + filters.join(" && ") + "){" + rule.value.toJS(env) + "}";
exportedRule.constant = rule.value.ev(env).is !== 'field';
exportedRule.filtered = zoomFiltered || (originalFilters !== '');
shaderAttrs[rule.name] = shaderAttrs[rule.name] || [];
shaderAttrs[rule.name].push(exportedRule);
});
return shaderAttrs;
};

View File

@ -92,7 +92,10 @@ tree.Filterset.prototype.toJS = function(env) {
val = filter._val.toString(true);
}
var attrs = "data";
return attrs + "." + filter.key.value + " " + op + " " + (val.is === 'string' ? "'"+ val +"'" : val);
if (op === '=~') {
return "(" + attrs + "['" + filter.key.value + "'] + '').match(" + (val.is === 'string' ? "'" + val.toString().replace(/'/g, "\\'").replace(/&amp;/g, '&') + "'" : val) + ")";
}
return attrs + "['" + filter.key.value + "'] " + op + " " + (val.is === 'string' ? "'" + val.toString().replace(/'/g, "\\'").replace(/&amp;/g, '&') + "'" : val);
}).join(' && ');
};

View File

@ -33,10 +33,18 @@ tree.Value.prototype = {
var val = this.ev(env);
var v = val.toString();
if(val.is === "color" || val.is === 'uri' || val.is === 'string' || val.is === 'keyword') {
v = "'" + v + "'";
v = "'" + v.replace(/&amp;/g, '&') + "'";
} else if (Array.isArray(this.value) && this.value.length > 1) {
// This covers something like `line-dasharray: 5, 10;`
// where the return _value has more than one element.
// Without this the generated code will look like:
// _value = 5, 10; which will ignore the 10.
v = '[' + this.value.join(',') + ']';
} else if (val.is === 'field') {
// replace [variable] by ctx['variable']
v = v.replace(/\[(.*)\]/g, "data['$1']");
v = v.replace(/\[([^\]]*)\]/g, function(matched) {
return matched.replace(/\[(.*)\]/g, "data['$1']");
});
}else if (val.is === 'call') {
v = JSON.stringify({
name: val.name,

2110
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "carto",
"version": "0.15.1-cdb1",
"version": "0.15.1-cdb5",
"description": "CartoCSS Stylesheet Compiler",
"url": "https://github.com/cartodb/carto",
"repository": {
@ -37,7 +37,7 @@
"node": ">=0.4.x"
},
"dependencies": {
"underscore": "~1.6.0",
"underscore": "1.8.3",
"mapnik-reference": "~6.0.2",
"optimist": "~0.6.0"
},
@ -53,6 +53,11 @@
"scripts": {
"pretest": "npm install",
"test": "mocha -R spec",
"coverage": "istanbul cover ./node_modules/.bin/_mocha && coveralls < ./coverage/lcov.info"
"tdd": "env HIDE_LOGS=true mocha -w -R spec",
"coverage": "istanbul cover ./node_modules/.bin/_mocha && coveralls < ./coverage/lcov.info",
"bump": "npm version patch",
"bump:major": "npm version major",
"bump:minor": "npm version minor",
"postversion": "git push origin master --follow-tags"
}
}

102
test/filtered.test.js Normal file
View File

@ -0,0 +1,102 @@
/**
* Test the filtered field.
*
* When compiled, a rule provides metainformation fields like index, constant...etc
* one of this fields is the "filtered field".
*
* This field gives information about whether a property is filtered or not.
*
* A property is filtered if it was activated inside a filter. In the following cartocss
* code marker-color.filtered will be true because it's inside a population filter.
*
* #layer {
* maker-width: 20;
* [population > 100] {
* marker-color: red; // this property is filtered
* }
* }
*
* "zoom" is a special case, and it only should be considered when its value is not the default.
*/
var assert = require('assert');
var Carto = require('../lib/carto/index.js');
var renderer = new Carto.RendererJS({ strict: true });
describe('property.filtered', function () {
it('should be false when the property is not filtered', function () {
var style = [
'#layer {',
' marker-fill: red;',
'}'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(!layers['marker-fill'].filtered);
});
it('should be true when the property is filtered', function () {
var style = [
'#layer {',
' [foo > 30] {',
' marker-fill: red;',
' }',
'}'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(layers['marker-fill'].filtered);
});
it('should be true when the property is filtered at first level', function () {
var style = [
'#layer [foo > 30] {',
' marker-fill: red;',
'}`'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(layers['marker-fill'].filtered);
});
it('should be false when the property is not filterd but there is another filtered properties', function () {
var style = [
'#layer {',
' marker-fill: red;',
' [bar < 200]{',
' marker-allow-overlap: false;',
' }',
'}`'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(!layers['marker-fill'].filtered);
assert(layers['marker-allow-overlap'].filtered);
});
it('should be true when the property is filtered and have a default value', function () {
var style = [
'#layer {',
' marker-fill: red;',
' [bar < 200]{',
' marker-fill: blue;',
' }',
'}`'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(layers['marker-fill'].filtered);
});
it('should be true when filtering by zoom', function () {
var style = [
'#layer {',
' [zoom < 5]{',
' marker-fill: blue;',
' }',
'}`'
].join('\n');
var layers = renderer.render(style).layers[0].shader;
assert(layers['marker-fill'].filtered);
});
});

View File

@ -54,7 +54,8 @@ var _mapnik_reference_latest = {
["x-gradient", 0],
["y-gradient", 0],
["invert", 0],
["sharpen", 0]
["sharpen", 0],
["ramp", 0]
],
"doc": "A list of image filters."
},

View File

@ -0,0 +1,148 @@
var assert = require('assert');
var carto = require('../lib/carto');
var tree = require('../lib/carto/tree');
describe('RendererJS Strict Mode', function() {
var style = [
'#world {',
'polygon-fill: red;',
'line-width: 2;',
'line-color: #f00;',
'[frame-offset = 1] {',
'line-width: 3;',
'}',
'[frame-offset = 2] {',
'line-width: 3;',
'}',
'}',
'',
'#worls[frame-offset = 10] {',
'line-width: 4;',
'}'
].join('\n');
var reference = {
version: '1.0.0',
style: {},
layer: {},
colors: {},
filter: {},
symbolizers: {
line: {
"stroke": {
"css": "line-color",
"default-value": "rgba(0,0,0,1)",
"type": "color",
"default-meaning": "black and fully opaque (alpha = 1), same as rgb(0,0,0)",
"doc": "The color of a drawn line"
},
"stroke-width": {
"css": "line-width",
"default-value": 1,
"type": "float",
"doc": "The width of a line in pixels"
},
"stroke-opacity": {
"css": "line-opacity",
"default-value": 1,
"type": "float",
"default-meaning": "opaque",
"doc": "The opacity of a line"
},
"stroke-linejoin": {
"css": "line-join",
"default-value": "miter",
"type": [
"miter",
"miter-revert",
"round",
"bevel"
],
"expression": true,
"doc": "The behavior of lines when joining.",
"default-meaning": "The line joins will be rendered using a miter look."
},
"stroke-linecap": {
"css": "line-cap",
"default-value": "butt",
"type": [
"butt",
"round",
"square"
],
"expression": true,
"doc": "The display of line endings.",
"default-meaning": "The line endings will be rendered using a butt look."
},
"comp-op": {
"css": "line-comp-op",
"default-value": "overlay",
"default-meaning": "Add the current symbolizer on top of other symbolizer.",
"doc": "Composite operation. This defines how this symbolizer should behave relative to symbolizers atop or below it.",
"type": [
"multiply",
"add",
"overlay"
],
"expression": true
},
"stroke-dasharray": {
"css": "line-dasharray",
"type": "numbers",
"expression": true,
"doc": "A pair of length values [a,b], where (a) is the dash length and (b) is the gap length respectively. More than two values are supported for more complex patterns.",
"default-value": "none",
"default-meaning": "The line will be drawn without dashes."
}
}
}
};
var expectedErrorMessageRegex = /Unrecognized rule: polygon-fill/;
before(function() {
this.referenceData = tree.Reference.data;
});
after(function() {
if (this.referenceData) {
tree.Reference.setData(this.referenceData);
}
});
it('should fail if a feature is not supported and strict is turned on', function () {
assert.throws(
function () {
var RendererJS = new carto.RendererJS({reference: reference, mapnik_version: '1.0.0', strict: true });
var shader = RendererJS.render(style);
},
expectedErrorMessageRegex
);
});
function rendererStrictModeOffTest(RendererJS) {
return function () {
var shader = RendererJS.render(style);
assert.ok(shader.layers);
assert.equal(shader.layers.length, 2);
};
}
it('should pass if a feature is not supported but strict mode is not specified', rendererStrictModeOffTest(
new carto.RendererJS({reference: reference, mapnik_version: '1.0.0' })
));
it('should pass if a feature is not supported but strict is turned off', function () {
new carto.RendererJS({reference: reference, mapnik_version: '1.0.0', strict: false })
});
it('should pass if a feature is supported and strict is turned on', function () {
var RendererJS = new carto.RendererJS({reference: reference, mapnik_version: '1.0.0', strict: true });
var cartocss = '#layer { line-width: 10 }';
var shader = RendererJS.render(cartocss);
assert.ok(shader);
});
});

View File

@ -1,27 +1,27 @@
var SHOW_LOGS = (process.env.HIDE_LOGS !== 'true');
var assert = require('assert');
var carto = require('../lib/carto');
describe('RenderingJS', function() {
var shader;
var style = [
'#world {',
'line-width: 2;',
'line-color: #f00;',
'[frame-offset = 1] {',
'line-width: 3;',
'}',
'[frame-offset = 2] {',
'line-width: 3;',
'}',
'}',
'',
'#worls[frame-offset = 10] {',
'line-width: 4;',
'#world {',
'line-width: 2;',
'line-color: #f00;',
'[frame-offset = 1] {',
'line-width: 3;',
'}',
'[frame-offset = 2] {',
'line-width: 3;',
'}',
'}',
'',
'#worls[frame-offset = 10] {',
'line-width: 4;',
'}'
].join('\n');
beforeEach(function() {
shader = (new carto.RendererJS({ debug: true })).render(style);
shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
});
it ("shold render layers", function() {
@ -45,7 +45,7 @@ describe('RenderingJS', function() {
it ("shold render variables", function() {
var style = '#test { marker-width: [testing]; }';
shader = (new carto.RendererJS({ debug: true })).render(style);
shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
var layer = shader.getLayers()[0];
var props = layer.getStyle({testing: 2}, { 'zoom': 0, 'frame-offset': 10 });
assert( props['marker-width'] === 2);
@ -53,7 +53,7 @@ describe('RenderingJS', function() {
it ("should allow filter based rendering", function() {
var style = '#test { marker-width: 10; [zoom = 1] { marker-width: 1; } }';
shader = (new carto.RendererJS({ debug: true })).render(style);
shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
var layer = shader.getLayers()[0];
var props = layer.getStyle({}, { 'zoom': 0, 'frame-offset': 10 });
assert( props['marker-width'] === 10);
@ -63,8 +63,8 @@ describe('RenderingJS', function() {
it ("symbolizers should be in rendering order", function() {
var style = '#test { polygon-fill: red; line-color: red; }';
style += '#test2 { line-color: red;polygon-fill: red; line-witdh: 10; }';
var shader = (new carto.RendererJS({ debug: true })).render(style);
style += '#test2 { line-color: red;polygon-fill: red; line-width: 10; }';
var shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
var layer0 = shader.getLayers()[0];
assert(layer0.getSymbolizers()[0] === 'polygon');
assert(layer0.getSymbolizers()[1] === 'line');
@ -76,7 +76,7 @@ describe('RenderingJS', function() {
it ("colorize should return a list of colours in same order", function() {
var style = '#test { image-filters: colorize-alpha(blue, cyan, green, yellow, orange, red); }';
var shader = (new carto.RendererJS({ debug: true })).render(style);
var shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
var layer0 = shader.getLayers()[0];
var st = layer0.getStyle({ value: 1 }, {"frame-offset": 0, "zoom": 3});
var expectedColours = [[0, 0, 255], [0, 255, 255], [0, 128, 0], [255, 255, 0], [255, 165, 0], [255, 0, 0]];
@ -106,7 +106,7 @@ describe('RenderingJS', function() {
' [frame-offset = 2] { marker-width: 15; marker-fill-opacity: 0.02;}',
'}'
].join('\n');
var shader = (new carto.RendererJS({ debug: true })).render(css);
var shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(css);
var markerURLs = shader.getImageURLs();
var against = ["http://localhost:8081/gal.svg", "http://upload.wikimedia.org/wikipedia/commons/4/43/Flag_of_the_Galactic_Empire.svg", "http://upload.wikimedia.org/wikipedia/commons/c/c9/Flag_of_Syldavia.svg"];
for(var i = 0; i<against.length; i++){
@ -116,20 +116,124 @@ describe('RenderingJS', function() {
it ("should return variable for styles that change", function() {
var style = '#test { marker-width: [prop]; }';
var shader = (new carto.RendererJS({ debug: true })).render(style);
var shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
var layer0 = shader.getLayers()[0];
assert(layer0.isVariable());
style = '#test { marker-width: 1; }';
shader = (new carto.RendererJS({ debug: true })).render(style);
shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
layer0 = shader.getLayers()[0];
assert(!layer0.isVariable());
style = '#test { marker-width: [prop]; marker-fill: red; }';
shader = (new carto.RendererJS({ debug: true })).render(style);
shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
layer0 = shader.getLayers()[0];
assert(layer0.isVariable());
});
it("should parse styles with string", function() {
var style = '#test { [column = "test\'ing"] { marker-width: 10; } }';
var shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
var layer = shader.getLayers()[0];
var props = layer.getStyle({column: 'test\'ing'}, { 'zoom': 0, 'frame-offset': 10 });
assert(props['marker-width'] === 10);
});
it("should parse styles with filters not supported by dot notation", function() {
var style = '#test["mapnik::geometry_type"=1] { marker-width: 10; }';
var shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(style);
var layer = shader.getLayers()[0];
var props = layer.getStyle({"mapnik::geometry_type": 1}, { 'zoom': 0 });
assert.equal(props['marker-width'], 10);
var emptyFilterProps = layer.getStyle({"mapnik::geometry_type": 2}, { 'zoom': 0 });
assert.equal(emptyFilterProps['marker-width'], null);
});
it ("should parse turbocarto", function(){
var css = [
'#layer {',
' marker-width: ramp([cartodb_id], (#fff, #bbb), jenks);',
'}'
].join('\n');
var shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(css);
var layer = shader.getLayers()[0];
var st = layer.shader['marker-width'].style({}, {zoom: 1})
assert.equal(st.name, "ramp")
assert.equal(st.args.length, 3);
assert.equal(st.args[1].value[0].rgb[0], 255);
assert.equal(st.args[1].value[0].rgb[1], 255);
assert.equal(st.args[1].value[0].rgb[2], 255);
assert.equal(st.args[2].value, 'jenks');
})
it("should parse turbocarto with inner functions", function(){
var css = [
'#layer {',
' marker-width: ramp([cartodb_id], cartocolor(Bold), category(10));',
'}'
].join('\n');
var shader = (new carto.RendererJS({ debug: SHOW_LOGS })).render(css);
var layer = shader.getLayers()[0];
var st = layer.shader['marker-width'].style({}, {zoom: 1});
assert.equal(st.name, "ramp");
assert.equal(st.args.length, 3);
assert.equal(st.args[1].name, 'cartocolor');
assert.equal(st.args[1].args[0].value, 'Bold');
assert.equal(st.args[2].name, 'category');
assert.equal(st.args[2].args[0].value, 10);
});
it("should work with multiple operands", function(){
var css = [
'#layer {',
' marker-width: [value] * [value] * 0.5;',
'}'
].join('\n');
var shader = (new carto.RendererJS({ debug: false })).render(css);
var layer = shader.getLayers()[0];
var width = layer.shader['marker-width'].style({value: 4}, {zoom: 1});
assert.equal(width, 8);
});
it("should work with numbers", function(){
var css = [
'#layer {',
' line-dasharray: 5, 10;',
'}'
].join('\n');
var shader = (new carto.RendererJS({ debug: false })).render(css);
var layer = shader.getLayers()[0];
var dasharray = layer.shader['line-dasharray'].style({value: 4}, {zoom: 1});
assert.deepEqual(dasharray, [5, 10]);
});
it("should not throw `ReferenceError` with `=~` operator", function(){
var css = [
'#layer[name=~".*wadus*"] {',
' marker-width: 14;',
'}'
].join('\n');
assert.doesNotThrow(function () {
var shader = (new carto.RendererJS({})).render(css);
var layer = shader.getLayers()[0];
var value = layer.shader['marker-width'].style({ name: 'wadus' }, { zoom: 1 });
assert.equal(value, 14);
}, ReferenceError);
});
it("`=~` operator should support numbers", function(){
var css = [
'#layer[value=~"^10"] {',
' marker-width: 14;',
'}'
].join('\n');
assert.doesNotThrow(function () {
var shader = (new carto.RendererJS({})).render(css);
var layer = shader.getLayers()[0];
var value = layer.shader['marker-width'].style({ value: 10 }, { zoom: 1 });
assert.equal(value, 14);
}, Error);
});
});

File diff suppressed because it is too large Load Diff