backbone.facetr
Version:
A library to perform faceted search on Backbone collections
428 lines (372 loc) • 21.2 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="Backbone Facetr">
<meta name="author" content="Francesco Macri @ Arillo GmbH">
<meta name="viewport" content="width=device-width">
<title>Backbone.Facetr - Todos Example</title>
<style>
a { text-decoration: none }
a:hover { text-decoration: underline }
#TestContainer {
width: 1000px;
margin: 0 auto
}
#Facets, #Items {
display: inline-block;
vertical-align: top
}
#Facets {
width: 250px
}
#Facets .facet .values .value .active {
color: #f00
}
#Facets .facet .values .value .unavailable {
text-decoration: line-through;
color: #000
}
#Items {
width: 600px;
}
#Items ul {
height: 450px;
overflow: auto
}
</style>
</head>
<body>
<div id="TestContainer">
<h1>Facetr test</h1>
<div>
<label for="SortAttr">Sort by</label>
<select id="SortAttr">
<option>Country</option>
<option>Age</option>
</select>
<select id="SortDir">
<option>ASC</option>
<option>DESC</option>
</select>
</div>
<div>
<label for="Search">Search by FirstName</label>
<input id="Search" type="text" size="20" placeholder="insert regex"/>
</div>
<div id="Facets">
</div>
<div id="Items">
<h2>Items (<span> </span> of <span> </span>)</h2>
<div>
<span id="ExecTime">Exec (ms): <span> </span> <img src=""/></span> -
<span id="RenderTime">Render (ms): <span></span> <img src=""/></span>
</div>
<ul>
</ul>
</div>
</div>
<!-- templates -->
<script type="text/html" id="Facets_template">
<% _.each(facets, function(facet) { %>
<div class="facet" id="<%= facet.data.name %>">
<h4><%= facet.data.label %></h4>
<% if(!facet.data.hierarchical){ %>
<ul class="values">
<% for(var i = 0, len = facet.values.length, value = facet.values[i]; i < len; i++,value = facet.values[i]) { %>
<li class="value">
<%= valueTemplate({value:value}) %>
</li>
<% } %>
</ul>
<% } else { %>
<ul class="values">
<% for(var i = 0, len = facet.groupedValues.length, value = facet.groupedValues[i]; i < len; i++,value = facet.groupedValues[i]) { %>
<li class="value">
<%= valueTemplate({value:value}) %>
<% if(value.groups && value.groups.length > 0){ %>
<ul>
<% for(var j = 0, len2 = value.groups.length, v1 = value.groups[j]; j < len2; j++, v1 = value.groups[j]){ %>
<li class="value">
<%= valueTemplate({value:v1}) %>
<% if(v1.groups && v1.groups.length > 0) { %>
<ul>
<% for(var k = 0, len3 = v1.groups.length, v2 = v1.groups[k]; k < len3; k++, v2 = v1.groups[k]){ %>
<li class="value">
<%= valueTemplate({value:v2}) %>
</li>
<% } %>
</ul>
<% } %>
</li>
<% } %>
</ul>
<% } %>
</li>
<% } %>
</ul>
<% } %>
</div>
<% }); %>
</script>
<script type="text/html" id="FacetValue_template">
<a href="#" class="<%= (value.active) ? 'active' : ((value.count === 0) ? 'unavailable':'available') %>">
<span data-value="<%= value.value %>"><%= value.label || value.value %></span>
<span>(<%= value.activeCount %>/<%= value.count %>)</span>
</a>
</script>
<script type="text/html" id="Item_template">
<li>
<span><%= Name.FirstName+' '+Name.LastName %>, </span>
<span><%= Age %>, </span>
<span><%= Country %>, </span>
<span><%= Profession %>,</span>
<span>Hobbies: <%= Hobbies %></span>
</li>
</script>
<!-- include custom libs -->
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<!-- include Backbone.Facetr dependencies -->
<script src="../node_modules/underscore/underscore.js"></script>
<script src="../node_modules/backbone/backbone.js"></script>
<!-- include Backbone.Facetr code -->
<script src="../dist/backbone.facetr.js"></script>
<script>
(function($, _, Backbone, undefined) {
// generate dataset
var dataSetSize = parseInt(location.hash.replace('#','')) || 15,
items = window.items = (function(size) {
var dataSet = [],
i = 0,
names = 'Michael,Sonny,Fredo,Tom,Connie,Vito,Carmela,Vincent,Kay'.split(','),
surnames = 'Corleone,Hagen,Mancini,Adams'.split(','),
countries = 'Italy,USA,Canada,UK,Ireland,New Zealand,Australia'.split(','),
hobbies = 'fishing,shopping,painting,singing,drawing,playing the ukulele'.split(','),
professions = 'manager,project manager,team manager,fisherman,artist,musician'.split(','),
minAge = 15,
maxAge = 95;
while(i < size) {
var item = {
Name : {
FirstName : names[Math.floor((Math.random()*names.length))],
LastName : surnames[Math.floor((Math.random()*surnames.length))]
},
Age : Math.floor((Math.random()*(maxAge-minAge))+minAge),
Country : countries[Math.floor((Math.random()*countries.length))],
Profession: professions[Math.floor((Math.random()*professions.length))],
Hobbies : (function(){
var hbs = [], nr = Math.floor((Math.random()*5)+1), taken = [];
for(var j = 0; j < nr; j++) {
var hobbie = hobbies[Math.floor((Math.random()*hobbies.length))];
while(_.indexOf(taken, hobbie) !== -1) {
hobbie = hobbies[Math.floor((Math.random()*hobbies.length))];
}
hbs.push(hobbie);
taken.push(hobbie);
};
return hbs;
}())
};
dataSet.push(item);
i++;
};
return dataSet;
}(dataSetSize));
$('#Items h2 span').html(dataSetSize);
var execTime = 0, renderTime = 0,
images = {
loading : "img/loading.gif",
done: "img/done.gif"
},
$els = {
itemsNr: $('#Items h2 span').first(),
exec : {
span: $('#ExecTime span'),
img: $('#ExecTime img')
},
render : {
span: $('#RenderTime span'),
img: $('#RenderTime img')
}
};
$(function() {
var collection = window.collection = new Backbone.Collection(),
itemsView = new (Backbone.View.extend({
el: '#Items',
collection: new Backbone.Collection(),
initialize: function() {
this.$ul = this.$('ul');
this.collection.reset(items);
this.collection.add({
Name : {
FirstName : 'John',
LastName : 'Smith'
},
Age : 0,
Profession: 'team manager',
Country : undefined,
Hobbies : [ ]
});
this.timeout = undefined;
this.origLen = false;
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.addAll, this);
Facetr(this.collection).on('clear change remove sort', this.addAll, this);
this.addAll();
},
showRenderTime: function(){
renderTime = (new Date().getTime()) - renderTime;
$els.render.span.html(renderTime);
$els.render.img.attr('src', images.done);
},
addOne: function(model) {
var itemHTML = _.template($('#Item_template').html(), model.toJSON());
this.$ul.append(itemHTML);
},
addAll: function() {
this.$ul.html('');
var i = 0, len = this.collection.length, self = this;
this.$('ul').remove();
renderTime = new Date().getTime();
$els.render.span.html('');
$els.render.img.attr('src', images.loading);
clearTimeout(this.timeout);
if(i !== len) {
(function loop() {
var model = self.collection.at(i);
if(model) {
self.addOne(model);
}
i = i + 1;
if(i < len) {
if(i % 50 === 0) {
self.timeout = setTimeout(loop, 1);
} else {
loop();
}
} else {
self.$el.append(self.$ul);
self.showRenderTime();
$('#Items h2 span').html(Facetr(self.collection).origLength());
}
$els.itemsNr.html(i);
})();
} else {
$els.itemsNr.html(0);
}
}
}))({
collection: collection
}),
facetsView = new (Backbone.View.extend({
el: '#Facets',
events: {
'click .value a' : 'toggleValue'
},
initialize: function() {
Facetr(this.collection).facet('Country').label('Country').sortByValue().asc();
Facetr(this.collection).facet('Name.LastName').label('Last Name').sortByCount().desc();
Facetr(this.collection).facet('Hobbies').label('Hobbies').sortByCount().desc();
Facetr(this.collection).facetsOrder(['Name.LastName','Country']);
Facetr(this.collection).facet('Profession').sortByActiveCount().desc();
var hierarchySettings = [
{
value: "manager",
label: "Manager",
groups: [
{
value: "project manager",
label: "Project Manager",
groups: [
{
value: "team manager",
label: "Team Manager"
}
]
}
]
},
{
value: "artist",
label: "Artist",
groups: [
{
value: "musician",
label: "Musician"
},
{
value: "painter",
label: "Painter"
}
]
}
];
Facetr(this.collection).facet('Profession').hierarchy(hierarchySettings);
this.collection.on('reset change add remove', this.render, this);
Facetr(this.collection).on('filter unfilter facetsOrderChange', this.render, this);
Facetr(this.collection).on('filter unfilter facetsOrderChange', this.showExecTime, this);
execTime = new Date().getTime();
$els.exec.span.html('');
$els.exec.img.attr('src',images.loading);
this.render();
this.sort();
var self = this;
$("select").on("change", function() {
self.sort();
});
var searchEl = $("#Search");
var search = function() {
var el = this;
execTime = new Date().getTime();
Facetr(self.collection).addFilter('searchByKeyword', function(model) {
var regex = new RegExp($(el).val(), 'i');
return regex.test(model.get('Name').FirstName);
});
}, searchDeb = _.debounce(search, 500);
_.bind(search, searchEl);
searchEl.on("keyup", searchDeb);
this.showExecTime();
},
sort: function() {
this.sortBy($("select#SortAttr").find("option:selected").text(), $("select#SortDir").find("option:selected").text());
},
sortBy: function(attr, dir) {
execTime = new Date().getTime();
// dir [ASC|DESC] gets lowercased to match Facetr methods asc and desc
Facetr(this.collection).sortBy(attr)[dir.toLowerCase()]();
},
toggleValue: function(e) {
execTime = new Date().getTime();
$els.exec.span.html('');
$els.exec.img.attr('src',images.loading);
e.preventDefault();
var $target = $(e.currentTarget),
facet = $target.closest('.facet').attr('id'),
value = $target.find('span').first().attr('data-value');
if($target.hasClass('available')) {
Facetr(this.collection).facet(facet).value(value);
} else if($target.hasClass('active')) {
Facetr(this.collection).facet(facet).removeValue(value);
}
},
showExecTime: function(){
execTime = (new Date().getTime()) - execTime;
$els.exec.span.html(execTime);
$els.exec.img.attr('src', images.done);
},
render: function(facetName, facetValue) {
var facetsHTML = _.template($('#Facets_template').html(), {
facets : Facetr(this.collection).toJSON(),
valueTemplate : _.template($('#FacetValue_template').html())
});
this.$el.html(facetsHTML);
}
}))({
collection: collection
});
});
}(jQuery, _, Backbone));
</script>
</body>
</html>