plugins/sniffer/public/javascripts/sniffer.js
sniffer = {
search: {
filters: { __categoryIds: [], distance: 0, circle: undefined, homePosition: undefined },
loadSearchInput: function (options) {
var input = jQuery(".sniffer-search-input");
input.hint();
input.autocomplete({
source: options.sourceUrl,
focus: function(event, ui) {
input.val(ui.item.label);
return false;
},
select: function (event, ui) {
category = {id: ui.item.value, name: ui.item.label, interest_type: 'supplier'};
if (sniffer.search.category.exists(category.id))
sniffer.search.category.updateInterestType(category.id);
else
sniffer.search.category.append([category]);
// Add new map marks:
jQuery.ajax(options.addUrl.replace('_id_', category.id), {
dataType: 'json',
error: function( jqXHR, textStatus, errorThrown ) {
var contextTxt = 'Ajax request to add new map marks fail.';
log.error( contextTxt, textStatus, errorThrown );
alert( contextTxt +'\n\n'+ textStatus );
},
success: function (data) {
for (var profile,i=0; profile=data.enterprises[i]; i++) {
if (profile.id != currentProfile.id){
var marker = sniffer.search.map.marker.add(profile);
sniffer.search.map.marker.index(data.productCategory.id, marker);
}
}
}
});
input.val('');
if (jQuery('#sniffer-product-search .hidden-pane').length > 0) {
sniffer.search.showFilters();
searchBoxTimeout = setTimeout(function(){ sniffer.search.hideFilters(); }, 1000);
};
return false;
},
});
},
filter: function () {
// Creates a new boundary based on home position
var bounds = new google.maps.LatLngBounds(sniffer.search.filters.homePosition);
var visibleCount = 0;
jQuery.each(sniffer.search.map.markerList, function(index, marker) {
var visible = (
!sniffer.search.filters.distance ||
sniffer.search.filters.distance >= marker.profile.sniffer_plugin_distance
) && sniffer.search.category.matchFilters(marker);
marker.setVisible(visible);
// If marker is visible, expands boundary to fit new marker
if (visible) {
bounds.extend(marker.getPosition());
visibleCount++;
}
});
// Set bounds to fit all markers
mapBounds = bounds;
return visibleCount;
},
showFilters: function (event) {
if (event) {
clearTimeout(searchBoxTimeout);
};
jQuery('#sniffer-product-search .focus-pane')[0].classList.remove('hidden-pane');
jQuery('#sniffer-product-search .legend')[0].classList.remove('hidden-pane');
},
hideFilters: function (event) {
jQuery('#sniffer-product-search .focus-pane')[0].classList.add('hidden-pane');
jQuery('#sniffer-product-search .legend')[0].classList.add('hidden-pane');
},
updateDistField: function (input) {
var distance = parseInt(input.value);
var label = jQuery(input.parentNode.parentNode);
label.removeClass('disabled');
if (isNaN(distance)) distance = 0;
if (distance == 0) {
label.addClass('disabled');
input.value = '';
}
else input.value = distance;
this.maxDistance(distance);
},
maxDistance: function (distance) {
distance = parseInt(distance);
sniffer.search.filters.distance = distance > 0 ? distance : undefined;
var visibleMarkersCount = sniffer.search.filter();
sniffer.search.setCircleRadius(distance);
sniffer.search.setSubtitle(distance, visibleMarkersCount);
},
setCircleRadius: function (distance) {
if (distance > 0) {
sniffer.search.filters.circle.setRadius(distance * 1000);
mapBounds = sniffer.search.filters.circle.getBounds();
} else {
sniffer.search.filters.circle.setRadius(0);
};
mapCenter();
},
setSubtitle: function (distance, count) {
if (distance > 0) {
jQuery('#sniffer-title .sniffer-subtitle').show();
jQuery('#sniffer-title-distance').html(distance);
jQuery('#sniffer-title-results').html(count);
} else {
jQuery('#sniffer-title .sniffer-subtitle').hide();
};
},
profile: {
findMarker: function (id) {
var marker;
jQuery.each(sniffer.search.map.markerList, function(index, m) {
if (m.profile.id == id)
marker = m;
});
return marker;
},
},
category: {
matchFilters: function (marker) {
var match = false,
markerIndex = sniffer.search.map.markerIndex,
categoryIds = sniffer.search.filters.__categoryIds;
for (var categoryId,i=0; categoryId=categoryIds[i]; i++) {
match = match || !!(markerIndex[categoryId] &&
markerIndex[categoryId].indexOf(marker) > -1);
}
return match;
},
toggleFilter: function (input) {
var id = parseInt(jQuery(input).attr('name'));
if (input.checked)
sniffer.search.category.applyFilter(id);
else
sniffer.search.category.unapplyFilter(id);
},
applyFilter: function (categoryId) {
var categoryIds = sniffer.search.filters.__categoryIds;
categoryIds.push(categoryId);
categoryIds = _.uniq(categoryIds);
sniffer.search.filters.__categoryIds = categoryIds;
sniffer.search.filter();
},
unapplyFilter: function (categoryId) {
var categoryIds = sniffer.search.filters.__categoryIds;
categoryIds = _.without(categoryIds, categoryId);
sniffer.search.filters.__categoryIds = categoryIds;
sniffer.search.filter();
},
updateCount: function () {
_.each(['consumers','suppliers','both'], function(item) {
// We search for entries of type item (singular) in the category
// table.
var count = jQuery('#categories-table .'+item.replace(/s$/,'')).length;
if (count > 0)
jQuery('#sniffer-product-search .legend .'+item+' .count')[0].innerHTML = '('+count+')';
else
jQuery('#sniffer-product-search .legend .'+item+' .count')[0].innerHTML = '';
});
},
exists: function (id) {
var find = jQuery('#categories-table input[name='+id+']');
return find.length > 0;
},
updateInterestType: function (id) {
var row = jQuery('#categories-table input[name='+id+']');
row = row.closest('tr').find('.consumer')
if (row.length > 0) {
row[0].classList.remove('consumer');
row[0].classList.add('both');
}
sniffer.search.category.updateCount();
},
template: function (categories) {
var template = jQuery('#sniffer-category-add-template');
return _.map(categories, function (category) {
if (sniffer.search.category.exists(category.id)) return;
return _.template(template.html(), {category: category});
}).join('');
},
append: function (categories) {
var target = jQuery('#categories-table');
var template = sniffer.search.category.template(categories);
target.append(template);
sniffer.search.category.updateCount();
},
},
map: {
markerIndex: [],
markerList: [],
homeIcon: "/plugins/sniffer/images/marker_home.png",
suppliersIcon: "/plugins/sniffer/images/marker_suppliers.png",
consumersIcon: "/plugins/sniffer/images/marker_consumers.png",
bothIcon: "/plugins/sniffer/images/marker_both.png",
marker: {
create: function (lat, lng, title, icon, url_or_function) {
var point_str = lat + ":" + lng;
if (mapPoints[point_str]) {
lng += (Math.random() - 0.5) * 0.02;
lat += (Math.random() - 0.5) * 0.02;
} else {
mapPoints[point_str] = true;
}
var template = jQuery('#marker-template');
var element = jQuery(_.template(template.html(), {icon: icon, title: title})).get(0);
var point = new google.maps.LatLng(lat, lng);
var marker = new CustomMarker({map: map, element: element, position: point});
jQuery(marker.element).click(function() { url_or_function(marker); });
mapBounds.extend(point);
return marker;
},
add: function(profile, filtered) {
if (filtered == undefined)
filtered = true;
var sp = profile.suppliersProducts;
var cp = profile.consumersProducts;
var marker = sniffer.search.profile.findMarker(profile.id);
if (marker) {
[].push.apply(sp, marker.profile.suppliersProducts);
sp = _.uniq(sp, function(product) { return product.id; });
[].push.apply(cp, marker.profile.consumersProducts);
cp = _.uniq(cp, function(product) { return product.id; });
}
if (_.size(sp) > 0 && _.size(cp) > 0)
icon = sniffer.search.map.bothIcon;
else if (_.size(sp) > 0)
icon = sniffer.search.map.suppliersIcon;
else
icon = sniffer.search.map.consumersIcon;
if (profile.icon) icon = profile.icon;
if (marker) {
marker.setOptions({ icon: icon });
} else {
marker = sniffer.search.map.marker.create(
profile.lat, profile.lng, profile.name, icon,
sniffer.search.map.balloon.fill
);
}
marker.profile = profile;
marker.cachedData = null;
if (filtered) {
sniffer.search.map.markerList.push(marker);
}
// Add circle overlay and bind to marker
if (profile.id == currentProfile.id){
sniffer.search.filters['circle'] = new google.maps.Circle({
map: map,
radius: sniffer.search.filters.distance * 1000, // in meters
fillColor: '#e50000',
strokeColor: '#5c0000',
strokeWeight: 1
});
sniffer.search.filters.circle.bindTo('center', marker, 'position');
}
return marker;
},
index: function (product_category_id, marker) {
var makersForCategory = sniffer.search.map.markerIndex[product_category_id];
if (!makersForCategory) makersForCategory = [];
if ( makersForCategory.indexOf(marker) == -1 )
makersForCategory.push(marker);
sniffer.search.map.markerIndex[product_category_id] = makersForCategory;
sniffer.search.category.applyFilter(product_category_id);
},
},
balloon: {
open: function (marker, html) {
marker.infoBox = new InfoBox({boxStyle: {width: null}, closeBoxURL: ""});
marker.infoBox.setPosition(marker.getPosition());
marker.infoBox.setContent(html);
marker.infoBox.open(map, marker);
},
fill: function (marker) {
// close all opened markers before opening a new one
jQuery.each(sniffer.search.map.markerList, function(index, marker) {
if (!(typeof marker.infoBox === 'undefined'))
marker.infoBox.close();
});
var balloonJQ = jQuery(
'<div class="sniffer-balloon-wrap">'+
'<div class="sniffer-balloon loading"></div>'+
'</div>'
);
sniffer.search.map.balloon.open(marker, balloonJQ[0]);
var fillBalloon = function () {
balloonJQ.find('.sniffer-balloon').removeClass('loading').prepend(marker.cachedData);
closeBt = jQuery('.sniffer-balloon-close', balloonJQ)[0];
marker.infoBox.closeListener_ = google.maps.event.addDomListener(closeBt, 'click',
function(){
marker.infoBox.close();
google.maps.event.trigger(marker.infoBox, "closeclick");
});
}
if (marker.cachedData)
fillBalloon();
else
jQuery.ajax(marker.profile.balloonUrl, {
type: 'POST',
data: marker.profile,
success: function (data) {
marker.cachedData = jQuery(data).html();
fillBalloon();
},
error: function (jqXHR, textStatus, errorThrown) {
log.error('balloon.fill()', errorThrown);
marker.cachedData = '<div class="error">'+
'<strong>'+textStatus+'</strong><br>'+errorThrown+'</div>';
fillBalloon();
}
});
},
},
resize: function () {
var wrap = jQuery('#sniffer-search-wrap');
var map = jQuery('#map');
var legend = jQuery('#legend-wrap');
wrap.css('height', jQuery(window).height() - wrap.offset().top);
map.css('height', wrap.outerHeight() - legend.outerHeight(true));
},
load: function (options) {
jQuery(window).load(sniffer.search.map.resize);
jQuery(window).resize(sniffer.search.map.resize);
mapLoad(options.zoom);
var myProfile = currentProfile;
myProfile.balloonUrl = options.myBalloonUrl;
myProfile.icon = sniffer.search.map.homeIcon;
var marker = sniffer.search.map.marker.add(myProfile, false);
sniffer.search.filters.homePosition = marker.getPosition();
_.each(options.profiles, function (profile) {
var sp = profile.suppliersProducts;
var cp = profile.consumersProducts;
profile.balloonUrl = options.balloonUrl.replace('_id_', profile.id);
var marker = sniffer.search.map.marker.add(profile);
_.each(_.union(sp, cp), function (p) {
sniffer.search.map.marker.index(p.product_category_id, marker);
});
});
sniffer.search.filter();
mapCenter();
},
},
},
};
// underscore use of <@ instead of <%
_.templateSettings = {
interpolate: /\<\@\=(.+?)\@\>/gim,
evaluate: /\<\@(.+?)\@\>/gim
};
// from http://stackoverflow.com/questions/17033397/javascript-strings-with-keyword-parameters
String.prototype.format = function(obj) {
return this.replace(/%\{([^}]+)\}/g,function(_,k){ return obj[k] });
};
var searchBoxTimeout;