Add regex filter, tests, clean up code more. Fixes #66
This commit is contained in:
parent
9960cd2667
commit
262ef7e722
@ -438,16 +438,15 @@ carto.Parser = function Parser(env) {
|
||||
// "milky way" 'he\'s the one!'
|
||||
//
|
||||
quoted: function() {
|
||||
var str;
|
||||
if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return;
|
||||
|
||||
if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
|
||||
var str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/);
|
||||
if (str) {
|
||||
return new tree.Quoted(str[0], str[1] || str[2]);
|
||||
}
|
||||
},
|
||||
|
||||
comparison: function() {
|
||||
var str = $(/^=|!=|<=|>=|<|>/);
|
||||
var str = $(/^=~|=|!=|<=|>=|<|>/);
|
||||
if (str) {
|
||||
return str;
|
||||
}
|
||||
|
@ -25,9 +25,15 @@ tree.Directive.prototype = {
|
||||
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) }
|
||||
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'));
|
||||
|
@ -1,6 +1,8 @@
|
||||
(function(tree) {
|
||||
|
||||
tree.Expression = function Expression(value) { this.value = value };
|
||||
tree.Expression = function Expression(value) {
|
||||
this.value = value;
|
||||
};
|
||||
tree.Expression.prototype = {
|
||||
eval: function(env) {
|
||||
if (this.value.length > 1) {
|
||||
|
@ -19,7 +19,7 @@ tree.Filter = function Filter(key, op, val, index, filename) {
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
if (this.op !== '=' && this.op !== '!=') {
|
||||
if (ops[this.op][1]) {
|
||||
this.val = 1 * this.val;
|
||||
}
|
||||
|
||||
@ -27,18 +27,19 @@ tree.Filter = function Filter(key, op, val, index, filename) {
|
||||
};
|
||||
|
||||
|
||||
// XML-safe versions of comparators
|
||||
var opXML = {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'=': '=',
|
||||
'!=': '!=',
|
||||
'<=': '<=',
|
||||
'>=': '>='
|
||||
// xmlsafe, numeric, suffix
|
||||
var ops = {
|
||||
'<': [' < ', true],
|
||||
'>': [' > ', true],
|
||||
'=': [' = ', false],
|
||||
'!=': [' != ', false],
|
||||
'<=': [' <= ', true],
|
||||
'>=': [' >= ', true],
|
||||
'=~': ['.match(', false, ')']
|
||||
};
|
||||
|
||||
tree.Filter.prototype.toXML = function(env) {
|
||||
if (this.op !== '=' && this.op !== '!=' && isNaN(this.val)) {
|
||||
if (ops[this.op][1] && isNaN(this.val)) {
|
||||
env.error({
|
||||
message: 'Cannot use operator "' + this.op + '" with value ' + this.val,
|
||||
index: this.index,
|
||||
@ -50,7 +51,7 @@ tree.Filter.prototype.toXML = function(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 || this.key) + ']' + ops[this.op][0] + '' + (val || this.val) + (ops[this.op][2] || '');
|
||||
};
|
||||
|
||||
tree.Filter.prototype.toString = function() {
|
||||
|
@ -66,12 +66,14 @@ Object.defineProperty(tree.Filterset.prototype, 'cloneWith', {
|
||||
|
||||
// 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)
|
||||
for (var id in this) {
|
||||
clone[id] = this[id];
|
||||
}
|
||||
|
||||
// Only add new filters that actually change the filter.
|
||||
while (id = additions.shift())
|
||||
while (id = additions.shift()) {
|
||||
clone.add(id);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
@ -98,12 +100,10 @@ Object.defineProperty(tree.Filterset.prototype, 'addable', {
|
||||
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 '>':
|
||||
@ -151,9 +151,11 @@ Object.defineProperty(tree.Filterset.prototype, 'add', {
|
||||
|
||||
switch (filter.op) {
|
||||
case '=':
|
||||
for (var id in this)
|
||||
if (this[id].key == key)
|
||||
for (var id in this) {
|
||||
if (this[id].key == key) {
|
||||
delete this[id];
|
||||
}
|
||||
}
|
||||
this[key + '='] = filter;
|
||||
break;
|
||||
|
||||
@ -161,17 +163,28 @@ Object.defineProperty(tree.Filterset.prototype, 'add', {
|
||||
this[key + '!=' + filter.val] = 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)
|
||||
// If there are other filters that are also >
|
||||
// but are less than this one, they don't matter, so
|
||||
// remove them.
|
||||
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)
|
||||
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 = '>';
|
||||
@ -183,16 +196,20 @@ Object.defineProperty(tree.Filterset.prototype, 'add', {
|
||||
break;
|
||||
|
||||
case '<':
|
||||
for (var id in this)
|
||||
if (this[id].key == key && this[id].val >= filter.val)
|
||||
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)
|
||||
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 = '<';
|
||||
|
@ -26,13 +26,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'));
|
||||
|
@ -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 }
|
||||
eval: function() { return this; },
|
||||
toString: function() { return this.value; }
|
||||
};
|
||||
|
||||
})(require('../tree'));
|
||||
|
@ -4,11 +4,11 @@ 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);
|
||||
value = parseInt(value, 10);
|
||||
if (value > tree.Zoom.maxZoom || value < 0) {
|
||||
throw {
|
||||
message: 'Only zoom levels between 0 and '
|
||||
+ tree.Zoom.maxZoom + ' supported.',
|
||||
message: 'Only zoom levels between 0 and ' +
|
||||
tree.Zoom.maxZoom + ' supported.',
|
||||
index: index
|
||||
};
|
||||
}
|
||||
@ -81,14 +81,14 @@ tree.Zoom.toXML = function(zoom) {
|
||||
var start = null, end = null;
|
||||
for (var i = 0; i <= tree.Zoom.maxZoom; i++) {
|
||||
if (zoom & (1 << i)) {
|
||||
if (start == null) start = 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;
|
||||
};
|
||||
|
14
test/rendering/regex.mml
Normal file
14
test/rendering/regex.mml
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"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": [
|
||||
"regex.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/test_data/shape_demo.zip",
|
||||
"type": "shape"
|
||||
}
|
||||
}]
|
||||
}
|
5
test/rendering/regex.mss
Normal file
5
test/rendering/regex.mss
Normal file
@ -0,0 +1,5 @@
|
||||
#world[ISO =~ "U*"] {
|
||||
polygon-fill: #FFF;
|
||||
line-color:#F00;
|
||||
line-width: 0.5;
|
||||
}
|
22
test/rendering/regex.result
Normal file
22
test/rendering/regex.result
Normal file
@ -0,0 +1,22 @@
|
||||
<?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.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
|
||||
|
||||
|
||||
<Style name="world" filter-mode="first">
|
||||
<Rule>
|
||||
<Filter>([ISO].match('U*'))</Filter>
|
||||
<PolygonSymbolizer fill="#ffffff" />
|
||||
<LineSymbolizer stroke="#ff0000" stroke-width="0.5" />
|
||||
</Rule>
|
||||
</Style>
|
||||
<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">
|
||||
<StyleName>world</StyleName>
|
||||
<Datasource>
|
||||
<Parameter name="file"><![CDATA[[absolute path]]]></Parameter>
|
||||
<Parameter name="type"><![CDATA[shape]]></Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
|
||||
</Map>
|
Loading…
Reference in New Issue
Block a user