kopia lustrzana https://github.com/FacilMap/facilmap
Make legend much more clever regarding filters, use positive filters if they are shorter
rodzic
05fe205e40
commit
68857c0d0e
|
@ -1,6 +1,6 @@
|
|||
(function(fm, $, ng, undefined) {
|
||||
|
||||
fm.app.factory("fmFilter", function(compileExpression, $rootScope, $uibModal) {
|
||||
fm.app.factory("fmFilter", function(compileExpression, $rootScope, $uibModal, fmUtils) {
|
||||
var currentVal;
|
||||
|
||||
var fmFilter = {
|
||||
|
@ -53,26 +53,66 @@
|
|||
return '"' + (""+str).replace(/["\\]/g, '\\\1').replace(/\n/g, "\\n") + '"';
|
||||
},
|
||||
|
||||
_getMatchesWithBrackets: function(str, regexp) {
|
||||
var ret = [ ];
|
||||
|
||||
(str || "").replace(new RegExp(regexp, "gi"), function() {
|
||||
var offset = arguments[arguments.length-2];
|
||||
var match = arguments[0];
|
||||
|
||||
var open = match.match(/\(/g);
|
||||
var close = match.match(/\)/g);
|
||||
var needBraces = (open ? open.length : 0) - (close ? close.length : 0);
|
||||
for(var i=offset+match.length; i<str.length && needBraces > 0; i++) {
|
||||
if(str[i] == "(")
|
||||
needBraces++;
|
||||
else if(str[i] == ")")
|
||||
needBraces--;
|
||||
}
|
||||
|
||||
ret.push(str.substring(offset, i));
|
||||
});
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
makeTypeFilter: function(previousFilter, typeId, filteredData) {
|
||||
function removePart(str, regexp) {
|
||||
return (str || "")
|
||||
.replace(new RegExp("^" + regexp + "($|\\s+and\\s+|\\s+or\\s+)", "ig"), "")
|
||||
.replace(new RegExp("\\s+(and|or)\\s+" + regexp + "($|[^0-9a-z])", "ig"), function() { return arguments[arguments.length-3]; })
|
||||
str = str || "";
|
||||
regexp.forEach(function(r) {
|
||||
str = str
|
||||
.replace(new RegExp("^" + r + "($|\\s+and\\s+|\\s+or\\s+)", "ig"), "")
|
||||
.replace(new RegExp("\\s+(and|or)\\s+" + r + "($|[^0-9a-z])", "ig"), function() { return arguments[arguments.length-3]; })
|
||||
});
|
||||
return str;
|
||||
}
|
||||
|
||||
var ret = removePart(previousFilter, "typeId\\s*!=\\s*" + typeId);
|
||||
ret = removePart(ret, "not\\s+\\(typeId\\s*==\\s*" + typeId + "\\s([^\\(\\)]*(\\([^\\)]*\\))?)*?\\)");
|
||||
var ret = removePart(previousFilter,
|
||||
fmFilter._getMatchesWithBrackets(previousFilter, "(not\\s+)?\\(typeId\\s*==\\s*" + typeId).map(fmUtils.quoteRegExp)
|
||||
.concat([ "typeId\\s*[!=]=\\s*" + typeId ]));
|
||||
|
||||
if(typeof filteredData == "boolean") {
|
||||
if(filteredData)
|
||||
ret = (ret ? ret + " and " : "") + "typeId!=" + typeId;
|
||||
} else {
|
||||
var append = [ ];
|
||||
for(var i in filteredData)
|
||||
append.push("prop(data," + fmFilter.quote(i) + ")" + (filteredData[i].length > 1 ? " in (" + filteredData[i].map(fmFilter.quote).join(",") + ")" : "==" + fmFilter.quote(filteredData[i][0])));
|
||||
for(var i in filteredData) {
|
||||
var no = Object.keys(filteredData[i]).filter(function(it) { return !filteredData[i][it]; });
|
||||
var yes = Object.keys(filteredData[i]).filter(function(it) { return filteredData[i][it]; });
|
||||
|
||||
if(no.length == 0) // No item is not filtered, so we can filter the whole type
|
||||
return (ret ? ret + " and " : "") + "typeId!=" + typeId;
|
||||
|
||||
if(yes.length > 0) {
|
||||
var negative = "prop(data," + fmFilter.quote(i) + ")" + (no.length > 1 ? " not in (" + no.map(fmFilter.quote).join(",") + ")" : "!=" + fmFilter.quote(no[0]));
|
||||
var positive = "prop(data," + fmFilter.quote(i) + ")" + (yes.length > 1 ? " in (" + yes.map(fmFilter.quote).join(",") + ")" : "==" + fmFilter.quote(yes[0]));
|
||||
|
||||
append.push(negative.length < positive.length ? negative : positive);
|
||||
}
|
||||
}
|
||||
|
||||
if(append.length > 0)
|
||||
ret = (ret ? ret + " and " : "") + "not (typeId=="+typeId+" and " + append.join(" or ") + ")";
|
||||
ret = (ret ? ret + " and " : "") + "not (typeId=="+typeId+" and " + (append.length > 1 ? "(" + append.join(" or ") + ")" : append[0]) + ")";
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<div class="panel panel-default" ng-show="legendItems.length > 0" ng-class="{'hidden-xs': !showXs}">
|
||||
<div class="panel-body">
|
||||
<hr ng-repeat-start="type in legendItems" ng-if="$index > 0">
|
||||
<h3 ng-click="toggleFilter(type)" ng-class="{ filtered: isFiltered(type) }">{{type.name}}</h3>
|
||||
<h3 ng-click="toggleFilter(type)" ng-class="{ filtered: type.filtered }">{{type.name}}</h3>
|
||||
<dl ng-repeat-end>
|
||||
<dt ng-repeat-start="item in type.items" ng-class="thisClass = [ 'fm-' + type.type, { filtered: isFiltered(type, item) } ]" ng-click="toggleFilter(type, item)" ng-bind-html="item | fmMapLegendMakeSymbol:type.type"></dt>
|
||||
<dt ng-repeat-start="item in type.items" ng-class="thisClass = [ 'fm-' + type.type, { filtered: item.filtered } ]" ng-click="toggleFilter(type, item)" ng-bind-html="item | fmMapLegendMakeSymbol:type.type"></dt>
|
||||
<dd ng-repeat-end ng-class="thisClass" ng-click="toggleFilter(type, item)">{{item.value}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
for(var i in scope.types) {
|
||||
var type = scope.types[i];
|
||||
var items = [ ];
|
||||
var fields = { };
|
||||
|
||||
if(type.colourFixed || (type.type == "marker" && type.symbolFixed && type.defaultSymbol && fmIcons[type.defaultSymbol]) || (type.type == "line" && type.widthFixed)) {
|
||||
var item = { value: type.name };
|
||||
|
@ -27,8 +28,10 @@
|
|||
if(!field.type == "dropdown" || (!field.controlColour && (type.type != "marker" || !field.controlSymbol) && (type.type != "line" || !field.controlWidth)))
|
||||
return;
|
||||
|
||||
fields[field.name] = [ ];
|
||||
|
||||
(field.options || [ ]).forEach(function(option) {
|
||||
var item = { value: option.value, field: field.name };
|
||||
var item = { value: option.value, field: field.name, filtered: true };
|
||||
if(field.controlColour)
|
||||
item.colour = option.colour;
|
||||
if(type.type == "marker" && field.controlSymbol)
|
||||
|
@ -36,17 +39,41 @@
|
|||
if(type.type == "line" && field.controlWidth)
|
||||
item.width = option.width;
|
||||
items.push(item);
|
||||
fields[field.name].push(item.value);
|
||||
});
|
||||
});
|
||||
|
||||
if(items.length > 0)
|
||||
scope.legendItems.push({ type: type.type, typeId: type.id, name: type.name, items: items });
|
||||
if(items.length > 0) {
|
||||
var type = { type: type.type, typeId: type.id, name: type.name, items: items, filtered: true };
|
||||
|
||||
// Check which fields are filtered
|
||||
_allCombinations(fields, function(data) {
|
||||
if(map.socket.filterFunc({ typeId: type.typeId, data: data }, true)) {
|
||||
type.filtered = false;
|
||||
|
||||
items.forEach(function(it) {
|
||||
if(!it.field)
|
||||
it.filtered = false;
|
||||
});
|
||||
|
||||
for(var i in data) {
|
||||
items.forEach(function(it) {
|
||||
if(it.field == i && it.value == data[i])
|
||||
it.filtered = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scope.legendItems.push(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
scope.$watch("types", update, true);
|
||||
map.socket.on("filter", update);
|
||||
|
||||
var el = $($templateCache.get("map/legend/legend.html")).insertAfter(map.map.getContainer());
|
||||
$compile(el)(scope);
|
||||
|
@ -61,28 +88,41 @@
|
|||
el.css("max-height", getMaxHeight()+"px");
|
||||
}
|
||||
|
||||
function _allCombinations(fields, cb) {
|
||||
var fieldKeys = Object.keys(fields);
|
||||
|
||||
function rec(i, vals) {
|
||||
if(i == fieldKeys.length)
|
||||
return cb(vals);
|
||||
|
||||
var field = fields[fieldKeys[i]];
|
||||
for(var j=0; j<field.length; j++) {
|
||||
vals[fieldKeys[i]] = field[j];
|
||||
rec(i+1, vals);
|
||||
}
|
||||
}
|
||||
|
||||
rec(0, { });
|
||||
}
|
||||
|
||||
resize();
|
||||
scope.$watch(getMaxHeight, resize);
|
||||
$(window).resize(resize);
|
||||
|
||||
scope.isFiltered = function(typeInfo, item) {
|
||||
var obj = { typeId: typeInfo.typeId, data: { } };
|
||||
if(item && item.field)
|
||||
obj.data[item.field] = item.value;
|
||||
|
||||
return !map.socket.filterFunc(obj, true);
|
||||
};
|
||||
|
||||
scope.toggleFilter = function(typeInfo, item) {
|
||||
var filters = { };
|
||||
if(!item || !item.field) // We are toggling the visibility of one whole type
|
||||
filters = !scope.isFiltered(typeInfo, item);
|
||||
filters = !typeInfo.filtered;
|
||||
else {
|
||||
typeInfo.items.forEach(function(it) {
|
||||
if(it.field && scope.isFiltered(typeInfo, it) == (it != item)) {
|
||||
if(it.field) {
|
||||
if(!filters[it.field])
|
||||
filters[it.field] = [ ];
|
||||
filters[it.field].push(it.value);
|
||||
filters[it.field] = { };
|
||||
|
||||
if(!typeInfo.filtered || it.field == item.field)
|
||||
filters[it.field][it.value] = (it.filtered == (it != item));
|
||||
else // If the whole type is filtered, we have to enable the filters of the other fields, otherwise the type will still be completely filtered
|
||||
filters[it.field][it.value] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue