Compare commits

...

615 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
Raul Ochoa
8050ec843f Release 0.15.1-cdb1 2015-02-20 19:48:15 +01:00
Francisco Dans
885849fe82 Merge pull request #9 from CartoDB/image-filters-fixes
Image filters fixes
2015-01-21 14:41:51 +01:00
Francisco Dans
14c0d3f550 adds color sequence test 2015-01-21 14:38:43 +01:00
Francisco Dans
0f46b57020 returns correct object for color list 2015-01-21 12:54:04 +01:00
Francisco Dans
152954ee70 adds additional filters to ref 2015-01-21 12:53:13 +01:00
Francisco Dans
2631c928b7 adds Makefile to produce uncompressed and minified dists 2015-01-19 14:29:11 +01:00
Francisco Dans
9e3ae6e6fd adds uncompressed version of carto 2015-01-12 18:39:49 +01:00
Francisco Dans
eae8886b95 new dist 2015-01-12 18:35:15 +01:00
Francisco Dans
00c7a631b1 Merge pull request #8 from CartoDB/icon-list
Adding method to extract all "marker-file" properties
2015-01-08 17:35:53 +01:00
Francisco Dans
bd03e0c454 adds basic test for marker-files 2015-01-08 17:30:49 +01:00
Francisco Dans
981e117731 uses proper equality checks 2015-01-08 17:10:43 +01:00
Francisco Dans
d2a557acd9 removes old reference, renames method 2015-01-08 16:40:02 +01:00
Francisco Dans
f91ac22bfc adds method to gather image asset file names 2015-01-08 16:21:24 +01:00
javi
db1cf0a8aa missing torque prop 2014-12-18 11:02:20 +01:00
javi
46b3f4857f version bump 2014-12-18 09:04:03 +01:00
javi
b5b03cc8d7 expose the default reference used 2014-12-18 09:02:13 +01:00
javi
8f86216fe0 updates the reference to work with the new parser
see db3c03bd1a
2014-12-18 09:01:59 +01:00
Raul Ochoa
bbfe3b3084 Merge pull request #6 from CartoDB/browserify_fixes
browserify fixes
2014-12-17 14:47:42 +01:00
javi
56d69ab68a browserify fixes 2014-12-17 14:35:42 +01:00
javi
e491c64ceb improved package.json 2014-12-16 18:39:18 +01:00
Raul Ochoa
26c30d2fb5 Remove doc block for non-existent param 2014-12-09 12:52:42 +01:00
Raul Ochoa
6ae21b3ee0 Merge pull request #4 from CartoDB/remove-run-coverage-on-travis
Remove coverage run from travis configuration as it is failing
2014-12-05 17:36:42 +01:00
Raul Ochoa
12d1b1a4fe Merge pull request #3 from CartoDB/console-output-on-debug-only
Only use console output if debug option is present
2014-12-05 17:36:24 +01:00
Raul Ochoa
8a5f75546f Remove coverage run from travis configuration as it is failing 2014-12-05 13:06:53 +01:00
Raul Ochoa
74d807a3ae Only use console output if debug option is present 2014-12-05 13:01:15 +01:00
Raul Ochoa
bf6537071a Check for style property 2014-12-04 19:09:43 +01:00
Raul Ochoa
1cb891ef92 Return style function from shader 2014-12-04 18:58:37 +01:00
javi
79a770f0af removed unnused files 2014-12-04 18:43:13 +01:00
javi
44610ab1c4 using torque reference 2014-12-04 18:42:30 +01:00
javi
7c35dda115 Merge remote-tracking branch 'origin/browserify' into update_to_master
Conflicts:
	.gitignore
	.travis.yml
	CHANGELOG.md
	Makefile
	README.md
	bin/mml2json.js
	lib/carto/index.js
	lib/carto/parser.js
	lib/carto/tree/call.js
	lib/carto/tree/filterset.js
	lib/carto/tree/reference.js
	lib/carto/tree/ruleset.js
	package.json
	test/errorhandling.test.js
	test/errorhandling/invalid_value.mss
	test/errorhandling/invalid_value.result
	test/errorhandling/issue297.mss
	test/errorhandling/issue297.result
	test/rendering-mss.test.js
	test/rendering/transforms.mss
	test/rendering/transforms.result
	test/specificity/demo.result
	test/specificity/filters_and_ids.result
	test/specificity/issue60.result
	test/version.test.js
2014-12-04 18:35:24 +01:00
javi
146976c8a3 using process.browser instead of type window 2014-12-04 17:29:15 +01:00
javi
ae111041dd making explicit imports 2014-12-04 17:28:54 +01:00
javi
2e00705b64 adds support for browserify
this pr also changes some code to make compatible with the browser. It exposes the global variable "carto"
One of the problems is mapnik reference, the module is not read to work with browserify since it loads modules dynamically. It does not make sense to make it compatible since mapnik-reference is not going to be used so people must to set the reference needed before parse/render:

carto.tree.Reference.setData(reference)
2014-12-04 16:29:20 +01:00
Dane Springmeyer
b19ade3850 use latest mapnik-reference@6.0.2 2014-09-26 13:10:51 -07:00
Dane Springmeyer
fe770630bb Add test for proposed syntax for passing variable to mapnik (just as string) - refs #269 2014-09-25 18:57:48 -07:00
Dane Springmeyer
8df31b4fe6 Merge pull request #373 from mapbox/expressions
Al(most) all symbolizer properties as expressions
2014-09-25 18:56:53 -07:00
Dane Springmeyer
e29b900342 call this branch v0.14.0 2014-09-25 18:17:48 -07:00
Dane Springmeyer
150e7166f6 mapnik ref: default to Mapnik 3.0.0 2014-09-25 18:13:35 -07:00
Dane Springmeyer
1d6e4a6f5d drop node v0.8.x testing on travis 2014-09-25 15:12:54 -07:00
Dane Springmeyer
52343ff833 text-name is an expression so flat string is okay - tweak issue297 to actually be invalid since text-face-name is not currently an expression 2014-09-25 15:06:56 -07:00
Dane Springmeyer
544491b91d quoting of enumerations is okay, so let this be okay to carto tests 2014-09-25 14:58:58 -07:00
Dane Springmeyer
7d3ba895f5 trim rendering results to avoid comparing ending newlines 2014-09-25 14:56:29 -07:00
Dane Springmeyer
d5bf19a64f remove back compat shim now that new mapnik-ref@6.0.0 is tagged 2014-09-25 12:37:52 -07:00
Dane Springmeyer
5890802d6d use new mapnik-reference@6.0.0 2014-09-25 12:33:06 -07:00
Saman Bemel-Benrud
26a918d2e6 Merge pull request #372 from mapbox/fix-formatting
Fix formatting
2014-09-25 12:33:48 -04:00
samanpwbb
1ba5e0035e Re-generate 2014-09-25 12:24:01 -04:00
samanpwbb
1e0657ee1e bump / add newline 2014-09-23 18:58:14 -04:00
samanpwbb
2a5c85a9d5 smaller headings 2014-09-23 18:54:19 -04:00
samanpwbb
cbd4c0250d better rules 2014-09-23 18:52:24 -04:00
samanpwbb
08ca40d3f7 try linebreaks 2014-09-23 18:51:11 -04:00
samanpwbb
9386a56464 whitespace adjustments 2014-09-23 18:47:10 -04:00
samanpwbb
d24998705d adjust docs formatting 2014-09-23 18:45:55 -04:00
Dane Springmeyer
4de08ce68d new mapnik-reference that declares all expressions for mapnik 3.x 2014-09-23 15:31:58 -07:00
samanpwbb
9f65589279 formatting fixes 2014-09-23 18:19:02 -04:00
Saman Bemel-Benrud
6f2ec7b335 Merge pull request #371 from mapbox/no-site
add minimal markdown docs
2014-09-23 18:10:13 -04:00
samanpwbb
89a282be82 add minimal markdown docs 2014-09-23 17:52:36 -04:00
Dane Springmeyer
51baca34ae if test does not error (as expected) then assert false instead of hanging 2014-09-23 14:22:59 -07:00
Young Hahn
481a0fc406 0.13.0 2014-09-04 17:22:19 -04:00
Young Hahn
60e33e609b Merge pull request #366 from mapbox/newref
Use newer mapnik ref
2014-09-04 17:17:10 -04:00
Young Hahn
339d781ca6 Use newer mapnik ref. 2014-09-04 16:34:48 -04:00
Tom MacWright
b7819be42d Merge pull request #365 from mapbox/variable-transforms-test-coverage
Add support for variable transforms - refs #163, and improve test covera...
2014-09-03 15:21:16 -04:00
Tom MacWright
c570c2cd0e Add support for variable transforms - refs #163, and improve test coverage 2014-09-03 15:12:31 -04:00
javi santana
30b4fe1fc6 Merge pull request #2 from CartoDB/update_to_master_fix
env parameter added to several function calls
2014-08-29 10:52:11 +02:00
Jan Marsch
1ddefbe8eb env parameter added to several function calls
filtercondition-value now properly quotes strings
2014-08-25 12:34:29 +02:00
Young Hahn
57ddf46813 0.12.1 2014-08-02 21:47:27 -04:00
Young Hahn
ba720bcb84 Update changelog 2014-08-02 21:42:03 -04:00
Young Hahn
d608fa93b8 0.12.0 2014-08-02 21:41:17 -04:00
Young Hahn
bb81e5c785 Merge pull request #364 from mapbox/no-xml2js
Drop xml2js and thus mml2json
2014-08-02 18:40:27 -07:00
Young Hahn
bde0d0e2ab Drop xml2js and thus mml2json. 2014-08-02 17:40:54 -04:00
Tom MacWright
145f1cc0b1 test color fns 2014-07-03 12:00:40 -04:00
Tom MacWright
2273e0174f Test colors and comments 2014-07-03 11:53:12 -04:00
Tom MacWright
b9a00ed68b Use coveralls and add coverage 2014-07-03 11:44:35 -04:00
javi
a40a87cd39 modified to support rendering order and symbolizer 2014-06-23 18:03:28 +02:00
javi
88bddbabc4 fixed makefile lowercase :( 2014-06-16 18:58:14 +02:00
javi
ef59be8abf fixed filter set (need test) 2014-06-16 18:58:04 +02:00
javi
b83d9c4037 fixed makefile 2014-06-16 18:57:50 +02:00
javi
678157b478 added getShader 2014-06-16 18:57:42 +02:00
javi
1a21c88484 added a test for filter based render 2014-06-16 18:57:24 +02:00
javi
ddb4bd338b moved code where it belongs 2014-06-16 12:54:42 +02:00
javi
6fad5676b4 removed renderer options, just return styles 2014-06-16 12:45:17 +02:00
javi
6dc65cc991 update to mapbox 0.0.11 master 2014-06-11 17:52:06 +02:00
javi
10b81aa2c8 updated doc 2014-06-06 10:04:19 +02:00
javi
2fc7473a8c added rectangle as marker-type 2014-06-06 10:04:11 +02:00
Young Hahn
b675a648c0 0.11.0 2014-05-20 17:15:09 -04:00
Young Hahn
e0fe7bce17 Merge pull request #346 from mapbox/sync
Sync
2014-05-20 17:12:17 -04:00
Young Hahn
eb2623d677 Add tests that run the carto binary. 2014-05-20 17:00:37 -04:00
Young Hahn
302d409fab Update error handling tests. 2014-05-20 16:10:31 -04:00
Young Hahn
c5d8f4510e Switch carto to be synchronous. 2014-05-20 16:01:43 -04:00
Young Hahn
cd18bea7ba 0.10.0 2014-05-15 00:25:17 -04:00
Young Hahn
55b70b86c8 Merge pull request #342 from mapbox/no-max-extent
Nuke forced maximum-extent
2014-05-14 19:52:17 -04:00
Young Hahn
d9f97d3202 Nuke forced maximum-extent property to enable buffered geometries beyond visual extent. 2014-05-08 16:37:12 -04:00
Dane Springmeyer
a52412c41e Merge pull request #340 from scw/patch-1
minor typos.
2014-04-28 18:16:09 -07:00
Shaun Walbridge
be4e687aa3 minor typos. 2014-04-28 21:02:08 -04:00
Dane Springmeyer
4b386326c7 don't output style if opacity and comp-op are defaults - refs #219 and #339 2014-04-28 16:05:59 -07:00
Dane Springmeyer
4f771ed2a5 fix #339 2014-04-28 15:57:51 -07:00
Dane Springmeyer
580e946cc0 add currently failing testcase for #339 2014-04-28 15:56:52 -07:00
Dane Springmeyer
5dc4610785 use latest mapnik-reference 2014-04-25 16:11:28 -07:00
Dane Springmeyer
9d44691111 fix #313 2014-04-17 10:38:32 -07:00
Dane Springmeyer
bb2c045325 add testcase for #315 if I'm understanding right should produce no output 2014-04-10 22:20:33 -04:00
Dane Springmeyer
9f18e9cc2c bump to v0.9.6 in package.json 2014-04-10 22:10:23 -04:00
Dane Springmeyer
c6f787f761 add v0.9.6 to changelog 2014-04-10 22:06:27 -04:00
Dane Springmeyer
cfab4f6369 fix #303 2014-04-10 22:03:09 -04:00
Dane Springmeyer
fb06bb4bb1 fix #319 2014-04-10 21:51:19 -04:00
Dane Springmeyer
87f57bdb38 Merge pull request #319 from mojodna/text-face-name-escaping
[bug] text-face-names containing &s aren't escaped properly
2014-04-10 21:50:25 -04:00
Dane Springmeyer
b9309a7f80 Merge pull request #308 from strk/aposquote
Fix escaping of single quote.
2014-04-10 21:22:50 -04:00
Dane Springmeyer
96886c73c6 use mapnik-reference@5.0.8 2014-04-10 21:07:47 -04:00
Dane Springmeyer
443b81012e upgrade underscore and xml2js 2014-04-10 21:02:00 -04:00
Dane Springmeyer
f691a47306 more readme modernization tweaks 2014-04-07 16:56:25 -04:00
Dane Springmeyer
02a657f373 modernize readme and move cascadenik mentions to https://github.com/mapbox/carto/wiki/Differences-With-Cascadenik 2014-04-07 16:49:27 -04:00
Dane Springmeyer
a5e5c045c9 add support for image-filters-inflate - refs mapnik/mapnik#2165 2014-02-27 10:41:43 -08:00
javi
c288d09dcc dist file 2014-02-25 09:53:20 +01:00
javi
5c8963f02d added source-over to reference 2014-02-25 09:53:15 +01:00
Dane Springmeyer
12a3d6cad2 start using mapnik-reference master 2014-02-19 11:57:58 -08:00
Tom MacWright
bbdb6e5988 Merge pull request #325 from iwillig/iwillig/fix-jsdocs
IW: Fix docs strings
2014-02-07 08:04:55 -05:00
Ivan Willig
f6a76ec666 IW: Fix docs strings 2014-02-07 07:41:03 -05:00
javi
83c478875f backport from master to support unsigned types 2014-01-23 17:46:13 +01:00
javi
449be47e91 dist file 2014-01-21 10:17:27 +01:00
javi
a240695b65 added torque properties to general reference 2014-01-21 10:17:22 +01:00
Dane Springmeyer
84618be5df travis: stop testing on node v0.6.x as per isaacs/npm#4379 2014-01-07 16:04:10 -08:00
samanpwbb
f333705cc2 revert rename 2013-12-09 20:39:54 -05:00
javi
f45aa875b7 fixed keywords 2013-11-26 18:39:07 +01:00
javi
834985ba81 dist files 2013-11-25 16:36:25 +01:00
javi
d1c6c9fb82 fixed rendering with variables 2013-11-25 16:36:14 +01:00
Seth Fitzsimmons
0b584a84e7 Failing test for text-face-names containing &s 2013-11-22 16:36:57 -08:00
javi
fadcb3391d added utility function eval to get the real value for a property 2013-11-18 17:31:18 +01:00
javi
66d7c45a0c fixed string eval 2013-11-18 17:31:00 +01:00
Tom MacWright
65a7d6589e Merge pull request #314 from strk/exponent-in-filter
Support exponential notation in filter values
2013-11-07 07:09:11 -08:00
Sandro Santilli
ce4c61cc7f Support exponential notation in filter values
Closes #311
Includes testcase
2013-11-07 12:48:19 +01:00
javi
677ed7122a dist file 2013-11-05 08:52:14 +01:00
javi
f3fd24583d fixed IE7 and IE8 script loading 2013-11-05 08:52:08 +01:00
Dane Springmeyer
5a7429d30d Bump to v0.9.5 with many fixes and optimizations 2013-10-25 13:23:25 -07:00
Tom MacWright
cb177b7a24 Throw a more descriptive error for invalid colors in ops. Fixes #309 2013-10-23 19:45:19 -04:00
Dane Springmeyer
15140e49ee travis: add more node versions 2013-10-18 18:07:25 -04:00
Dane Springmeyer
c4f02e4681 update developing doc 2013-10-05 16:06:14 -07:00
Sandro Santilli
60030b1e69 Fix escaping of single quote. Closes #307, updates tests. 2013-10-03 13:40:50 +02:00
Dane Springmeyer
21f03b3f9e track latest mapnik-reference 2013-09-26 16:20:26 -07:00
Dane Springmeyer
922740da5a fix direct-image-filters output and add test for image-filters 2013-09-25 17:21:27 -07:00
Dane Springmeyer
c1d750a246 Merge pull request #306 from dboze/fix-drainpool
check for millstone.drainpool only if -l flag is passed
2013-09-24 13:34:39 -07:00
Daniel Bozeman
72a79ab073 checks for millstone.drainpool only if -l flag is passed 2013-09-24 15:16:17 -05:00
Dane Springmeyer
f6dea7fcb3 drain millstone pool forcefully so we can exit quickly - refs #301 and mapbox/millstone#110 2013-09-23 18:55:38 -07:00
Tom MacWright
4b5e217107 Merge pull request #305 from yohanboniface/empty_name_invalid_xml
Fix empty name value resulting in non closed tag
2013-09-21 08:12:24 -07:00
Yohan Boniface
6b51cd370b Fix empty name value resulting in non closed tag 2013-09-21 17:05:10 +02:00
Dane Springmeyer
1c5c84587e bump mapnik-reference version and prep changelog for v0.9.5 release 2013-09-19 10:57:57 -07:00
Tom MacWright
2c9692b029 Merge pull request #294 from kapouer/patch-1
layer srs is inherited from map srs
2013-09-17 07:26:08 -07:00
Tom MacWright
3a6cae7836 Merge pull request #300 from strk/master-invalid-non-selfclosing-mml
Integrate mml parsing test for issue #297
2013-09-17 07:25:40 -07:00
Sandro Santilli
ba51c74771 Integrate mml parsing test for issue #297 2013-09-12 10:40:35 +02:00
Jérémy Lal
bd454b4b6b Add test for #294 2013-09-12 10:23:46 +02:00
Jérémy Lal
b63fe0a9af layer srs is inherited from map srs
Do not output srs attribute when srs is undefined,
since layer srs is inherited from map srs, it is optional.
2013-09-12 09:56:45 +02:00
Tom MacWright
525bdc5bef Add test for #197. Fixes #197 2013-09-11 11:26:28 -04:00
Tom MacWright
5938ebb609 Correctly deal with invalid content in non-selfclosing tags. Fixes #297 2013-09-11 11:18:40 -04:00
javi
d99c5d6c14 dist files 2013-08-28 12:37:51 +02:00
javi
2bfc8a154b uniq frames 2013-08-28 12:37:45 +02:00
javi
5a8461a6c1 fixed renderer 2013-08-28 12:12:06 +02:00
javi
dfacf064e8 added frame offset 2013-08-28 11:52:18 +02:00
javi
6562d77935 fixed js renderer 2013-08-27 17:34:55 +02:00
Tom MacWright
2ead8dafa2 Backtrack on invalid zoom. Fixes #290 2013-08-09 19:58:52 -04:00
Tom MacWright
b18f162d87 Merge pull request #293 from yohanboniface/attachment-tests
More attachment tests (cf #245)
2013-08-09 14:53:18 -07:00
Yohan Boniface
8497bd36f7 More attachment tests (cf #245) 2013-08-09 22:03:55 +02:00
Dane Springmeyer
2729aefd6e Merge pull request #292 from yohanboniface/attachment-tests
Attachment tests (cf #245)
2013-08-09 09:40:49 -07:00
Yohan Boniface
7a3b659d4e More tests for attachement (cf #245) 2013-08-09 15:39:12 +02:00
Yohan Boniface
50a27c213c Add test/rendering-mss/npm-debug.log to .gitignore 2013-08-09 15:37:06 +02:00
Dane Springmeyer
d1af35763a add currently failing test for #290 2013-08-07 20:46:32 -04:00
Tom MacWright
92b67a0a73 Test for #288 to keep it closed, space before zoom bug 2013-08-06 11:45:02 -04:00
Tom MacWright
d170825685 Merge pull request #289 from javisantana/master
fixes mapbox/carto#288
2013-08-06 08:42:38 -07:00
javi
d9e412696d dist files 2013-08-05 18:16:26 +02:00
javi
08db949fbf fixes mapbox/carto#288 2013-08-05 18:16:10 +02:00
javi
d788fb8f07 fixes mapbox/carto#288 2013-08-05 18:12:42 +02:00
javi
e32aeb9fbb dist files 2013-07-31 16:53:38 +02:00
javi
d7d49214cd support for uris in javascript compiler 2013-07-31 16:53:33 +02:00
javi
ca42300648 dist 2013-07-31 12:43:09 +02:00
javi
393fc249b2 be able to set the the reference 2013-07-31 12:33:39 +02:00
javi
2ae7743c7d created assert stub for the browser 2013-07-31 12:33:19 +02:00
javi
1d752708c4 improved makefile 2013-07-31 12:32:55 +02:00
javi
4f963b8bb3 error management, an exception is thrown when there is a parse error 2013-07-29 13:55:31 +02:00
javi
bc9d67b328 dist file 2013-07-29 12:43:05 +02:00
javi
656b6db9a3 added rendered to transform cartocss to javascript object 2013-07-29 12:42:59 +02:00
javi
a57a162248 fixed build 2013-07-29 12:42:41 +02:00
Dane Springmeyer
60396cbeef add support for direct-image-filters 2013-07-24 22:03:13 -04:00
Dane Springmeyer
f88711db72 add scale-hsla function to work with latest mapnik-reference: 66502bfde0 2013-07-24 21:59:05 -04:00
Tom MacWright
4f0e998dab Merge branch 'master' of github.com:mapbox/carto 2013-07-16 14:09:09 -04:00
Tom MacWright
fd3338ccb5 Add developing docs 2013-07-16 14:08:54 -04:00
javi
3d87cd490d fixed global imports 2013-07-11 16:14:32 +02:00
Dane Springmeyer
1d637717d5 fix typo - refs #245 2013-07-09 15:41:00 -04:00
Dane Springmeyer
45fae55ac0 add more variations (currently passing) on the issue #284 test 2013-07-09 15:33:42 -04:00
Tom MacWright
bbeff81a16 Fix byFilter regression 2013-07-09 15:00:13 -04:00
Tom MacWright
afac483b35 More comments
:
2013-07-09 14:38:27 -04:00
Tom MacWright
66b0c1ff7a Update mocha and optimist dependencies 2013-07-09 14:03:59 -04:00
Dane Springmeyer
93264c6e41 add test for #284 - passes as 6eda91a and fails in master 2013-07-02 19:00:33 -04:00
javi
97f03a2eb3 added functions to browser build 2013-06-26 14:50:53 +02:00
Young Hahn
45b5c107af Merge pull request #279 from mapbox/dataonly
Allow Layer-only rendering without corresponding styles
2013-06-24 08:58:48 -07:00
javi
99ee75ab8b added tree functions to cart build for browser 2013-06-24 17:15:35 +02:00
Tom MacWright
84e0628a8e Merge pull request #280 from mapbox/custom-params
Allow custom map parameters
2013-06-17 06:16:50 -07:00
Young Hahn
15ab78a3a8 Allow custom map parameters. 2013-06-14 18:25:55 -04:00
Young Hahn
bb153521e2 Allow Layer-only rendering without corresponding styles. 2013-06-14 12:21:13 -04:00
Young Hahn
ff0fc2b1c8 Revert "Dimensions." Refs #258.
This reverts commit 0f65b869fd.
2013-06-14 10:47:43 -04:00
javi
264925324f updated reference to 2.1 2013-06-13 15:42:47 +02:00
javi
d81b4b9d18 browser version working 2013-06-10 13:26:19 +02:00
Tom MacWright
4cc262b563 Merge pull request #277 from mapbox/fix-attachment-order-breakage
fix #247
2013-06-01 09:13:50 -07:00
Dane Springmeyer
4203578093 fix #247 2013-05-31 17:17:36 -07:00
Dane Springmeyer
cfc90da91e Add regression test for issue #247 - broken in master at 4d4abb2, works in 6eda91a 2013-05-31 17:15:45 -07:00
Dane Springmeyer
8786bf51c7 bin/carto: be careful not to resolve inline stylesheet data 2013-05-29 15:52:59 -07:00
Dane Springmeyer
ae95aa7575 fix repository in package.json 2013-05-22 19:16:35 -07:00
Tom MacWright
b113bfea99 Prevent image filter duplication. Fixes #270 2013-04-16 10:24:48 -04:00
Tom MacWright
0f65b869fd Dimensions. 2013-04-15 16:10:00 -04:00
Tom MacWright
c21a763dc7 Refactor physical unit support 2013-04-15 15:59:17 -04:00
Tom MacWright
207b120dee Fix dimension.js code style 2013-04-15 15:49:39 -04:00
Tom MacWright
49b2324ea1 Handle errant zoom-as-field better. Fixes #269. 2013-04-15 15:45:53 -04:00
Dane Springmeyer
314cef0c75 Merge pull request #271 from mapbox/colorize-alpha
Support variable-length arguments in functions with -1
2013-04-08 13:08:55 -07:00
Tom MacWright
04b1602310 Support variable-length arguments in functions with -1 2013-04-05 16:10:27 -04:00
Tom MacWright
2f76fec686 Do not unnecessarily escape < and chain replacements 2013-03-29 11:47:04 -04:00
Tom MacWright
435452ba50 Merge pull request #265 from strk/master-quote-amp
Quote ampersend chars in XML text. Closes #263.
2013-03-29 08:44:59 -07:00
Tom MacWright
5d626d3c10 Do not output references to empty styles. Fixes #244 2013-03-27 11:59:09 -04:00
Tom MacWright
49c81edd3a Tolerate single-stop colorizers. Fixes #251 2013-03-27 11:48:47 -04:00
Tom MacWright
05797dd711 Tolerate commas in values. Fixes #266 2013-03-27 11:30:51 -04:00
Tom MacWright
86abdcd700 Fix #262,
when selectors are exactly the same, still push them into the list.
2013-03-27 10:44:12 -04:00
Sandro Santilli
d6585d3691 Quote all needed XML chars. See #263.
Includes testcase.
2013-03-27 12:36:25 +01:00
Sandro Santilli
acf94e5fab Quote ampersend chars in XML text. Closes #263.
Includes testcase.
2013-03-22 11:11:16 +01:00
Tom MacWright
60661b68c3 Add failing identity nesting case, refs #262 2013-03-17 19:31:22 -04:00
Tom MacWright
5e2ea67df9 Fix repeated comments in selectors. Fixes #260 2013-03-13 11:27:00 -04:00
Tom MacWright
26dbcf3ca3 Fix test for missing brackets 2013-03-11 16:34:43 -04:00
Tom MacWright
3382bfa29f Merge branch 'missing-bracket' 2013-03-11 16:32:48 -04:00
Tom MacWright
7ac2d81062 Merge pull request #257 from tomhughes/master
Add a man page for carto
2013-03-03 11:30:48 -08:00
Tom Hughes
caa639beb8 Add a man page for carto 2013-03-03 19:05:47 +00:00
Dane Springmeyer
039031b68d fix failing tests in master 2013-03-01 15:22:22 -05:00
Dane Springmeyer
19bf87cf3a fix expected result so test passes because it appears #239 is not actually a regression 2013-03-01 15:14:23 -05:00
Dane Springmeyer
8efc1c5d5e output both path to json mismatches to make debugging test failures easier 2013-03-01 15:08:29 -05:00
Dane Springmeyer
e8566e817e point to master mapnik-reference 2013-03-01 15:08:05 -05:00
root
7deb1b86e0 Merge branch 'layerzoom' 2013-02-26 11:40:58 -05:00
Tom MacWright
c5a67fa938 Missing bracket. Refs #254 2013-02-25 09:17:56 -05:00
Young Hahn
be78202e0b Allow layers to be rendered without datasources. 2013-02-21 15:29:33 -05:00
Tom MacWright
0e0bac0e5c Fix fadeout 2013-02-18 17:34:49 -05:00
Dane Springmeyer
6ca0c705c8 add raster colorizer (https://github.com/mapnik/mapnik/wiki/RasterColorizer) support to master - closes #5 - refs mapbox/tilemill#761 - depends on 41772edbd7 2013-02-12 11:17:56 -08:00
Tom MacWright
4c044f93fe Merge pull request #250 from tomhughes/master
Update xml2js dependency
2013-02-10 12:49:43 -08:00
Tom Hughes
48d89889fe Update to use v0.2.x of xml2js 2013-02-10 20:46:12 +00:00
root
47464fc18d Convert minzoom/maxzoom parameters on Layer properties. 2013-02-04 16:42:57 -05:00
Dane Springmeyer
74fa914c8c updated raster colorizer support 2013-02-01 15:57:29 -05:00
Tom MacWright
55fbafe0d0 Merge branch 'master' of github.com:mapbox/carto 2013-01-31 08:54:41 -05:00
Tom MacWright
5fa4478d40 Fix rgba handling, fixes #246 2013-01-31 08:54:27 -05:00
Dane Springmeyer
633754306e add testcase for issue #239 2013-01-23 16:59:22 -08:00
Dane Springmeyer
3f70b8a36c avoid unneeded whitespace in style XML output 2013-01-23 16:57:27 -08:00
Tom MacWright
16f60edc50 Merge pull request #243 from mapbox/localize-on-demand
Localize on demand
2013-01-23 16:16:39 -08:00
Dane Springmeyer
76b271ebc5 change carto command line to only try to localize mml docs if explicitly requested 2013-01-23 13:01:55 -08:00
Tom MacWright
3fd91ccb6b Predo changelog 2013-01-23 16:01:31 -05:00
Tom MacWright
e2764d12f1 Add rendering tests for units 2013-01-23 16:01:03 -05:00
Dane Springmeyer
8c51c59fe9 Merge pull request #240 from stefanklug/units
Add unit support to css
2013-01-23 12:47:30 -08:00
Stefan Klug
cf8c11f038 replace dpi by ppi 2013-01-23 10:20:04 +01:00
Tom MacWright
31504baabe Merge pull request #241 from stefanklug/extent
don't overwrite maximum-extent when specified in mml
2013-01-21 09:50:41 -08:00
Stefan Klug
8f8c1ad39b remove debug output 2013-01-21 18:43:49 +01:00
Stefan Klug
494c07d383 don't overwrite maximum-extent when specified in mml 2013-01-21 18:28:10 +01:00
Stefan Klug
de40fd4e42 added dpi parameter to carto bin
use optimist to parse command line arguments
2013-01-21 16:40:16 +01:00
Stefan Klug
6a5309e22e fixed regression introduced in last commit 2013-01-21 16:40:16 +01:00
Stefan Klug
56ac678c0a add support for the units m, mm, cm, pt, pc
these are converted to pixels depending on env.dpi (defaulting to 90.714)

adding and subtracting percentages works also
2013-01-21 16:40:15 +01:00
Tom MacWright
1769cb4a59 Merge branch 'master' of github.com:mapbox/carto 2013-01-16 17:42:31 -05:00
AJ Ashton
9deb60cd08 Fix numeric check regex 2013-01-16 17:15:28 -05:00
Tom MacWright
a687bec9b6 Merge pull request #236 from stefanklug/fix-global-install
fix carto to work as global node module
2013-01-11 05:41:46 -08:00
Stefan Klug
c9d88add12 fix carto to work as global node module 2013-01-11 13:17:58 +01:00
Tom MacWright
6ce476d2a2 parseFloat sucks 2013-01-08 16:42:50 -05:00
Dane Springmeyer
0d686bb8d8 fixup code comments - amends 87d4f9627b 2013-01-06 11:58:14 -08:00
Tom MacWright
f3bde1fde3 No more in operator, checking for undefined is much much faster 2013-01-04 18:45:19 -05:00
Dane Springmeyer
bfc9d40f43 fix testcase issue # 214 -> 204 - previously incorrectly referenced 214 - refs #214 and #204 2013-01-03 18:47:10 -08:00
Tom MacWright
f0e245183a Fix hang around massive stylesheets, fixes mapbox-base 2013-01-03 18:43:12 -05:00
Tom MacWright
4d4abb27b5 clean up renderer, possibly fix issue with attachments, work on function names 2013-01-03 18:32:04 -05:00
Tom MacWright
6eda91a541 Fixing basic jshint issues 2013-01-03 16:29:22 -05:00
Tom MacWright
87d4f9627b Rename eval to ev to fix many jshint problems and future safari problems 2013-01-03 16:19:25 -05:00
Tom MacWright
29b641c72d Detect and report basic self-conflicting filters.
This does not yet report anything for more complex cases. We were also testing for
tolerance of self-conflicting filters, which this breaks, in
partial_overrides. This patch may want to wait for warning reporting
since it may break stylesheets in the wild.
2013-01-03 15:06:07 -05:00
Tom MacWright
6f687ff9e3 Remove throws in function evaluation and handle color function arguments
correctly.
2013-01-03 13:47:51 -05:00
Tom MacWright
ece3eb3b0e Remove throw in zoom evaluation 2013-01-03 13:20:49 -05:00
Tom MacWright
d97286de57 Merge branch 'master' of github.com:mapbox/carto 2013-01-03 10:12:02 -05:00
Tom MacWright
c1e8c3b8f3 Support mapnik keywords in filters and error on bare keywords. Fixes 2013-01-03 10:11:30 -05:00
Dane Springmeyer
3cefa968a0 remove Mapnik2 name in readme for more explicit version string 2013-01-02 10:50:40 -08:00
Tom MacWright
b7773c6452 Tolerate everything but ] in field names. Fixes #230 2013-01-02 11:31:17 -05:00
Tom MacWright
9ed9c2b028 Merge pull request #231 from mapbox/condense
Condense
2013-01-02 08:25:44 -08:00
Tom MacWright
fc500db69b Pay attention to types in filterset, fixes #229. 2013-01-02 11:22:25 -05:00
Tom MacWright
0233c523ea Be backwards compatible for now. 2012-12-29 12:40:31 -05:00
Tom MacWright
4b8256e0f8 Fixing new parser reference to makeError. Fixes #228 2012-12-27 18:19:12 -05:00
Tom MacWright
551571fc17 Refactor 2012-12-27 18:11:48 -05:00
Tom MacWright
43073fa1e8 Further performance tuning, about a 10% improvement on
openstreetmap-carto
2012-12-26 22:23:04 -05:00
Tom MacWright
baab7dd0ec Simplify and unify parser code style 2012-12-26 18:32:47 -05:00
Tom MacWright
f87e8adc95 Unswap specificity tests: ordering is now same as master 2012-12-26 18:24:17 -05:00
Tom MacWright
3f31bcbe5f Fix absolute paths in other symbolizer types 2012-12-26 18:19:39 -05:00
Tom MacWright
9f00195100 Use for in rather than object create 2012-12-26 18:12:01 -05:00
Tom MacWright
6fed91d728 Update chunker, fixes #184. 2012-12-26 17:48:01 -05:00
Tom MacWright
a8133e0d77 Bump mocha version to current 2012-12-26 17:40:33 -05:00
Dane Springmeyer
06b147323f add currently failing test for handling of quoted attributes with : 2012-12-21 18:57:56 -08:00
Dane Springmeyer
47882ccb00 Add another empty rendering test 2012-12-21 18:37:39 -08:00
Dane Springmeyer
73e5178f1f re-enable the standalone-mss rendering tests and fix them by avoiding empty styles and empty rules - closes #219 2012-12-21 18:34:30 -08:00
Dane Springmeyer
8846bfbbcd use console.time for mss benchmarking 2012-12-21 17:49:53 -08:00
Dane Springmeyer
16db1c5b03 fix MSS standalone renderer, fixing failing error handling tests after f6c07afee6 2012-12-21 17:36:18 -08:00
Dane Springmeyer
800122e1af error handling tests should rethrow if the test itself does not throw at all 2012-12-21 17:35:48 -08:00
Dane Springmeyer
3ac86f5fd3 use tree off carto object for consistency 2012-12-21 16:39:29 -08:00
Dane Springmeyer
bd17eed9f5 fix race condition in specificity tests and change expected result so all specificity tests now pass 2012-12-21 16:29:59 -08:00
Dane Springmeyer
c707188ed5 serialize json representation rather than mapnik xml for easier comparision 2012-12-21 16:12:02 -08:00
Dane Springmeyer
539d293388 fix failing zoomselector test by adjusting expected test output as it appears we were testing a bug 2012-12-21 16:11:35 -08:00
Dane Springmeyer
12cd05764b Revert "partially fix regex nest test"
This reverts commit 92d239b7f8.
2012-12-21 15:39:18 -08:00
Dane Springmeyer
92d239b7f8 partially fix regex nest test 2012-12-21 15:32:59 -08:00
Dane Springmeyer
a7c1e0bc49 Merge branch 'condense' of github.com:mapbox/carto into condense 2012-12-21 15:22:25 -08:00
Dane Springmeyer
a55b4ca0e9 disable mss tests for now since they are intentionally failing - lets focus on unintential failures 2012-12-21 15:22:14 -08:00
Tom MacWright
958b61c343 Fix specificity tests 2012-12-21 18:17:59 -05:00
Dane Springmeyer
0a9cb6afe7 Merge branch 'condense' of github.com:mapbox/carto into condense 2012-12-21 15:16:26 -08:00
Tom MacWright
67b66b5568 Fix zoom interpretation in helper 2012-12-21 18:15:31 -05:00
Dane Springmeyer
9967393820 cleanup old failures that now pass 2012-12-21 15:14:36 -08:00
Tom MacWright
75f55f8d04 Update specificity test helper 2012-12-21 18:13:36 -05:00
Dane Springmeyer
2aba917e3d merge master into condense to bring in further race condition fixes in errorhandling tests 2012-12-21 15:13:22 -08:00
Dane Springmeyer
f2a6922586 fix race condition in renderer test and write failures instead of diffing since this can crash on large diffs like the fontset dupe test 2012-12-21 15:05:17 -08:00
Tom MacWright
f6c07afee6 Refine heap performance. Switch back to underscore since lodash does npm
all wrong.
2012-12-21 16:53:38 -05:00
Tom MacWright
22f5d0cc45 Support : in column names 2012-12-20 17:49:11 -05:00
Tom MacWright
f4722f516e Fix tiny, critical bug 2012-12-20 17:42:41 -05:00
Tom MacWright
607c4dba5a Be a little safer with naming 2012-12-20 17:35:03 -05:00
Tom MacWright
b3b7fec337 Compare filters numerically if possible 2012-12-20 15:45:43 -05:00
Tom MacWright
5145655c46 Remove oneline tests for now, update external image 2012-12-20 15:37:12 -05:00
Tom MacWright
a106e2768f Revert "Update chunker to less.js's version. Fixes #184"
This reverts commit 78b3a5a5d4.
2012-12-20 14:36:04 -05:00
Tom MacWright
04cf9013a8 Rewrite zoom filters as objects, support variables in zoom definitions. 2012-12-20 12:37:38 -05:00
Tom MacWright
3c4baaf8cb Update filtervariable test 2012-12-20 11:13:18 -05:00
Tom MacWright
667fd483cc Merge pull request #220 from mapbox/mss-renderer
Mss renderer
2012-12-19 16:35:02 -08:00
Tom MacWright
f693f062ec Allow for filters which compare fields. 2012-12-19 18:59:16 -05:00
Tom MacWright
89f8edbddc Optimize fontset detection 2012-12-19 18:43:05 -05:00
Tom MacWright
73f544333a Remove straggling import stuff 2012-12-19 18:24:35 -05:00
Tom MacWright
d4fe84a7cf Simplify, use lodash where possible 2012-12-19 17:43:35 -05:00
Tom MacWright
84a34be10a Add filter test 2012-12-19 17:37:17 -05:00
Tom MacWright
180cd0cc6e Add field test 2012-12-19 17:34:34 -05:00
Tom MacWright
78ea179c46 Add filterset.eval, support variable filter combinations. 2012-12-19 17:29:53 -05:00
Tom MacWright
9eee907467 Simplify filter system, support numeric variables in filters 2012-12-19 17:01:35 -05:00
Tom MacWright
8603799fa7 Add oneline test 2012-12-19 14:19:13 -05:00
Tom MacWright
78b3a5a5d4 Update chunker to less.js's version. Fixes #184 2012-12-19 14:18:35 -05:00
Tom MacWright
71547d059f Merge and fix renderer optimization' 2012-12-19 12:53:09 -05:00
Tom MacWright
0ebfa5f258 Revert renderer optim 2012-12-19 12:46:06 -05:00
Tom MacWright
4ef52a82af Profile and optimize reference lookups 2012-12-19 12:37:48 -05:00
Tom MacWright
ec0699dd45 Cleanup 2012-12-19 11:53:10 -05:00
Dane Springmeyer
e7ba697fc4 Deeper benchmark output 2012-12-18 19:49:38 -08:00
Dane Springmeyer
0d294c1075 add standalone mss rendering tests for desired empty style dropping behavior - refs #219 2012-12-18 18:25:13 -08:00
Dane Springmeyer
23d11fedc6 skip mss that is valid 2012-12-18 16:40:11 -08:00
Dane Springmeyer
e3e2b42277 do not overwrite filename for mss 2012-12-18 16:25:00 -08:00
Dane Springmeyer
675158cba9 call path.basename on mss filename input 2012-12-18 16:24:14 -08:00
Dane Springmeyer
1e6ede278f add to ignores 2012-12-18 16:14:35 -08:00
Dane Springmeyer
75875e2781 add tests for #214 - test 'a' currently fails for me 2012-12-18 16:12:47 -08:00
Dane Springmeyer
cd948535a5 add test for #218 2012-12-18 16:09:31 -08:00
Dane Springmeyer
33d325b0fc return errors if any have occurred - refs #128 2012-12-18 16:07:50 -08:00
Dane Springmeyer
4825f9aee8 only call done when the test is actually finished, whether error or not - refs #210 2012-12-18 16:02:44 -08:00
Dane Springmeyer
7d7cc2653c add tests specifically of error handling of parsing standalone mss files 2012-12-18 15:51:39 -08:00
Dane Springmeyer
0c04ad07b6 move the mss standalone renderer into core to make it easier to maintain - and add more fine grained benchmark output 2012-12-18 15:44:39 -08:00
Tom MacWright
1c569ce39d Simplify style.js 2012-12-18 17:41:08 -05:00
Tom MacWright
f41312597c Move .is to prototype, start testing quoteds 2012-12-18 17:16:40 -05:00
Tom MacWright
4181df378f Rewrite filterset in more vanilla-javascript style, add more tests 2012-12-18 16:50:53 -05:00
Tom MacWright
13bc8c3488 Start on a condense branch. This is related to #20
See https://gist.github.com/4329932 for more detail.
2012-12-18 13:27:04 -05:00
Tom MacWright
4c7af7f492 Merge pull request #217 from mapbox/stderr
Use stderr for error messages, use console instead of util
2012-12-18 08:57:23 -08:00
Tom MacWright
19ac7b2fb0 Use stderr for error messages, use console instead of util 2012-12-15 19:24:44 -05:00
Tom MacWright
217498a207 Push to 0.9.4 with regex nesting fix 2012-12-06 12:50:04 -05:00
Tom MacWright
25a2940ebc Fix regex nesting inheritance 2012-12-06 12:48:09 -05:00
Tom MacWright
2fcbdaacfd Stricter equality for empty string arguments to functions, and tests. 2012-11-29 15:17:06 -05:00
Dane Springmeyer
8ef4efbe39 fix handling of no errors from error handling tests - closes #210 2012-11-29 11:36:39 -08:00
Dane Springmeyer
a21a195f1b log layer id when benchmarking 2012-10-24 16:19:48 -07:00
Dane Springmeyer
dc798c0e07 Merge branch 'master' of github.com:mapbox/carto 2012-10-24 13:06:02 -07:00
Dane Springmeyer
410f47b5ce report correct, current carto version via package.json - closes #205 2012-10-24 13:04:25 -07:00
Tom MacWright
1eaf7e4fbf Remove & from old-style less combinators in the parser 2012-10-21 21:08:11 -04:00
Tom MacWright
e1a484018f Merge pull request #200 from mapbox/mss-out
add mss support to carto command line program
2012-10-21 15:04:18 -07:00
Tom MacWright
8fc2c06b45 Evaluate calls before keywords. Fixes #203 2012-10-21 17:57:45 -04:00
Tom MacWright
5cbe78457c Merge pull request #201 from mapbox/nested-classes
Fix nested class behavior.
2012-10-15 19:21:12 -07:00
Dane Springmeyer
840e16a3f2 improve carto command line to be able to handle just converting mss chunks - mostly for easy testing 2012-10-15 13:14:47 -07:00
Tom MacWright
f9fcffeeba Fix nested class behavior. This changes the output of complex_cascades
and changes current behavior, but to the best of my sleep-deprived
cognition I think we were testing for incorrect behavior.
2012-10-15 10:16:27 -07:00
Tom MacWright
5978135bb6 Finalize renaming 2012-10-09 21:44:59 -04:00
Tom MacWright
dde52e43aa Note gravitystorm 2012-10-09 21:43:44 -04:00
Tom MacWright
1efa60757b Update readme 2012-10-09 21:42:40 -04:00
Tom MacWright
13448ee297 Bump package 2012-10-09 21:35:24 -04:00
Tom MacWright
cce8440755 Push changelog 2012-10-09 21:35:04 -04:00
Tom MacWright
00e797dc2d Did you mean for properties, with proper levenstein distance for
better matching. Fixes #199
2012-10-09 21:30:42 -04:00
Tom MacWright
e7658c618e Much better error reporting, did you mean 2012-10-09 09:09:17 -04:00
Tom MacWright
45657268f9 More verbose and informative error messages, especially when dealing
with keyword values. Fixes #109
2012-10-08 18:39:09 -04:00
Tom MacWright
1d9b5d93ca Permit keywords as strings. Fixes #193 2012-10-08 18:28:27 -04:00
Tom MacWright
4b7fc5fb57 Fix concatenation of strings with variables and other strings 2012-10-08 18:12:54 -04:00
Tom MacWright
67f007064a Report unclosed strings as well. Fixes #188" 2012-10-08 18:03:18 -04:00
Dane Springmeyer
896b247fe9 avoid wrapping in CDATA if special inline 'Format' is used - closes #196 2012-10-05 17:03:13 -07:00
Tom MacWright
3d216f6295 Test for #192 to keep it closed 2012-09-29 16:54:10 -04:00
Tom MacWright
afdac4b94f Merge pull request #192 from gravitystorm/patch-2
Include the instance name in the rule id (fixes #177)
2012-09-29 13:53:31 -07:00
Tom MacWright
e055301fa8 Merge pull request #191 from gravitystorm/patch-1
Remove legacy comment
2012-09-29 13:51:49 -07:00
Andy Allan
25343589ab Include the instance name in the rule id
This fixes #177. The addRules method of definition.js checks rule inheritance using the id of the rule, and only adds rules from ancestors if the id is different. However, without this patch, foo/line-color and bar/line-color have the same id and therefore aren't recognised as applying to different instances.
2012-09-28 16:37:00 +02:00
Andy Allan
0560ae9f86 Remove legacy comment
Remove the comment for the extend method, which was removed back in 7c81eb353d
2012-09-28 16:27:23 +02:00
Tom MacWright
4a9b5b5939 Remove directives. Fixes #157 2012-09-21 15:12:39 -04:00
Tom MacWright
101026fe3c Fix parsing of selectors with comments between elements.
This brings over cloudhead/less.js@3331637913 and fixes #179
2012-09-21 14:52:47 -04:00
Tom MacWright
912988e174 Fix parsing of whitespace in calls.
This brings over cloudmade/less.js@333d1def4 to permit whitespace in
front of arguments of function calls, and tweaks the related test to
make sure it stays that way. This fixes #154
2012-09-21 14:27:19 -04:00
Tom MacWright
033a5cd5a5 Add missing MML file for transforms test 2012-09-21 14:20:21 -04:00
Dane Springmeyer
6a794cd610 add raster-mesh-size test and bump to 0.9.2 2012-09-06 22:33:27 -07:00
Dane Springmeyer
63152ef4e3 fix tests 2012-09-06 22:31:31 -07:00
Dane Springmeyer
31ea5ff6c6 use mapnik-reference tag 2012-09-06 22:23:51 -07:00
Tom MacWright
03867bf61b Point to numbertypes branch of mapnik-reference 2012-09-04 12:58:32 -04:00
Tom MacWright
829fe84baf Add unsigned support and test code 2012-09-04 12:57:25 -04:00
Dane Springmeyer
f4132221d6 use latest mapnik-reference 2012-08-30 17:16:38 -07:00
Dane Springmeyer
5e5de824de bump to 0.9.1 2012-08-15 15:04:49 -07:00
Tom MacWright
02b8cae3c7 Remove anonymous type. Fixes #148 2012-08-15 17:27:22 -04:00
Tom MacWright
0a10327fb1 Update mocha version and use spec reporter 2012-08-15 17:20:43 -04:00
Tom MacWright
bd3073bbed Remove variable is for the time being 2012-08-15 17:12:37 -04:00
Tom MacWright
73a7c6b87a Merge pull request #173 from mapbox/fix-mapnik-functions
Fix mapnik functions
2012-08-15 14:09:02 -07:00
Dane Springmeyer
788aa03e80 use latest mapnik-reference 2012-08-14 13:49:20 -07:00
Dane Springmeyer
874f51870a adapt to Mapnik 2.1 syntax for new features that are settling out - specifically now using commas to separate chained transforms or image-filters and using () in agg-stack-blur image filter - refs #171 and #140 2012-08-14 13:26:33 -07:00
Tom MacWright
26e690dc0d Make more types identify themselves with is, don't tolerate
incorrectly-cased functions.
2012-08-13 17:37:51 -04:00
Tom MacWright
5b024a1465 Fix code style. 2012-08-13 11:15:45 -04:00
Tom MacWright
0c4de0dc38 Merge pull request #170 from strk/throw_on_unsupported_mapnik_version
Error out on unsupported mapnik version
2012-08-13 08:14:31 -07:00
Sandro Santilli
0377e809ea Have setVersion return a bool, rather than throwing an exception 2012-08-13 10:44:59 +02:00
Sandro Santilli
b24288b7ef Have render() catch Reference errors by invoking the user callback 2012-08-10 19:33:14 +02:00
Sandro Santilli
42e9c476f5 Throw an exception when an unsupported mapnik version is requested 2012-08-08 09:31:13 +02:00
Tom MacWright
dd8169ea84 Rename sys to util to fix last commit 2012-08-07 11:32:07 -04:00
Dane Springmeyer
6d8c963af2 remove unused sys require 2012-08-02 14:48:04 -07:00
Dane Springmeyer
53e7ece4cc finish update to use mapnik-reference 0.4.0 2012-08-01 18:16:48 -07:00
Dane Springmeyer
3710517fdc bump to 0.9.0 with breaking rename changes in experimental features via mapnik-reference 2012-08-01 17:55:56 -07:00
javi
6fb339e21c test files 2012-07-25 17:42:49 +02:00
javi
4bb0fc0ebe changed some stuff to get cart working on the browser 2012-07-25 17:42:41 +02:00
Tom MacWright
bfc92c26bc Stubbing out assert, trying to get higher-level stylesheets 2012-07-18 17:27:19 +02:00
Tom MacWright
f11259d734 Carto, with a few small tweaks, starting to operate in-browser 2012-07-18 17:27:19 +02:00
cloudhead
2481c21365 deleted compiler.jar 2012-07-18 17:23:52 +02:00
cloudhead
e7a3d65cad Added make min for minification though google's js closure compiler. 2012-07-18 17:23:51 +02:00
Dane Springmeyer
f52f7da5eb push to 0.8.1, mapnik-reference to ~3.1.0 2012-06-29 13:20:09 -07:00
Tom MacWright
4841e40a72 Rename to CartoCSS 2012-06-28 14:59:14 -04:00
Tom MacWright
262aa3c1df Bump package to 0.8.0 2012-06-28 14:58:08 -04:00
Tom MacWright
05718df884 Push to 0.8.0, push mapnik-reference dependency to 3.0.0. Changes are
detailed in the CHANGELOG and are extensive; this is
backwards-compatible and introduces new field and functions types
in reference.json which allow more powerful use of transforms,
image filters, and expressions in Mapnik
2012-06-28 14:17:17 -04:00
Tom MacWright
d1574100af Merge 2012-06-28 13:58:50 -04:00
Tom MacWright
255b0219b6 Merge 2012-06-28 13:58:04 -04:00
Tom MacWright
d57006dd97 Add advanced field test, fixes for combining fields with colors that otherwise go back to mapnik 2012-06-27 14:58:56 -04:00
Dane Springmeyer
b165c2080d add required/used mapnik-reference to 0.7.0 changelog - closes #155 2012-06-27 11:37:56 -07:00
Dane Springmeyer
f53a226505 update changelog in prep for 0.7.1 2012-06-27 11:19:49 -07:00
Dane Springmeyer
9a27483af6 use mapnik-reference 2.2.x 2012-06-27 11:07:38 -07:00
Tom MacWright
651a650f2e Simplify quoted implementation, move field logic up to
operate level, add Literal, add tests for transforms with fields.
2012-06-26 17:54:01 -04:00
Tom MacWright
a66fa6c462 Fix quoting situation around fields 2012-06-26 17:03:34 -04:00
Tom MacWright
1f28f7b79e Fix error checking around string operations, fix quoting, though it is
still broken in the quoted + field + quoted case.
2012-06-26 15:54:36 -04:00
Tom MacWright
672a271cbe Change field result, fix building height result. 2012-06-26 14:41:13 -04:00
Tom MacWright
d244fd0da8 Sys is now util, switch to util 2012-06-26 14:38:51 -04:00
Tom MacWright
d7e6cdf082 Improve operating with mapnik field types 2012-06-26 12:57:10 -04:00
Tom MacWright
bdfcd7aa6d Note some new features in the changelog, add modulus operation 2012-06-26 12:37:05 -04:00
Tom MacWright
28ab872e72 Support operations between strings and fields, support concatenating
strings.
2012-06-26 12:14:13 -04:00
AJ Ashton
ff0f0fc9f9 No longer writes out layer status if it is undefined 2012-06-25 16:01:42 -04:00
AJ Ashton
991feb9ba3 Prevents undefined layer status 2012-06-25 13:29:18 -04:00
Tom MacWright
6132d82359 Merge pull request #149 from mapbox/layer-status
Add ability to enable/disable layers
2012-06-25 09:51:10 -07:00
Dane Springmeyer
51c3ab65d7 Merge pull request #152 from strk/make_check
Provide GNU standard Makefile target "check" (#151)
2012-06-25 09:43:47 -07:00
Tom MacWright
ea89af6ae2 Add field type, parsing, and failing test. 2012-06-22 18:35:00 -04:00
Tom MacWright
8a97258025 Point to transform-functions branch 2012-06-22 18:10:16 -04:00
Tom MacWright
590b2092f7 Point to tarball, not zip file 2012-06-22 18:08:35 -04:00
Tom MacWright
265554813b Update results for temporary image-transform renaming, update
reference to mapnik-reference to point to the transforms branch,
prep a changelog message
2012-06-22 18:06:15 -04:00
Tom MacWright
a941bda795 Add backwards compatibility for transforms tests 2012-06-22 12:38:20 -04:00
Tom MacWright
e27ce12ea0 Support variables and expressions as the arguments to functions. 2012-06-22 12:32:36 -04:00
Tom MacWright
593092fedd Support backwards-compatibility for functions type specified as
strings, like in `point-transform`
2012-06-22 12:30:17 -04:00
Sandro Santilli
481b19f30e Provide GNU standard Makefile target "check" (#151) 2012-06-22 11:57:18 +02:00
Tom MacWright
f00c048b19 Merge pull request #143 from danzel/master
Add -n/--nolocalizing option to skip calling millstone when you don't want to
2012-06-21 06:37:03 -07:00
Tom MacWright
42784dcca2 Revert last, still in transforms branch 2012-06-20 19:04:18 -04:00
AJ Ashton
7611f717c8 Add ability to enable/disable layers 2012-06-20 18:36:10 -04:00
Tom MacWright
5ff9051104 Support named transforms in Carto with variable interpolation. Removes
Anonymous type.
2012-06-20 17:34:27 -04:00
Tom MacWright
fd35ddb5c6 Remove remaining references to mixins 2012-06-20 14:31:59 -04:00
Tom MacWright
4c3aa03012 Version 0.7.0: adds style level opacity support. Fixes #146. 2012-06-19 17:33:42 -04:00
Tom MacWright
0f86eb910e Bump mapnik-reference for new functions specification 2012-06-19 16:47:43 -04:00
Tom MacWright
a822e0b0b4 Support future mapnik-reference code for image-filters functions,
and other functions-types in the future. Remove shorthand dead code.
2012-06-19 16:43:48 -04:00
Tom MacWright
c7a3ded1a2 Version 0.6.0, push mapnik-reference dep 2012-06-19 16:11:45 -04:00
Tom MacWright
c9129188c7 Remove dead code around data URIs 2012-06-19 15:36:50 -04:00
Tom MacWright
17d569039b Remove dead shorthand font code 2012-06-19 15:33:57 -04:00
Tom MacWright
056429759c Prune comments, improve style, re-read 2012-06-19 15:25:46 -04:00
Tom MacWright
d58712ced6 Update changelog with new param to Renderer 2012-06-18 17:33:59 -04:00
Tom MacWright
1645df71a4 Changelog for 0.5.0. Adds second parameter to Render for specifying
Mapnik version. Handle unrecognized selectors slightly better for
backwards-and-forwards stylesheets.
2012-06-18 17:32:48 -04:00
Tom MacWright
c38cc9c58b Add travis-ci indicator 2012-06-18 17:11:47 -04:00
Tom MacWright
0c838290dc Add Travis-CI chunk 2012-06-18 17:05:54 -04:00
Tom MacWright
b839983a9f Bump mapnik-reference dependency, add result for image_filters operation 2012-06-18 16:28:51 -04:00
Tom MacWright
2891afb707 Assorted style fixes. 2012-06-18 16:22:02 -04:00
Tom MacWright
40b032c9bb Remove reference.json in favor of adding a mapnik-reference
dependency which will update easily from the other repository.
Also update tests for 2.1.0 compatibility
2012-06-18 15:35:19 -04:00
Tom MacWright
bea9b43787 update reference.json, will need update on mapnik/reference.json side 2012-06-18 11:38:19 -04:00
danzel
c2919a9177 Change --nolocalizing to --nosymlink. Turns out Millstone actually does a bunch of other smart stuff. Now millstone is required always (as it was before, it just acted like it could work without it). 2012-06-12 14:59:15 +12:00
danzel
d07a7c813b Add -n/--nolocalizing option to skip calling millstone when you don't want to.
Also fixes running without millstone as this was broken.
2012-06-12 13:43:03 +12:00
Tom MacWright
fef914cdc2 Support future syntax of agg-stack-blur 2012-05-22 16:19:28 -04:00
Tom MacWright
d353fc48cb Adding per-symbolizer composite operations. Just works 2012-05-22 14:56:00 -04:00
Tom MacWright
ad67549f08 Merge up to head again 2012-05-22 14:23:34 -04:00
Tom MacWright
f2cd50bdc7 Merge up to master reference.json 2012-05-22 14:17:42 -04:00
Tom MacWright
306500bd2f Update reference.json to reference.json repository updates 2012-05-21 14:41:28 -04:00
Tom MacWright
2c2a45411e Improve readme 2012-05-21 14:07:14 -04:00
Tom MacWright
3e3b4f09bc Add comp-op support. 2012-05-10 18:28:57 -04:00
Tom MacWright
66012862db Working push. Still needs agg-gradient 2012-05-10 18:12:51 -04:00
Tom MacWright
5efa4bd40e First push toward compositing 2012-05-10 17:08:09 -04:00
Tom MacWright
9d2863b6d2 Update README.md 2012-05-08 16:44:30 -03:00
Tom MacWright
6eff56e2e2 Enforce types 2012-05-08 15:43:37 -04:00
Tom MacWright
262ef7e722 Add regex filter, tests, clean up code more. Fixes #66 2012-05-08 15:26:08 -04:00
Tom MacWright
9960cd2667 Add maximum-extent property. Fixes #43. 2012-05-08 12:30:28 -04:00
Tom MacWright
afad11d3fe XML-escape quotes in quoted values. 2012-05-02 14:05:38 -04:00
Tom MacWright
a7748e6392 Port tests to mocha, remove many global variable leaks. 2012-05-02 12:56:38 -04:00
Tom MacWright
34deb99997 Add test suite for #32 2012-05-02 12:08:10 -04:00
Tom MacWright
c48a02196e Adding error tests for 119 and 124. Fixes #124 2012-05-01 15:48:01 -04:00
Tom MacWright
2c93bed235 Don't permit map properties outside of the map.
Fixes #119
2012-05-01 15:46:10 -04:00
Tom MacWright
d17416b54f Test for 123 fix. Fixes #123 2012-05-01 15:17:46 -04:00
Tom MacWright
3c79836a9e Remove mixin 2012-05-01 15:12:17 -04:00
Tom MacWright
c7b7eca95d General housecleaning, removing js 2012-04-30 17:49:39 -04:00
Dane Springmeyer
70d5240402 bump to 0.4.10 with reference.json update 2012-04-30 16:31:48 -04:00
Young Hahn
7ebb0e878b 0.4.9 2012-04-30 02:53:12 -04:00
Young Hahn
1c8e8ff795 Merge branch 'master' into parameters 2012-04-30 02:49:37 -04:00
Young Hahn
eb386093f0 Changelog update. 2012-04-30 02:49:28 -04:00
Young Hahn
b644e9588b Add test for parameter rendering. 2012-04-30 02:48:22 -04:00
Young Hahn
795f2c82d0 Merge branch 'master' into parameters 2012-04-30 02:22:54 -04:00
Young Hahn
5e63b49558 Remove irrelevant/unused MML keys. 2012-04-30 02:22:47 -04:00
Young Hahn
26951e6589 Render mml keys to mapnik parameters. 2012-04-30 01:10:34 -04:00
AJ Ashton
8c2876c7a8 Bumping pkg version 2012-04-20 15:17:14 -04:00
AJ Ashton
378a43e730 Sync reference.json: adding marker-ignore-placement 2012-04-20 11:47:56 -04:00
Dane Springmeyer
107abdf2e8 bump to 0.4.7 2012-04-16 16:17:20 -07:00
Dane Springmeyer
421ca69233 use latest xml2js and underscore 2012-04-16 15:33:46 -07:00
Dane Springmeyer
c1b4903c0f remove unused 'sys' module usage avoiding deprecation warning 2012-04-16 15:33:31 -07:00
Dane Springmeyer
535cbb881a make mml2json node-v6 compatible 2012-04-16 15:32:08 -07:00
Dane Springmeyer
2e750634bf sync reference.json at bbfd176574 2012-04-16 13:39:15 -07:00
Dane Springmeyer
f01d9dbafa switch away from require('sys') which is deprecated (this solves the TileMill startup warning) 2012-04-05 11:25:32 -07:00
Dane Springmeyer
75aa04ef72 v0.4.6 2012-03-08 15:06:18 -08:00
Dane Springmeyer
0b244fd13d use expresso 0.9.x for node-v6 compatibility (all tests still pass with node v4) 2012-03-07 12:54:49 -08:00
Dane Springmeyer
3f47fa1898 fix git ignores 2012-03-07 12:50:44 -08:00
Dane Springmeyer
d01c45d714 carto works fine with node v6, so allow it 2012-03-07 12:42:16 -08:00
Dane Springmeyer
ed19aa1bc1 node v6 compatible 2012-03-07 12:41:42 -08:00
Jeff Miccolis
14cce7def8 Update README.md 2012-02-24 13:40:52 -05:00
Young Hahn
8dc0f92a0d Remove package.json entry for cartox. 2012-02-15 17:54:04 -05:00
Tom MacWright
fe8efcc1e4 Drop cartox. Fixes #91 2012-01-23 16:15:43 -05:00
Tom MacWright
f2e7ff151c Updating reference up to reference.json project improvements 2012-01-23 16:11:53 -05:00
tristen
93cad03edb Adding syntax highlighting option for vim to the readme 2012-01-05 11:17:49 -05:00
Tom MacWright
c7a7ae720a v0.4.5 2011-12-09 18:03:24 -05:00
Tom MacWright
f4b09ce286 Adding 0.4.4 entry 2011-12-09 18:02:44 -05:00
Tom MacWright
d334bdc6ba Wrap text-name in CDATA to prevent invalid XML output 2011-12-09 18:00:34 -05:00
Tom MacWright
fe0cc34f8f Let invalids eval. Works on #124. 2011-12-07 23:06:20 -05:00
Tom MacWright
90fbe75213 Adding quick test case for not-enough-args error 2011-12-07 22:58:51 -05:00
Tom MacWright
67ddce9ab6 Improve error reporting around function calls with incorrect arity 2011-12-07 22:57:55 -05:00
422 changed files with 28873 additions and 2992 deletions

7
.gitignore vendored
View File

@ -1 +1,6 @@
/npm_modules
/node_modules
.DS_Store
test/rendering/layers/
test/rendering/cache/
test/rendering-mss/npm-debug.log
.idea/

9
.travis.yml Normal file
View File

@ -0,0 +1,9 @@
language: node_js
node_js:
- '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,5 +1,156 @@
## Changelog
## 0.14.0
* Support for Mapnik 3.x
* Bump `mapnik-reference` dependency to ~6.0.1.
## 0.13.0
* Allows optional args in transforms.
* Bump `mapnik-reference` dependency to 5.1.x.
## 0.12.0
* Drop mml2json and xml2js dependency.
## 0.11.0
* Switch API to be synchronous. All errors should be caught using try/catch now.
## 0.10.0
* Remove automatic inclusion of `maximum-extent` on Map element to allow geometries that are buffered past extent bounds (e.g. dateline).
* Bump `mapnik-reference` dependency to ~5.0.9 (with `shield-halo-rasterizer`)
## 0.9.6
* Fixed support for `text-face-name` values with `&` like `El&Font Bubble`
* Fixed support for filtering on fields containing single quotes. Now `#layer[name="it's"] { ... }` is possible.
* Fixed support for filtering on fields containing `&`. Now `#layer["Hello&Goodbye"="yes"] { ... }` is possible.
* Added support for exponential notation in filters. Now `#layer[value = 1.2e3] { ... }` is possible.
* Bump `mapnik-reference` dependency to ~5.0.8 (with support for Mapnik v2.3.0 and 3.x)
## 0.9.5
* Various speed optimizations to help address #20 (#231)
* Fixed support for fields that contain the word `zoom` in them (previous clashed with `zoom` keyword)
* Fixed support for a space in front of `zoom` keyword (#288)
* Improved error messages when color functions encounter invalid color (#309)
* The `carto` command line tool now exits cleanly when millstone is used
* The `carto` command line tool now only localized with millstone if requested (#243)
* Added man page for `carto` (#257)
* Fix repeated comments in selectors. Fixes #260
* Fixed `image-filter` duplication (#270)
* Quote all needed XML chars. See #263.
* Added higher tolerance for various characters in field names (#230)
* Bump `mapnik-reference` dependency to ~5.0.7 (with support for Mapnik v2.2.0)
* Adds compatibility with screen units.
* Fixed ability to use carto as global module (#236)
* Now using 'console' instead of `util` for `stderr` (#217)
## 0.9.4
* Fixes nesting of regex calls
## 0.9.3
* Allows `text-face-name` properties to be unquoted
* Detects inline Format XML tags in `text-name` and passes such output
straight to XML for advanced text names.
* Fixes bugs around concatenation of strings in expressions
* Fixes parsing of comments in between selectors
* Fixes parsing of whitespace in calls
* Improved error messages for unknown properties - advises user on
the property name most closely matching the incorrect input.
* Improved errors for calls, advises user on number of arguments
* Fixes instance inheritance - thanks @gravitystorm!
## 0.9.2
Tagged Sept 6, 2012
* Bump `mapnik-reference` dependency to ~5.0.0
* Better support for unsigned types in certain Mapnik styling properties
## 0.9.1
Tagged Aug 15, 2012
* Improved error handling for different target `mapnik-reference` versions (strk)
* Bump `mapnik-reference` dependency to ~4.0.3
* Fixed handling of image-filter syntax as per [Mapnik changes](https://github.com/mapnik/mapnik/issues/1384)
## 0.9.0
* Bump `mapnik-reference` dependency to ~4.0.0 to pull in new properties.
* Adapted to `comp-op` rename upstream in `mapnik-reference`.
* Adapted to `transform` rename upstream in `mapnik-reference` and Mapnik.
## 0.8.1
* Bump `mapnik-reference` dependency to ~3.1.0 to pull in new properties.
## 0.8.0
* Adds the modulus operator `%` as an option
* Adds a new field-type like `[FIELD]` instead of "[FIELD]"
* Supports function syntax for transforms, optionally with variables and arguments.
### 0.7.1
* Updated mapnik-reference to `~2.2.1`
* Added support for `status` parameter on layers.
* Command line `carto` program gained `--nosymlink` option to pass to millstone to use absolute paths instead of symlinking files.
* Removed unsupported mixin code.
### 0.7.0
* Updated mapnik-reference to `~2.1.0`
* Support an `opacity` property on any style that is a style-level property
### 0.6.0
* Bump `mapnik-reference` dependency to 1.0.0 to allow for using `buffer-size` in the
`Map` element.
### 0.5.0
* Now uses the [mapnik-reference](https://github.com/mapnik/mapnik-reference) npm module
instead of copying `reference.json` when it's updated
* Adds a second parameter to `carto.Renderer` - an object which has a key `mapnik_version`
that specifies the version of Mapnik this stylesheet should target.
### 0.4.10
* Updated reference.json
### 0.4.9
* Render TileJSON, Mapnik options to Mapnik XML parameters.
### 0.4.8
* Updated reference.json
### 0.4.7
* Removed deprecation warnings re: sys/util
* Updated reference.json
* Updated underscore dependency
### 0.4.6
* Node >=v0.6.x compatibility
* Dropped cartox
* Updated reference.json
### 0.4.5
* Fixes text-name with HTML entities
* Fixes function calls with incorrect number of arguments
* Fixes invalid code segments not having eval
### 0.4.3
* Fixes serialization bug with invalid selectors.

34
DEVELOPING.md Normal file
View File

@ -0,0 +1,34 @@
## Developing
Installing:
git clone git@github.com:mapbox/carto.git
npm install
Test:
npm test
Running the head binary:
./bin/carto
## Documentation
This repository contains auto-generated documentation of the content of Carto
that's published on Mapbox.com.
git fetch origin gh-pages:gh-pages
Edit `_docs/package.json` to point to the head version of [mapnik-reference](https://github.com/mapnik/mapnik-reference).
cd _docs
npm install
node generate.js
Then run up a directory and run the testing server:
cd ../
jekyll serve -p 4000
Test the new site at `localhost:4000/carto` and if things look good then git add your changes and push.

View File

@ -2,21 +2,34 @@
# Run all tests
#
expresso = ./node_modules/.bin/expresso
docco = ./node_modules/.bin/docco
expresso = ./node_modules/.bin/mocha
UGLIFYJS=./node_modules/.bin/uglifyjs
BROWSERIFY = ./node_modules/.bin/browserify
dist/carto.js: dist/carto.uncompressed.js $(shell $(BROWSERIFY) --list lib/carto/index.js)
$(UGLIFYJS) dist/carto.uncompressed.js > $@
dist/carto.uncompressed.js: dist $(shell $(BROWSERIFY) --list lib/carto/index.js)
$(BROWSERIFY) lib/carto/index.js --exclude node_modules/underscore/underscore.js --standalone carto > $@
lint:
./node_modules/.bin/jshint lib/carto/*.js lib/carto/tree/*.js
ifndef only
test:
$(expresso) -I lib test/*.test.js
@NODE_PATH=./lib:$NODE_PATH $(expresso) -R spec -I lib test/*.test.js
else
test:
$(expresso) -I lib test/${only}.test.js
@NODE_PATH=./lib:$NODE_PATH $(expresso) -R spec -I lib test/${only}.test.js
endif
doc:
$(docco) lib/carto/*.js lib/carto/tree/*.js
check: test
dist:
mkdir -p dist
.PHONY: test

279
README.md
View File

@ -1,268 +1,83 @@
# carto
# CartoCSS
Is a stylesheet renderer for Mapnik. It's an evolution of the [Cascadenik](https://github.com/mapnik/Cascadenik) idea and language, with an emphasis on speed and flexibility.
[![Build Status](https://travis-ci.org/CartoDB/carto.png?branch=master)](https://travis-ci.org/CartoDB/carto)
## Installation
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
npm install carto
## Quick Start
## MML
_incompatibility_
```javascript
// shader is a CartoCSS object
* MML files are assumed to be JSON, not XML. The files are near-identical to the XML files accepted by Cascadenik, just translated into JSON.
* Carto will not embed files or download URLs for you. Stylesheets should be embedded directly into your MML JSON and any datasources should be paths (relative or absolute) that would be acceptable in Mapnik XML.
The [millstone project](https://github.com/mapbox/millstone) aims to fill this need.
var cartocss = [
'#layer {',
' marker-width: [property]',
' marker-fill: red',
'}'
].join('')
var shader = new carto.RendererJS().render(cartocss);
var layers = shader.getLayers()
for (var i = 0; i < layers.length; ++i) {
var layer = layers[i];
console.log("layer name: ", layer.fullName())
console.log("- frames: ", layer.frames())
console.log("- attachment: ", layer.attachment())
carto.js MML:
var layerShader = layer.getStyle({ property: 1 }, { zoom: 10 })
console.log(layerShader['marker-width']) // 1
console.log(layerShader['marker-fill']) // #FF0000
}
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [{"id":"style.mss","data":"Map {\n background-color: #fff;\n}\n\n#world {\n line-color: #ccc;\n line-width: 0.5;\n polygon-fill: #eee;\n}"}],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=latlong +ellps=WGS84 +datum=WGS84 +no_defs",
"Datasource": {
"file": "world_borders",
"type": "shape"
}
}]
}
```
Cascadenik MML
# API
<pre>&lt;Stylesheet&gt;&lt;![CDATA[
Map
{
map-bgcolor: #69f;
}
## RendererJS
Layer
{
line-width: 1;
line-color: #696;
polygon-fill: #6f9;
}
]]&gt;&lt;/Stylesheet&gt;
&lt;Layer srs=&quot;+proj=latlong +ellps=WGS84 +datum=WGS84 +no_defs&quot;&gt;
&lt;Datasource&gt;
&lt;Parameter name=&quot;type&quot;&gt;shape&lt;/Parameter&gt;
&lt;Parameter name=&quot;file&quot;&gt;world_borders&lt;/Parameter&gt;
&lt;/Datasource&gt;
&lt;/Layer&gt;
&lt;/Map&gt;</pre>
### render(cartocss)
## Attachments and Instances
_new_
## CartoCSS
In CSS, a certain object can only have one instance of a property. A `<div>` has a specific border width and color, rules that match better than others (#id instead of .class) override previous definitions. `carto.js` acts the same way normally for the sake of familiarity and organization, but Mapnik itself is more powerful.
compiled cartocss object
Layers in Mapnik can have multiple [borders](http://trac.mapnik.org/wiki/LineSymbolizer) and multiple copies of other attributes. This ability is useful in drawing line outlines, like in the case of road borders or 'glow' effects around coasts. `carto.js` makes this accessible by allowing attachments to styles:
### getLayers
#world {
line-color: #fff;
line-width: 3;
}
return the layers, an array of ``CartoCSS.Layer`` object
#world::outline {
line-color: #000;
line-width: 6;
}
### getDefault
Attachments are optional: if you don't define them, carto.js does overriding of styles just like Cascadenik.
This brings us to another _incompatibility_: `line-inline` and `line-outline` have been removed from the language, because attachments are capable of the same trick.
While attachments allow creating implicit "layers" with the same data, using **instances** allows you to create multiple symbolizers in the same style/layer:
#roads {
casing/line-width: 6;
casing/line-color: #333;
line-width: 4;
line-color: #666;
}
This makes Mapnik first draw the line of color #333 with a width of 6, and then immediately afterwards, it draws the same line again with width 4 and color #666. Contrast that to attachments: Mapnik would first draw all casings before proceeding to the actual lines.
## text-name
_incompatibility_
Instead of the name attribute of the [TextSymbolizer](http://trac.mapnik.org/wiki/TextSymbolizer) and [ShieldSymbolizer](http://trac.mapnik.org/wiki/ShieldSymbolizer) being a part of the selector, it is a property of a rule. Thus the evaluation is less complex and one can use expressions in names.
<table>
<tr>
<th>cascadenik</th>
<th>carto.js</th>
</tr>
<tr>
<td valign='top'>
<pre>
#world NAME {
text-face-name: "Arial";
}</pre>
</td>
<td valign='top'>
<pre>
#world {
text-name: "NAME";
text-face-name: "Arial";
}</pre>
</td>
</tr>
</table>
## Mapnik2
_new_
`carto.js` is only compatible with [Mapnik2](http://trac.mapnik.org/wiki/Mapnik2). Compatibility with Mapnik 0.7.x is not planned.
## Rasters and Buildings
_new_
Rasters are supported in carto.js - it knows how to download `.vrt`, `.tiff`, and soon other raster formats, and the properties of the [RasterSymbolizer](http://trac.mapnik.org/wiki/RasterSymbolizer) are exposed in the language.
The [BuildingSymbolizer](http://trac.mapnik.org/wiki/BuildingSymbolizer) is also supported in `carto.js`. The code stores symbolizer types and properties in a JSON file (in `tree/reference.json`), so new Mapnik features can be quickly implemented here.
## Variables & Expressions
_new_
`carto.js` inherits from its basis in [less.js](http://lesscss.org/) some new features in CSS. One can define variables in stylesheets, and use expressions to modify them.
@mybackground: #2B4D2D;
Map {
background-color: @mybackground
}
#world {
polygon-fill: @mybackground + #222;
line-color: darken(@mybackground, 10%);
}
## Nested Styles
_new_
`carto.js` also inherits nesting of rules from less.js.
/* Applies to all layers with .land class */
.land {
line-color: #ccc;
line-width: 0.5;
polygon-fill: #eee;
/* Applies to #lakes.land */
#lakes {
polygon-fill: #000;
}
}
This can be a convenient way to group style changes by zoom level:
[zoom > 1] {
/* Applies to all layers at zoom > 1 */
polygon-gamma: 0.3;
#world {
polygon-fill: #323;
}
#lakes {
polygon-fill: #144;
}
}
## FontSets
_new_
By defining multiple fonts in a `text-face-name` definition, you create [FontSets](http://trac.mapnik.org/wiki/FontSet) in `carto.js`. These are useful for supporting multiple character sets and fallback fonts for distributed styles.
<table>
<tr>
<th>carto</th><th>XML</th>
</tr>
<tr>
<td valign='top'>
<pre>#world {
text-name: "[NAME]";
text-size: 11;
text-face-name: "Georgia Regular", "Arial Italic";
}</pre>
</td>
<td valign='top'>
<pre>&lt;FontSet name=&quot;fontset-0&quot;&gt;
&lt;Font face-name=&quot;Georgia Regular&quot;/&gt;
&lt;Font face-name=&quot;Arial Italic&quot;/&gt;
&lt;/FontSet&gt;
&lt;Style name=&quot;world-text&quot;&gt;
&lt;Rule&gt;
&lt;TextSymbolizer fontset-name=&quot;fontset-0&quot;
size=&quot;11&quot;
name=&quot;[NAME]&quot;/&gt;
&lt;/Rule&gt;
&lt;/Style&gt;</pre>
</td>
<tr>
</table>
return the default layer (``CartoCSS.Layer``), usually the Map layer
## Usage
### findLayer(where)
#### Using the binary
find a layer using where object.
Install `millstone` to enable support for localizing external resources (URLs and local files) referenced in your mml file.
```
shader.findLayer({ name: 'test' })
```
npm install millstone
carto map_file.json
## CartoCSS.Layer
#### Using the code
### getStyle(props, context)
Currently `carto.js` is designed to be invoked from [node.js](http://nodejs.org/).
The `Renderer` interface is the main API for developers, and it takes an MML file as a string as input.
return the evaluated style:
- props: object containing properties needed to render the style. If the cartocss style uses
some variables they should be passed in this object
- context: rendering context variables like ``zoom`` or animation ``frame``
// defined variables:
// - input (the name or identifier of the file being parsed)
// - data (a string containing the MML or an object of MML)
var carto = require('carto');
new carto.Renderer({
filename: input,
local_data_dir: path.dirname(input),
}).render(data, function(err, output) {
if (err) {
if (Array.isArray(err)) {
err.forEach(function(e) {
carto.writeError(e, options);
});
} else { throw err; }
} else {
sys.puts(output);
}
});
## TextMate bundle
There's a TextMate bundle that offers syntax highlighting for `.mss` and `.mml` files in the `build` directory. To install, download or clone this repository, then double-click on the `carto.tmbundle` icon in that folder.
The TextMate bundle **requires** [node-mapnik](https://github.com/mapnik/node-mapnik) and Carto installed globally - the versions that are installed locally in TileMill or other tools can't be automatically discovered.
npm install mapnik -g
npm install carto -g
## Credits
`carto.js` is based on [less.js](https://github.com/cloudhead/less.js), a CSS compiler written by Alexis Sellier.
It depends on:
* [underscore.js](https://github.com/documentcloud/underscore/)
Only for running tests:
## Reference Documentation
* [expresso](https://github.com/visionmedia/expresso)
* [sax-js](https://github.com/isaacs/sax-js/)
* [mapbox.com/carto](http://mapbox.com/carto/)
## Authors
* Tom MacWright (tmcw)
* Konstantin Käfer (kkaefer)
* AJ Ashton (ajashton)
* Dane Springmeyer (springmeyer)

213
bin/carto
View File

@ -2,46 +2,40 @@
var path = require('path'),
fs = require('fs'),
sys = require('sys'),
carto = require('carto');
carto = require('../lib/carto'),
url = require('url'),
_ = require('underscore');
var args = process.argv.slice(1);
var options = {};
var existsSync = require('fs').existsSync || require('path').existsSync
args = args.filter(function (arg) {
var match;
var optimist = require('optimist')
.usage("Usage: $0 <source MML file>")
.options('h', {alias:'help', describe:'Display this help message'})
.options('v', {alias:'version', boolean:true, describe:'Display version information'})
.options('b', {alias:'benchmark', boolean:true, describe:'Outputs total compile time'})
.options('l', {alias:'localize', boolean:true, default:false, describe:'Use millstone to localize resources when loading an MML'})
.options('n', {alias:'nosymlink', boolean:true, describe:'Use absolute paths instead of symlinking files'})
.options('ppi', {describe:'Pixels per inch used to convert m, mm, cm, in, pt, pc to pixels', default:90.714});
if (match = arg.match(/^--?([a-z][0-9a-z-]*)$/i)) { arg = match[1] }
else { return arg }
var options = optimist.argv;
switch (arg) {
case 'v':
case 'version':
sys.puts("carto " + carto.version.join('.') + " (Carto map stylesheet compiler)");
process.exit(0);
break;
case 'b':
case 'benchmark':
options.benchmark = true;
break;
if (options.help) {
optimist.showHelp();
process.exit(0);
}
default:
sys.puts("Usage: carto <source MML file>");
sys.puts("Options:");
sys.puts(" -v --version Parse JSON map manifest");
sys.puts(" -b --benchmark Outputs total compile time");
process.exit(0);
break;
}
});
if (options.version) {
console.log("carto " + carto.version.join('.') + " (Carto map stylesheet compiler)");
process.exit(0);
}
var input = args[1];
var input = options._[0];
if (input && input[0] != '/') {
input = path.join(process.cwd(), input);
}
if (!input) {
sys.puts("carto: no input files");
console.log("carto: no input files ('carto -h or --help' for help)");
process.exit(1);
}
@ -49,63 +43,120 @@ if (options.benchmark) {
var start = +new Date;
}
var ext = path.extname(input);
if (!ext) {
console.log("carto: please pass either a .mml file or .mss file");
process.exit(1);
}
if (!existsSync(input)) {
console.log("carto: file does not exist: '" + input + "'");
process.exit(1);
}
function compileMML(err, data) {
// force drain the millstone download pool now
// to ensure we can exit without waiting
if (options.localize && millstone.drainPool) {
millstone.drainPool(function() {});
}
if (err) {
console.error(err);
process.exit(1);
}
var renderer = new carto.Renderer({
filename: input,
benchmark: options.benchmark,
ppi: options.ppi
});
try {
var output = renderer.render(data);
} catch (e) {
if (e.stack) {
console.error(e.stack);
} else {
console.error(e);
}
process.exit(1);
}
if (!options.benchmark) {
console.log(output);
} else {
var duration = (+new Date) - start;
console.log('TOTAL: ' + (duration) + 'ms');
}
};
function compileMSS(err, data) {
if (err) {
console.error(err);
process.exit(1);
}
var renderer = new carto.Renderer({
filename: path.basename(input),
benchmark: options.benchmark,
ppi: options.ppi
});
try {
var output = renderer.renderMSS(data);
} catch (e) {
if (e.stack) {
console.error(e.stack);
} else {
console.error(e);
}
process.exit(1);
}
if (!options.benchmark) {
console.log(output);
} else {
var duration = (+new Date) - start;
console.log('TOTAL: ' + (duration) + 'ms');
}
};
try {
var data = fs.readFileSync(input, 'utf-8');
} catch(err) {
sys.puts("carto: " + err.message.replace(/^[A-Z]+, /, ''));
console.error("carto: " + err.message.replace(/^[A-Z]+, /, ''));
process.exit(1);
}
try {
data = JSON.parse(data);
} catch(err) {
sys.puts("carto: " + err.message.replace(/^[A-Z]+, /, ''));
process.exit(1);
}
var millstone = undefined;
try {
require.resolve('millstone');
millstone = require('millstone');
} catch (err) {}
if (!millstone) {
console.warn('carto: Millstone not found. Externals will not be resolved.');
return compile(null, data);
} else {
millstone.resolve({
mml: data,
base: path.dirname(input),
cache: path.join(path.dirname(input), 'cache')
}, compile);
}
function compile(err, data) {
if (err) throw err;
if (ext == '.mml') {
try {
new carto.Renderer({
filename: input,
benchmark: options.benchmark
}).render(data, function(err, output) {
if (err) {
console.log(err);
throw err;
process.exit(1);
} else {
if (!options.benchmark) {
sys.puts(output);
} else {
var duration = (+new Date) - start;
console.log('TOTAL: ' + (duration) + 'ms');
}
}
});
} catch (e) {
if (e.stack) {
sys.error(e.stack);
} else {
sys.error(e);
}
data = JSON.parse(data);
} catch(err) {
console.error("carto: " + err.message.replace(/^[A-Z]+, /, ''));
process.exit(1);
}
};
if (options.localize) {
var millstone = undefined;
try {
require.resolve('millstone');
millstone = require('millstone');
} catch (err) {
console.error('carto: Millstone not found, required if localizing stylesheet resources. ' + err.message.replace(/^[A-Z]+, /, ''));
process.exit(1);
}
millstone.resolve({
mml: data,
base: path.dirname(input),
cache: path.join(path.dirname(input), 'cache'),
nosymlink: options.nosymlink
}, compileMML);
} else {
data.Stylesheet = data.Stylesheet.map(function(x) {
if (typeof x !== 'string') {
return { id: x, data: x.data }
}
return { id: x, data: fs.readFileSync(path.join(path.dirname(input), x), 'utf8') }
});
compileMML(null,data);
}
} else if (ext == '.mss') {
compileMSS(null,data);
} else {
console.log("carto: please pass either a .mml file or .mss file");
}

View File

@ -1,233 +0,0 @@
#!/usr/bin/env node
var sax = require('sax'),
fs = require('fs'),
_ = require('underscore')._,
path = require('path'),
sys = require('sys');
require.paths.unshift(path.join(__dirname, '../lib'), path.join(__dirname, '../lib/node'));
var carto = require('carto'),
args = process.argv.slice(1);
var options = {
silent: false,
json: false
};
xml2tree = function(xml, callback) {
var parser = sax.parser(true);
var tree = [ {} ];
parser.onopentag = function(node) {
if (!(node.name in tree[0])) tree[0][node.name] = [];
tree[0][node.name].push(node.attributes);
tree.unshift(node.attributes);
};
parser.onclosetag = function() {
tree.shift();
if (tree.length === 1) callback(tree[0]);
};
parser.ontext = parser.oncdata = function(text) {
if (text.trim()) tree[0].text = (tree[0].text || '') + text;
};
parser.write(xml.toString());
}
args = args.filter(function (arg) {
var match;
if (match = arg.match(/^--?([a-z][0-9a-z-]*)$/i)) { arg = match[1] }
else { return arg }
switch (arg) {
case 'h':
case 'help':
sys.puts("Usage: cartox source");
process.exit(0);
break;
}
});
var input = args[1];
if (input && input[0] != '/') {
input = path.join(process.cwd(), input);
}
if (!input) {
sys.puts("cartox: no input files");
process.exit(1);
}
var output = args[2];
if (output && output[0] != '/') {
output = path.join(process.cwd(), output);
}
function upStyle(s) {
this.name = s.name;
this.rules = [];
}
upStyle.prototype.toMSS = function() {
return '.' + this.name
+ ' {\n'
+ this.rules.join('\n')
+ '\n}';
};
function upRule(xmlRule) {
this.filters = xmlRule.Filter ? this.upFilter(xmlRule.Filter[0].text) : [];
this.zooms = (xmlRule.MaxScaleDenominator || xmlRule.MinScaleDenomintor)
? this.upZoom(xmlRule) : [];
this.rules = this.upSymbolizers(xmlRule);
}
upRule.prototype.upZoom = function(xmlRule) {
var zoomFilters = [];
var findZoom = function(denom) {
for (var i in carto.tree.Zoom.ranges) {
if (carto.tree.Zoom.ranges[i] == denom) return i;
}
};
if (xmlRule.MaxScaleDenominator) {
zoomFilters.push('[zoom >= ' + findZoom(xmlRule.MaxScaleDenominator[0].text) + ']');
}
if (xmlRule.MinScaleDenominator) {
zoomFilters.push('[zoom <= ' + findZoom(xmlRule.MinScaleDenominator[0].text) + ']');
}
return zoomFilters;
};
upRule.prototype.upFilter = function(xmlFilter) {
var filters = [];
var thisfilter = [];
var invert = false;
var xmlTok = xmlFilter.split(/\s+/);
var curTok = '';
for (var i in xmlTok) {
curTok = xmlTok[i].replace(/(\[|\]|\)|\()/g, '');
if (curTok == 'and') {
filters.push(thisfilter);
thisfilter = [];
} else if (curTok == 'not') {
invert = true;
} else if (invert && (['='].indexOf(curTok) !== -1)) {
thisfilter.push('!=');
invert = false;
} else {
thisfilter.push(curTok);
}
}
if (thisfilter) filters.push(thisfilter);
return filters.map(function(f) {
return f.join(' ');
});
};
upRule.prototype.upSymbolizers = function(xmlRule) {
var css_rules = [];
var symnames = _.map(_.keys(carto.tree.Reference.data.symbolizers), function(symbolizer) {
return [symbolizer.charAt(0).toUpperCase() +
symbolizer.slice(1).replace(/\-./, function(str) {
return str[1].toUpperCase();
}) + 'Symbolizer', carto.tree.Reference.data.symbolizers[symbolizer]];
});
var symmap = _.reduce(symnames, function(memo, s) {
memo[s[0]] = s[1];
return memo;
}, {});
var cssmap = function(symbolizer, name) {
return symmap[symbolizer][name].css;
}
for (var i in xmlRule) {
if (i in symmap) {
for (var j in xmlRule[i][0]) {
if (j == 'CssParameter') {
sys.error("cartox: no support for CssParameter, please upgrade your xml to Mapnik 2.0 syntax first");
process.exit(1);
}
if (!symmap[i][j]) {
sys.error('Property ' + j + ' not yet supported');
} else if (symmap[i][j].type == 'uri') {
css_rules.push(cssmap(i, j) + ': url("' + xmlRule[i][0][j] + '");');
} else if (['float', 'color', 'boolean', 'numbers'].indexOf(symmap[i][j].type) !== -1 || _.isArray(symmap[i][j].type)) {
css_rules.push(cssmap(i, j) + ': ' + xmlRule[i][0][j] + ';');
} else {
css_rules.push(cssmap(i, j) + ': "' + xmlRule[i][0][j] + '";');
}
}
}
}
return css_rules;
};
upRule.prototype.toMSS = function() {
if (this.filters.length || this.zooms.length) {
return ' ' + this.filters.map(function(f) {
return '[' + f + ']';
}).join('') +
this.zooms.map(function(f) {
return f;
}).join('') +
' {\n '
+ this.rules.join('\n ')
+ '\n }\n';
} else {
' ' + this.rules.join('\n ');
}
}
function upDatasource(ds) {
var params = {};
ds[0].Parameter.forEach(function(param) {
params[param.name] = param.text;
});
return params;
}
fs.readFile(input, 'utf-8', function (e, data) {
var styles = [];
var document = {};
var layers = [];
if (e) throw e;
xml2tree(data, function(mapnik_xml) {
mapnik_xml.Map[0].Style.forEach(function(s) {
var newStyle = new upStyle(s);
s.Rule.forEach(function(r) {
newStyle.rules.push((new upRule(r)).toMSS());
});
styles.push(newStyle.toMSS());
});
styles = styles.reverse();
mapnik_xml.Map[0].Layer.forEach(function(l) {
var additional_classes = l.StyleName ? ' ' + l.StyleName.map(function(s) { return s.text; }).join(' ') : '';
var newLayer = {
name: l.name,
class: l.name + '_style' + additional_classes,
srs: l.srs
};
newLayer.Datasource = upDatasource(l.Datasource);
layers.push(newLayer);
});
if (output) {
document.Stylesheet = [output.replace('mml', 'mss')];
document.Layer = layers;
fs.writeFile(output, JSON.stringify(document), 'utf8', function(err) {
sys.error('write MML');
});
fs.writeFile(output.replace('mml', 'mss'), styles.join('\n\n'), 'utf8', function(err) {
sys.error('write MSS');
});
} else {
document.Stylesheet = [{ id: 'gen', data: styles.join('\n') }];
document.Layer = layers;
console.log(JSON.stringify(document));
}
});
});

View File

@ -1,67 +0,0 @@
#!/usr/bin/env node
var xml2js = require('xml2js'),
fs = require('fs'),
sys = require('sys');
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();
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);
});

View File

@ -264,11 +264,11 @@
</dict>
</dict>
<key>match</key>
<string>\b(background-color|background-image|srs|buffer|font-directory|polygon-fill|polygon-gamma|polygon-opacity|polygon-meta-output|polygon-meta-writer|line-color|line-width|line-opacity|line-join|line-cap|line-gamma|line-dasharray|line-meta-output|line-meta-writer|marker-file|marker-opacity|marker-line-color|marker-line-width|marker-line-opacity|marker-placement|marker-type|marker-width|marker-height|marker-fill|marker-allow-overlap|marker-spacing|marker-max-error|marker-transform|marker-meta-output|marker-meta-writer|shield-name|shield-face-name|shield-size|shield-fill|shield-min-distance|shield-halo-fill|shield-halo-radius|shield-spacing|shield-character-spacing|shield-line-spacing|shield-file|shield-width|shield-height|shield-type|shield-text-dx|shield-text-dy|shield-dx|shield-dy|shield-meta-output|shield-meta-writer|line-pattern-file|line-pattern-width|line-pattern-height|line-pattern-type|line-pattern-meta-output|line-pattern-meta-writer|polygon-pattern-file|polygon-pattern-width|polygon-pattern-height|polygon-pattern-type|polygon-pattern-meta-output|polygon-pattern-meta-writer|raster-opacity|raster-mode|raster-scaling|point-file|point-width|point-height|point-type|point-allow-overlap|point-placement|point-meta-output|point-meta-writer|text-name|text-face-name|text-size|text-ratio|text-wrap-width|text-spacing|text-character-spacing|text-line-spacing|text-label-position-tolerance|text-max-char-angle-delta|text-fill|text-halo-fill|text-halo-radius|text-dx|text-dy|text-avoid-edges|text-min-distance|text-min-padding|text-allow-overlap|text-placement|text-placement-type|text-placements|text-transform|text-meta-output|text-meta-writer|building-fill|building-fill-opacity|building-height)\s*:</string>
<string>\b(background-color|background-image|srs|buffer|font-directory|polygon-fill|polygon-gamma|polygon-opacity|polygon-meta-output|polygon-meta-writer|line-color|line-width|line-opacity|line-join|line-cap|line-gamma|line-dasharray|line-meta-output|line-meta-writer|marker-file|marker-opacity|marker-line-color|marker-line-width|marker-line-opacity|marker-placement|marker-type|marker-width|marker-height|marker-fill|marker-allow-overlap|marker-spacing|marker-max-error|marker-transform|marker-meta-output|marker-meta-writer|shield-name|shield-face-name|shield-size|shield-fill|shield-min-distance|shield-halo-fill|shield-halo-radius|shield-spacing|shield-character-spacing|shield-line-spacing|shield-file|shield-width|shield-height|shield-type|shield-text-dx|shield-text-dy|shield-dx|shield-dy|shield-meta-output|shield-meta-writer|line-pattern-file|line-pattern-width|line-pattern-height|line-pattern-type|line-pattern-meta-output|line-pattern-meta-writer|polygon-pattern-file|polygon-pattern-width|polygon-pattern-height|polygon-pattern-type|polygon-pattern-meta-output|polygon-pattern-meta-writer|raster-opacity|raster-comp-op|raster-scaling|point-file|point-width|point-height|point-type|point-allow-overlap|point-placement|point-meta-output|point-meta-writer|text-name|text-face-name|text-size|text-ratio|text-wrap-width|text-spacing|text-character-spacing|text-line-spacing|text-label-position-tolerance|text-max-char-angle-delta|text-fill|text-halo-fill|text-halo-radius|text-dx|text-dy|text-avoid-edges|text-min-distance|text-min-padding|text-allow-overlap|text-placement|text-placement-type|text-placements|text-transform|text-meta-output|text-meta-writer|building-fill|building-fill-opacity|building-height)\s*:</string>
</dict>
<dict>
<key>match</key>
<string>\b(miter|round|bevel|butt|round|square|point|line|arrow|ellipse|png|jpg|svg|normal|grain_merge|grain_merge2|multiply|multiply2|divide|divide2|screen|hard_light|fast|bilinear|bilinear8|centroid|interior|point|line|vertex|interior|none|uppercase|lowercase)\b</string>
<string>\b(miter|round|bevel|butt|round|square|point|line|arrow|ellipse|png|jpg|svg|normal|grain_merge|grain_merge2|multiply|multiply2|divide|divide2|screen|hard_light|near|bilinear|bilinear8|centroid|interior|point|line|vertex|interior|none|uppercase|lowercase)\b</string>
<key>name</key>
<string>meta.property-value.carto</string>
</dict>

7
build/header.js Normal file
View File

@ -0,0 +1,7 @@
//
// LESS - Leaner CSS v@VERSION
// http://lesscss.org
//
// Copyright (c) 2010, Alexis Sellier
// Licensed under the MIT license.
//

View File

@ -2,8 +2,7 @@
var path = require('path'),
fs = require('fs'),
_ = require('underscore')._,
sys = require('sys');
_ = require('underscore')._;
var carto = require('../lib/carto');

View File

@ -47,7 +47,7 @@ syn region cartoFontDescriptorFunction contained matchgroup=cartoFunctionName st
syn match cartoUnicodeRange contained "U+[0-9A-Fa-f?]\+"
syn match cartoUnicodeRange contained "U+\x\+-\x\+"
syn match cartoKeywordAttr "/\|miter\|round\|bevel\|butt\|round\|square\|point\|line\|arrow\|ellipse\|point\|line\|vertex\|interior\|local\|global\|normal\|grain_merge\|grain_merge2\|multiply\|multiply2\|divide\|divide2\|screen\|hard_light\|fast\|bilinear\|bilinear8\|bicubic\|spline16\|gaussian\|lanczos\|centroid\|interior\|top\|middle\|bottom\|point\|line\|vertex\|interior\|dummy\|simple\|none\|uppercase\|lowercase\|capitalize\|/"
syn match cartoKeywordAttr "/\|miter\|round\|bevel\|butt\|round\|square\|point\|line\|arrow\|ellipse\|point\|line\|vertex\|interior\|local\|global\|normal\|grain_merge\|grain_merge2\|multiply\|multiply2\|divide\|divide2\|screen\|hard_light\|near\|bilinear\|bilinear8\|bicubic\|spline16\|gaussian\|lanczos\|centroid\|interior\|top\|middle\|bottom\|point\|line\|vertex\|interior\|dummy\|simple\|none\|uppercase\|lowercase\|capitalize\|/"
" syn keyword cartoColor contained {{#colors}}{{.}} {{/colors}}
syn match cartoColor "/\|aliceblue\|antiquewhite\|aqua\|aquamarine\|azure\|beige\|bisque\|black\|blanchedalmond\|blue\|blueviolet\|brown\|burlywood\|cadetblue\|chartreuse\|chocolate\|coral\|cornflowerblue\|cornsilk\|crimson\|cyan\|darkblue\|darkcyan\|darkgoldenrod\|darkgray\|darkgreen\|darkgrey\|darkkhaki\|darkmagenta\|darkolivegreen\|darkorange\|darkorchid\|darkred\|darksalmon\|darkseagreen\|darkslateblue\|darkslategrey\|darkturquoise\|darkviolet\|deeppink\|deepskyblue\|dimgray\|dimgrey\|dodgerblue\|firebrick\|floralwhite\|forestgreen\|fuchsia\|gainsboro\|ghostwhite\|gold\|goldenrod\|gray\|grey\|green\|greenyellow\|honeydew\|hotpink\|indianred\|indigo\|ivory\|khaki\|lavender\|lavenderblush\|lawngreen\|lemonchiffon\|lightblue\|lightcoral\|lightcyan\|lightgoldenrodyellow\|lightgray\|lightgreen\|lightgrey\|lightpink\|lightsalmon\|lightseagreen\|lightskyblue\|lightslategray\|lightslategrey\|lightsteelblue\|lightyellow\|lime\|limegreen\|linen\|magenta\|maroon\|mediumaquamarine\|mediumblue\|mediumorchid\|mediumpurple\|mediumseagreen\|mediumslateblue\|mediumspringgreen\|mediumturquoise\|mediumvioletred\|midnightblue\|mintcream\|mistyrose\|moccasin\|navajowhite\|navy\|oldlace\|olive\|olivedrab\|orange\|orangered\|orchid\|palegoldenrod\|palegreen\|paleturquoise\|palevioletred\|papayawhip\|peachpuff\|peru\|pink\|plum\|powderblue\|purple\|red\|rosybrown\|royalblue\|saddlebrown\|salmon\|sandybrown\|seagreen\|seashell\|sienna\|silver\|skyblue\|slateblue\|slategray\|slategrey\|snow\|springgreen\|steelblue\|tan\|teal\|thistle\|tomato\|turquoise\|violet\|wheat\|white\|whitesmoke\|yellow\|yellowgreen\|transparent\|/"

4
dist/carto.js vendored Normal file

File diff suppressed because one or more lines are too long

7375
dist/carto.uncompressed.js vendored Normal file

File diff suppressed because it is too large Load Diff

15
docs-generator/README.md Normal file
View File

@ -0,0 +1,15 @@
# Generating CartoCSS docs
From the `docs-generator/` directory:
```
$ npm install
```
Then:
```
$ node generate.js
```
Will save docs to `docs/`.

View File

@ -0,0 +1,23 @@
var fs = require('fs'),
path = require('path'),
refs = require('mapnik-reference'),
_ = require('underscore');
function tmpl(x) {
return _.template(fs.readFileSync(path.join(__dirname, x), 'utf-8'));
}
var index = tmpl('index._');
var table = tmpl('symbolizers._');
var versions = Object.keys(refs.version);
for (var v in refs.version) {
var ref = refs.version[v];
fs.writeFileSync(path.join(__dirname, '../docs/' + v + '.md'), index({
symbolizers: ref.symbolizers,
table: table,
version: v,
versions: versions,
_: _
}));
}

144
docs-generator/index._ Normal file
View File

@ -0,0 +1,144 @@
# Carto documentation
The following is a list of properties provided in CartoCSS that you can apply to map elements.
<%= table({symbolizers:symbolizers}) %>
### Values
Below is a list of values and an explanation of any expression that can be applied to properties in CartCSS.
### Color
CartoCSS accepts a variety of syntaxes for colors - HTML-style hex values, rgb, rgba, hsl, and hsla. It also supports the predefined HTML colors names, like `yellow` and `blue`.
``` css
#line {
line-color: #ff0;
line-color: #ffff00;
line-color: rgb(255, 255, 0);
line-color: rgba(255, 255, 0, 1);
line-color: hsl(100, 50%, 50%);
line-color: hsla(100, 50%, 50%, 1);
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 operation functions [borrowed from less](http://lesscss.org/functions/#color-operations):
``` css
// lighten and darken colors
lighten(#ace, 10%);
darken(#ace, 10%);
// saturate and desaturate
saturate(#550000, 10%);
desaturate(#00ff00, 10%);
// increase or decrease the opacity of a color
fadein(#fafafa, 10%);
fadeout(#fefefe, 14%);
// spin rotates a color around the color wheel by degrees
spin(#ff00ff, 10);
// mix generates a color in between two other colors.
mix(#fff, #000, 50%);
```
These functions all take arguments which can be color variables, literal colors, or the results of other functions operating on colors.
### Float
Float is a fancy way of saying 'number'. In CartoCSS, you specify _just a number_ - unlike CSS, there are no units, but everything is specified in pixels.
``` css
#line {
line-width: 2;
}
```
It's also possible to do simple math with number values:
``` css
#line {
line-width: 4 / 2; // division
line-width: 4 + 2; // addition
line-width: 4 - 2; // subtraction
line-width: 4 * 2; // multiplication
line-width: 4 % 2; // modulus
}
```
### URI
URI is a fancy way of saying URL. When an argument is a URI, you use the same kind of `url('place.png')` notation that you would with HTML. Quotes around the URL aren't required, but are highly recommended. URIs can be paths to places on your computer, or on the internet.
```css
#markers {
marker-file: url('marker.png');
}
```
### String
A string is basically just text. In the case of CartoCSS, you're going to put it in quotes. Strings can be anything, though pay attention to the cases of `text-name` and `shield-name` - they actually will refer to features, which you refer to by putting them in brackets, as seen in the example below.
```css
#labels {
text-name: "[MY_FIELD]";
}
```
### Boolean
Boolean means yes or no, so it accepts the values `true` or `false`.
```css
#markers {
marker-allow-overlap:true;
}
```
### Expressions
Expressions are statements that can include fields, numbers, and other types in a really flexible way. You have run into expressions before, in the realm of 'fields', where you'd specify `"[FIELD]"`, but expressions allow you to drop the quotes and also do quick addition, division, multiplication, and concatenation from within Carto syntax.
```css
#buildings {
building-height: [HEIGHT_FIELD] * 10;
}
```
### Numbers
Numbers are comma-separated lists of one or more number in a specific order. They're used in line dash arrays, in which the numbers specify intervals of line, break, and line again.
```css
#disputedboundary {
line-dasharray: 1, 4, 2;
}
```
### Percentages
In Carto, the percentage symbol, `%` universally means `value/100`. It's meant to be used with ratio-related properties, like opacity rules.
_You should not use percentages as widths, heights, or other properties - unlike CSS, percentages are not relative to cascaded classes or page size, they're, as stated, simply the value divided by one hundred._
```css
#world {
// this syntax
polygon-opacity: 50%;
// is equivalent to
polygon-opacity: 0.5;
}
```
### Functions
Functions are comma-separated lists of one or more functions. For instance, transforms use the `functions` type to allow for transforms within Carto, which are optionally chainable.
```css
#point {
point-transform: scale(2, 2);
}
```

View File

@ -0,0 +1,20 @@
{
"name": "carto-site",
"private": true,
"version": "0.0.0",
"description": "Mapnik Stylesheet Compiler",
"url": "https://github.com/mapbox/carto",
"repositories": [{
"type": "git",
"url": "http://github.com/mapbox/carto.git"
}],
"author": {
"name": "MapBox",
"url": "http://mapbox.com/",
"email": "info@mapbox.com"
},
"dependencies": {
"mapnik-reference": "5.0.x",
"underscore": "~1.3.3"
}
}

View File

@ -0,0 +1,13 @@
<% _(symbolizers).each(function(symbolizer, name) { %>
<% if (name == '*') { %>## All elements<% } else { %>## <%= name %><% } %>
<% _(symbolizer).chain().filter(function(p) { return p.css; }).each(function(p) { %>
##### <%= p.css.replace(/\s/g, '') %> <% if (_.isArray(p.type)) { %>`keyword`<% } else { %>`<%= p.type %>`<% } %>
<% if (_.isArray(p.type)) { %><% _(p.type).each(function(type) { %>`<%= type %>`<% }); %><% } %>
<% if (typeof p['default-value'] !== '') { %>Default Value: <%= p['default-value'] %><% } %>
<% if (p['default-meaning']) { %>_(<%- p['default-meaning'] %>)_<% } %>
<% if (typeof p['range'] !== 'undefined') { %>Range: <%= '' + p['range'] %><% } %>
<% if (p.doc) { %><%- p.doc%><% } %>
* * *
<% }); %>
<% }); %>

1159
docs/2.0.0.md Normal file

File diff suppressed because it is too large Load Diff

1159
docs/2.0.1.md Normal file

File diff suppressed because it is too large Load Diff

1159
docs/2.0.2.md Normal file

File diff suppressed because it is too large Load Diff

1486
docs/2.1.0.md Normal file

File diff suppressed because it is too large Load Diff

1495
docs/2.1.1.md Normal file

File diff suppressed because it is too large Load Diff

1687
docs/latest.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,19 +5,40 @@ tree.functions = {
return this.rgba(r, g, b, 1.0);
},
rgba: function (r, g, b, a) {
var rgb = [r, g, b].map(function (c) { return number(c); }),
a = number(a);
var rgb = [r, g, b].map(function (c) { return number(c); });
a = number(a);
if (rgb.some(isNaN) || isNaN(a)) return null;
return new tree.Color(rgb, a);
},
// Only require val
stop: function (val) {
var color, mode;
if (arguments.length > 1) color = arguments[1];
if (arguments.length > 2) mode = arguments[2];
return {
is: 'tag',
val: val,
color: color,
mode: mode,
toString: function(env) {
return '\n\t<stop value="' + val.ev(env) + '"' +
(color ? ' color="' + color.ev(env) + '" ' : '') +
(mode ? ' mode="' + mode.ev(env) + '" ' : '') +
'/>';
}
};
},
hsl: function (h, s, l) {
return this.hsla(h, s, l, 1.0);
},
hsla: function (h, s, l, a) {
h = (number(h) % 360) / 360;
s = number(s); l = number(l); a = number(a);
if ([h, s, l, a].some(isNaN)) return null;
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
var m1 = l * 2 - m2;
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s,
m1 = l * 2 - m2;
return this.rgba(hue(h + 1/3) * 255,
hue(h) * 255,
@ -33,18 +54,23 @@ tree.functions = {
}
},
hue: function (color) {
if (!('toHSL' in color)) return null;
return new tree.Dimension(Math.round(color.toHSL().h));
},
saturation: function (color) {
if (!('toHSL' in color)) return null;
return new tree.Dimension(Math.round(color.toHSL().s * 100), '%');
},
lightness: function (color) {
if (!('toHSL' in color)) return null;
return new tree.Dimension(Math.round(color.toHSL().l * 100), '%');
},
alpha: function (color) {
if (!('toHSL' in color)) return null;
return new tree.Dimension(color.toHSL().a);
},
saturate: function (color, amount) {
if (!('toHSL' in color)) return null;
var hsl = color.toHSL();
hsl.s += amount.value / 100;
@ -52,6 +78,7 @@ tree.functions = {
return hsla(hsl);
},
desaturate: function (color, amount) {
if (!('toHSL' in color)) return null;
var hsl = color.toHSL();
hsl.s -= amount.value / 100;
@ -59,6 +86,7 @@ tree.functions = {
return hsla(hsl);
},
lighten: function (color, amount) {
if (!('toHSL' in color)) return null;
var hsl = color.toHSL();
hsl.l += amount.value / 100;
@ -66,6 +94,7 @@ tree.functions = {
return hsla(hsl);
},
darken: function (color, amount) {
if (!('toHSL' in color)) return null;
var hsl = color.toHSL();
hsl.l -= amount.value / 100;
@ -73,6 +102,7 @@ tree.functions = {
return hsla(hsl);
},
fadein: function (color, amount) {
if (!('toHSL' in color)) return null;
var hsl = color.toHSL();
hsl.a += amount.value / 100;
@ -80,6 +110,7 @@ tree.functions = {
return hsla(hsl);
},
fadeout: function (color, amount) {
if (!('toHSL' in color)) return null;
var hsl = color.toHSL();
hsl.a -= amount.value / 100;
@ -87,6 +118,7 @@ tree.functions = {
return hsla(hsl);
},
spin: function (color, amount) {
if (!('toHSL' in color)) return null;
var hsl = color.toHSL();
var hue = (hsl.h + amount.value) % 360;
@ -94,6 +126,13 @@ tree.functions = {
return hsla(hsl);
},
replace: function (entity, a, b) {
if (entity.is === 'field') {
return entity.toString + '.replace(' + a.toString() + ', ' + b.toString() + ')';
} else {
return entity.replace(a, b);
}
},
//
// Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
// http://sass-lang.com
@ -117,9 +156,6 @@ tree.functions = {
greyscale: function (color) {
return this.desaturate(color, new tree.Dimension(100));
},
e: function (str) {
return new tree.Anonymous(str instanceof tree.JavaScript ? str.evaluated : str);
},
'%': function (quoted /* arg, arg, ...*/) {
var args = Array.prototype.slice.call(arguments, 1),
str = quoted.value;
@ -129,12 +165,33 @@ tree.functions = {
.replace(/%[da]/, args[i].toString());
}
str = str.replace(/%%/g, '%');
return new tree.Quoted('"' + str + '"', str);
return new tree.Quoted(str);
}
};
function hsla(hsla) {
return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a);
var image_filter_functors = [
'emboss', 'blur', 'gray', 'sobel', 'edge-detect',
'x-gradient', 'y-gradient', 'sharpen'];
for (var i = 0; i < image_filter_functors.length; i++) {
var f = image_filter_functors[i];
tree.functions[f] = (function(f) {
return function() {
return new tree.ImageFilter(f);
};
})(f);
}
tree.functions['agg-stack-blur'] = function(x, y) {
return new tree.ImageFilter('agg-stack-blur', [x, y]);
};
tree.functions['scale-hsla'] = function(h0,h1,s0,s1,l0,l1,a0,a1) {
return new tree.ImageFilter('scale-hsla', [h0,h1,s0,s1,l0,l1,a0,a1]);
};
function hsla(h) {
return tree.functions.hsla(h.h, h.s, h.l, h.a);
}
function number(n) {
@ -143,7 +200,7 @@ function number(n) {
} else if (typeof(n) === 'number') {
return n;
} else {
throw new Error('Color functions take numbers as parameters.');
return NaN;
}
}

View File

@ -1,9 +1,27 @@
var sys = require('sys');
var util = require('util'),
fs = require('fs'),
path = require('path');
function getVersion() {
if (process.browser) {
return require('../../package.json').version.split('.');
} else if (parseInt(process.version.split('.')[1], 10) > 4) {
return require('../../package.json').version.split('.');
} else {
// older node
var package_json = JSON.parse(fs.readFileSync(path.join(__dirname,'../../package.json')));
return package_json.version.split('.');
}
}
var carto = {
version: [0, 4, 3],
version: getVersion(),
Parser: require('./parser').Parser,
Renderer: require('./renderer').Renderer,
tree: require('./tree'),
RendererJS: require('./renderer_js'),
default_reference: require('./torque-reference'),
// @TODO
writeError: function(ctx, options) {
@ -18,7 +36,7 @@ var carto = {
options.indent = options.indent || '';
if (!('index' in ctx) || !extract) {
return sys.error(options.indent + (ctx.stack || ctx.message));
return util.error(options.indent + (ctx.stack || ctx.message));
}
if (typeof(extract[0]) === 'string') {
@ -35,30 +53,49 @@ 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');
ctx.filename && (message += stylize(' in ', 'red') + ctx.filename);
if (ctx.filename) (message += stylize(' in ', 'red') + ctx.filename);
sys.error(message, error);
util.error(message, error);
if (ctx.callLine) {
sys.error(stylize('from ', 'red') + (ctx.filename || ''));
sys.error(stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract);
util.error(stylize('from ', 'red') + (ctx.filename || ''));
util.error(stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract);
}
if (ctx.stack) { sys.error(stylize(ctx.stack, 'red')); }
if (ctx.stack) { util.error(stylize(ctx.stack, 'red')); }
}
};
[ 'alpha', 'anonymous', 'call', 'color', 'comment', 'definition', 'dimension',
'directive', 'element', 'expression', 'filterset', 'filter',
'keyword', 'layer', 'mixin', 'operation', 'quoted',
'reference', 'rule', 'ruleset', 'selector', 'style', 'url', 'value',
'variable', 'zoom', 'invalid', 'fontset'
].forEach(function(n) {
require('./tree/' + n);
});
require('./tree/call');
require('./tree/color');
require('./tree/comment');
require('./tree/definition');
require('./tree/dimension');
require('./tree/element');
require('./tree/expression');
require('./tree/filterset');
require('./tree/filter');
require('./tree/field');
require('./tree/keyword');
require('./tree/layer');
require('./tree/literal');
require('./tree/operation');
require('./tree/quoted');
require('./tree/imagefilter');
require('./tree/reference');
require('./tree/rule');
require('./tree/ruleset');
require('./tree/selector');
require('./tree/style');
require('./tree/url');
require('./tree/value');
require('./tree/variable');
require('./tree/zoom');
require('./tree/invalid');
require('./tree/fontset');
require('./tree/frame_offset');
require('./functions');
for (var k in carto) { exports[k] = carto[k]; }
@ -74,7 +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';
}

File diff suppressed because it is too large Load Diff

View File

@ -1,105 +1,240 @@
var _ = require('underscore');
var _ = global._ || require('underscore');
var carto = require('./index');
var tree = require('./tree');
carto.Renderer = function Renderer(env) {
carto.Renderer = function Renderer(env, options) {
this.env = env || {};
this.options = options || {};
this.options.mapnik_version = this.options.mapnik_version || '3.0.0';
};
// Prepare a MML document (given as an object) into a
// fully-localized XML file ready for Mapnik2 consumption
//
// - @param {String} str the JSON file as a string.
// - @param {Object} env renderer environment options.
carto.Renderer.prototype.render = function render(m, callback) {
/**
* Prepare a MSS document (given as an string) into a
* XML Style fragment (mostly useful for debugging)
*
* @param {String} data the mss contents as a string.
*/
carto.Renderer.prototype.renderMSS = function render(data) {
// effects is a container for side-effects, which currently
// are limited to FontSets.
env = _(this.env).defaults({
var env = _.defaults(this.env, {
benchmark: false,
validation_data: false,
effects: []
});
if (!carto.tree.Reference.setVersion(this.options.mapnik_version)) {
throw new Error("Could not set mapnik version to " + this.options.mapnik_version);
}
var output = [];
var styles = [];
if (env.benchmark) console.time('Parsing MSS');
var parser = (carto.Parser(env)).parse(data);
if (env.benchmark) console.timeEnd('Parsing MSS');
if (env.benchmark) console.time('Rule generation');
var rule_list = parser.toList(env);
if (env.benchmark) console.timeEnd('Rule generation');
if (env.benchmark) console.time('Rule inheritance');
var rules = inheritDefinitions(rule_list, env);
if (env.benchmark) console.timeEnd('Rule inheritance');
if (env.benchmark) console.time('Style sort');
var sorted = sortStyles(rules,env);
if (env.benchmark) console.timeEnd('Style sort');
if (env.benchmark) console.time('Total Style generation');
for (var k = 0, rule, style_name; k < sorted.length; k++) {
rule = sorted[k];
style_name = 'style' + (rule.attachment !== '__default__' ? '-' + rule.attachment : '');
styles.push(style_name);
var bench_name = '\tStyle "'+style_name+'" (#'+k+') toXML';
if (env.benchmark) console.time(bench_name);
// env.effects can be modified by this call
output.push(carto.tree.StyleXML(style_name, rule.attachment, rule, env));
if (env.benchmark) console.timeEnd(bench_name);
}
if (env.benchmark) console.timeEnd('Total Style generation');
if (env.errors) throw env.errors;
return output.join('\n');
};
/**
* Prepare a MML document (given as an object) into a
* fully-localized XML file ready for Mapnik2 consumption
*
* @param {String} m - the JSON file as a string.
*/
carto.Renderer.prototype.render = function render(m) {
// effects is a container for side-effects, which currently
// are limited to FontSets.
var env = _.defaults(this.env, {
benchmark: false,
validation_data: false,
effects: [],
ppi: 90.714
});
if (!carto.tree.Reference.setVersion(this.options.mapnik_version)) {
throw new Error("Could not set mapnik version to " + this.options.mapnik_version);
}
var output = [];
// Transform stylesheets into rulesets.
var rulesets = _(m.Stylesheet).chain()
// Transform stylesheets into definitions.
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});
// @TODO try/catch?
var time = +new Date(),
root = (carto.Parser(env)).parse(s.data);
if (env.benchmark)
console.warn('Parsing time: ' + ((new Date() - time)) + 'ms');
console.warn('Parsing time: ' + (new Date() - time) + 'ms');
return root.toList(env);
})
.flatten()
.value();
function appliesTo(name, classIndex) {
return function(definition) {
return definition.appliesTo(l.name, classIndex);
};
}
// Iterate through layers and create styles custom-built
// for each of them, and apply those styles to the layers.
m.Layer.forEach(function(l) {
l.styles = [];
var styles, l, classIndex, rules, sorted, matching;
for (var i = 0; i < m.Layer.length; i++) {
l = m.Layer[i];
styles = [];
classIndex = {};
if (env.benchmark) console.warn('processing layer: ' + l.id);
// Classes are given as space-separated alphanumeric strings.
var classes = (l['class'] || '').split(/\s+/g);
var matching = rulesets.filter(function(definition) {
return definition.appliesTo(l.name, classes);
});
var rules = inheritRules(matching, env);
sorted = sortStyles(rules, env);
_(sorted).each(function(rule) {
var style = new tree.Style(l.name, rule.attachment, rule);
if (style) {
l.styles.push(style.name);
// env.effects can be modified by this call
output.push(style.toXML(env));
}
});
if (l.styles.length) {
output.push((new carto.tree.Layer(l)).toXML());
for (var j = 0; j < classes.length; j++) {
classIndex[classes[j]] = true;
}
});
matching = definitions.filter(appliesTo(l.name, classIndex));
rules = inheritDefinitions(matching, env);
sorted = sortStyles(rules, env);
for (var k = 0, rule, style_name; k < sorted.length; k++) {
rule = sorted[k];
style_name = l.name + (rule.attachment !== '__default__' ? '-' + rule.attachment : '');
// env.effects can be modified by this call
var styleXML = carto.tree.StyleXML(style_name, rule.attachment, rule, env);
if (styleXML) {
output.push(styleXML);
styles.push(style_name);
}
}
output.push(carto.tree.LayerXML(l, styles));
}
output.unshift(env.effects.map(function(e) {
return e.toXML(env);
}).join('\n'));
try {
var map_properties = getMapProperties(m, rulesets, env);
} catch (err) {
env.error(err);
return callback(err);
}
var map_properties = getMapProperties(m, definitions, env);
// Exit on errors.
if (env.errors) return callback(env.errors);
if (env.errors) throw env.errors;
// Pass TileJSON and other custom parameters through to Mapnik XML.
var parameters = _.reduce(m, function(memo, v, k) {
if (!v && v !== 0) return memo;
switch (k) {
// Known skippable properties.
case 'srs':
case 'Layer':
case 'Stylesheet':
break;
// Non URL-bound TileJSON properties.
case 'bounds':
case 'center':
case 'minzoom':
case 'maxzoom':
case 'version':
memo.push(' <Parameter name="' + k + '">' + v + '</Parameter>');
break;
// Properties that require CDATA.
case 'name':
case 'description':
case 'legend':
case 'attribution':
case 'template':
memo.push(' <Parameter name="' + k + '"><![CDATA[' + v + ']]></Parameter>');
break;
// Mapnik image format.
case 'format':
memo.push(' <Parameter name="' + k + '">' + v + '</Parameter>');
break;
// Mapnik interactivity settings.
case 'interactivity':
memo.push(' <Parameter name="interactivity_layer">' + v.layer + '</Parameter>');
memo.push(' <Parameter name="interactivity_fields">' + v.fields + '</Parameter>');
break;
// Support any additional scalar properties.
default:
if ('string' === typeof v) {
memo.push(' <Parameter name="' + k + '"><![CDATA[' + v + ']]></Parameter>');
} else if ('number' === typeof v) {
memo.push(' <Parameter name="' + k + '">' + v + '</Parameter>');
} else if ('boolean' === typeof v) {
memo.push(' <Parameter name="' + k + '">' + v + '</Parameter>');
}
break;
}
return memo;
}, []);
if (parameters.length) output.unshift(
'<Parameters>\n' +
parameters.join('\n') +
'\n</Parameters>\n'
);
var properties = _.map(map_properties, function(v) { return ' ' + v; }).join('');
var properties = _(map_properties).map(function(v) { return ' ' + v; }).join('');
output.unshift(
'<?xml version="1.0" ' +
'encoding="utf-8"?>\n' +
'<!DOCTYPE Map[]>\n' +
'<Map' + properties + '>\n');
'<Map' + properties +'>\n');
output.push('</Map>');
return callback(null, output.join('\n'));
return output.join('\n');
};
// This function currently modifies 'current'
function addRules(current, definition, existing) {
var newFilters = definition.filters;
var newRules = definition.rules;
var updatedFilters, clone, previous;
/**
* This function currently modifies 'current'
* @param {Array} current current list of rules
* @param {Object} definition a Definition object to add to the rules
* @param {Object} byFilter an object/dictionary of existing filters. This is
* actually keyed `attachment->filter`
* @param {Object} env the current environment
*/
function addRules(current, definition, byFilter, env) {
var newFilters = definition.filters,
newRules = definition.rules,
updatedFilters, clone, previous;
// The current definition might have been split up into
// multiple definitions already.
for (var k = 0; k < current.length; k++) {
updatedFilters = current[k].filters.cloneWith(newFilters);
if (updatedFilters) {
previous = existing[updatedFilters];
previous = byFilter[updatedFilters];
if (previous) {
// There's already a definition with those exact
// filters. Add the current definitions' rules
@ -116,32 +251,63 @@ function addRules(current, definition, existing) {
// to make sure that in the next loop iteration, we're
// not performing the same task for this element again,
// hence the k++.
byFilter[updatedFilters] = clone;
current.splice(k, 0, clone);
k++;
}
}
} else if (updatedFilters === null) {
// if updatedFilters is null, then adding the filters doesn't
// invalidate or split the selector, so we addRules to the
// combined selector
// Filters can be added, but they don't change the
// filters. This means we don't have to split the
// definition.
//
// this is cloned here because of shared classes, see
// sharedclass.mss
current[k] = current[k].clone();
current[k].addRules(newRules);
}
// if updatedFeatures is false, then the filters split the rule,
// so they aren't the same inheritance chain
}
return current;
}
// Apply inherited styles from their ancestors to them.
function inheritRules(definitions, env) {
/**
* Apply inherited styles from their ancestors to them.
*
* called either once per render (in the case of mss) or per layer
* (for mml)
*
* @param {Object} definitions - a list of definitions objects
* that contain .rules
* @param {Object} env - the environment
* @return {Array<Array>} an array of arrays is returned,
* in which each array refers to a specific attachment
*/
function inheritDefinitions(definitions, env) {
var inheritTime = +new Date();
// definitions are ordered by specificity,
// high (index 0) to low
var byAttachment = {}, byFilter = {};
var byAttachment = {},
byFilter = {};
var result = [];
var current, previous, attachment;
// Evaluate the filters specified by each definition with the given
// environment to correctly resolve variable references
definitions.forEach(function(d) {
d.filters.ev(env);
});
for (var i = 0; i < definitions.length; i++) {
attachment = definitions[i].attachment;
current = [definitions[i]];
if (!byAttachment[attachment]) {
byAttachment[attachment] = [];
byAttachment[attachment].attachment = attachment;
@ -149,12 +315,11 @@ function inheritRules(definitions, env) {
result.push(byAttachment[attachment]);
}
current = [definitions[i]];
// Iterate over all subsequent rules.
for (var j = i + 1; j < definitions.length; j++) {
if (definitions[j].attachment === attachment) {
// Only inherit rules from the same attachment.
current = addRules(current, definitions[j], byFilter);
current = addRules(current, definitions[j], byFilter[attachment], env);
}
}
@ -167,46 +332,54 @@ function inheritRules(definitions, env) {
if (env.benchmark) console.warn('Inheritance time: ' + ((new Date() - inheritTime)) + 'ms');
return result;
}
// Sort styles by the minimum index of their rules.
// This sorts a slice of the styles, so it returns a sorted
// array but does not change the input.
function sortStylesIndex(a, b) { return b.index - a.index; }
function sortStyles(styles, env) {
styles.forEach(function(style) {
for (var i = 0; i < styles.length; i++) {
var style = styles[i];
style.index = Infinity;
style.forEach(function(block) {
block.rules.forEach(function(rule) {
for (var b = 0; b < style.length; b++) {
var rules = style[b].rules;
for (var r = 0; r < rules.length; r++) {
var rule = rules[r];
if (rule.index < style.index) {
style.index = rule.index;
}
});
});
});
}
}
}
var result = styles.slice();
result.sort(function(a, b) {
return b.index - a.index;
});
result.sort(sortStylesIndex);
return result;
}
// Find a rule like Map { background-color: #fff; },
// if any, and return a list of properties to be inserted
// into the <Map element of the resulting XML. Translates
// properties of the mml object at `m` directly into XML
// properties.
//
// - @param {Object} m the mml object.
// - @param {Array} rulesets the output of toList.
// - @param {Object} env.
// - @return {String} rendered properties.
function getMapProperties(m, rulesets, env) {
/**
* Find a rule like Map { background-color: #fff; },
* if any, and return a list of properties to be inserted
* into the <Map element of the resulting XML. Translates
* properties of the mml object at `m` directly into XML
* properties.
*
* @param {Object} m the mml object.
* @param {Array} definitions the output of toList.
* @param {Object} env
* @return {String} rendered properties.
*/
function getMapProperties(m, definitions, env) {
var rules = {};
var symbolizers = tree.Reference.data.symbolizers.map;
var symbolizers = carto.tree.Reference.data.symbolizers.map;
_(m).each(function(value, key) {
if (key in symbolizers) rules[key] = key + '="' + value + '"';
});
rulesets.filter(function(r) {
definitions.filter(function(r) {
return r.elements.join('') === 'Map';
}).forEach(function(r) {
for (var i = 0; i < r.rules.length; i++) {
@ -217,10 +390,13 @@ function getMapProperties(m, rulesets, env) {
index: r.rules[i].index
});
}
rules[key] = r.rules[i].eval(env).toXML(env);
rules[key] = r.rules[i].ev(env).toXML(env);
}
});
return rules;
}
module.exports = carto;
module.exports.addRules = addRules;
module.exports.inheritDefinitions = inheritDefinitions;
module.exports.sortStyles = sortStyles;

302
lib/carto/renderer_js.js Normal file
View File

@ -0,0 +1,302 @@
(function(carto) {
var tree = require('./tree');
var _ = global._ || require('underscore');
function CartoCSS(style, options) {
this.options = options || {};
this.imageURLs = [];
if(style) {
this.setStyle(style);
}
}
CartoCSS.Layer = function(shader, options) {
this.options = options;
this.shader = shader;
};
CartoCSS.Layer.prototype = {
fullName: function() {
return this.shader.attachment;
},
name: function() {
return this.fullName().split('::')[0];
},
// frames this layer need to be rendered
frames: function() {
return this.shader.frames;
},
attachment: function() {
return this.fullName().split('::')[1];
},
eval: function(prop) {
var p = this.shader[prop];
if (!p || !p.style) return;
return p.style({}, { zoom: 0, 'frame-offset': 0 });
},
/*
* `props`: feature properties
* `context`: rendering properties, i.e zoom
*/
getStyle: function(props, context) {
var style = {};
for(var i in this.shader) {
if(i !== 'attachment' && i !== 'zoom' && i !== 'frames' && i !== 'symbolizers') {
style[i] = this.shader[i].style(props, context);
}
}
return style;
},
/**
* 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'
*/
getSymbolizers: function() {
return this.shader.symbolizers;
},
/**
* returns if the style varies with some feature property.
* Useful to optimize rendering
*/
isVariable: function() {
for(var i in this.shader) {
if(i !== 'attachment' && i !== 'zoom' && i !== 'frames' && i !== 'symbolizers') {
if (!this.shader[i].constant) {
return true;
}
}
}
return false;
},
getShader: function() {
return this.shader;
},
/**
* returns true if a feature needs to be rendered
*/
filter: function(featureType, props, context) {
for(var i in this.shader) {
var s = this.shader[i](props, context);
if(s) {
return true;
}
}
return false;
},
//
// 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
// is a circle, second one is an image sprite
//
// the other geometry types are the same than geojson (polygon, linestring...)
//
transformGeometry: function(type) {
return type;
},
transformGeometries: function(geojson) {
return geojson;
}
};
CartoCSS.prototype = {
setStyle: function(style) {
var layers = this.parse(style);
if(!layers) {
throw new Error(this.parse_env.errors);
}
this.layers = layers.map(function(shader) {
return new CartoCSS.Layer(shader);
});
},
getLayers: function() {
return this.layers;
},
getDefault: function() {
return this.findLayer({ attachment: '__default__' });
},
findLayer: function(where) {
return _.find(this.layers, function(value) {
for (var key in where) {
var v = value[key];
if (typeof(v) === 'function') {
v = v.call(value);
}
if (where[key] !== v) return false;
}
return true;
});
},
_createFn: function(ops) {
var body = ops.join('\n');
if(this.options.debug) console.log(body);
return Function("data","ctx", "var _value = null; " + body + "; return _value; ");
},
_compile: function(shader) {
if(typeof shader === 'string') {
shader = eval("(function() { return " + shader +"; })()");
}
this.shader_src = shader;
for(var attr in shader) {
var c = mapper[attr];
if(c) {
this.compiled[c] = eval("(function() { return shader[attr]; })();");
}
}
},
getImageURLs: function(){
return this.imageURLs;
},
parse: function(cartocss) {
var parse_env = {
frames: [],
errors: [],
error: function(obj) {
this.errors.push(obj);
}
};
this.parse_env = parse_env;
var ruleset = null;
try {
ruleset = (new carto.Parser(parse_env)).parse(cartocss);
} catch(e) {
// add the style.mss string to match the response from the server
parse_env.errors.push(e.message);
return;
}
if(ruleset) {
function defKey(def) {
return def.elements[0] + "::" + def.attachment;
}
var defs = ruleset.toList(parse_env);
defs.reverse();
// group by elements[0].value::attachment
var layers = {};
for(var i = 0; i < defs.length; ++i) {
var def = defs[i];
var key = defKey(def);
var layer = layers[key] = (layers[key] || {
symbolizers: []
});
for(var u = 0; u<def.rules.length; u++){
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);
if (this.options.debug) console.log("props", props);
for(var v in props) {
var lyr = layer[v] = layer[v] || {
constant: false,
symbolizer: null,
js: [],
index: 0
};
// build javascript statements
lyr.js.push(props[v].map(function(a) { return a.js; }).join('\n'));
// get symbolizer for prop
lyr.symbolizer = _.first(props[v].map(function(a) { return a.symbolizer; }));
// 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;
}
}
var ordered_layers = [];
if (this.options.debug) console.log(layers);
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]) {
if(this.options.debug) console.log("**", k);
for(var prop in layer) {
if (prop !== 'zoom' && prop !== 'frames' && prop !== 'symbolizers') {
if(this.options.debug) console.log("*", prop);
layer[prop].style = this._createFn(layer[prop].js);
layer.symbolizers.push(layer[prop].symbolizer);
layer.symbolizers = _.uniq(layer.symbolizers);
}
}
layer.attachment = k;
ordered_layers.push(layer);
done[k] = true;
}
layer.zoom |= def.zoom;
layer.frames.push(def.frame_offset);
}
// uniq the frames
for(i = 0; i < ordered_layers.length; ++i) {
ordered_layers[i].frames = _.uniq(ordered_layers[i].frames);
}
return ordered_layers;
}
return null;
}
};
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) {
tree.Reference.setData(this.reference);
return new CartoCSS(cartocss, this.options);
}
if(typeof(module) !== 'undefined') {
module.exports = carto.RendererJS;
}
})(require('../carto'));

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,11 @@
/**
* TODO: document this. What does this do?
*/
module.exports.find = function (obj, fun) {
for (var i = 0, r; i < obj.length; i++) {
if (r = fun.call(obj, obj[i])) { return r; }
}
return null;
};
if(typeof(module) !== "undefined") {
module.exports.find = function (obj, fun) {
for (var i = 0, r; i < obj.length; i++) {
if (r = fun.call(obj, obj[i])) { return r; }
}
return null;
};
}

View File

@ -1,14 +0,0 @@
(function(tree) {
tree.Alpha = function Alpha(val) {
this.value = val;
};
tree.Alpha.prototype = {
toString: function() {
return 'alpha(opacity=' +
(this.value.toString ? this.value.toString() : this.value) + ')';
},
eval: function() { return this; }
};
})(require('../tree'));

View File

@ -1,13 +0,0 @@
(function(tree) {
tree.Anonymous = function Anonymous(string) {
this.value = string.value || string;
};
tree.Anonymous.prototype = {
toString: function() {
return this.value;
},
eval: function() { return this; }
};
})(require('../tree'));

View File

@ -1,27 +1,23 @@
(function(tree) {
//
// A function call node.
//
tree.Call = function Call(name, args) {
var _ = global._ || require('underscore');
tree.Call = function Call(name, args, index) {
this.name = name;
this.args = args;
this.index = index;
};
tree.Call.prototype = {
//
// When evaluating a function call,
is: 'call',
// When evuating a function call,
// we either find the function in `tree.functions` [1],
// in which case we call it, passing the evaluated arguments,
// or we simply print it out as it appeared originally [2].
//
// The *functions.js* file contains the built-in functions.
//
// The reason why we evaluate the arguments, is in the case where
// we try to pass a variable to a function, like: `saturate(@color)`.
// The function should receive the value, not the variable.
//
eval: function(env) {
var args = this.args.map(function(a) { return a.eval(env); });
'ev': function(env) {
var args = this.args.map(function(a) { return a.ev(env); });
for (var i = 0; i < args.length; i++) {
if (args[i].is === 'undefined') {
@ -32,16 +28,84 @@ tree.Call.prototype = {
}
}
if (this.name in tree.functions) { // 1.
return tree.functions[this.name].apply(tree.functions, args);
} else { // 2.
return new tree.Anonymous(this.name +
'(' + args.map(function(a) { return a.toString(); }).join(', ') + ')');
if (this.name in tree.functions) {
if (tree.functions[this.name].length <= args.length) {
var val = tree.functions[this.name].apply(tree.functions, args);
if (val === null) {
env.error({
message: 'incorrect arguments given to ' + this.name + '()',
index: this.index,
type: 'runtime',
filename: this.filename
});
return { is: 'undefined', value: 'undefined' };
}
return val;
} else {
env.error({
message: 'incorrect number of arguments for ' + this.name +
'(). ' + tree.functions[this.name].length + ' expected.',
index: this.index,
type: 'runtime',
filename: this.filename
});
return {
is: 'undefined',
value: 'undefined'
};
}
} else {
var fn = tree.Reference.mapnikFunctions[this.name];
if (fn === undefined) {
var functions = _.pairs(tree.Reference.mapnikFunctions);
// cheap closest, needs improvement.
var name = this.name;
var mean = functions.map(function(f) {
return [f[0], tree.Reference.editDistance(name, f[0]), f[1]];
}).sort(function(a, b) {
return a[1] - b[1];
});
env.error({
message: 'unknown function ' + this.name + '(), did you mean ' +
mean[0][0] + '(' + mean[0][2] + ')',
index: this.index,
type: 'runtime',
filename: this.filename
});
return {
is: 'undefined',
value: 'undefined'
};
}
if (fn !== args.length &&
!(Array.isArray(fn) && _.include(fn, args.length)) &&
// support variable-arg functions like `colorize-alpha`
fn !== -1) {
env.error({
message: 'function ' + this.name + '() takes ' +
fn + ' arguments and was given ' + args.length,
index: this.index,
type: 'runtime',
filename: this.filename
});
return {
is: 'undefined',
value: 'undefined'
};
} else {
// Save the evaluated versions of arguments
this.args = args;
return this;
}
}
},
toString: function(env) {
return this.eval(env).toString();
toString: function(env, format) {
if (this.args.length) {
return this.name + '(' + this.args.join(',') + ')';
} else {
return this.name;
}
}
};

View File

@ -1,16 +1,14 @@
(function(tree) {
//
// RGB Colors - #ff0014, #eee
//
// can be initialized with a 3 or 6 char string or a 3 or 4 element
// numerical array
tree.Color = function Color(rgb, a) {
//
// The end goal here, is to parse the arguments
// into an integer triplet, such as `128, 255, 0`
//
// This facilitates operations and conversions.
//
if (Array.isArray(rgb)) {
this.rgb = rgb;
this.rgb = rgb.slice(0, 3);
} else if (rgb.length == 6) {
this.rgb = rgb.match(/.{2}/g).map(function(c) {
return parseInt(c, 16);
@ -20,18 +18,24 @@ tree.Color = function Color(rgb, a) {
return parseInt(c + c, 16);
});
}
this.is = 'color';
this.alpha = typeof(a) === 'number' ? a : 1;
};
tree.Color.prototype = {
eval: function() { return this; },
//
if (typeof(a) === 'number') {
this.alpha = a;
} else if (rgb.length === 4) {
this.alpha = rgb[3];
} else {
this.alpha = 1;
}
};
tree.Color.prototype = {
is: 'color',
'ev': function() { return this; },
// If we have some transparency, the only way to represent it
// is via `rgba`. Otherwise, we use the hex representation,
// which has better compatibility with older browsers.
// Values are capped between `0` and `255`, rounded and zero-padded.
//
toString: function() {
if (this.alpha < 1.0) {
return 'rgba(' + this.rgb.map(function(c) {
@ -46,13 +50,11 @@ tree.Color.prototype = {
}
},
//
// Operations have to be done per-channel, if not,
// channels will spill onto each other. Once we have
// our result, in the form of an integer triplet,
// we create a new Color node to hold the result.
//
operate: function(op, other) {
operate: function(env, op, other) {
var result = [];
if (! (other instanceof tree.Color)) {
@ -90,5 +92,4 @@ tree.Color.prototype = {
}
};
})(require('../tree'));

View File

@ -4,11 +4,12 @@ tree.Comment = function Comment(value, silent) {
this.value = value;
this.silent = !!silent;
};
tree.Comment.prototype = {
toString: function(env) {
return '<!--' + this.value + '-->';
},
eval: function() { return this; }
'ev': function() { return this; }
};
})(require('../tree'));

View File

@ -1,18 +1,26 @@
(function(tree) {
var assert = require('assert');
var assert = require('assert'),
_ = global._ || require('underscore');
// A definition is the combination of a selector and rules, like
// #foo {
// polygon-opacity:1.0;
// }
//
// The selector can have filters
tree.Definition = function Definition(selector, rules) {
this.elements = selector.elements;
assert.ok(selector.filters instanceof tree.Filterset);
this.rules = rules;
this.ruleIndex = [];
this.ruleIndex = {};
for (var i = 0; i < this.rules.length; i++) {
if ('zoom' in this.rules[i]) this.rules[i] = this.rules[i].clone();
this.rules[i].zoom = selector.zoom;
this.ruleIndex.push(this.rules[i].updateID());
this.ruleIndex[this.rules[i].updateID()] = true;
}
this.filters = selector.filters;
this.zoom = selector.zoom;
this.frame_offset = selector.frame_offset;
this.attachment = selector.attachment || '__default__';
this.specificity = selector.specificity();
};
@ -29,7 +37,7 @@ tree.Definition.prototype.clone = function(filters) {
if (filters) assert.ok(filters instanceof tree.Filterset);
var clone = Object.create(tree.Definition.prototype);
clone.rules = this.rules.slice();
clone.ruleIndex = this.ruleIndex.slice();
clone.ruleIndex = _.clone(this.ruleIndex);
clone.filters = filters ? filters : this.filters.clone();
clone.attachment = this.attachment;
return clone;
@ -40,9 +48,9 @@ tree.Definition.prototype.addRules = function(rules) {
// Add only unique rules.
for (var i = 0; i < rules.length; i++) {
if (this.ruleIndex.indexOf(rules[i].id) < 0) {
if (!this.ruleIndex[rules[i].id]) {
this.rules.push(rules[i]);
this.ruleIndex.push(rules[i].id);
this.ruleIndex[rules[i].id] = true;
added++;
}
}
@ -50,25 +58,34 @@ tree.Definition.prototype.addRules = function(rules) {
return added;
};
/**
* Determine whether this selector matches a given id
* and array of classes, by determining whether
* all elements it contains match.
*/
// Determine whether this selector matches a given id
// and array of classes, by determining whether
// all elements it contains match.
tree.Definition.prototype.appliesTo = function(id, classes) {
for (var i = 0; i < this.elements.length; i++) {
if (!this.elements[i].matches(id, classes)) {
return false;
}
for (var i = 0, l = this.elements.length; i < l; i++) {
var elem = this.elements[i];
if (!(elem.wildcard ||
(elem.type === 'class' && classes[elem.clean]) ||
(elem.type === 'id' && id === elem.clean))) return false;
}
return true;
};
function symbolizerName(symbolizer) {
function capitalize(str) { return str[1].toUpperCase(); }
return symbolizer.charAt(0).toUpperCase() +
symbolizer.slice(1).replace(/\-./, capitalize) + 'Symbolizer';
}
// Get a simple list of the symbolizers, in order
function symbolizerList(sym_order) {
return sym_order.sort(function(a, b) { return a[1] - b[1]; })
.map(function(v) { return v[0]; });
}
tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) {
var xml = ' <Rule>\n';
xml += tree.Zoom.toXML(zoom).join('');
xml += this.filters.toXML(env);
var xml = zoom.toXML(env).join('') + this.filters.toXML(env);
// Sort symbolizers by the index of their first property definition
var sym_order = [], indexes = [];
for (var key in symbolizers) {
@ -80,17 +97,20 @@ tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) {
sym_order.push([key, min_idx]);
}
// Get a simple list of the symbolizers, in order
sym_order = sym_order.sort(function(a, b) {
return a[1] - b[1];
}).map(function(v) {
return v[0];
});
sym_order = symbolizerList(sym_order);
var sym_count = 0;
for (var i = 0; i < sym_order.length; i++) {
attributes = symbolizers[sym_order[i]];
var attributes = symbolizers[sym_order[i]];
var symbolizer = sym_order[i].split('/').pop();
if (fail = tree.Reference.requiredProperties(symbolizer, attributes)) {
// Skip the magical * symbolizer which is used for universal properties
// which are bubbled up to Style elements intead of Symbolizer elements.
if (symbolizer === '*') continue;
sym_count++;
var fail = tree.Reference.requiredProperties(symbolizer, attributes);
if (fail) {
var rule = attributes[Object.keys(attributes).shift()];
env.error({
message: fail,
@ -99,32 +119,43 @@ tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) {
});
}
var name = symbolizer.charAt(0).toUpperCase() +
symbolizer.slice(1).replace(/\-./, function(str) {
return str[1].toUpperCase();
}) + 'Symbolizer';
var name = symbolizerName(symbolizer);
var selfclosing = true, tagcontent;
xml += ' <' + name + ' ';
for (var key in attributes) {
var x = tree.Reference.selector(attributes[key].name);
for (var j in attributes) {
if (symbolizer === 'map') env.error({
message: 'Map properties are not permitted in other rules',
index: attributes[j].index,
filename: attributes[j].filename
});
var x = tree.Reference.selector(attributes[j].name);
if (x && x.serialization && x.serialization === 'content') {
selfclosing = false;
tagcontent = attributes[key].eval(env).toXML(env, true);
tagcontent = attributes[j].ev(env).toXML(env, true);
} else if (x && x.serialization && x.serialization === 'tag') {
selfclosing = false;
tagcontent = attributes[j].ev(env).toXML(env, true);
} else {
xml += attributes[key].eval(env).toXML(env) + ' ';
xml += attributes[j].ev(env).toXML(env) + ' ';
}
}
if (selfclosing) {
xml += '/>\n';
} else {
xml += '>' + tagcontent + '</' + name + '>\n';
} else if (typeof tagcontent !== "undefined") {
if (tagcontent.indexOf('<') != -1) {
xml += '>' + tagcontent + '</' + name + '>\n';
} else {
xml += '><![CDATA[' + tagcontent + ']]></' + name + '>\n';
}
}
}
xml += ' </Rule>\n';
return xml;
if (!sym_count || !xml) return '';
return ' <Rule>\n' + xml + ' </Rule>\n';
};
// Take a zoom range of zooms and 'i', the index of a rule in this.rules,
// and finds all applicable symbolizers
tree.Definition.prototype.collectSymbolizers = function(zooms, i) {
var symbolizers = {}, child;
@ -148,18 +179,18 @@ tree.Definition.prototype.collectSymbolizers = function(zooms, i) {
}
};
// The tree.Zoom.toString function ignores the holes in zoom ranges and outputs
// scaledenominators that cover the whole range from the first to last bit set.
// This algorithm can produces zoom ranges that may have holes. However,
// when using the filter-mode="first", more specific zoom filters will always
// end up before broader ranges. The filter-mode will pick those first before
// resorting to the zoom range with the hole and stop processing further rules.
tree.Definition.prototype.toXML = function(env, existing) {
// The tree.Zoom.toString function ignores the holes in zoom ranges and outputs
// scaledenominators that cover the whole range from the first to last bit set.
// This algorithm can produces zoom ranges that may have holes. However,
// when using the filter-mode="first", more specific zoom filters will always
// end up before broader ranges. The filter-mode will pick those first before
// resorting to the zoom range with the hole and stop processing further rules.
var filter = this.filters.toString();
if (!(filter in existing)) existing[filter] = tree.Zoom.all;
var available = tree.Zoom.all, xml = '', zoom, symbolizers;
var zooms = { available: tree.Zoom.all };
var available = tree.Zoom.all, xml = '', zoom, symbolizers,
zooms = { available: tree.Zoom.all };
for (var i = 0; i < this.rules.length && available; i++) {
zooms.rule = this.rules[i].zoom;
if (!(existing[filter] & zooms.rule)) continue;
@ -167,7 +198,8 @@ tree.Definition.prototype.toXML = function(env, existing) {
while (zooms.current = zooms.rule & available) {
if (symbolizers = this.collectSymbolizers(zooms, i)) {
if (!(existing[filter] & zooms.current)) continue;
xml += this.symbolizersToXML(env, symbolizers, existing[filter] & zooms.current);
xml += this.symbolizersToXML(env, symbolizers,
(new tree.Zoom()).setZoom(existing[filter] & zooms.current));
existing[filter] &= ~zooms.current;
}
}
@ -176,4 +208,41 @@ tree.Definition.prototype.toXML = function(env, existing) {
return xml;
};
tree.Definition.prototype.toJS = function(env) {
var shaderAttrs = {};
var frame_offset = this.frame_offset;
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);
}
if (frame_offset) {
filters.push('ctx["frame-offset"] === ' + frame_offset);
}
_.each(this.rules, function (rule) {
var exportedRule = {};
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;
};
})(require('../tree'));

View File

@ -1,42 +1,98 @@
(function(tree) {
var _ = global._ || require('underscore');
//
// A number with a unit
//
tree.Dimension = function Dimension(value, unit, index) {
this.value = parseFloat(value);
this.unit = unit || null;
this.is = 'float';
this.index = index;
};
tree.Dimension.prototype = {
eval: function (env) {
if (this.unit && ['px', '%'].indexOf(this.unit) === -1) {
env.error({
is: 'float',
physical_units: ['m', 'cm', 'in', 'mm', 'pt', 'pc'],
screen_units: ['px', '%'],
all_units: ['m', 'cm', 'in', 'mm', 'pt', 'pc', 'px', '%'],
densities: {
m: 0.0254,
mm: 25.4,
cm: 2.54,
pt: 72,
pc: 6
},
ev: function (env) {
if (this.unit && !_.contains(this.all_units, this.unit)) {
env.error({
message: "Invalid unit: '" + this.unit + "'",
index: this.index
});
return { is: 'undefined', value: 'undefined' };
}
// normalize units which are not px or %
if (this.unit && _.contains(this.physical_units, this.unit)) {
if (!env.ppi) {
env.error({
message: "ppi is not set, so metric units can't be used",
index: this.index
});
return { is: 'undefined', value: 'undefined' };
}
// convert all units to inch
// convert inch to px using ppi
this.value = (this.value / this.densities[this.unit]) * env.ppi;
this.unit = 'px';
}
return this;
},
round: function() {
this.value = Math.round(this.value);
return this;
},
toColor: function() {
return new tree.Color([this.value, this.value, this.value]);
},
toString: function() {
return this.value;
round: function() {
this.value = Math.round(this.value);
return this;
},
toString: function() {
return this.value.toString();
},
operate: function(env, op, other) {
if (this.unit === '%' && other.unit !== '%') {
env.error({
message: 'If two operands differ, the first must not be %',
index: this.index
});
return {
is: 'undefined',
value: 'undefined'
};
}
// In an operation between two Dimensions,
// we default to the first Dimension's unit,
// so `1px + 2em` will yield `3px`.
// In the future, we could implement some unit
// conversions such that `100cm + 10mm` would yield
// `101cm`.
operate: function(op, other) {
if (this.unit !== '%' && other.unit === '%') {
if (op === '*' || op === '/' || op === '%') {
env.error({
message: 'Percent values can only be added or subtracted from other values',
index: this.index
});
return {
is: 'undefined',
value: 'undefined'
};
}
return new tree.Dimension(tree.operate(op,
this.value, this.value * other.value * 0.01),
this.unit);
}
//here the operands are either the same (% or undefined or px), or one is undefined and the other is px
return new tree.Dimension(tree.operate(op, this.value, other.value),
this.unit || other.unit);
this.unit || other.unit);
}
};

View File

@ -1,33 +0,0 @@
(function(tree) {
tree.Directive = function Directive(name, value) {
this.name = name;
if (Array.isArray(value)) {
this.ruleset = new tree.Ruleset([], value);
} else {
this.value = value;
}
};
tree.Directive.prototype = {
toString: function(ctx, env) {
if (this.ruleset) {
this.ruleset.root = true;
return this.name + ' {\n ' +
this.ruleset.toString(ctx, env).trim().replace(/\n/g, '\n ') +
'\n}\n';
} else {
return this.name + ' ' + this.value.toString() + ';\n';
}
},
eval: function(env) {
env.frames.unshift(this);
this.ruleset = this.ruleset && this.ruleset.eval(env);
env.frames.shift();
return this;
},
variable: function(name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
find: function() { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
rulesets: function() { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
};
})(require('../tree'));

View File

@ -3,31 +3,28 @@
// An element is an id or class selector
tree.Element = function Element(value) {
this.value = value.trim();
if (this.value[0] === '#') {
this.type = 'id';
this.clean = this.value.replace(/^#/, '');
}
if (this.value[0] === '.') {
this.type = 'class';
this.clean = this.value.replace(/^\./, '');
}
if (this.value.indexOf('*') !== -1) {
this.type = 'wildcard';
}
};
// Determine the 'specificity matrix' of this
// specific selector
tree.Element.prototype.specificity = function() {
return [
(this.value[0] == '#') ? 1 : 0, // a
(this.value[0] == '.') ? 1 : 0 // b
(this.type === 'id') ? 1 : 0, // a
(this.type === 'class') ? 1 : 0 // b
];
};
tree.Element.prototype.toString = function() {
return this.value;
};
// Determine whether this element matches an id or classes.
// An element is a single id or class, or check whether the given
// array of classes contains this, or the id is equal to this.
//
// Takes a plain string for id and plain strings in the array of
// classes.
tree.Element.prototype.matches = function(id, classes) {
return (classes.indexOf(this.value.replace(/^\./, '')) !== -1) ||
(this.value.replace(/^#/, '') === id) ||
(this.value === '*');
};
tree.Element.prototype.toString = function() { return this.value; };
})(require('../tree'));

View File

@ -1,16 +1,21 @@
(function(tree) {
tree.Expression = function Expression(value) { this.value = value };
tree.Expression = function Expression(value) {
this.value = value;
};
tree.Expression.prototype = {
eval: function(env) {
is: 'expression',
ev: function(env) {
if (this.value.length > 1) {
return new tree.Expression(this.value.map(function(e) {
return e.eval(env);
return e.ev(env);
}));
} else {
return this.value[0].eval(env);
return this.value[0].ev(env);
}
},
toString: function(env) {
return this.value.map(function(e) {
return e.toString(env);

17
lib/carto/tree/field.js Normal file
View File

@ -0,0 +1,17 @@
(function(tree) {
tree.Field = function Field(content) {
this.value = content || '';
};
tree.Field.prototype = {
is: 'field',
toString: function() {
return '[' + this.value + ']';
},
'ev': function() {
return this;
}
};
})(require('../tree'));

View File

@ -1,56 +1,64 @@
(function(tree) {
tree.Filter = function Filter(key, op, val, index, filename) {
if (key.is) {
this.key = key.value;
this._key = key;
} else {
this.key = key;
}
this.key = key;
this.op = op;
this.val = val;
this.index = index;
this.filename = filename;
if (val.is) {
this.val = val.value;
this._val = val;
} else {
this.val = val;
}
if (this.op !== '=' && this.op !== '!=') {
this.val = 1 * this.val;
}
this.id = this.key + this.op + this.val;
};
// xmlsafe, numeric, suffix
var ops = {
'<': [' &lt; ', 'numeric'],
'>': [' &gt; ', 'numeric'],
'=': [' = ', 'both'],
'!=': [' != ', 'both'],
'<=': [' &lt;= ', 'numeric'],
'>=': [' &gt;= ', 'numeric'],
'=~': ['.match(', 'string', ')']
};
// XML-safe versions of comparators
var opXML = {
'<': '&lt;',
'>': '&gt;',
'=': '=',
'!=': '!=',
'<=': '&lt;=',
'>=': '&gt;='
tree.Filter.prototype.ev = function(env) {
this.key = this.key.ev(env);
this.val = this.val.ev(env);
return this;
};
tree.Filter.prototype.toXML = function(env) {
if (this.op !== '=' && this.op !== '!=' && isNaN(this.val)) {
if (tree.Reference.data.filter) {
if (this.key.is === 'keyword' && -1 === tree.Reference.data.filter.value.indexOf(this.key.toString())) {
env.error({
message: this.key.toString() + ' is not a valid keyword in a filter expression',
index: this.index,
filename: this.filename
});
}
if (this.val.is === 'keyword' && -1 === tree.Reference.data.filter.value.indexOf(this.val.toString())) {
env.error({
message: this.val.toString() + ' is not a valid keyword in a filter expression',
index: this.index,
filename: this.filename
});
}
}
var key = this.key.toString(false);
var val = this.val.toString(this.val.is == 'string');
if (
(ops[this.op][1] == 'numeric' && isNaN(val) && this.val.is !== 'field') ||
(ops[this.op][1] == 'string' && (val)[0] != "'")
) {
env.error({
message: 'Cannot use operator "' + this.op + '" with value ' + this.val,
index: this.index,
filename: this.filename
});
}
if (this.val.eval) this._val = this.val.eval(env);
if (this.key.eval) this._key = this.key.eval(env);
if (this._key) var key = this._key.toString(false);
if (this._val) var val = this._val.toString(this._val.is == 'string');
return '[' + (key || this.key) + '] ' + opXML[this.op] + ' ' + (val || this.val);
return key + ops[this.op][0] + val + (ops[this.op][2] || '');
};
tree.Filter.prototype.toString = function() {

View File

@ -1,207 +1,270 @@
var tree = require('../tree');
var _ = global._ || require('underscore');
tree.Filterset = function Filterset() {};
tree.Filterset = function Filterset() {
this.filters = {};
};
Object.defineProperty(tree.Filterset.prototype, 'toXML', {
enumerable: false,
value: function(env) {
var filters = [];
for (var id in this) {
filters.push('(' + this[id].toXML(env).trim() + ')');
}
if (filters.length) {
return ' <Filter>' + filters.join(' and ') + '</Filter>\n';
} else {
return '';
}
tree.Filterset.prototype.toXML = function(env) {
var filters = [];
for (var id in this.filters) {
filters.push('(' + this.filters[id].toXML(env).trim() + ')');
}
});
Object.defineProperty(tree.Filterset.prototype, 'toString', {
enumerable: false,
value: function() {
var arr = [];
for (var id in this) arr.push(this[id].id);
arr.sort();
return arr.join('\t');
if (filters.length) {
return ' <Filter>' + filters.join(' and ') + '</Filter>\n';
} else {
return '';
}
});
};
Object.defineProperty(tree.Filterset.prototype, 'clone', {
enumerable: false,
value: function() {
var clone = new tree.Filterset();
for (var id in this) {
clone[id] = this[id];
}
return clone;
tree.Filterset.prototype.toString = function() {
var arr = [];
for (var id in this.filters) arr.push(this.filters[id].id);
return arr.sort().join('\t');
};
tree.Filterset.prototype.ev = function(env) {
for (var i in this.filters) {
this.filters[i].ev(env);
}
});
return this;
};
tree.Filterset.prototype.clone = function() {
var clone = new tree.Filterset();
for (var id in this.filters) {
clone.filters[id] = this.filters[id];
}
return clone;
};
// Note: other has to be a tree.Filterset.
Object.defineProperty(tree.Filterset.prototype, 'cloneWith', {
enumerable: false,
value: function(other) {
var additions;
for (var id in other) {
var status = this.addable(other[id]);
if (status === false) {
return false;
tree.Filterset.prototype.cloneWith = function(other) {
var additions = [];
for (var id in other.filters) {
var status = this.addable(other.filters[id]);
// status is true, false or null. if it's null we don't fail this
// clone nor do we add the filter.
if (status === false) {
return false;
}
if (status === true) {
// Adding the filter will override another value.
additions.push(other.filters[id]);
}
}
// Adding the other filters doesn't make this filterset invalid, but it
// doesn't add anything to it either.
if (!additions.length) {
return null;
}
// We can successfully add all filters. Now clone the filterset and add the
// new rules.
var clone = new tree.Filterset();
// We can add the rules that are already present without going through the
// add function as a Filterset is always in it's simplest canonical form.
for (id in this.filters) {
clone.filters[id] = this.filters[id];
}
// Only add new filters that actually change the filter.
while (id = additions.shift()) {
clone.add(id);
}
return clone;
};
tree.Filterset.prototype.toJS = function(env) {
var opMap = {
'=': '==='
};
return _.map(this.filters, function(filter) {
var op = filter.op;
if(op in opMap) {
op = opMap[op];
}
var val = filter.val;
if(filter._val !== undefined) {
val = filter._val.toString(true);
}
var attrs = "data";
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(' && ');
};
// Returns true when the new filter can be added, false otherwise.
// It can also return null, and on the other side we test for === true or
// false
tree.Filterset.prototype.addable = function(filter) {
var key = filter.key.toString(),
value = filter.val.toString();
if (value.match(/^[0-9]+(\.[0-9]*)?$/)) value = parseFloat(value);
switch (filter.op) {
case '=':
// if there is already foo= and we're adding foo=
if (this.filters[key + '='] !== undefined) {
if (this.filters[key + '='].val.toString() != value) {
return false;
} else {
return null;
}
}
if (status === true) {
// Adding the filter will override another value.
if (!additions) additions = [];
additions.push(other[id]);
if (this.filters[key + '!=' + value] !== undefined) return false;
if (this.filters[key + '>'] !== undefined && this.filters[key + '>'].val >= value) return false;
if (this.filters[key + '<'] !== undefined && this.filters[key + '<'].val <= value) return false;
if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val > value) return false;
if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val < value) return false;
return true;
case '=~':
return true;
case '!=':
if (this.filters[key + '='] !== undefined) return (this.filters[key + '='].val == value) ? false : null;
if (this.filters[key + '!=' + value] !== undefined) return null;
if (this.filters[key + '>'] !== undefined && this.filters[key + '>'].val >= value) return null;
if (this.filters[key + '<'] !== undefined && this.filters[key + '<'].val <= value) return null;
if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val > value) return null;
if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val < value) return null;
return true;
case '>':
if (key + '=' in this.filters) {
if (this.filters[key + '='].val <= value) {
return false;
} else {
return null;
}
}
if (this.filters[key + '<'] !== undefined && this.filters[key + '<'].val <= value) return false;
if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val <= value) return false;
if (this.filters[key + '>'] !== undefined && this.filters[key + '>'].val >= value) return null;
if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val > value) return null;
return true;
case '>=':
if (this.filters[key + '=' ] !== undefined) return (this.filters[key + '='].val < value) ? false : null;
if (this.filters[key + '<' ] !== undefined && this.filters[key + '<'].val <= value) return false;
if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val < value) return false;
if (this.filters[key + '>' ] !== undefined && this.filters[key + '>'].val >= value) return null;
if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val >= value) return null;
return true;
case '<':
if (this.filters[key + '=' ] !== undefined) return (this.filters[key + '='].val >= value) ? false : null;
if (this.filters[key + '>' ] !== undefined && this.filters[key + '>'].val >= value) return false;
if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val >= value) return false;
if (this.filters[key + '<' ] !== undefined && this.filters[key + '<'].val <= value) return null;
if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val < value) return null;
return true;
case '<=':
if (this.filters[key + '=' ] !== undefined) return (this.filters[key + '='].val > value) ? false : null;
if (this.filters[key + '>' ] !== undefined && this.filters[key + '>'].val >= value) return false;
if (this.filters[key + '>='] !== undefined && this.filters[key + '>='].val > value) return false;
if (this.filters[key + '<' ] !== undefined && this.filters[key + '<'].val <= value) return null;
if (this.filters[key + '<='] !== undefined && this.filters[key + '<='].val <= value) return null;
return true;
}
};
// Does the new filter constitute a conflict?
tree.Filterset.prototype.conflict = function(filter) {
var key = filter.key.toString(),
value = filter.val.toString();
if (!isNaN(parseFloat(value))) value = parseFloat(value);
// if (a=b) && (a=c)
// if (a=b) && (a!=b)
// or (a!=b) && (a=b)
if ((filter.op === '=' && this.filters[key + '='] !== undefined &&
value != this.filters[key + '='].val.toString()) ||
(filter.op === '!=' && this.filters[key + '='] !== undefined &&
value == this.filters[key + '='].val.toString()) ||
(filter.op === '=' && this.filters[key + '!='] !== undefined &&
value == this.filters[key + '!='].val.toString())) {
return filter.toString() + ' added to ' + this.toString() + ' produces an invalid filter';
}
return false;
};
// Only call this function for filters that have been cleared by .addable().
tree.Filterset.prototype.add = function(filter, env) {
var key = filter.key.toString(),
id,
op = filter.op,
conflict = this.conflict(filter),
numval;
if (conflict) return conflict;
if (op === '=') {
for (var i in this.filters) {
if (this.filters[i].key == key) delete this.filters[i];
}
this.filters[key + '='] = filter;
} else if (op === '!=') {
this.filters[key + '!=' + filter.val] = filter;
} else if (op === '=~') {
this.filters[key + '=~' + filter.val] = filter;
} else if (op === '>') {
// If there are other filters that are also >
// but are less than this one, they don't matter, so
// remove them.
for (var j in this.filters) {
if (this.filters[j].key == key && this.filters[j].val <= filter.val) {
delete this.filters[j];
}
}
// Adding the other filters doesn't make this filterset invalid, but it
// doesn't add anything to it either.
if (!additions) return null;
// We can successfully add all filters. Now clone the filterset and add the
// new rules.
var clone = new tree.Filterset();
// We can add the rules that are already present without going through the
// add function as a Filterset is always in it's simplest canonical form.
for (var id in this)
clone[id] = this[id];
// Only add new filters that actually change the filter.
while (id = additions.shift())
clone.add(id);
return clone;
}
});
/**
* Returns true when the new filter can be added, false otherwise.
*/
Object.defineProperty(tree.Filterset.prototype, 'addable', {
enumerable: false,
value: function(filter) {
var key = filter.key, value = filter.val;
switch (filter.op) {
case '=':
if (key + '=' in this) return (this[key + '='].val != value) ? false : null;
if (key + '!=' + value in this) return false;
if (key + '>' in this && this[key + '>'].val >= value) return false;
if (key + '<' in this && this[key + '<'].val <= value) return false;
if (key + '>=' in this && this[key + '>='].val > value) return false;
if (key + '<=' in this && this[key + '<='].val < value) return false;
return true;
case '!=':
if (key + '=' in this) return (this[key + '='].val == value) ? false : null;
if (key + '!=' + value in this) return null;
if (key + '>' in this && this[key + '>'].val >= value) return null;
if (key + '<' in this && this[key + '<'].val <= value) return null;
if (key + '>=' in this && this[key + '>='].val > value) return null;
if (key + '<=' in this && this[key + '<='].val < value) return null;
return true;
case '>':
if (key + '=' in this) return (this[key + '='].val <= value) ? false : null;
if (key + '<' in this && this[key + '<'].val <= value) return false;
if (key + '<=' in this && this[key + '<='].val <= value) return false;
if (key + '>' in this && this[key + '>'].val >= value) return null;
if (key + '>=' in this && this[key + '>='].val > value) return null;
return true;
case '>=':
if (key + '=' in this) return (this[key + '='].val < value) ? false : null;
if (key + '<' in this && this[key + '<'].val <= value) return false;
if (key + '<=' in this && this[key + '<='].val < value) return false;
if (key + '>' in this && this[key + '>'].val >= value) return null;
if (key + '>=' in this && this[key + '>='].val >= value) return null;
return true;
case '<':
if (key + '=' in this) return (this[key + '='].val >= value) ? false : null;
if (key + '>' in this && this[key + '>'].val >= value) return false;
if (key + '>=' in this && this[key + '>='].val >= value) return false;
if (key + '<' in this && this[key + '<'].val <= value) return null;
if (key + '<=' in this && this[key + '<='].val < value) return null;
return true;
case '<=':
if (key + '=' in this) return (this[key + '='].val > value) ? false : null;
if (key + '>' in this && this[key + '>'].val >= value) return false;
if (key + '>=' in this && this[key + '>='].val > value) return false;
if (key + '<' in this && this[key + '<'].val <= value) return null;
if (key + '<=' in this && this[key + '<='].val <= value) return null;
return true;
this.filters[key + '>'] = filter;
} else if (op === '>=') {
for (var k in this.filters) {
numval = (+this.filters[k].val.toString());
if (this.filters[k].key == key && numval < filter.val) {
delete this.filters[k];
}
}
if (this.filters[key + '!=' + filter.val] !== undefined) {
delete this.filters[key + '!=' + filter.val];
filter.op = '>';
this.filters[key + '>'] = filter;
}
else {
this.filters[key + '>='] = filter;
}
} else if (op === '<') {
for (var l in this.filters) {
numval = (+this.filters[l].val.toString());
if (this.filters[l].key == key && numval >= filter.val) {
delete this.filters[l];
}
}
this.filters[key + '<'] = filter;
} else if (op === '<=') {
for (var m in this.filters) {
numval = (+this.filters[m].val.toString());
if (this.filters[m].key == key && numval > filter.val) {
delete this.filters[m];
}
}
if (this.filters[key + '!=' + filter.val] !== undefined) {
delete this.filters[key + '!=' + filter.val];
filter.op = '<';
this.filters[key + '<'] = filter;
}
else {
this.filters[key + '<='] = filter;
}
}
});
/**
* Only call this function for filters that have been cleared by .addable().
*/
Object.defineProperty(tree.Filterset.prototype, 'add', {
enumerable: false,
value: function(filter) {
var key = filter.key;
switch (filter.op) {
case '=':
for (var id in this)
if (this[id].key == key)
delete this[id];
this[key + '='] = filter;
break;
case '!=':
this[key + '!=' + filter.val] = filter;
break;
case '>':
for (var id in this)
if (this[id].key == key && this[id].val <= filter.val)
delete this[id];
this[key + '>'] = filter;
break;
case '>=':
for (var id in this)
if (this[id].key == key && this[id].val < filter.val)
delete this[id];
if (key + '!=' + filter.val in this) {
delete this[key + '!=' + filter.val];
filter.op = '>';
this[key + '>'] = filter;
}
else {
this[key + '>='] = filter;
}
break;
case '<':
for (var id in this)
if (this[id].key == key && this[id].val >= filter.val)
delete this[id];
this[key + '<'] = filter;
break;
case '<=':
for (var id in this)
if (this[id].key == key && this[id].val > filter.val)
delete this[id];
if (key + '!=' + filter.val in this) {
delete this[key + '!=' + filter.val];
filter.op = '<';
this[key + '<'] = filter;
}
else {
this[key + '<='] = filter;
}
break;
}
}
});
};

View File

@ -1,23 +1,16 @@
(function(tree) {
tree._getFontSet = function(env, fonts) {
var find_existing = function(fonts) {
var findFonts = fonts.join('');
for (var i = 0; i < env.effects.length; i++) {
if (findFonts == env.effects[i].fonts.join('')) {
return env.effects[i];
}
}
};
var existing = false;
if (existing = find_existing(fonts)) {
return existing;
} else {
var new_fontset = new tree.FontSet(env, fonts);
env.effects.push(new_fontset);
return new_fontset;
var fontKey = fonts.join('');
if (env._fontMap && env._fontMap[fontKey]) {
return env._fontMap[fontKey];
}
var new_fontset = new tree.FontSet(env, fonts);
env.effects.push(new_fontset);
if (!env._fontMap) env._fontMap = {};
env._fontMap[fontKey] = new_fontset;
return new_fontset;
};
tree.FontSet = function FontSet(env, fonts) {
@ -26,13 +19,13 @@ tree.FontSet = function FontSet(env, fonts) {
};
tree.FontSet.prototype.toXML = function(env) {
return '<FontSet name="'
+ this.name
+ '">\n'
+ this.fonts.map(function(f) {
return '<FontSet name="' +
this.name +
'">\n' +
this.fonts.map(function(f) {
return ' <Font face-name="' + f +'"/>';
}).join('\n')
+ '\n</FontSet>'
}).join('\n') +
'\n</FontSet>';
};
})(require('../tree'));

View File

@ -0,0 +1,27 @@
var tree = require('../tree');
// Storage for Frame offset value
// and stores them as bit-sequences so that they can be combined,
// inverted, and compared quickly.
tree.FrameOffset = function(op, value, index) {
value = parseInt(value, 10);
if (value > tree.FrameOffset.max || value <= 0) {
throw {
message: 'Only frame-offset levels between 1 and ' +
tree.FrameOffset.max + ' supported.',
index: index
};
}
if (op !== '=') {
throw {
message: 'only = operator is supported for frame-offset',
index: index
};
}
return value;
};
tree.FrameOffset.max = 32;
tree.FrameOffset.none = 0;

View File

@ -0,0 +1,22 @@
(function(tree) {
tree.ImageFilter = function ImageFilter(filter, args) {
this.filter = filter;
this.args = args || null;
};
tree.ImageFilter.prototype = {
is: 'imagefilter',
ev: function() { return this; },
toString: function() {
if (this.args) {
return this.filter + '(' + this.args.join(',') + ')';
} else {
return this.filter;
}
}
};
})(require('../tree'));

View File

@ -1,77 +0,0 @@
(function(tree) {
//
// CSS @import node
//
// The general strategy here is that we don't want to wait
// for the parsing to be completed, before we start importing
// the file. That's because in the context of a browser,
// most of the time will be spent waiting for the server to respond.
//
// On creation, we push the import path to our import queue, though
// `import,push`, we also pass it a callback, which it'll call once
// the file has been fetched, and parsed.
//
tree.Import = function Import(path, imports) {
var that = this;
this._path = path;
// The '.mess' extension is optional
if (path instanceof tree.Quoted) {
this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.mess';
} else {
this.path = path.value.value || path.value;
}
this.css = /css$/.test(this.path);
// Only pre-compile .mess files
if (! this.css) {
imports.push(this.path, function(root) {
if (! root) {
throw new Error('Error parsing ' + that.path);
}
that.root = root;
});
}
};
//
// The actual import node doesn't return anything, when converted to CSS.
// The reason is that it's used at the evaluation stage, so that the rules
// it imports can be treated like any other rules.
//
// In `eval`, we make sure all Import nodes get evaluated, recursively, so
// we end up with a flat structure, which can easily be imported in the parent
// ruleset.
//
tree.Import.prototype = {
toString: function() {
if (this.css) {
return '@import ' + this._path.toString() + ';\n';
} else {
return '';
}
},
eval: function(env) {
var ruleset;
if (this.css) {
return this;
} else {
ruleset = new tree.Ruleset(null, this.root.rules.slice(0));
for (var i = 0; i < ruleset.rules.length; i++) {
if (ruleset.rules[i] instanceof tree.Import) {
Array.prototype
.splice
.apply(ruleset.rules,
[i, 1].concat(ruleset.rules[i].eval(env)));
}
}
return ruleset.rules;
}
}
};
})(require('../tree'));

View File

@ -5,4 +5,18 @@ tree.Invalid = function Invalid(chunk, index, message) {
this.type = 'syntax';
this.message = message || "Invalid code: " + this.chunk;
};
tree.Invalid.prototype.is = 'invalid';
tree.Invalid.prototype.ev = function(env) {
env.error({
chunk: this.chunk,
index: this.index,
type: 'syntax',
message: this.message || "Invalid code: " + this.chunk
});
return {
is: 'undefined'
};
};
})(require('../tree'));

View File

@ -1,38 +0,0 @@
(function(tree) {
tree.JavaScript = function JavaScript(string, index) {
this.expression = string;
this.index = index;
};
tree.JavaScript.prototype = {
toString: function() {
return JSON.stringify(this.evaluated);
},
eval: function(env) {
var result,
expression = new Function('return (' + this.expression + ')'),
context = {};
for (var k in env.frames[0].variables()) {
context[k.slice(1)] = {
value: env.frames[0].variables()[k].value,
toJS: function() {
return this.value.eval(env).toString();
}
};
}
try {
this.evaluated = expression.call(context);
} catch (e) {
throw {
message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
index: this.index
};
}
return this;
}
};
})(require('../tree'));

View File

@ -10,8 +10,8 @@ tree.Keyword = function Keyword(value) {
this.is = special[value] ? special[value] : 'keyword';
};
tree.Keyword.prototype = {
eval: function() { return this },
toString: function() { return this.value }
ev: function() { return this; },
toString: function() { return this.value; }
};
})(require('../tree'));

View File

@ -1,35 +1,36 @@
(function(tree) {
tree.Layer = function Layer(obj) {
this.name = obj.name;
this.styles = obj.styles;
this.properties = obj.properties || {};
this.srs = obj.srs;
this.datasource = obj.Datasource;
};
tree.Layer.prototype.toXML = function() {
tree.LayerXML = function(obj, styles) {
var dsoptions = [];
for (var i in this.datasource) {
for (var i in obj.Datasource) {
dsoptions.push('<Parameter name="' + i + '"><![CDATA[' +
this.datasource[i] + ']]></Parameter>');
obj.Datasource[i] + ']]></Parameter>');
}
var prop_string = '';
for (var i in this.properties) {
prop_string += ' ' + i + '="' + this.properties[i] + '"\n';
for (var prop in obj.properties) {
if (prop === 'minzoom') {
prop_string += ' maxzoom="' + tree.Zoom.ranges[obj.properties[prop]] + '"\n';
} else if (prop === 'maxzoom') {
prop_string += ' minzoom="' + tree.Zoom.ranges[obj.properties[prop]+1] + '"\n';
} else {
prop_string += ' ' + prop + '="' + obj.properties[prop] + '"\n';
}
}
return '<Layer' +
' name="' + this.name + '"\n' +
' name="' + obj.name + '"\n' +
prop_string +
' srs="' + this.srs + '">\n ' +
this.styles.reverse().map(function(s) {
((typeof obj.status === 'undefined') ? '' : ' status="' + obj.status + '"\n') +
((typeof obj.srs === 'undefined') ? '' : ' srs="' + obj.srs + '"') + '>\n ' +
styles.reverse().map(function(s) {
return '<StyleName>' + s + '</StyleName>';
}).join('\n ') +
(dsoptions.length ?
'\n <Datasource>\n ' +
dsoptions.join('\n ') +
'\n </Datasource>\n' +
'\n </Datasource>\n'
: '') +
' </Layer>\n';
};

20
lib/carto/tree/literal.js Normal file
View File

@ -0,0 +1,20 @@
// A literal is a literal string for Mapnik - the
// result of the combination of a `tree.Field` with any
// other type.
(function(tree) {
tree.Literal = function Field(content) {
this.value = content || '';
this.is = 'field';
};
tree.Literal.prototype = {
toString: function() {
return this.value;
},
'ev': function() {
return this;
}
};
})(require('../tree'));

View File

@ -1,99 +0,0 @@
(function(tree) {
tree.mixin = {};
tree.mixin.Call = function Call(elements, args, index) {
this.selector = new tree.Selector(null, null, elements);
this.arguments = args;
this.index = index;
};
tree.mixin.Call.prototype = {
eval: function(env) {
var mixins, rules = [], match = false;
for (var i = 0; i < env.frames.length; i++) {
if ((mixins = env.frames[i].find(this.selector)).length > 0) {
for (var m = 0; m < mixins.length; m++) {
if (mixins[m].match(this.arguments, env)) {
try {
Array.prototype.push.apply(
rules, mixins[m].eval(env, this.arguments).rules);
match = true;
} catch (e) {
throw { message: e.message, index: e.index, stack: e.stack, call: this.index };
}
}
}
if (match) {
return rules;
} else {
throw { message: 'No matching definition was found for `' +
this.selector.toString().trim() + '(' +
this.arguments.map(function(a) {
return a.toString();
}).join(', ') + ')`',
index: this.index };
}
}
}
throw { message: this.selector.toString().trim() + ' is undefined',
index: this.index };
}
};
tree.mixin.Definition = function Definition(name, params, rules) {
this.name = name;
this.selectors = [new tree.Selector(null, null, [new tree.Element(null, name)])];
this.params = params;
this.arity = params.length;
this.rules = rules;
this._lookups = {};
this.required = params.reduce(function(count, p) {
if (p.name && !p.value) { return count + 1 }
else { return count }
}, 0);
this.parent = tree.Ruleset.prototype;
this.frames = [];
};
tree.mixin.Definition.prototype = {
toString: function() { return '' },
variable: function(name) { return this.parent.variable.call(this, name) },
variables: function() { return this.parent.variables.call(this) },
find: function() { return this.parent.find.apply(this, arguments) },
rulesets: function() { return this.parent.rulesets.apply(this) },
eval: function(env, args) {
var frame = new tree.Ruleset(null, []), context;
for (var i = 0, val; i < this.params.length; i++) {
if (this.params[i].name) {
if (val = (args && args[i]) || this.params[i].value) {
frame.rules.unshift(new tree.Rule(this.params[i].name, val.eval(env)));
} else {
throw { message: 'wrong number of arguments for ' + this.name +
' (' + args.length + ' for ' + this.arity + ')' };
}
}
}
return new tree.Ruleset(null, this.rules.slice(0)).eval({
frames: [this, frame].concat(this.frames, env.frames)
});
},
match: function(args, env) {
var argsLength = (args && args.length) || 0, len;
if (argsLength < this.required) { return false }
len = Math.min(argsLength, this.arity);
for (var i = 0; i < len; i++) {
if (!this.params[i].name) {
if (args[i].eval(env).toString() != this.params[i].value.eval(env).toString()) {
return false;
}
}
}
return true;
}
};
})(require('../tree'));

View File

@ -1,14 +1,18 @@
// An operation is an expression with an op in between two operands,
// like 2 + 1.
(function(tree) {
tree.Operation = function Operation(op, operands, index) {
this.op = op.trim();
this.operands = operands;
this.index = index;
};
tree.Operation.prototype.eval = function(env) {
var a = this.operands[0].eval(env),
b = this.operands[1].eval(env),
tree.Operation.prototype.is = 'operation';
tree.Operation.prototype.ev = function(env) {
var a = this.operands[0].ev(env),
b = this.operands[1].ev(env),
temp;
if (a.is === 'undefined' || b.is === 'undefined') {
@ -30,12 +34,14 @@ tree.Operation.prototype.eval = function(env) {
}
}
if (a instanceof tree.Quoted || b instanceof tree.Quoted) {
// Only concatenate plain strings, because this is easily
// pre-processed
if (a instanceof tree.Quoted && b instanceof tree.Quoted && this.op !== '+') {
env.error({
message: 'One cannot add, subtract, divide, or multiply strings.',
message: "Can't subtract, divide, or multiply strings.",
index: this.index,
type: 'runtime',
filename: this.filename,
filename: this.filename
});
return {
is: 'undefined',
@ -43,7 +49,39 @@ tree.Operation.prototype.eval = function(env) {
};
}
return a.operate(this.op, b);
// Fields, literals, dimensions, and quoted strings can be combined.
if (a instanceof tree.Field || b instanceof tree.Field ||
a instanceof tree.Literal || b instanceof tree.Literal) {
if (a.is === 'color' || b.is === 'color') {
env.error({
message: "Can't subtract, divide, or multiply colors in expressions.",
index: this.index,
type: 'runtime',
filename: this.filename
});
return {
is: 'undefined',
value: 'undefined'
};
} else {
return new tree.Literal(a.ev(env).toString(true) + this.op + b.ev(env).toString(true));
}
}
if (a.operate === undefined) {
env.error({
message: 'Cannot do math with type ' + a.is + '.',
index: this.index,
type: 'runtime',
filename: this.filename
});
return {
is: 'undefined',
value: 'undefined'
};
}
return a.operate(env, this.op, b);
};
tree.operate = function(op, a, b) {
@ -51,6 +89,7 @@ tree.operate = function(op, a, b) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '%': return a % b;
case '/': return a / b;
}
};

View File

@ -1,16 +1,29 @@
(function(tree) {
tree.Quoted = function Quoted(str, content) {
tree.Quoted = function Quoted(content) {
this.value = content || '';
this.quote = str.charAt(0);
this.is = 'string';
};
tree.Quoted.prototype = {
is: 'string',
toString: function(quotes) {
return (quotes === true) ? "'" + this.value + "'" : this.value;
var escapedValue = this.value
.replace(/&/g, '&amp;')
var xmlvalue = escapedValue
.replace(/\'/g, '\\\'')
.replace(/\"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/\>/g, '&gt;');
return (quotes === true) ? "'" + xmlvalue + "'" : escapedValue;
},
eval: function() {
'ev': function() {
return this;
},
operate: function(env, op, other) {
return new tree.Quoted(tree.operate(op, this.toString(), other.toString(this.contains_field)));
}
};

View File

@ -1,85 +1,98 @@
// Carto pulls in a reference from the `mapnik-reference`
// module. This file builds indexes from that file for its various
// options, and provides validation methods for property: value
// combinations.
(function(tree) {
var fs = require('fs');
tree.Reference = {
data: JSON.parse(fs.readFileSync(__dirname + '/reference.json'))
var _ = global._ || require('underscore'),
ref = {};
ref.setData = function(data) {
ref.data = data;
ref.selector_cache = generateSelectorCache(data);
ref.mapnikFunctions = generateMapnikFunctions(data);
ref.mapnikFunctions.matrix = [6];
ref.mapnikFunctions.translate = [1, 2];
ref.mapnikFunctions.scale = [1, 2];
ref.mapnikFunctions.rotate = [1, 3];
ref.mapnikFunctions.skewX = [1];
ref.mapnikFunctions.skewY = [1];
ref.required_cache = generateRequiredProperties(data);
};
tree.Reference.required_prop_list_cache = {};
ref.setVersion = function(version) {
var mapnik_reference = require('mapnik-reference');
if (mapnik_reference.version.hasOwnProperty(version)) {
ref.setData(mapnik_reference.version[version]);
return true;
} else {
return false;
}
};
tree.Reference.selectors = tree.Reference.selectors || (function() {
var list = [];
for (var i in tree.Reference.data.symbolizers) {
for (var j in tree.Reference.data.symbolizers[i]) {
if (tree.Reference.data.symbolizers[i][j].hasOwnProperty('css')) {
list.push(tree.Reference.data.symbolizers[i][j].css);
ref.selectorData = function(selector, i) {
if (ref.selector_cache[selector]) return ref.selector_cache[selector][i];
};
ref.validSelector = function(selector) { return !!ref.selector_cache[selector]; };
ref.selectorName = function(selector) { return ref.selectorData(selector, 2); };
ref.selector = function(selector) { return ref.selectorData(selector, 0); };
ref.symbolizer = function(selector) { return ref.selectorData(selector, 1); };
function generateSelectorCache(data) {
var index = {};
for (var i in data.symbolizers) {
for (var j in data.symbolizers[i]) {
if (data.symbolizers[i][j].hasOwnProperty('css')) {
index[data.symbolizers[i][j].css] = [data.symbolizers[i][j], i, j];
}
}
}
return list;
})();
return index;
}
tree.Reference.validSelector = function(selector) {
return tree.Reference.selectors.indexOf(selector) !== -1;
};
tree.Reference.selectorName = function(selector) {
for (var i in tree.Reference.data.symbolizers) {
for (var j in tree.Reference.data.symbolizers[i]) {
if (selector == tree.Reference.data.symbolizers[i][j].css) {
return j;
function generateMapnikFunctions(data) {
var functions = {};
for (var i in data.symbolizers) {
for (var j in data.symbolizers[i]) {
if (data.symbolizers[i][j].type === 'functions') {
for (var k = 0; k < data.symbolizers[i][j].functions.length; k++) {
var fn = data.symbolizers[i][j].functions[k];
functions[fn[0]] = fn[1];
}
}
}
}
};
return functions;
}
tree.Reference.selector = function(selector) {
for (var i in tree.Reference.data.symbolizers) {
for (var j in tree.Reference.data.symbolizers[i]) {
if (selector == tree.Reference.data.symbolizers[i][j].css) {
return tree.Reference.data.symbolizers[i][j];
function generateRequiredProperties(data) {
var cache = {};
for (var symbolizer_name in data.symbolizers) {
cache[symbolizer_name] = [];
for (var j in data.symbolizers[symbolizer_name]) {
if (data.symbolizers[symbolizer_name][j].required) {
cache[symbolizer_name].push(data.symbolizers[symbolizer_name][j].css);
}
}
}
};
return cache;
}
tree.Reference.symbolizer = function(selector) {
for (var i in tree.Reference.data.symbolizers) {
for (var j in tree.Reference.data.symbolizers[i]) {
if (selector == tree.Reference.data.symbolizers[i][j].css) {
return i;
}
}
}
};
tree.Reference.requiredPropertyList = function(symbolizer_name) {
if (this.required_prop_list_cache[symbolizer_name]) {
return this.required_prop_list_cache[symbolizer_name];
}
var properties = [];
for (var j in tree.Reference.data.symbolizers[symbolizer_name]) {
if (tree.Reference.data.symbolizers[symbolizer_name][j].required) {
properties.push(tree.Reference.data.symbolizers[symbolizer_name][j].css);
}
}
return this.required_prop_list_cache[symbolizer_name] = properties;
};
tree.Reference.requiredProperties = function(symbolizer_name, rules) {
var req = tree.Reference.requiredPropertyList(symbolizer_name);
for (i in req) {
ref.requiredProperties = function(symbolizer_name, rules) {
var req = ref.required_cache[symbolizer_name];
for (var i in req) {
if (!(req[i] in rules)) {
return 'Property ' + req[i] + ' required for defining '
+ symbolizer_name + ' styles.';
return 'Property ' + req[i] + ' required for defining ' +
symbolizer_name + ' styles.';
}
}
};
/**
* TODO: finish implementation - this is dead code
*/
tree.Reference._validateValue = {
// TODO: finish implementation - this is dead code
ref._validateValue = {
'font': function(env, value) {
if (env.validation_data && env.validation_data.fonts) {
return env.validation_data.fonts.indexOf(value) != -1;
@ -89,48 +102,118 @@ tree.Reference._validateValue = {
}
};
tree.Reference.isFont = function(selector) {
return tree.Reference.selector(selector).validate == 'font';
}
ref.isFont = function(selector) {
return ref.selector(selector).validate == 'font';
};
tree.Reference.validValue = function(env, selector, value) {
if (value[0]) {
return tree.Reference.selector(selector).type == value[0].is;
} else {
// TODO: handle in reusable way
if (value.value[0].is == 'keyword') {
return tree.Reference
.selector(selector).type
.indexOf(value.value[0].value) !== -1;
} else if (value.value[0].is == 'undefined') {
// caught earlier in the chain - ignore here so that
// error is not overridden
return true;
} else if (tree.Reference.selector(selector).type == 'numbers') {
for (i in value.value) {
if (value.value[i].is !== 'float') {
return false;
}
}
return true;
} else {
if (tree.Reference.selector(selector).validate) {
var valid = false;
for (var i = 0; i < value.value.length; i++) {
if (tree.Reference.selector(selector).type == value.value[i].is &&
tree.Reference
._validateValue
[tree.Reference.selector(selector).validate]
(env, value.value[i].value)) {
return true;
}
}
return valid;
// https://gist.github.com/982927
ref.editDistance = function(a, b){
if (a.length === 0) return b.length;
if (b.length === 0) return a.length;
var matrix = [];
for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
for (i = 1; i <= b.length; i++) {
for (j = 1; j <= a.length; j++) {
if (b.charAt(i-1) == a.charAt(j-1)) {
matrix[i][j] = matrix[i-1][j-1];
} else {
return tree.Reference.selector(selector).type == value.value[0].is;
matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
Math.min(matrix[i][j-1] + 1, // insertion
matrix[i-1][j] + 1)); // deletion
}
}
}
return matrix[b.length][a.length];
};
function validateFunctions(value, selector) {
if (value.value[0].is === 'string') return true;
for (var i in value.value) {
for (var j in value.value[i].value) {
if (value.value[i].value[j].is !== 'call') return false;
var f = _.find(ref
.selector(selector).functions, function(x) {
return x[0] == value.value[i].value[j].name;
});
if (!(f && f[1] == -1)) {
// This filter is unknown or given an incorrect number of arguments
if (!f || f[1] !== value.value[i].value[j].args.length) return false;
}
}
}
return true;
}
function validateKeyword(value, selector) {
if (typeof ref.selector(selector).type === 'object') {
return ref.selector(selector).type
.indexOf(value.value[0].value) !== -1;
} else {
// allow unquoted keywords as strings
return ref.selector(selector).type === 'string';
}
}
ref.validValue = function(env, selector, value) {
var i, j;
// TODO: handle in reusable way
if (!ref.selector(selector)) {
return false;
} else if (value.value[0].is == 'keyword') {
return validateKeyword(value, selector);
} else if (value.value[0].is == 'undefined') {
// caught earlier in the chain - ignore here so that
// error is not overridden
return true;
} else if (ref.selector(selector).type == 'numbers') {
for (i in value.value) {
if (value.value[i].is !== 'float') {
return false;
}
}
return true;
} else if (ref.selector(selector).type == 'tags') {
if (!value.value) return false;
if (!value.value[0].value) {
return value.value[0].is === 'tag';
}
for (i = 0; i < value.value[0].value.length; i++) {
if (value.value[0].value[i].is !== 'tag') return false;
}
return true;
} else if (ref.selector(selector).type == 'functions') {
// For backwards compatibility, you can specify a string for `functions`-compatible
// values, though they will not be validated.
return validateFunctions(value, selector);
} else if (ref.selector(selector).type === 'unsigned') {
if (value.value[0].is === 'float') {
value.value[0].round();
return true;
} else {
return false;
}
} else if ((ref.selector(selector).expression)) {
return true;
} else {
if (ref.selector(selector).validate) {
var valid = false;
for (i = 0; i < value.value.length; i++) {
if (ref.selector(selector).type == value.value[i].is &&
ref
._validateValue
[ref.selector(selector).validate]
(env, value.value[i].value)) {
return true;
}
}
return valid;
} else {
return ref.selector(selector).type == value.value[0].is;
}
}
};
tree.Reference = ref;
})(require('../tree'));

View File

@ -1,4 +1,7 @@
(function(tree) {
// a rule is a single property and value combination, or variable
// name and value combination, like
// polygon-opacity: 1.0; or @opacity: 1.0;
tree.Rule = function Rule(name, value, index, filename) {
var parts = name.split('/');
this.name = parts.pop();
@ -11,9 +14,11 @@ tree.Rule = function Rule(name, value, index, filename) {
this.variable = (name.charAt(0) === '@');
};
tree.Rule.prototype.is = 'rule';
tree.Rule.prototype.clone = function() {
var clone = Object.create(tree.Rule.prototype);
clone.name = this.name
clone.name = this.name;
clone.value = this.value;
clone.index = this.index;
clone.instance = this.instance;
@ -24,21 +29,32 @@ tree.Rule.prototype.clone = function() {
};
tree.Rule.prototype.updateID = function() {
return this.id = this.zoom + '#' + this.name;
return this.id = this.zoom + '#' + this.instance + '#' + this.name;
};
tree.Rule.prototype.toString = function() {
return '[' + tree.Zoom.toString(this.zoom) + '] ' + this.name + ': ' + this.value;
};
function getMean(name) {
return Object.keys(tree.Reference.selector_cache).map(function(f) {
return [f, tree.Reference.editDistance(name, f)];
}).sort(function(a, b) { return a[1] - b[1]; });
}
// second argument, if true, outputs the value of this
// rule without the usual attribute="content" wrapping. Right
// now this is just for the TextSymbolizer, but applies to other
// properties in reference.json which specify serialization=content
tree.Rule.prototype.toXML = function(env, content) {
tree.Rule.prototype.toXML = function(env, content, sep, format) {
if (!tree.Reference.validSelector(this.name)) {
var mean = getMean(this.name);
var mean_message = '';
if (mean[0][1] < 3) {
mean_message = '. Did you mean ' + mean[0][0] + '?';
}
return env.error({
message: "Unrecognized rule: " + this.name,
message: "Unrecognized rule: " + this.name + mean_message,
index: this.index,
type: 'syntax',
filename: this.filename
@ -47,18 +63,35 @@ tree.Rule.prototype.toXML = function(env, content) {
if ((this.value instanceof tree.Value) &&
!tree.Reference.validValue(env, this.name, this.value)) {
return env.error({
message: 'Invalid value for ' +
this.name +
', a valid ' +
(tree.Reference.selector(this.name).validate ||
tree.Reference.selector(this.name).type) +
' is expected. ' + this.value +
' was given.',
index: this.index,
type: 'syntax',
filename: this.filename
});
if (!tree.Reference.selector(this.name)) {
return env.error({
message: 'Unrecognized property: ' +
this.name,
index: this.index,
type: 'syntax',
filename: this.filename
});
} else {
var typename;
if (tree.Reference.selector(this.name).validate) {
typename = tree.Reference.selector(this.name).validate;
} else if (typeof tree.Reference.selector(this.name).type === 'object') {
typename = 'keyword (options: ' + tree.Reference.selector(this.name).type.join(', ') + ')';
} else {
typename = tree.Reference.selector(this.name).type;
}
return env.error({
message: 'Invalid value for ' +
this.name +
', the type ' + typename +
' is expected. ' + this.value +
' (of type ' + this.value.value[0].is + ') ' +
' was given.',
index: this.index,
type: 'syntax',
filename: this.filename
});
}
}
if (this.variable) {
@ -67,7 +100,7 @@ tree.Rule.prototype.toXML = function(env, content) {
var f = tree._getFontSet(env, this.value.value);
return 'fontset-name="' + f.name + '"';
} else if (content) {
return this.value.toString(env, this.name);
return this.value.toString(env, this.name, sep);
} else {
return tree.Reference.selectorName(this.name) +
'="' +
@ -76,26 +109,12 @@ tree.Rule.prototype.toXML = function(env, content) {
}
};
/**
* TODO: Rule eval chain should add fontsets to env.frames
*/
tree.Rule.prototype.eval = function(context) {
// TODO: Rule ev chain should add fontsets to env.frames
tree.Rule.prototype.ev = function(context) {
return new tree.Rule(this.name,
this.value.eval(context),
this.value.ev(context),
this.index,
this.filename);
};
tree.Shorthand = function Shorthand(a, b) {
this.a = a;
this.b = b;
};
tree.Shorthand.prototype = {
toString: function(env) {
return this.a.toString(env) + '/' + this.b.toString(env);
},
eval: function() { return this }
};
})(require('../tree'));

View File

@ -7,46 +7,19 @@ tree.Ruleset = function Ruleset(selectors, rules) {
this._lookups = {};
};
tree.Ruleset.prototype = {
eval: function(env) {
var ruleset = new tree.Ruleset(this.selectors, this.rules.slice(0));
is: 'ruleset',
'ev': function(env) {
var i,
ruleset = new tree.Ruleset(this.selectors, this.rules.slice(0));
ruleset.root = this.root;
// push the current ruleset to the frames stack
env.frames.unshift(ruleset);
// Evaluate imports
if (ruleset.root) {
for (var i = 0; i < ruleset.rules.length; i++) {
if (ruleset.rules[i] instanceof tree.Import) {
Array.prototype.splice
.apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
}
}
}
// Store the frames around mixin definitions,
// so they can be evaluated like closures when the time comes.
for (var i = 0; i < ruleset.rules.length; i++) {
if (ruleset.rules[i] instanceof tree.mixin.Definition) {
ruleset.rules[i].frames = env.frames.slice(0);
}
}
// Evaluate mixin calls.
for (var i = 0; i < ruleset.rules.length; i++) {
if (ruleset.rules[i] instanceof tree.mixin.Call) {
Array.prototype.splice
.apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
}
}
// Evaluate everything else
for (var i = 0, rule; i < ruleset.rules.length; i++) {
for (i = 0, rule; i < ruleset.rules.length; i++) {
rule = ruleset.rules[i];
if (! (rule instanceof tree.mixin.Definition)) {
ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
}
ruleset.rules[i] = rule.ev ? rule.ev(env) : rule;
}
// Pop the stack
@ -58,7 +31,7 @@ tree.Ruleset.prototype = {
return !args || args.length === 0;
},
variables: function() {
if (this._variables) { return this._variables }
if (this._variables) { return this._variables; }
else {
return this._variables = this.rules.reduce(function(hash, r) {
if (r instanceof tree.Rule && r.variable === true) {
@ -71,19 +44,11 @@ tree.Ruleset.prototype = {
variable: function(name) {
return this.variables()[name];
},
/**
* Extend this rule by adding rules from another ruleset
*
* Currently this is designed to accept less specific
* rules and add their values only if this ruleset doesn't
* contain them.
*/
rulesets: function() {
if (this._rulesets) { return this._rulesets }
if (this._rulesets) { return this._rulesets; }
else {
return this._rulesets = this.rules.filter(function(r) {
return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
return (r instanceof tree.Ruleset);
});
}
},
@ -92,15 +57,16 @@ tree.Ruleset.prototype = {
var rules = [], rule, match,
key = selector.toString();
if (key in this._lookups) { return this._lookups[key] }
if (key in this._lookups) { return this._lookups[key]; }
this.rulesets().forEach(function(rule) {
if (rule !== self) {
for (var j = 0; j < rule.selectors.length; j++) {
if (match = selector.match(rule.selectors[j])) {
match = selector.match(rule.selectors[j]);
if (match) {
if (selector.elements.length > 1) {
Array.prototype.push.apply(rules, rule.find(
new tree.Selector(null, null, selector.elements.slice(1)), self));
new tree.Selector(null, null, null, selector.elements.slice(1)), self));
} else {
rules.push(rule);
}
@ -111,19 +77,35 @@ tree.Ruleset.prototype = {
});
return this._lookups[key] = rules;
},
// Zooms can use variables. This replaces tree.Zoom objects on selectors
// with simple bit-arrays that we can compare easily.
evZooms: function(env) {
for (var i = 0; i < this.selectors.length; i++) {
var zval = tree.Zoom.all;
for (var z = 0; z < this.selectors[i].zoom.length; z++) {
zval = zval & this.selectors[i].zoom[z].ev(env).zoom;
}
this.selectors[i].zoom = zval;
}
},
flatten: function(result, parents, env) {
var selectors = [];
if (this.selectors.length == 0) {
var selectors = [], i, j;
if (this.selectors.length === 0) {
env.frames = env.frames.concat(this.rules);
}
for (var i = 0; i < this.selectors.length; i++) {
// evaluate zoom variables on this object.
this.evZooms(env);
for (i = 0; i < this.selectors.length; i++) {
var child = this.selectors[i];
// This is an invalid filterset.
if (!child.filters) continue;
if (!child.filters) {
// TODO: is this internal inconsistency?
// This is an invalid filterset.
continue;
}
if (parents.length) {
for (var j = 0; j < parents.length; j++) {
for (j = 0; j < parents.length; j++) {
var parent = parents[j];
var mergedFilters = parent.filters.cloneWith(child.filters);
@ -132,7 +114,10 @@ tree.Ruleset.prototype = {
// filters. This means that we only have to clone when
// the zoom levels or the attachment is different too.
if (parent.zoom === (parent.zoom & child.zoom) &&
parent.attachment === child.attachment) {
parent.frame_offset === child.frame_offset &&
parent.attachment === child.attachment &&
parent.elements.join() === child.elements.join()) {
selectors.push(parent);
continue;
} else {
mergedFilters = parent.filters;
@ -146,6 +131,7 @@ tree.Ruleset.prototype = {
var clone = Object.create(tree.Selector.prototype);
clone.filters = mergedFilters;
clone.zoom = parent.zoom & child.zoom;
clone.frame_offset = child.frame_offset;
clone.elements = parent.elements.concat(child.elements);
if (parent.attachment && child.attachment) {
clone.attachment = parent.attachment + '/' + child.attachment;
@ -161,9 +147,10 @@ tree.Ruleset.prototype = {
}
var rules = [];
for (var i = 0; i < this.rules.length; i++) {
for (i = 0; i < this.rules.length; i++) {
var rule = this.rules[i];
// Recursively flatten any nested rulesets
if (rule instanceof tree.Ruleset) {
rule.flatten(result, selectors, env);
} else if (rule instanceof tree.Rule) {
@ -174,7 +161,7 @@ tree.Ruleset.prototype = {
}
var index = rules.length ? rules[0].index : false;
for (var i = 0; i < selectors.length; i++) {
for (i = 0; i < selectors.length; i++) {
// For specificity sort, use the position of the first rule to allow
// defining attachments that are under current element as a descendant
// selector.

View File

@ -1,23 +1,20 @@
var assert = require('assert');
(function(tree) {
tree.Selector = function Selector(filters, zoom, elements, attachment, conditions, index) {
tree.Selector = function Selector(filters, zoom, frame_offset, elements, attachment, conditions, index) {
this.elements = elements || [];
this.attachment = attachment;
this.filters = filters || {};
this.frame_offset = frame_offset;
this.zoom = typeof zoom !== 'undefined' ? zoom : tree.Zoom.all;
this.conditions = conditions;
this.index = index;
};
/**
* Determine the specificity of this selector
* based on the specificity of its elements - calling
* Element.specificity() in order to do so
*
* [ID, Class, Filters, Position in document]
*/
// Determine the specificity of this selector
// based on the specificity of its elements - calling
// Element.specificity() in order to do so
//
// [ID, Class, Filters, Position in document]
tree.Selector.prototype.specificity = function() {
return this.elements.reduce(function(memo, e) {
var spec = e.specificity();

View File

@ -1,18 +1,68 @@
(function(tree) {
var _ = global._ || require('underscore');
tree.Style = function Style(name, attachment, definitions) {
this.attachment = attachment;
this.definitions = definitions;
this.name = name + (attachment !== '__default__' ? '-' + attachment : '');
};
tree.Style.prototype.toXML = function(env) {
// Given a style's name, attachment, definitions, and an environment object,
// return a stringified style for Mapnik
tree.StyleXML = function(name, attachment, definitions, env) {
var existing = {};
var rules = this.definitions.map(function(definition) {
var image_filters = [], image_filters_inflate = [], direct_image_filters = [], comp_op = [], opacity = [];
for (var i = 0; i < definitions.length; i++) {
for (var j = 0; j < definitions[i].rules.length; j++) {
if (definitions[i].rules[j].name === 'image-filters') {
image_filters.push(definitions[i].rules[j]);
}
if (definitions[i].rules[j].name === 'image-filters-inflate') {
image_filters_inflate.push(definitions[i].rules[j]);
}
if (definitions[i].rules[j].name === 'direct-image-filters') {
direct_image_filters.push(definitions[i].rules[j]);
}
if (definitions[i].rules[j].name === 'comp-op') {
comp_op.push(definitions[i].rules[j]);
}
if (definitions[i].rules[j].name === 'opacity') {
opacity.push(definitions[i].rules[j]);
}
}
}
var rules = definitions.map(function(definition) {
return definition.toXML(env, existing);
});
return '<Style name="' + this.name + '" filter-mode="first">\n' + rules.join('') + '</Style>';
var attrs_xml = '';
if (image_filters.length) {
attrs_xml += ' image-filters="' + _.chain(image_filters)
// prevent identical filters from being duplicated in the style
.uniq(function(i) { return i.id; }).map(function(f) {
return f.ev(env).toXML(env, true, ',', 'image-filter');
}).value().join(',') + '"';
}
if (image_filters_inflate.length) {
attrs_xml += ' image-filters-inflate="' + image_filters_inflate[0].value.ev(env).toString() + '"';
}
if (direct_image_filters.length) {
attrs_xml += ' direct-image-filters="' + _.chain(direct_image_filters)
// prevent identical filters from being duplicated in the style
.uniq(function(i) { return i.id; }).map(function(f) {
return f.ev(env).toXML(env, true, ',', 'direct-image-filter');
}).value().join(',') + '"';
}
if (comp_op.length && comp_op[0].value.ev(env).value != 'src-over') {
attrs_xml += ' comp-op="' + comp_op[0].value.ev(env).toString() + '"';
}
if (opacity.length && opacity[0].value.ev(env).value != 1) {
attrs_xml += ' opacity="' + opacity[0].value.ev(env).toString() + '"';
}
var rule_string = rules.join('');
if (!attrs_xml && !rule_string) return '';
return '<Style name="' + name + '" filter-mode="first"' + attrs_xml + '>\n' + rule_string + '</Style>';
};
})(require('../tree'));

View File

@ -1,26 +1,17 @@
(function(tree) {
tree.URL = function URL(val, paths) {
if (val.data) {
this.attrs = val;
} else {
// Add the base path if the URL is relative and we are in the browser
if (!/^(?:https?:\/|file:\/)?\//.test(val.value) && paths.length > 0 && typeof(process) === 'undefined') {
val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
}
this.value = val;
this.paths = paths;
this.is = 'uri';
}
this.value = val;
this.paths = paths;
};
tree.URL.prototype = {
is: 'uri',
toString: function() {
return this.value.toString();
},
eval: function(ctx) {
return this.attrs ? this : new tree.URL(this.value.eval(ctx), this.paths);
// URL case no longer supported.
// @TODO: throw an error?
ev: function(ctx) {
return new tree.URL(this.value.ev(ctx), this.paths);
}
};

View File

@ -2,22 +2,23 @@
tree.Value = function Value(value) {
this.value = value;
this.is = 'value';
};
tree.Value.prototype = {
eval: function(env) {
is: 'value',
ev: function(env) {
if (this.value.length === 1) {
return this.value[0].eval(env);
return this.value[0].ev(env);
} else {
return new tree.Value(this.value.map(function(v) {
return v.eval(env);
return v.ev(env);
}));
}
},
toString: function(env, selector) {
toString: function(env, selector, sep, format) {
return this.value.map(function(e) {
return e.toString(env);
}).join(', ');
return e.toString(env, format);
}).join(sep || ', ');
},
clone: function() {
var obj = Object.create(tree.Value.prototype);
@ -25,7 +26,34 @@ tree.Value.prototype = {
else obj.value = this.value;
obj.is = this.is;
return obj;
},
toJS: function(env) {
//var v = this.value[0].value[0];
var val = this.ev(env);
var v = val.toString();
if(val.is === "color" || val.is === 'uri' || val.is === 'string' || val.is === 'keyword') {
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, 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 + ";";
}
};
})(require('../tree'));

View File

@ -5,26 +5,30 @@ tree.Variable = function Variable(name, index, filename) {
this.index = index;
this.filename = filename;
};
tree.Variable.prototype = {
eval: function(env) {
is: 'variable',
toString: function() {
return this.name;
},
ev: function(env) {
var variable,
v,
that = this,
name = this.name;
if (this._css) return this._css;
var thisframe = env.frames.filter(function(f) {
return f.name == that.name;
});
return f.name == this.name;
}.bind(this));
if (thisframe.length) {
return thisframe[0].value.eval(env);
return thisframe[0].value.ev(env);
} else {
env.error({
message: 'variable ' + this.name + ' is undefined',
index: this.index,
type: 'runtime',
filename: this.filename,
filename: this.filename
});
return {
is: 'undefined',

View File

@ -4,23 +4,34 @@ var tree = require('../tree');
// and stores them as bit-sequences so that they can be combined,
// inverted, and compared quickly.
tree.Zoom = function(op, value, index) {
value = parseInt(value);
if (value > tree.Zoom.maxZoom || value < 0) {
throw {
message: 'Only zoom levels between 0 and '
+ tree.Zoom.maxZoom + ' supported.',
index: index
};
}
this.op = op;
this.value = value;
this.index = index;
};
tree.Zoom.prototype.setZoom = function(zoom) {
this.zoom = zoom;
return this;
};
tree.Zoom.prototype.ev = function(env) {
var start = 0,
end = Infinity,
value = parseInt(this.value.ev(env).toString(), 10),
zoom = 0;
switch (op) {
if (value > tree.Zoom.maxZoom || value < 0) {
env.error({
message: 'Only zoom levels between 0 and ' +
tree.Zoom.maxZoom + ' supported.',
index: this.index
});
}
switch (this.op) {
case '=':
return 1 << value;
break;
this.zoom = 1 << value;
return this;
case '>':
start = value + 1;
break;
@ -39,7 +50,12 @@ tree.Zoom = function(op, value, index) {
zoom |= (1 << i);
}
}
return zoom;
this.zoom = zoom;
return this;
};
tree.Zoom.prototype.toString = function() {
return this.zoom;
};
// Covers all zoomlevels from 0 to 22
@ -75,29 +91,28 @@ tree.Zoom.ranges = {
};
// Only works for single range zooms. `[XXX....XXXXX.........]` is invalid.
tree.Zoom.toXML = function(zoom) {
tree.Zoom.prototype.toXML = function() {
var conditions = [];
if (zoom != tree.Zoom.all) {
if (this.zoom != tree.Zoom.all) {
var start = null, end = null;
for (var i = 0; i <= tree.Zoom.maxZoom; i++) {
if (zoom & (1 << i)) {
if (start == null) start = i;
if (this.zoom & (1 << i)) {
if (start === null) start = i;
end = i;
}
}
if (start > 0) conditions.push(' <MaxScaleDenominator>'
+ tree.Zoom.ranges[start] + '</MaxScaleDenominator>\n');
if (end < 22) conditions.push(' <MinScaleDenominator>'
+ tree.Zoom.ranges[end + 1] + '</MinScaleDenominator>\n');
if (start > 0) conditions.push(' <MaxScaleDenominator>' +
tree.Zoom.ranges[start] + '</MaxScaleDenominator>\n');
if (end < 22) conditions.push(' <MinScaleDenominator>' +
tree.Zoom.ranges[end + 1] + '</MinScaleDenominator>\n');
}
return conditions;
};
tree.Zoom.toString = function(zoom) {
tree.Zoom.prototype.toString = function() {
var str = '';
for (var i = 0; i <= tree.Zoom.maxZoom; i++) {
str += (zoom & (1 << i)) ? 'X' : '.';
str += (this.zoom & (1 << i)) ? 'X' : '.';
}
return str;
};

24
man/carto.1 Normal file
View File

@ -0,0 +1,24 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.12.
.TH CARTO "1" "March 2013" "carto 0.9.4" "User Commands"
.SH NAME
carto \- Carto map stylesheet compiler
.SH SYNOPSIS
.B carto [OPTION]
\fI<source MML file>\fR
.SH DESCRIPTION
Carto is a stylesheet renderer for Mapnik. It's an evolution of
the Cascadenik idea and language, with an emphasis on speed and
flexibility.
.SH OPTIONS
.TP
\fB\-v\fR \fB\-\-version\fR
Parse JSON map manifest
.TP
\fB\-b\fR \fB\-\-benchmark\fR
Outputs total compile time
.TP
\fB\-n\fR \fB\-\-nosymlink\fR
Use absolute paths instead of symlinking files
.SH REPORTING BUGS
Please report bugs on the GitHub issue tracker:
<\fBhttps://github.com/mapbox/carto/issues\fR>

2110
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,17 @@
{
"name": "carto",
"description": "Mapnik Stylesheet Compiler",
"url": "https://github.com/mapbox/carto",
"repositories": [{
"version": "0.15.1-cdb5",
"description": "CartoCSS Stylesheet Compiler",
"url": "https://github.com/cartodb/carto",
"repository": {
"type": "git",
"url": "http://github.com/mapbox/carto.git"
}],
"url": "http://github.com/cartodb/carto.git"
},
"author": {
"name": "MapBox",
"url": "http://mapbox.com/",
"email": "info@mapbox.com"
"name": "CartoDB",
"url": "http://cartodb.com/"
},
"keywords": [
"mapnik",
"maps",
"css",
"stylesheets"
@ -20,33 +19,45 @@
"contributors": [
"Tom MacWright <macwright@gmail.com>",
"Konstantin Käfer",
"Alexis Sellier <self@cloudhead.net>"
"Alexis Sellier <self@cloudhead.net>",
"Raul Ochoa <rochoa@cartodb.com>",
"Javi Santana <jsantana@cartodb.com>"
],
"licenses": [
{
"type": "Apache"
}
],
"version": "0.4.4",
"licenses": [{
"type": "Apache"
}],
"bin": {
"carto": "./bin/carto",
"cartox": "./bin/cartox",
"mml2json.js": "./bin/mml2json.js"
"carto": "./bin/carto"
},
"man": "./man/carto.1",
"main": "./lib/carto/index",
"engines": {
"node": "0.4.x"
"node": ">=0.4.x"
},
"dependencies": {
"underscore": "1.1.x",
"xml2js": ">= 0.1.0"
"underscore": "1.8.3",
"mapnik-reference": "~6.0.2",
"optimist": "~0.6.0"
},
"devDependencies": {
"expresso": "0.8.x",
"docco": "0.3.x",
"mocha": "1.12.x",
"jshint": "0.2.x",
"sax": "0.1.x"
"sax": "0.1.x",
"istanbul": "~0.2.14",
"coveralls": "~2.10.1",
"browserify": "~7.0.0",
"uglify-js": "1.3.3"
},
"scripts": {
"pretest": "npm install --dev",
"test": "expresso"
"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",
"bump": "npm version patch",
"bump:major": "npm version major",
"bump:minor": "npm version minor",
"postversion": "git push origin master --follow-tags"
}
}

View File

@ -1,12 +1,12 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"opened.mss"
],
"Layer": [{
"id": "data",
"name": "data",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://client-data.s3.amazonaws.com/ndi-afghanistan/district_boundaries.zip",
"type": "shape"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
<Style name="data-polygon" filter-mode="first">
@ -2003,7 +2003,7 @@
<Layer
id="data"
name="data"
srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
<StyleName>data-polygon</StyleName>
<Datasource>
<Parameter name="file">[absolute path]</Parameter>

37
test/bincarto.test.js Normal file
View File

@ -0,0 +1,37 @@
var assert = require('assert');
var exec = require('child_process').exec;
var path = require('path');
var util = require('util');
var helper = require('./support/helper');
var bin = path.resolve(path.join(__dirname, '..', 'bin', 'carto'));
var fs = require('fs');
describe('bin/carto', function() {
it('errors on no input', function(done) {
exec(bin, function(err, stdout, stderr) {
assert.equal(1, err.code);
assert.equal("carto: no input files ('carto -h or --help' for help)\n", stdout);
done();
});
});
it('renders mml', function(done) {
var file = path.join(__dirname, 'rendering', 'identity.mml');
exec(util.format('%s %s', bin, file), function(err, stdout, stderr) {
assert.ifError(err);
helper.compareToXMLFile(helper.resultFile(file), stdout, done, [
helper.removeAbsoluteImages,
helper.removeAbsoluteDatasources
]);
});
});
it('renders mss', function(done) {
var file = path.join(__dirname, 'rendering-mss', 'empty_name.mss');
exec(util.format('%s %s', bin, file), function(err, stdout, stderr) {
assert.ifError(err);
var expected = file.replace(path.extname(file),'')+'.xml';
var expected_data = fs.readFileSync(expected, 'utf8');
assert.equal(stdout,expected_data + '\n');
done();
});
});
});

25
test/color.test.js Normal file
View File

@ -0,0 +1,25 @@
var assert = require('assert');
var tree = require('../lib/carto/tree.js');
require('../lib/carto/functions');
require('../lib/carto/tree/color');
require('../lib/carto/tree/dimension');
describe('Color', function() {
describe('basic functionality', function() {
it('should be constructed', function() {
var f = new tree.Color([0, 0, 0], 1);
assert.deepEqual(f.toHSL(), {"h":0,"s":0,"l":0,"a":1});
assert.ok(f);
});
});
describe('functions', function() {
it('should be constructed', function() {
assert.deepEqual(tree.functions.rgb(0, 0, 0), new tree.Color([0, 0, 0], 1));
assert.deepEqual(tree.functions.hue(new tree.Color([0, 0, 0], 1)), new tree.Dimension(0));
assert.deepEqual(tree.functions.saturation(new tree.Color([0, 0, 0], 1)), new tree.Dimension(0, '%'));
assert.deepEqual(tree.functions.lightness(new tree.Color([0, 0, 0], 1)), new tree.Dimension(0, '%'));
assert.deepEqual(tree.functions.alpha(new tree.Color([0, 0, 0], 1)), new tree.Dimension(1));
assert.deepEqual(tree.functions.greyscale(new tree.Color([0, 0, 0], 1)), new tree.Color([0, 0, 0], 1));
});
});
});

14
test/comment.test.js Normal file
View File

@ -0,0 +1,14 @@
var assert = require('assert');
var tree = require('../lib/carto/tree.js');
require('../lib/carto/tree/comment');
describe('Comment', function() {
describe('basic functionality', function() {
it('should be constructed', function() {
var f = new tree.Comment('hello world');
assert.deepEqual(f.toString(), '<!--hello world-->');
assert.deepEqual(f.ev(), f);
assert.ok(f);
});
});
});

View File

@ -1,5 +1,4 @@
var path = require('path'),
sys = require('sys'),
assert = require('assert'),
fs = require('fs');
@ -7,8 +6,10 @@ var carto = require('../lib/carto');
var tree = require('../lib/carto/tree');
var helper = require('./support/helper');
describe('Error handling mml+mss', function() {
helper.files('errorhandling', 'mml', function(file) {
exports['errorhandling ' + path.basename(file)] = function(beforeExit) {
var basename = path.basename(file);
it('should handle errors in ' + basename, function(done) {
var completed = false;
var renderResult;
var mml = helper.mml(file);
@ -18,34 +19,58 @@ helper.files('errorhandling', 'mml', function(file) {
data_dir: path.join(__dirname, '../data'),
local_data_dir: path.join(__dirname, 'rendering'),
filename: file
}).render(mml, function (err) {
var result = helper.resultFile(file);
var output = err.message;
// @TODO for some reason, fs.readFile includes an additional \n
// at the end of read files. Determine why.
fs.readFile(helper.resultFile(file), 'utf8', function(err, data) {
if (!err) assert.deepEqual(output, data.substr(0, data.length - 1));
});
});
}).render(mml);
// should not get here
assert.ok(false);
done();
} catch(err) {
if (err.message.indexOf('***') > -1) throw err;
var result = helper.resultFile(file);
var output = err.message;
// @TODO for some reason, fs.readFile includes an additional \n
// at the end of read files. Determine why.
fs.readFile(helper.resultFile(file), 'utf8', function(err, data) {
if (!err) assert.deepEqual(output, data.substr(0, data.length - 1));
});
// fs.writeFileSync(helper.resultFile(file), output);
var data = fs.readFileSync(helper.resultFile(file), 'utf8');
assert.deepEqual(output, data);
done();
}
beforeExit(function() {
/*
if (!completed && renderResult) {
console.warn(helper.stylize('renderer produced:', 'bold'));
console.warn(renderResult);
}
assert.ok(completed, 'Rendering finished.');
*/
});
}
});
});
});
describe('Error handling mss', function() {
helper.files('errorhandling', 'mss', function(file) {
var basename = path.basename(file);
if (basename == 'multi_stylesheets_a.mss') {
return;
}
it('should handle errors in ' + basename, function(done) {
var completed = false;
var renderResult;
var mss = helper.mss(file);
try {
new carto.Renderer({
paths: [ path.dirname(file) ],
data_dir: path.join(__dirname, '../data'),
local_data_dir: path.join(__dirname, 'rendering'),
// note: we use the basename here so that the expected error result
// will match if the style was loaded from mml
filename: basename
}).renderMSS(mss);
// should not get here
assert.ok(false);
done();
} catch(err) {
if (err.message.indexOf('***') > -1) throw err;
var result = helper.resultFile(file);
var output = err.message;
// @TODO for some reason, fs.readFile includes an additional \n
// at the end of read files. Determine why.
// fs.writeFileSync(helper.resultFile(file), output);
var data = fs.readFileSync(helper.resultFile(file), 'utf8');
assert.deepEqual(output, data);
done();
}
});
});
});

View File

@ -0,0 +1,15 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"bad_op.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"
}
}]
}

View File

@ -0,0 +1,3 @@
#world {
line-width: 20% + 2px;
}

View File

@ -0,0 +1 @@
bad_op.mss:2:4 If two operands differ, the first must not be %

View File

@ -0,0 +1,15 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"bad_op_2.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"
}
}]
}

View File

@ -0,0 +1,3 @@
#world {
line-width: 20px * 2%;
}

View File

@ -0,0 +1 @@
bad_op_2.mss:2:4 Percent values can only be added or subtracted from other values

View File

@ -0,0 +1,15 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"color_functions.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"
}
}]
}

View File

@ -0,0 +1,4 @@
@foo: 'bar';
#world {
polygon-fill: hsl(1, @foo, 3);
}

View File

@ -0,0 +1 @@
color_functions.mss:3:31 incorrect arguments given to hsl()

View File

@ -0,0 +1,15 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"contradiction.mss"
],
"Layer": [{
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/district.geojson",
"type": "ogr",
"layer": "OGRGeoJSON"
}
}]
}

View File

@ -0,0 +1,3 @@
#world[FeatureCla!=""][FeatureCla=""] {
polygon-fill: #fff;
}

View File

@ -0,0 +1 @@
contradiction.mss:1:37 [[FeatureCla]=] added to [FeatureCla]!= produces an invalid filter

View File

@ -0,0 +1,3 @@
#world[FeatureCla=""][FeatureCla!=""] {
polygon-fill: #fff;
}

View File

@ -0,0 +1 @@
contradiction_2.mss:1:37 [[FeatureCla]!=] added to [FeatureCla]= produces an invalid filter

View File

@ -0,0 +1,15 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"function_args.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"
}
}]
}

View File

@ -0,0 +1,4 @@
#world {
point-transform: scale(2, 2);
image-filters: agg-stack-blu(2, 1);
}

View File

@ -0,0 +1 @@
function_args.mss:3:38 unknown function agg-stack-blu(), did you mean agg-stack-blur(2)

View File

@ -0,0 +1,3 @@
#world {
polygon-fill: spin(#f00f00f, 10);
}

View File

@ -0,0 +1 @@
invalid_color_in_fn.mss:2:34 incorrect arguments given to spin()

View File

@ -1,12 +1,12 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"invalid_property.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"

View File

@ -1 +1 @@
invalid_property.mss:3:2 Unrecognized rule: polygonopacity
invalid_property.mss:3:2 Unrecognized rule: polygonopacity. Did you mean polygon-opacity?

View File

@ -1,12 +1,12 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"invalid_value.mss"
],
"Layer": [{
"id": "world",
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"

View File

@ -1,3 +1,4 @@
#world[zoom=5] {
polygon-opacity: #f00;
text-face-name: 2;
text-name: 'foo';
}

Some files were not shown because too many files have changed in this diff Show More