aboutsummaryrefslogblamecommitdiffstats
path: root/static/ui.js
blob: b35e9483fa8153e222b7e01e3669684fdbb78c70 (plain) (tree)
1
2
3
4
5
6


                              


                                         



















                                                                    
                         

                                                                          


                                  

                                        
                                              

              
                                                    
                                         

                           
                                                     
                                         
               

                                         

                                       


                            






                                                    
                                         

                                                     
                                         







                                              







                                        
                                            














                                                     


                                         



                                    




                                                                        
                                             




                                                                                                                       
             




                                                          
                                                            
                           



                                               







                                                                 
 
                      


                                                                    





                                                                     


                                                                   


                      
                                            

                                                                 


                      
                                            

                                                           
     
 














                                                   

                                               
















                                 
 






                                                         
                        
                                                                                    


                           






                                  
                  

                            



                                              



                                            







                               
                   



















                                                                             
      
 
                      
                                                 



                                                        
                                            




                                                                  






                                                 
 

                                          











                           



                                                        


                                                                        

                                      



                                          


                                                                  
 
                                                              
                                           

         
                                           


                                       
                                        





                                                
               
                                           
                                   
                                                
                                  
                                      

                  
                                    
             


                                                                         
           



                           
                            









                                     



                                      

                                  
                              
      




                                               










                            




                          
                        
                                                                                 


                           


                                          






                                                                         
                                          


                                   




                                                     
      







                                     





                                                

                                



                      
                                           
                                        

                                     
               






                                    


                        

                            




                                               
                                                                               


                           
                          
                                    





                                         







                                                                
                                          



                                   


















                                                 

                                       
                                     




                                     


                      

                                 
                              
      




                                                     
                                                                                 


                           




                                                                






                                                                                       



                                            




                                          






                                                                
                                                 
                                                 
                                          

                        
                                          

                                    
                        
                                                   






































                                                                    


                                                   
 
         


                                       




                                                   
                       


                                   
                           
         
                                

                                   
         
                                

                                   
         








                          
                     











                                                           
                     

                      
var templates = {};

$(document).ready(function() {
    if ( $(window).width() < 1024 ) {
        $('#filters').addClass('hidden');
    }
    boot();
});

var AppModel =  Backbone.Model.extend({
    defaults: {
        'selectedIndex': 0,
        'starredFilter': false,
        'allFilter': false,
        'unreadFilter': true,
        'feedFilter': undefined,
        'searchFilter': undefined
    },

    initialize: function() {
        this.bind('change:selectedIndex', this.scroll_to_selected);
//        this.bind('change:selectedIndex', this.scroll_to_selected)
    },

    boot: function() {
        this.items.boot();
        this.tags.boot();
        this.feeds.fetch({set: true, remove: false})
        window.setInterval(function() { App.update_read_status() }, 5000);
    },

    filterToFeed: function(feed) {
        if (feed.get('selected')) {
            feed.set('selected', false);
            this.set('feedFilter', undefined);
        }
        else {
            App.tags.models.forEach ( function (t) {
                t.set('selected', false);
            });
            App.tag = null;
            App.feeds.models.forEach ( function (f) {
                f.set('selected', false);
            });

            this.set('feedFilter', feed);
            feed.set('selected', true);
        }
        this.items.reboot();
    },

    filterToTag: function(tag) {
        App.tag = null;
        if (tag.get('selected')) {
            tag.set('selected', false);
        }
        else {
            App.tags.models.forEach ( function (t) {
                t.set('selected', false);
            });
            App.feeds.models.forEach ( function (f) {
                f.set('selected', false);
            });
            this.set('feedFilter', undefined);
            tag.set('selected', true);
            App.tag = tag.get('title');
        }
        App.items.reboot();
    },

    filterToStarred: function() {
        this.set('starredFilter', true);
        this.set('allFilter', false);
        this.set('unreadFilter', false);
        this.items.reboot();
    },

    filterToAll: function() {
        this.set('searchFilter', undefined);
        this.set('starredFilter', false);
        this.set('allFilter', true);
        this.set('unreadFilter', false);
        this.items.reboot();
    },

    filterToUnread: function() {
        this.set('starredFilter', false);
        this.set('allFilter', false);
        this.set('unreadFilter', true);
        this.items.reboot();
    },

    filterToSearch: function() {
        this.set('searchFilter', $('#search').val());
        this.set('starredFilter', false);
        this.set('allFilter', true);
        this.set('unreadFilter', false);
        this.items.reboot();
    },

    update_read_status: function() {
        var screen_top = $(window).scrollTop();
        var screen_bottom = $(window).scrollTop() +  $(window).height();

        // // mark all visible items as read
        $.each($('.item'), function(i,v) {
            var item_top = $(v).offset().top;
            // console.log("i ", i, "item_top ", item_top, "screen_top ", screen_top, "screen_bottom ", screen_bottom);

            if( (item_top < screen_top)) {
                App.items.at(i).markRead();
            //    console.log('marking as read: ', i);
            }
        });
//        window.setTimeout(App.update_read_status, 5000);
    },

    scroll_to_selected: function() {
        var item = $('.item').eq(this.get('selectedIndex'));
        if(item.offset()) {
            var item_top = item.offset().top;
            $('.item').removeClass('selected');
            item.addClass('selected');
            $(window).scrollTop(item_top);
        }
        App.items.at(this.get('selectedIndex')).markRead();
        if(App.items.models.length>1) {
            if(this.get('selected')>=App.items.models.length-1) {
                App.items.boot();
            }
        }
    },

    next: function() {
        if(this.get('selectedIndex') < this.items.models.length-1) {
            this.set('selectedIndex', this.get('selectedIndex')+1);
        }
        if(this.get('selectedIndex') == this.items.models.length-1) {
            App.items.boot();
        }
    },

    previous: function() {
        if(this.get('selectedIndex') > 0) {
            this.set('selectedIndex', this.get('selectedIndex')-1);
        }
    },

    star: function() {
        if(this.get('selectedIndex') >= 0) {
            App.items.at(this.get('selectedIndex')).toggleStar();
        }
    },

    full: function() {
        if(this.get('selectedIndex') >= 0) {
            App.items.at(this.get('selectedIndex')).full();
        }
    }

});
var App = new AppModel();

var ControlsView = Backbone.View.extend({
    className: 'controls',

    events: {
        'click .starred_filter': 'filterToStarred',
        'click .all_filter': 'filterToAll',
        'click .unread_filter': 'filterToUnread',
        'click .new_feed': 'newFeed',
        'click .search_go': 'filterToSearch',
    },

    initialize: function() {
        _.bindAll(this, 'render');
        this.model.bind('change', this.render);
    },

    filterToStarred: function() {
        App.filterToStarred();
    },

    filterToAll: function() {
        App.filterToAll();
    },

    filterToUnread: function() {
        App.filterToUnread();
    },

    filterToSearch: function() {
        App.filterToSearch();
    },

    newFeed: function() {
        var feed_url = prompt('New url to subscribe to');
        var feed = new Feed({'url': feed_url});
        App.feeds.add(feed);
        feed.save();
    },

    render: function() {
        var h = $.tmpl(templates.controls_template, { 'app': this.model.toJSON() });
        $(this.el).html(h);
        return this;
    },

});



var Item = Backbone.Model.extend({
    idAttribute: "_id",
    url: '/item/',

    initialize: function() {
        var p_url = this.get('url');
        p_url = p_url.replace('https://', '');
        p_url = p_url.replace('http://', '');
        this.set('p_url', p_url);
        this.bind('change', this.maybeSave);
    },

    maybeSave: function() {
        if(this.hasChanged()) {
            this.save();
        }
    },

    markRead: function() {
        // recover if not tag
        if(this.get('read')) {
            return;
        }

        // var t = this.get('feed').tag;
        // var tag = App.tags.find(function(x){ return x.get('name') == t });
        this.set('read', true);
        // if(tag) {
        //     tag.set('unread', tag.get('unread')-1);
        // }
    },

    toggleStar: function() {
        this.set({'starred': !(this.get('starred'))} );
    },

    star: function() {
        this.set({'starred': true});
    },

    unstar: function() {
        this.set({'starred': false});
    },

    full: function() {
        this.set({'full': !(this.get('full'))} );
        // this should just use this.fetch() but
        // it kept GETing from /item instead of /item/id
        // so just hacking this in for now

        if(this.get('full_content') == "") {
            $.getJSON('/item/' + this.get('_id'), function(data) {
                var i = App.items.get(data['_id'])
                i.set('full_content', data['full_content']);
            });
        }
    }

});


var ItemCollection = Backbone.Collection.extend({
    model: Item,

    initialize: function() {
        _.bindAll(this, 'boot', 'reboot');
    },

    boot: function() {
        if(App.loading) {
            return;
        }
        if(App.noMore) {
            return;
        }

        App.loading = true;
        url = '/stream/';
        url=url+'?foo=bar'
        if(App.get('searchFilter')) {
            url = url + '&q=' + App.get('searchFilter');
        }
        if(App.get('feedFilter')) {
            url = url + '&feed_url=' + App.get('feedFilter').get('url');
        }
        if(App.get('starredFilter')) {
            url = url + '&starred=1';
        }
        if(App.tag != undefined) {
            url = url + '&tag=' + App.tag;
        }
        if(App.items.last()) {
             url = url + '&max_id=' + App.items.last().get('_id');
        }

        if(App.get('allFilter') || App.get('starredFilter')) {
            url = url + '&read_filter=all';
        }

        console.log('fetching from ', url);
        var t = this;
        $.getJSON(url, function(data) {
            var items = [];
            $.each(data, function(i,v) {
                var item = new Item(v);
                t.add(item);
                items.push(item);
                if(t.models.length==1){
                    App.set('selectedIndex', 0);
                }
            });
            // console.log("items ", items)
            if(items.length == 0) {
                // console.log("no more items");
                App.noMore = true;
                // App.loading = true;
            }
            else {
                App.loading = false;
            }
            // we wait and add them all at once for performance on mobile
            App.itemListView.addAll(items);

        });
    },

    reboot: function() {
        App.noMore = false;
        App.loading = false;
        this.reset();
        this.boot();
    },


});
App.items = new ItemCollection();


var ItemView = Backbone.View.extend({
    tagName: "div",
    className: "item",
    template: templates.item_template,
    events: {
        "click .star": "star",
        "click .unstar": "unstar",
        "click .full": "full",
    },

    initialize: function() {
        _.bindAll(this, 'render', 'star');
        this.model.bind('change', this.render);
    },

    star: function() {
        this.model.star();
        this.render();
    },

    unstar: function() {
        this.model.unstar();
        this.render();
    },

    full: function() {
        this.model.full();
        this.render();
    },

    render: function() {
        var h = $.tmpl(templates.item_template, { 'item': this.model.toJSON() });
        $(this.el).html(h);
        return this;
    },
});

var ItemListView = Backbone.View.extend( {
    initialize: function() {
        _.bindAll(this, 'addOne', 'addAll', 'change', 'render', 'reset');
        // App.items.bind('add', this.addOne);
        App.items.bind('reset', this.reset);
    },
    addOne: function(item) {
        var view = new ItemView({'model': item});
        this.$el.append(view.render().el);
    },
    addAll: function(items) {
        // Posts.each(this.addOne);
        for(i in items) {
            item = items[i];
            var view = new ItemView({'model': item});
            this.$el.append(view.render().el);
        };
    },
    change: function() {
    },
    render: function() {
    },
    reset: function() {
        this.$el.children().remove();
    }
});

var Tag = Backbone.Model.extend({
});

var TagCollection = Backbone.Collection.extend({
    model: Tag,
    initialize: function() {
        _.bindAll(this, 'boot');
    },

    boot: function() {
        var t = this;
        $.getJSON('/tag/', function(data) {
            $.each(data, function(i,v) {
                var tag = new Tag(v);
                t.add(tag);
            });
        });
    }
});
App.tags = new TagCollection();


var TagView = Backbone.View.extend({
    tagName: "li",	
    className: "tag",
    events: {
        "click": "filterTo",
    },
    initialize: function() {
        _.bindAll(this, 'render', 'filterTo');
        this.model.bind('change', this.render);
    },
    render: function() {
        var h = $.tmpl(templates.tag_template, { 'tag': this.model.toJSON() });
        $(this.el).html(h);
        return this;
    },
    filterTo: function() {
        App.filterToTag(this.model);
    }
});


var TagListView = Backbone.View.extend( {

    initialize: function() {
        _.bindAll(this, 'addOne', 'addAll', 'change', 'render');
        App.tags.bind('add', this.addOne);
        App.tags.bind('refresh', this.addAll);
        App.tags.bind('change', this.render);
    },
    addOne: function(tag) {
        var view = new TagView({'model': tag});
        this.$el.append(view.render().el);
    },
    addAll: function() {
        App.tags.each(this.addOne);
    },
    change: function() {
    },
    render: function() {
    },
});

App.tag = undefined;
App.page = 0;
App.read_filter = 'unread';


var Feed = Backbone.Model.extend({
    idAttribute: "_id",
});

var FeedCollection = Backbone.Collection.extend({
    model: Feed,
    url: '/feed/',

    initialize: function() {
        ///    _.bindAll(this, 'boot');
        //console.log('initialized');
    },
});
App.feeds = new FeedCollection();

var FeedView = Backbone.View.extend({
    tagName: "li",
    className: "feed",
    events: {
        "click .txt": "filterTo",
        "click .delete": "del",
        "click .edit": "edit",
    },
    initialize: function() {
        _.bindAll(this, 'render', 'filterTo', "del");
        this.model.bind('change', this.render);
    },
    render: function() {
        var h = $.tmpl(templates.feed_template, { 'feed': this.model.toJSON() });
        $(this.el).html(h);
        return this;
    },
    filterTo: function() {
        //        console.log('filtering to feed ', this.model);
        App.filterToFeed(this.model);
    },
    del: function() {
        if( window.confirm("Unsubscribe from " + this.model.get("url") + "?" ) ) {
            this.model.destroy();
            this.$el.remove();
        }
    },
    edit: function() {
        var cat = window.prompt("Category for this feed?", this.model.get("category"));
        if (cat != null) {
            this.model.set("category", cat);
            this.model.save();
        }
    },
});


var FeedListView = Backbone.View.extend( {
    initialize: function() {
        _.bindAll(this, 'addOne', 'addAll', 'change', 'render');
        App.feeds.bind('add', this.addOne);
        App.feeds.bind('refresh', this.addAll);
        App.feeds.bind('change', this.render);
    },
    addOne: function(feed) {
        // console.log('adding a feed...', feed);
        var view = new FeedView({'model': feed});
        this.$el.append(view.render().el);
    },
    addAll: function() {
        // console.log('feed add all...');
        App.feeds.each(this.addOne);
    },
    change: function() {
        // console.log('feeds changed add all...');
    },
    render: function() {
    },
});


// var page = 0;
// var read_filter = 'unread';

var selected_item = 0;

function boot() {
    templates['item_template'] = $('#item_template').html();
    templates['tag_template'] = $('#tag_template').html();
    templates['feed_template'] = $('#feed_template').html();
    templates['controls_template'] = $('#controls_template').html();

    App.itemListView = new ItemListView();
    App.itemListView.setElement($('#items'));
    App.tagListView = new TagListView();
    App.tagListView.setElement($('#tags'));
    App.feedListView = new FeedListView();
    App.feedListView.setElement($('#feeds'));
    App.controlsView = new ControlsView({model: App});
    App.controlsView.setElement($('#controls'));
    App.controlsView.render();

    infini_scroll();

    $('#unread_filter').on('click', function() {
        App.read_filter = 'unread';
        App.items.reboot();
    });

    $('#all_filter').on('click', function() {
        App.read_filter = 'all';
        App.items.reboot();
    });

//    $('.logo').on('click', function() {
        //        App.set('feedFilter', undefined);
        //        App.items.reboot();

//    });

    // keyboard shortcuts
    $('body').keydown(function(event) {
        if(document.activeElement.id == "search") {
            return;
        }
        if (event.which == 74) {
            event.preventDefault();
            App.next();
        }
        if (event.which == 75) {
            event.preventDefault();
            App.previous();
        }
        if (event.which == 83) {
            event.preventDefault();
            App.star();
        }
        if (event.which == 70) {
            event.preventDefault();
            App.full();
        }
    });

    App.boot();
}


// // this is legacy code

function infini_scroll() {
    if(App.loading) {
    }
    else {
        var dh = $('#items').height() - $(window).height();
        var st = $(window).scrollTop();
        if  ( (dh-st) < 100   ){
            App.items.boot();
        }
    }
    window.setTimeout(infini_scroll, 1000);
}


var ItemSelector =  {
    selected_index: 0,
}