From 51f12c9198175901eb54a12a537d27e862897dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Wed, 26 Jan 2011 11:34:51 -0500 Subject: [PATCH] split up zoomlevels into a separate thing. they are no longer in the filters array --- lib/mess/parser.js | 34 ++++++++++++++++++------- lib/mess/renderer.js | 32 +++++++++++------------ lib/mess/tree/comparison.js | 5 ++++ lib/mess/tree/filter.js | 10 +++++++- lib/mess/tree/ruleset.js | 5 ++++ lib/mess/tree/selector.js | 7 ++--- lib/mess/tree/value.js | 7 +++++ lib/mess/tree/zoomfilter.js | 4 +++ test/specificity/filters_and_ids.result | 6 ++--- 9 files changed, 77 insertions(+), 33 deletions(-) diff --git a/lib/mess/parser.js b/lib/mess/parser.js index c3c698e..fceb056 100644 --- a/lib/mess/parser.js +++ b/lib/mess/parser.js @@ -760,20 +760,28 @@ mess.Parser = function Parser(env) { // Selectors are made out of one or more Elements, see above. // selector: function() { - var a, attachment = null; + var a, attachment; var e, elements = []; var f, filters = []; + var z, zoom; while ( (e = $(this.element)) || + (z = $(this.zoom)) || (f = $(this.filter)) || (a = $(this.attachment)) ) { if (e) { elements.push(e); + } else if (z) { + if (zoom) { + zoom.intersect(z); + } else { + zoom = z; + } } else if (f) { filters.push(f); - } else if (attachment !== null) { + } else if (attachment) { throw errorMessage('Encountered second attachment name', i - 1); } else { attachment = a; @@ -783,27 +791,35 @@ mess.Parser = function Parser(env) { if (c === '{' || c === '}' || c === ';' || c === ',') { break } } - if (elements.length > 0 || filters.length > 0 || attachment !== null) { - return new tree.Selector(elements, filters, attachment, memo); + if (elements.length > 0 || filters.length > 0 || attachment || zoom) { + return new tree.Selector(elements, filters, zoom, attachment, memo); } }, filter: function() { save(); + var key, op, val; if (! $('[')) return; if (key = $(/^[a-zA-Z0-9-_]+/) || $(this.entities.quoted)) { if ((op = $(this.entities.comparison)) && (val = $(this.entities.quoted) || $(/^[\w-]+/))) { if (! $(']')) return; - if (key == 'zoom') { - return new tree.ZoomFilter(op, val, memo); - } else { - return new tree.Filter(key, op, val, memo); - } + return new tree.Filter(key, op, val, memo); } } }, + zoom: function() { + save(); + var op, val; + if ($(/^\[zoom/g) && + (op = $(this.entities.comparison)) && + (val = $(/^\d+/)) && + $(']')) { + return new tree.ZoomFilter(op, val, memo); + } + }, + // // The `block` rule is used by `ruleset` and `mixin.definition`. // It's a wrapper around the `primary` rule, with added `{}`. diff --git a/lib/mess/renderer.js b/lib/mess/renderer.js index 342f417..c34c2d3 100644 --- a/lib/mess/renderer.js +++ b/lib/mess/renderer.js @@ -244,6 +244,7 @@ mess.Renderer = function Renderer(env) { var winners = []; var ancestors = []; var belowThreshold = false; + var def; while (def = definitions.shift()) { if (belowThreshold) { @@ -275,20 +276,17 @@ mess.Renderer = function Renderer(env) { var negatedZoom = tree.ZoomFilter.newFromRange([0, Infinity]); for (var i = 0; i < definitions.length; i++) { - var definition = definitions[i]; - var zooms = definition.selector.filters.filter(function(f) { - return f instanceof tree.ZoomFilter; - }); - - // Merge all zoom filters without overwriting existing zoom - // filters; they might be referenced in other selectors. - var zoom = tree.ZoomFilter.newFromRange([0, Infinity]); - zooms.forEach(function(f) { zoom.intersection(f); }); + var selector = definitions[i].selector; // Only add the negated current zoom when there actually are zoom filters. - var negation = zooms.length ? zoom.negate() : false; + if (selector.zoom) { + var zoom = selector.zoom.clone(); + var negation = zoom.negate(); + zoom.intersection(negatedZoom); + } else { + var zoom = negatedZoom.clone(); + } - zoom.intersection(negatedZoom); var zoomRanges = zoom.getRanges(); if (!zoomRanges.length) { continue; @@ -299,13 +297,13 @@ mess.Renderer = function Renderer(env) { } // Resolve regular filters. - var filters = definition.selector.filters.filter(function(f) { + var filters = selector.filters.filter(function(f) { return f instanceof tree.Filter; }); + var negation = filters.map(function(f) { return f.negate(); }); // add in existing negations - // TODO: run uniq on this. filters.push.apply(filters, negatedFilters); // Throw out contradicting rules. @@ -316,12 +314,12 @@ mess.Renderer = function Renderer(env) { negatedFilters.push.apply(negatedFilters, negation); } - definition.selector.filters = filters; - definition.selector.zoom = zoom; + selector.filters = filters; + selector.zoom = zoom; // Add a separate rule for each zoom range. for (var j = 0; j < zoomRanges.length; j++) { - var rule = definition.clone(); + var rule = definitions[i].clone(); rule.selector.zoom = tree.ZoomFilter.newFromRange(zoomRanges[j]); rules.push(rule); } @@ -392,7 +390,7 @@ mess.Renderer = function Renderer(env) { // in order from high specificity to low. var bySymbolizer = that.splitSymbolizers(matching); - for (sym in bySymbolizer) { + for (var sym in bySymbolizer) { // Create styles out of chains of one-symbolizer rules, // and assign those styles to layers var new_style = new mess.tree.Style( diff --git a/lib/mess/tree/comparison.js b/lib/mess/tree/comparison.js index e360fd7..1e69671 100644 --- a/lib/mess/tree/comparison.js +++ b/lib/mess/tree/comparison.js @@ -26,6 +26,11 @@ tree.Comparison.prototype = { }, eval: function() { return this; + }, + clone: function() { + var obj = Object.create(Object.getPrototypeOf(this)); + obj.value = this.value; + return obj; } }; diff --git a/lib/mess/tree/filter.js b/lib/mess/tree/filter.js index 848de5d..fe558b9 100644 --- a/lib/mess/tree/filter.js +++ b/lib/mess/tree/filter.js @@ -36,7 +36,7 @@ tree.Filter.prototype.conflictsWith = function(filter) { filter.op.value === '=') { return true; } - + if (this.val.toString() === filter.val.toString() && ((this.op.value === '!=' && filter.op.value === '=') || (this.op.value === '=' && filter.op.value === '!='))) { @@ -56,6 +56,14 @@ tree.Filter.prototype.overrides = function(filter) { if (this.op.value === '=') return true; }; +tree.Filter.prototype.clone = function() { + var obj = Object.create(Object.getPrototypeOf(this)); + obj.key = this.key; + obj.op = this.op.clone(); + obj.val = this.val.clone ? this.val.clone() : this.val; + obj.index = this.index; + return obj; +}; tree.Filter.sound = function(filters) { for (var i = 0; i < filters.length; i++) { diff --git a/lib/mess/tree/ruleset.js b/lib/mess/tree/ruleset.js index 973c525..2f434ef 100644 --- a/lib/mess/tree/ruleset.js +++ b/lib/mess/tree/ruleset.js @@ -128,6 +128,11 @@ tree.Ruleset.prototype = { var instance = new tree.Selector(); instance.elements = parent.elements.concat(selector.elements); instance.filters = parent.filters.concat(selector.filters); + if (parent.zoom && selector.zoom) { + instance.zoom = parent.zoom.clone().intersection(selector.zoom); + } else if (parent.zoom || selector.zoom) { + instance.zoom = (parent.zoom || selector.zoom).clone() + } instance.attachment = selector.attachment; instance.index = selector.index; diff --git a/lib/mess/tree/selector.js b/lib/mess/tree/selector.js index 28adea4..f7bc9ff 100644 --- a/lib/mess/tree/selector.js +++ b/lib/mess/tree/selector.js @@ -1,9 +1,10 @@ (function(tree) { -tree.Selector = function Selector(elements, filters, attachment, index) { +tree.Selector = function Selector(elements, filters, zoom, attachment, index) { this.elements = elements || []; this.attachment = attachment || '__default__'; this.filters = filters || []; + this.zoom = zoom; this.index = index; }; @@ -27,12 +28,13 @@ tree.Selector.prototype.clone = function() { * [ID, Class, Filters, Position in document] */ tree.Selector.prototype.specificity = function() { + var zoomSpecificity = this.zoom ? this.zoom.specificity : 0; return this.elements.reduce(function(memo, e) { var spec = e.specificity(); memo[0] += spec[0]; memo[1] += spec[1]; return memo; - }, [0, 0, this.filters.length, this.index]); + }, [0, 0, this.filters.length + zoomSpecificity, this.index]); }; /** @@ -58,7 +60,6 @@ tree.Selector.prototype.layers = function(env) { tree.Selector.prototype.simplifiedFilters = function() { var simplified = []; filters: for (var i = 0; i < this.filters.length; i++) { - if (!(this.filters[i] instanceof tree.Filter)) continue filters; // Operate from the back so that we don't run into renumbering problems // when deleting items from the array. for (var j = simplified.length - 1; j >= 0; j--) { diff --git a/lib/mess/tree/value.js b/lib/mess/tree/value.js index 0154206..7ab8872 100644 --- a/lib/mess/tree/value.js +++ b/lib/mess/tree/value.js @@ -18,6 +18,13 @@ tree.Value.prototype = { return this.value.map(function(e) { return e.toString(env); }).join(', '); + }, + clone: function() { + var obj = Object.create(Object.getPrototypeOf(this)); + if (Array.isArray(obj)) obj.value = this.value.slice(); + else obj.value = this.value; + obj.is = this.is; + return obj; } }; diff --git a/lib/mess/tree/zoomfilter.js b/lib/mess/tree/zoomfilter.js index 5eba3a9..94c0417 100644 --- a/lib/mess/tree/zoomfilter.js +++ b/lib/mess/tree/zoomfilter.js @@ -9,6 +9,7 @@ tree.ZoomFilter = function ZoomFilter(op, value, index) { }; } this.range = tree.ZoomFilter.rangeFromCondition(op.value, value); + this.specificity = 1; }; tree.ZoomFilter.newFromRange = function(range) { @@ -99,7 +100,9 @@ tree.ZoomFilter.prototype.intersection = function(filter) { for (var i = 0; i < this.range.length; i++) { this.range[i] = this.range[i] && filter.range[i]; } + this.specificity += filter.specificity; } + return this; }; /** @@ -133,6 +136,7 @@ tree.ZoomFilter.prototype.toXML = function(env) { tree.ZoomFilter.prototype.clone = function() { var obj = Object.create(Object.getPrototypeOf(this)); obj.range = this.range.slice(); + obj.specificity = this.specificity; return obj; } diff --git a/test/specificity/filters_and_ids.result b/test/specificity/filters_and_ids.result index 3c28271..6f57c98 100644 --- a/test/specificity/filters_and_ids.result +++ b/test/specificity/filters_and_ids.result @@ -1,8 +1,8 @@ [ {"elements":["#world","#countries"],"filters":[{"key":"NAME","op":"=","val":"United States"}]}, - {"elements":["#world"],"filters":[{"key":"NAME","op":"=","val":"United States"},{"range":"0000001111111111111111"}]}, + {"elements":["#world"],"filters":[{"key":"NAME","op":"=","val":"United States"}],"zoom":{"range":"0000001111111111111111","specificity":1}}, {"elements":["#world"],"filters":[{"key":"NAME","op":"=","val":"United States"}]}, {"elements":["#world"],"filters":[{"key":"NAME","op":"=","val":"Canada"}]}, - {"elements":[],"filters":[{"range":"0000001111111111111111"}]}, + {"elements":[],"zoom":{"range":"0000001111111111111111","specificity":1}}, {"elements":[],"filters":[{"key":"NAME","op":"=","val":"United States"}]} -] +] \ No newline at end of file