Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Mute events in timeline #106

Merged
merged 24 commits into from

2 participants

@piatra

for #49
Still to do:

  • Apply filter to new events (that load after scrolling to the bottom)
  • Remove filters
dashboard/css/style.css
@@ -545,3 +545,11 @@ body {
overflow-y: auto;
max-height: 38rem;
}
+
+.timeline-item:hover .timeline-item--field-hiden {
+ opacity: 1;
+}
+
+.timeline-item--field-hiden {
@mihneadb Collaborator

hidden

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mihneadb
Collaborator

Nice, but you should also filter the already loaded events. :)

@piatra

Still to do: If you keep adding filters you end up with an empty page and the loading spinner that never triggers any events

@piatra

Done. Review please :) @mihneadb

@mihneadb
Collaborator

screen shot 2014-03-11 at 23 10 17
Why not make the filter area wider?
The "mute event" trigger should be more obvious - I spent some time looking for it and I knew to look for something :).
It would be great to be able to click on a label and get the items to show again (thinking toggling display:none).

Thanks! (Also, needs a rebase)

dashboard/js/timeline.js
@@ -17,6 +21,15 @@
return ret;
});
+ Handlebars.registerHelper('parseEventType', function(event) {
+ console.log(event);
@mihneadb Collaborator

let's get rid of this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mihneadb mihneadb commented on the diff
dashboard/js/timeline.js
@@ -17,6 +21,15 @@
return ret;
});
+ Handlebars.registerHelper('parseEventType', function(event) {
+ console.log(event);
+ if (!event) {
+ return 'unkown events';
+ }
+ var l = event.length;
+ return event.substr(0, l-5).toLowerCase() + ' events';
@mihneadb Collaborator

for some reason for stars I see "View only WatchEvent"

@piatra
piatra added a note

Fixed in new-er commit :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
dashboard/js/timeline.js
@@ -421,7 +436,17 @@
}
- fragment.appendChild($item[0]);
+ if (App.Timeline.filter.length) { // if there are filters
+ if ($item.data('event') && App.Timeline.filter.indexOf($item.data('event')) == -1) { // if the current item is not filtered out
+ console.log($item.data('event'));
@mihneadb Collaborator

drop this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mihneadb
Collaborator

Nice!

  • maybe the eye icon should be colored #999?
  • would be nice to see a list of what filters you have applied and be able to turn them off one by one (since you already have this implemented, it's just a matter of exposing it to the user)

Thanks!

@piatra
@mihneadb
Collaborator

Either display a wide bar, something like in the graphs:
screen shot 2014-04-11 at 12 38 35

Or let's save some space on the right and align the components like:
screen shot 2014-04-11 at 12 39 00

@piatra

Not ready for merge but would be nice to check for :bug:s
Added filters to the right side of the timeline

@mihneadb
Collaborator

Nice, I like the functionality.

I know it's not ready yet but noting some ideas down, just in case:

screen shot 2014-04-12 at 23 37 59
Would rather use the type of the event (or do we really not know)?

  • display a "no filters yet" on the right when there are none
  • hovering on a "filter" option should change bg of the item
  • action for clicking a set filter (on the right) should be obvious (UI feedback)
  • when using "display only this" vs "don't display this" the UI is the same on the right hand side, should be different

Thanks!

piatra added some commits
@piatra piatra Better filtering for timeline items
Use a backbone view for the timeline items which listens for a filter collection
when that collection changes it filters through the items. Easier to manage
d708919
@piatra piatra Made filter widget heading smaller 5e1b0b2
@piatra piatra Add no filter msg when collection is empty 787b45f
@piatra piatra Fix JSHint warnings b3c91c7
@piatra piatra Change widget bg on hover bae2bdc
@piatra piatra Better UI feedback for filters
Added X on hover to signal closing
Added title that explicitly says that click will remove
Added :hover state for better interaction
4aaf2fd
@piatra piatra Added `eye.svg` to differentiate between filter types 9ca613a
@mihneadb
Collaborator

Sweet! Please make it so that in the hover menu you can click on the whole item (not just the text) and let's :ship:.

@piatra

Yes :dancer:

@mihneadb
Collaborator

Thanks!

@mihneadb mihneadb merged commit 0f8f30f into uberVU:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 10, 2014
  1. @piatra

    Add image

    piatra authored
  2. @piatra
  3. @piatra
  4. @piatra

    Add clear filter button

    piatra authored
  5. @piatra

    Add filter tooltip

    piatra authored
  6. @piatra
Commits on Apr 11, 2014
  1. @piatra

    Add tooltip closer to the icon revealing it

    piatra authored
    There are some issues where moving the cursor over the tooltip will
    not keep it from dissapearing
  2. @piatra

    Add handlebars helper to parse event type

    piatra authored
    Now parsing camel case event types, there are some events with the type
    undefined
  3. @piatra
  4. @piatra

    Remove console.log statements

    piatra authored
  5. @piatra

    Make svg icon lighter

    piatra authored
Commits on Apr 12, 2014
  1. @piatra

    Remove unused style

    piatra authored
  2. @piatra

    Add filter next to timeline

    piatra authored
    Changed the timeline width also added template for filter items
  3. @piatra

    Removed previous filter control

    piatra authored
  4. @piatra

    Add filter logic

    piatra authored
Commits on Apr 14, 2014
  1. @piatra

    Better filtering for timeline items

    piatra authored
    Use a backbone view for the timeline items which listens for a filter collection
    when that collection changes it filters through the items. Easier to manage
  2. @piatra
  3. @piatra
  4. @piatra

    Fix JSHint warnings

    piatra authored
  5. @piatra

    Change widget bg on hover

    piatra authored
  6. @piatra

    Better UI feedback for filters

    piatra authored
    Added X on hover to signal closing
    Added title that explicitly says that click will remove
    Added :hover state for better interaction
  7. @piatra
Commits on Apr 17, 2014
  1. @piatra
  2. @piatra
This page is out of date. Refresh to see the latest.
View
103 dashboard/css/style.css
@@ -14,6 +14,10 @@ body {
display:inline-block;
}
+.heading--white {
+ color: white;
+}
+
#topbar h1 {
font-size: 2rem;
}
@@ -113,13 +117,11 @@ body {
}
#timeline {
- overflow: auto;
margin-bottom: 30px;
padding: 20px 40px;
padding-top: 0;
}
-
.timeline-item {
padding: 10px;
border-bottom: 1px solid rgba(113, 113, 113, 0.36);
@@ -164,6 +166,8 @@ body {
}
.timeline-item--read-more {
+ position: relative;
+ display: inline-block;
margin: -.5rem 0 0 0;
}
.timeline-item--read-more a {
@@ -246,14 +250,14 @@ body {
padding-right: 40px;
}
-.repo-select {
+.repo-select,
+.timeline-item--read-more {
position: relative;
}
- .repo-select ul {
+ .repo-select ul,
+ .timeline-item--event-controls {
position: absolute;
- right: 5px;
- top:-20px;
background: #fff;
border: 1px solid #ddd;
z-index: 1;
@@ -264,7 +268,13 @@ body {
min-height:30px;
}
- .repo-select ul:after {
+ .repo-select ul {
+ right: 5px;
+ top:-20px;
+ }
+
+ .repo-select ul:after,
+ .timeline-item--event-controls--tip {
content: '';
display: block;
position: absolute;
@@ -279,7 +289,8 @@ body {
border-left: 1px solid #ddd;
}
- .repo-select ul li {
+ .repo-select ul li,
+ .timeline-item--event-controls li {
list-style: none;
font-size: 1.2rem;
padding: .5rem 1rem;
@@ -521,6 +532,59 @@ body {
border-bottom: 1px solid #333;
}
+.timeline-item--event-controls {
+ display: inline-block;
+ bottom: -65px;
+ left: 1px;
+ margin:0;
+ opacity:0;
+ pointer-events: none;
+ border-radius: 3px;
+ width: 230px;
+}
+
+ .timeline-item--event-controls li {
+ padding: 0;
+ }
+
+ .timeline-item--event-controls a {
+ display: block;
+ font-size: .9rem;
+ padding: .25rem .5rem;
+ text-align: center;
+ }
+
+ .timeline-item--event-controls a:hover {
+ background: #ddd;
+ }
+
+ .timeline-item--event-controls li:first-child:hover ~ .timeline-item--event-controls--tip {
+ background: #ddd;
+ }
+
+ .timeline-item--read-more img {
+ visibility: hidden;
+ }
+
+ .timeline-item:hover .timeline-item--read-more img {
+ visibility: visible;
+ }
+
+ .timeline-item--read-more:hover .timeline-item--event-controls {
+ opacity: 1;
+ bottom: -65px;
+ -webkit-transition-duration: .3s;
+ -moz-transition-duration: .3s;
+ transition-duration: .3s;
+ pointer-events: auto;
+ }
+
+ .timeline-item--event-controls--tip {
+ top: -.5rem;
+ left: 3px;
+ z-index:-1;
+ }
+
.tooltip-template img {
max-width: 64px;
}
@@ -573,3 +637,26 @@ body {
opacity: 0.4;
margin-left: 0.5rem;
}
+
+.js-handler--timeline-filters {
+ padding: 0;
+ margin: 0;
+}
+
+.timeline-filter--item {
+ border: 1px solid #444;
+ border-radius: 3px;
+ padding: .5rem;
+ margin: .5rem 0;
+ list-style: none;
+ cursor: pointer;
+}
+
+ .timeline-filter--item:hover {
+ background: #444;
+ }
+
+ .timeline-filter--item:hover:after {
+ content: "\00d7";
+ margin: 0 .5rem;
+ }
View
1  dashboard/imgs/eye.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg baseProfile="tiny" height="24px" id="Layer_1" version="1.2" fill="#999999" viewBox="0 0 24 24" width="24px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M21.821,12.43c-0.083-0.119-2.062-2.944-4.793-4.875C15.612,6.552,13.826,6,12,6c-1.825,0-3.611,0.552-5.03,1.555 c-2.731,1.931-4.708,4.756-4.791,4.875c-0.238,0.343-0.238,0.798,0,1.141c0.083,0.119,2.06,2.944,4.791,4.875 C8.389,19.448,10.175,20,12,20c1.826,0,3.612-0.552,5.028-1.555c2.731-1.931,4.71-4.756,4.793-4.875 C22.06,13.228,22.06,12.772,21.821,12.43z M12,16.5c-1.934,0-3.5-1.57-3.5-3.5c0-1.934,1.566-3.5,3.5-3.5c1.93,0,3.5,1.566,3.5,3.5 C15.5,14.93,13.93,16.5,12,16.5z"/><g><path d="M14,13c0,1.102-0.898,2-2,2c-1.105,0-2-0.898-2-2c0-1.105,0.895-2,2-2C13.102,11,14,11.895,14,13z"/></g></svg>
View
42 dashboard/index.html
@@ -48,8 +48,17 @@ <h1 class="inline" id="user-repo"></h1>
<div class="small-12 large-12 columns" id="tab-container">
<div class="tab" id="tab-timeline">
- <div id="timeline" class="large-centered">
- <div class="timeline-item loading" id="timeline-loading">Loading more events</div>
+ <div class="row">
+ <div class="small-12 large-10 columns">
+ <div id="timeline" class="large-centered">
+ <div class="timeline-item loading" id="timeline-loading">Loading more events</div>
+ </div>
+ </div>
+ <div class="small-0 large-2 columns">
+ <h4 class="heading--white">Filters</h4>
+ <ul class="js-handler--timeline-filters">
+ </ul>
+ </div>
</div>
</div>
@@ -118,7 +127,7 @@ <h1 class="inline" id="user-repo"></h1>
</div>
<script id="timeline-item-template" type="text/x-handlebars-template">
- <div class="timeline-item row">
+ <div class="timeline-item row" data-event="{{ type }}">
{{#if avatar }}
<div class="small-2 columns position-relative">
<img src="{{ avatar }}" class="timeline-item--avatar">
@@ -181,6 +190,14 @@ <h1 class="inline" id="user-repo"></h1>
<a href="{{ link }}">Read more</a>
</p>
{{/if}}
+ <div class="timeline-item--read-more">
+ <img src="/imgs/eye.svg">
+ <ul class="timeline-item--event-controls">
+ <li><a data-event="{{ type }}" class="js-handler--mute-all" href="#">View only {{parseEventType type }}</a></li>
+ <li><a data-event="{{ type }}" class="js-handler--mute-event" href="#">Mute all {{parseEventType type }}</a></li>
+ <li class="timeline-item--event-controls--tip"></li>
+ </ul>
+ </div>
</div>
<div class="small-2 columns text-right timeline-aside">
<p class="timestamp muted">{{ timestamp }}</p>
@@ -197,7 +214,7 @@ <h1 class="inline" id="user-repo"></h1>
</script>
<script id="timeline-item-basic" type="text/x-handlebars-template">
- <div class="timeline-item row">
+ <div class="timeline-item row" data-event="{{ type }}">
<div class="small-2 columns text-centered">
<img src="/imgs/{{ img }}.svg" alt="{{ action }}" />
</div>
@@ -206,6 +223,14 @@ <h1 class="inline" id="user-repo"></h1>
{{#with username}}
<a href="{{ url }}">{{ username }}</a>
{{/with}} {{ action }} {{{ object }}}</p>
+ <div class="timeline-item--read-more">
+ <img src="/imgs/eye.svg">
+ <ul class="timeline-item--event-controls">
+ <li><a data-event="{{ type }}" class="js-handler--mute-all" href="#">View only {{parseEventType type }}</a></li>
+ <li><a data-event="{{ type }}" class="js-handler--mute-event" href="#">Mute all {{parseEventType type }}</a></li>
+ <li class="timeline-item--event-controls--tip"></li>
+ </ul>
+ </div>
</div>
<div class="small-2 columns text-right">
<p class="timestamp muted">{{ timestamp }}</p>
@@ -345,6 +370,15 @@ <h6 class="graph-title">No milestones</h6>
{{/unless}}
</script>
+<script id="timeline-filter-item" type="text/x-handlebars-template">
+ <li class="timeline-filter--item" title="click to remove">
+ {{ name }}
+ {{#if exclusive}}
+ <img src="/imgs/eye.svg">
+ {{/if}}
+ </li>
+</script>
+
<script src="/js/jquery.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
View
4 dashboard/js/app.js
@@ -211,6 +211,10 @@
$('#user-repo').text(App.REPO);
+ // clear any filtering options
+ App.Timeline.filter = [];
+ App.Timeline.exclusive = '';
+
if (!App.Components.timeline) {
initTimeline();
App.Components.timeline = true;
View
275 dashboard/js/timeline.js
@@ -1,14 +1,21 @@
+/* globals Handlebars, $, moment, Backbone, App */
+
(function(){
'use strict';
window.App = window.App || {};
window.App.PER_PAGE = 40;
+ window.App.Timeline = {
+ filter: [], // filter out items
+ exclusive: '', // allow just one item
+ coll: false // backbone filter collection
+ }; // filter out timeline items
var authorTemplate = Handlebars.compile($('#timeline-author-template').html());
var collabs = null;
Handlebars.registerHelper('eachCommit', function(context, options) {
- var ret = "";
+ var ret = '';
for(var i=0, j=context.length; i<j; i++) {
ret = ret + options.fn(context[i]);
@@ -17,6 +24,14 @@
return ret;
});
+ Handlebars.registerHelper('parseEventType', function(event) {
+ if (!event) {
+ return 'unkown events';
+ }
+ var l = event.length;
+ return event.substr(0, l-5).toLowerCase() + ' events';
@mihneadb Collaborator

for some reason for stars I see "View only WatchEvent"

@piatra
piatra added a note

Fixed in new-er commit :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ });
+
function formatAuthor(author) {
var login = author.login;
var avatarURL = author.avatar_url;
@@ -79,7 +94,7 @@
var TIMELINE_MAPPING = {
'CommitCommentEvent': {
action: function(e) {
- return "commented on";
+ return 'commented on';
},
object: function(e) {
var url = e.payload.comment.html_url;
@@ -94,11 +109,11 @@
},
'CreateEvent': {
action: function(e) {
- var s = "created";
+ var s = 'created';
if (e.payload.ref_type == 'branch') {
- return s + " branch";
+ return s + ' branch';
} else if (e.payload.ref_type == 'tag') {
- return s + " tag";
+ return s + ' tag';
}
return s;
},
@@ -108,11 +123,11 @@
},
'DeleteEvent': {
action: function(e) {
- var s = "deleted";
+ var s = 'deleted';
if (e.payload.ref_type == 'branch') {
- return s + " branch";
+ return s + ' branch';
} else if (e.payload.ref_type == 'tag') {
- return s + " tag";
+ return s + ' tag';
}
return s;
},
@@ -122,7 +137,7 @@
},
'ForkEvent': {
action: function(e) {
- return "forked to";
+ return 'forked to';
},
object: function(e) {
var name = e.payload.forkee.full_name;
@@ -137,7 +152,7 @@
},
object: function(e) {
var page = e.payload.pages[0];
- return "wiki page " + formatLink(page.html_url, page.page_name);
+ return 'wiki page ' + formatLink(page.html_url, page.page_name);
}
},
'IssueCommentEvent': {
@@ -145,7 +160,7 @@
return e.payload.issue.title;
},
action: function(e) {
- return "commented on";
+ return 'commented on';
},
object: function(e) {
return formatIssue(e.payload.issue);
@@ -211,19 +226,19 @@
},
'MemberEvent': {
action: function(e) {
- return "added";
+ return 'added';
},
object: function(e) {
var user = e.payload.member;
- return formatLink(user.html_url, user.login) + " as a collaborator";
+ return formatLink(user.html_url, user.login) + ' as a collaborator';
}
},
'PublicEvent': {
action: function(e) {
- return "open sourced";
+ return 'open sourced';
},
object: function(e) {
- return "the repository";
+ return 'the repository';
}
},
'PullRequestEvent': {
@@ -232,7 +247,7 @@
},
object: function(e) {
var pullReq = e.payload.pull_request;
- return "pull request " + formatIssue(pullReq);
+ return 'pull request ' + formatIssue(pullReq);
},
title: function(e) {
return e.payload.pull_request.title;
@@ -255,14 +270,14 @@
},
'PullRequestReviewCommentEvent': {
action: function(e) {
- return "reviewed";
+ return 'reviewed';
},
object: function(e) {
var url = e.payload.comment.pull_request_url;
var number = url.substring(url.lastIndexOf('/') + 1);
var htmlURL = e.payload.comment.html_url;
var pullReqURL = htmlURL.substring(0, htmlURL.lastIndexOf('#'));
- return "pull request " + App.utils.makeLink(pullReqURL, '#' + number);
+ return 'pull request ' + App.utils.makeLink(pullReqURL, '#' + number);
},
link: function(e) {
return e.payload.comment.html_url;
@@ -278,11 +293,11 @@
},
'PushEvent': {
action: function(e) {
- return "pushed";
+ return 'pushed';
},
object: function(e) {
var count = e.payload.size;
- return count + " commits";
+ return count + ' commits';
},
link: function(e) {
var head = e.payload.head;
@@ -309,10 +324,10 @@
},
'EndOfTimeline': {
action: function (e) {
- return "No more events available";
+ return 'No more events available';
},
object: function (e) {
- return "<p class='text-centered'>No more events available</p>";
+ return '<p class="text-centered">No more events available</p>';
}
}
};
@@ -344,7 +359,8 @@
action: 'commented: ',
object: '',
timestamp: moment(e.created_at).fromNow(),
- title: e.title
+ title: e.title,
+ type: e.type
};
} else {
context = {
@@ -362,7 +378,8 @@
action: mapping.action(e),
object: mapping.object(e),
timestamp: moment(e.created_at).fromNow(),
- title: mapping.title ? mapping.title(e) : ''
+ title: mapping.title ? mapping.title(e) : '',
+ type: e.type
};
}
@@ -421,7 +438,10 @@
}
- fragment.appendChild($item[0]);
+ // check items before inserting in the DOM
+ if (filterItem($item.data('event'))) {
+ fragment.appendChild($item[0]);
+ }
});
$(fragment).insertBefore($loading);
@@ -430,10 +450,10 @@
mapping = TIMELINE_MAPPING.EndOfTimeline;
$(document).off('scroll');
var context = {
- author: "Sorry!",
+ author: 'Sorry!',
action: '',
object: mapping.object(),
- timestamp: ""
+ timestamp: ''
};
var $item = $(template(context));
@@ -446,7 +466,38 @@
}
+ function filterItem(evt) {
+ // get array of filters from collection
+ var filters = App.Timeline.coll.map(function(c) {
+ return {
+ name: c.get('name'),
+ exclusive: c.get('exclusive')
+ };
+ });
+
+ if (!filters.length) return true;
+
+ if (filters.length == 1 && filters[0].exclusive) {
+ if (evt == filters[0].name) {
+ return true;
+ }
+ } else {
+ if (filters.length) {
+ var found = filters.some(function(el) {
+ return el.name == evt;
+ });
+ if (!found) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
window.populateTimeline = function populateTimeline(count, starting_from) {
+
$('li[data-tab=tab-timeline]').addClass('selected');
if (!count) {
@@ -456,18 +507,186 @@
starting_from = 0;
}
+ if (starting_from == 0) {
+ App.Timeline.coll.reset(); // empty out the filter collection
+ }
+
App.utils.httpGet(App.BASE + '/collaborators', function(data) {
collabs = data.data;
$.get(App.BASE + '/recent_events', {count: count, starting_from: starting_from})
.success(processTimelineData)
- .fail(displayFailMessage);
+ .fail(displayFailMessage)
+ .done(attachListeners);
});
};
+ function attachListeners() {
+ $('.js-handler--mute-all').on('click', muteAllEvents);
+ $('.js-handler--mute-event').on('click', muteEvent);
+ }
+
+ // mute all events except the one selected
+ function muteAllEvents(e) {
+ e.preventDefault();
+ var $this = $(this);
+ var eventType = $this.data('event');
+ App.Timeline.filter = [];
+ App.Timeline.coll.reset();
+ App.Timeline.exclusive = eventType;
+
+ if (App.Timeline.filter.indexOf(eventType) == -1) {
+ App.Timeline.coll.add({name: eventType, exclusive: 1});
+ App.Timeline.filter.push(eventType);
+ }
+ }
+
+ // mute the selected event
+ function muteEvent(e) {
+ e.preventDefault();
+ var $this = $(this);
+ var eventType = $this.data('event');
+
+ App.Timeline.exclusive = '';
+ if (App.Timeline.filter.indexOf(eventType) == -1) {
+ App.Timeline.coll.add({name: eventType});
+ App.Timeline.filter.push(eventType);
+ }
+ }
+
+ App.Timeline.Timeline = Backbone.View.extend({
+ $el: $('#timeline'),
+ initialize: function() {
+ this.collection.on('add', this.filter, this);
+ this.collection.on('remove', this.filter, this);
+ this.collection.on('reset', this.filter, this);
+ },
+ showAll: function() {
+ App.Timeline.filter = [];
+ App.Timeline.exclusive = '';
+ var items = $('.timeline-item');
+ items.each(function (idx, el) {
+ $(el).show();
+ });
+ },
+ filter: function() {
+ var items = $('.timeline-item');
+ var visibileElements = items.length;
+ var filters = this.collection.map(function(c) {
+ return {
+ name: c.get('name'),
+ exclusive: c.get('exclusive')
+ };
+ });
+
+ if (filters.length) {
+ items.each(function(idx, el) {
+ var $el = $(el);
+ var attr = $el.data('event');
+ if (filterItem(attr)) {
+ $el.show();
+ } else {
+ $el.hide();
+ visibileElements--;
+ }
+ });
+ } else {
+ this.showAll();
+ }
+
+ // removing elements might leave too few on screen
+ if (visibileElements < 10) {
+ populateTimeline(50, items.length);
+ }
+ }
+ });
+
window.emptyTimeline = function() {
var $timeline = $('#timeline');
var $loading = $('.timeline-item:last-child', $timeline).clone();
$timeline.empty().append($loading);
};
+ var FilterModel = Backbone.Model.extend({
+ name: 'filter name',
+ exclusive: 0
+ });
+
+ var FilterCollection = Backbone.Collection.extend({
+ model: FilterModel
+ });
+
+ var FilterView = Backbone.View.extend({
+ el: $('.js-handler--timeline-filters'),
+
+ template: $('#timeline-filter-item').html(),
+
+ events: {
+ 'click li': 'remove'
+ },
+
+ placeholderText: 'No filters added',
+
+ initialize: function() {
+ this.render();
+ this.collection.on('add', this.addOne, this);
+ this.collection.on('remove', this.removeOne, this);
+ this.collection.on('reset', this.reset, this);
+ },
+
+ reset: function() {
+ this.$el.empty();
+ this.placeholder();
+ },
+
+ remove: function(e) {
+ var itemName = $(e.target).text().trim();
+ if (!itemName || itemName == this.placeholderText) return;
+
+ this.collection.models.forEach(function(m) {
+ if (m.get('name') == itemName)
+ App.Timeline.coll.remove(m);
+ });
+
+ if (!this.collection.length) {
+ this.collection.reset();
+ }
+ },
+
+ placeholder: function() {
+ var markup = Handlebars.compile(this.template);
+ var $el = $(markup({name: this.placeholderText}));
+ this.$el.append($el);
+ },
+
+ addOne: function(item) {
+
+ // remove `no filters added` msg
+ if (this.collection.length == 1) {
+ this.$el.empty();
+ }
+
+ var markup = Handlebars.compile(this.template);
+ var $el = $(markup(item.toJSON()));
+ this.$el.append($el);
+ },
+
+ removeOne: function() {
+ this.reset();
+ this.render();
+ },
+
+ render: function() {
+ this.collection.each(this.addOne, this);
+ }
+ });
+
+ App.Timeline.coll = new FilterCollection();
+ var filters = new FilterView({
+ collection: App.Timeline.coll
+ });
+ var timeline = new App.Timeline.Timeline({
+ collection: App.Timeline.coll
+ });
+
+
})();
Something went wrong with that request. Please try again.