diff options
| author | Adam Mathes <adam@trenchant.org> | 2018-10-04 20:24:37 -0700 | 
|---|---|---|
| committer | Adam Mathes <adam@trenchant.org> | 2018-10-04 20:24:37 -0700 | 
| commit | 8903f360f58b7364c468d1a5a0afdd66774dc4da (patch) | |
| tree | 87dccb4e4d0a79d34398d74c44df89d6ac2f353c | |
| parent | 6a6cc29e2b5e55b785ef3cbcba2e6a85e29625f4 (diff) | |
| download | neko-8903f360f58b7364c468d1a5a0afdd66774dc4da.tar.gz neko-8903f360f58b7364c468d1a5a0afdd66774dc4da.tar.bz2 neko-8903f360f58b7364c468d1a5a0afdd66774dc4da.zip  | |
stop clobbering the f key
| -rw-r--r-- | static/ui.js | 4 | ||||
| -rw-r--r-- | web/rice-box.go | 12 | 
2 files changed, 6 insertions, 10 deletions
diff --git a/static/ui.js b/static/ui.js index 9742314..cc7ad8d 100644 --- a/static/ui.js +++ b/static/ui.js @@ -632,10 +632,6 @@ function boot() {              event.preventDefault();              App.star();          } -        if (event.which == 70) { -            event.preventDefault(); -            App.full(); -        }      });      App.boot(); diff --git a/web/rice-box.go b/web/rice-box.go index 62070ab..49857d6 100644 --- a/web/rice-box.go +++ b/web/rice-box.go @@ -55,18 +55,18 @@ func init() {  	}  	fileb := &embedded.EmbeddedFile{  		Filename:    "style.css", -		FileModTime: time.Unix(1538242745, 0), +		FileModTime: time.Unix(1538242901, 0),  		Content:     string("html {\n    font-size: 18px;\n}\nbody {\n    font-size: 1.0000rem;\n    line-height: 1.500rem;\n    min-width: 45rem;\n}\nh1 {\n    font-size: 3.1875rem;\n    line-height: 3.7500rem;\n    margin-top: 1.25rem;\n    margin-bottom: 2.5rem;\n}\nh2 {\n    font-size: 1.7500rem;\n    line-height: 2.5000rem;\n    margin-top: 2.5rem;\n    margin-bottom: 1.25rem;\n}\nh3 {\n    font-size: 1.3125rem;\n    line-height: 2.5000rem;\n    margin-top: 1.25rem;\n    margin-bottom: 0rem;\n}\np, ul {\n    margin-top: 0rem;\n    margin-bottom: 1.25rem;\n}\n\n#filters h4 {\n    margin-bottom: 0;\n}\n\nblockquote {\n    padding: 1rem 1rem 0 1rem;\n}\n\nbody {\n    font-family: Palatino, Georgia;\n}\n\nh1, h2, h3, h4, h5, #controls {\n    font-family: 'Helvetica Neue';\n    font-weight: 'bold';\n}\n\n#controls, .control {\n    font-variant: small-caps;\n    text-transform: lowercase;\n}\n\n\n#controls ul {\n    margin-top: 1em;\n    padding: 0;\n}\n\n#controls li {\n    width: 100%;\n    list-style: none;\n    margin-left: 0;\n    text-align: center;\n    margin-top: 10px;\n    background-color: whitesmoke;\n}\n\nh2 {\n    margin: 0;\n    padding: 0;\n}\n\na, a:hover, a:active {\n    text-decoration: none;\n    color: blue;\n}\n\nimg {\n    max-width: 100%;\n    height: auto;\n}\n\np {\n    margin-top: 1em;\n}\n\np.dateline {\n    margin-top: 0;\n    font-weight: normal;\n    font-size: .75em;\n    color: #ccc;\n}\n\np.dateline a {\n    color: #ccc;\n}\n\n.item {\n    padding: 1rem;\n    margin-top: 5rem;\n}\n\nh3 {\n    padding: 0;\n    margin: 0;\n}\n\n#items {\n    margin: 2rem auto;\n    max-width: 35rem;\n    overflow: hidden;\n}\n\n\n#filters {\n    position: fixed;\n    top: 0;\n    left: 0;\n    padding: 3rem 1rem 0 1rem;\n    width: 10rem;\n    cursor: pointer;\n    color: #777;\n    height: 100%;\n    overflow-y: auto;\n    background-color: #ccc;\n    transition-property: left;\n    transition-duration: .4s;\n}\n\n#filters.hidden {\n    left: -13rem;\n}\n\n#tags, #feeds {\n    font-family: 'Helvetica Neue';\n    list-style: none;\n    margin: 0;\n    padding: 0;\n}\n\n#tags li, #feeds li {\n    font-size: .75em;\n    padding-bottom: .25em;\n}\n\npre {\n    display: none;\n}\n\n.read {\n}\n\n.selected {\n}\n\n.newFilter {\n    color: black;\n}\n\n\n.all .newFilter {\n    color: #777;\n}\n\n.logo {\n    margin: 0;\n    padding: 1rem;\n    position: fixed;\n    top: 0;\n    left: 0;\n    color: white;\n    line-height: 1em;\n    z-index: 1;\n    cursor: pointer;\n    width: 10rem;\n    font-size: 2rem;\n}\n\n.hasunread {\n    color: blue;\n}\n\n.active, .all .everythingFilter {\n    color: black;\n}\n\n.full {\n    cursor: pointer;\n}\n\n.star {\n    color: black;\n    cursor: pointer;\n}\n\n.unstar {\n    color: blue;\n    cursor: pointer;\n}\n\n#loading {\n    width: 100%;\n    font-size: 24px;\n    background-color: #ccc;\n}\n\nbutton {\n    font-family: 'Helvetica Neue';\n    font-size: 16px;\n    padding: 3px 10px;\n    background: whitesmoke;\n    color: blue;\n    font-weight: bold;\n    border: none;\n    width: 100%;\n}\n\n\n@media only screen and (max-device-width: 768px) {\n    html {\n        width: 100%;\n        font-size: 22px;\n    }\n\n    body {\n        min-width: 0;\n        width: 100%;\n        max-width: 100%;\n        /* overflow: hidden; */\n    }\n\n    .logo {\n        width: 100%;\n    }\n\n    #filters li, #filters h4 {\n        font-size: 1.5rem;\n    }\n\n   /* #filters h4, #filters h1 { */\n   /*      display: none; */\n   /*  } */\n\n    /* #controls { */\n    /*     width: 100%; */\n    /*     font-size: 1.5rem; */\n    /* } */\n\n    /* #controls ul { */\n    /*     padding: 0; */\n    /*     margin: 0; */\n    /*     text-align: center; */\n    /* } */\n\n    /* #controls li { */\n    /*     display: inline; */\n    /* } */\n\n    /* #filters { */\n    /*     width: 100%; */\n    /*     height: 50px; */\n    /*     position: fixed; */\n    /*     top: 0; */\n    /*     left: 0; */\n    /*     background: #ccc; */\n    /* } */\n\n\n     #items { */\n        margin: 0 auto;\n        max-width: 100%;\n    }\n}\n\n@media only screen and (max-device-width: 400px) {\n    html {\n        font-size: 16px;\n    }\n\n    body {\n        min-width: 0;\n        width: 100%;\n    }\n}\n\nbody.dark {\n    color: #fff;\n    background-color: #24292e;\n}\n\nbody.black {\n    color: #fff;\n    background-color: black;\n}\n\nbody.dark a, body.black a {\n    color: rgb(90, 200, 250);\n}\n\nbody.dark button, body.dark #controls li a {\n    color: black;\n}\n\n\nbody.black button, body.black #controls li a {\n    color: black;\n}\n\n.themes button {\n    width: 30%;\n}\n"),  	}  	filec := &embedded.EmbeddedFile{  		Filename:    "ui.html", -		FileModTime: time.Unix(1538242667, 0), +		FileModTime: time.Unix(1538242901, 0),  		Content:     string("<!DOCTYPE html>\n<html>\n  <head>\n    <title>neko rss mode</title>\n    <link rel=\"stylesheet\" href=\"/static/style.css\" />\n    <script src=\"/static/jquery-3.3.1.min.js\"></script>\n    <script src=\"/static/jquery.tmpl.min.js\"></script>\n    <script src=\"/static/underscore-1.8.3.min.js\"></script>\n    <script src=\"/static/backbone-1.3.3.min.js\"></script>\n    <script>\n      PUBLIC_VERSION = false;\n    </script>\n    <script src=\"/static/ui.js\"></script>\n    <meta name=\"viewport\" content=\"width=device-width,height=device-height, initial-scale=1, maximum-scale=1\" />\n    <base target=\"_blank\">\n  </head>\n  <body>\n      <h1 class=\"logo\" onclick=\"$('#filters').toggleClass('hidden');\">🐱</h1>\n\n      <div id=\"filters\">\n\n        <div id=\"controls\"></div>\n        <h4 onclick=\"$('#tags').toggle();\">Tags</h4>        \n        <ul id=\"tags\" style=\"display: none;\">\n        </ul>\n        \n        <h4 onclick=\"$('#feeds').toggle();\">Feeds</h4>        \n        <ul id=\"feeds\" style=\"display: none;\">\n        </ul>\n\n        <h4 onclick=\"$('#export').toggle();\">Export</h4>\n        <ul id=\"export\" style=\"display: none;\">\n            <li><a href=\"/export/opml\">opml</a></li>\n            <li><a href=\"/export/text\">text</a></li>\n            <li><a href=\"/export/json\">json</a></li>\n        </ul>\n      </div>\n        \n    </div>\n\n   <div id=\"c\">\n      <div id=\"items\">\n      </div>      \n    </div>\n    \n    <script id=\"item_template\" type=\"text/jqtmp\">\n      <h2><a class=\"i\" id=\"i_${item_id}\" href=\"${item.url}\">${item.title }</a> \n    <span class={{if item.starred}}\"unstar\"{{else}}\"star\"{{/if}}>★</span>\n    </h2>\n      <p class=\"dateline\" style=\"clear: both;\">\n        <a href=\"${item.feed_url}\">${item.feed_title}</a> | <a href=\"${item.url}\">${item.p_url}</a>\n    | ${item.feed_category} |\n    <span class=\"full\">{{if item.full}}hide{{else}}scrape{{/if}} full text</span>\n    \n    </p>\n      {{if item.header_image}}\n      <div class=\"img\"><img src=\"${item.header_image}\" /></div>\n      {{/if}}\n      <div class=\"description\">\n        {{if item.full}}\n           {{html item.full_content}}\n        {{else}}\n           {{html item.description}}\n        {{/if}}\n      </div>\n    </script>\n\n    <script id=\"tag_template\" type=\"text/jqtmp\">\n      {{if tag.selected}}<b>{{/if}}\n      ${tag.title}\n      {{if tag.selected}}</b>{{/if}}\n    </script>\n\n    <script id=\"feed_template\" type=\"text/jqtmp\">\n      {{if feed.selected}}<b>{{/if}}\n      <span class=\"txt\">\n      {{if feed.title}}\n      ${feed.title}\n      {{else}}\n      ${feed.url}\n      {{/if}}\n\n      </span>\n      <span class=\"edit\">[e]</span>\n      <span class=\"delete\">[x]</span>\n      {{if feed.selected}}</b>{{/if}}\n    </script>\n\n    <script id=\"controls_template\" type=\"text/jqtmp\">\n      <ul>\n        <li class=\"unread_filter\">\n          <a {{if app.unreadFilter}}style=\"font-weight: bold;\"{{/if}} \n             >unread</a>\n        </li>\n        <li class=\"all_filter\">\n          <a \n             {{if app.allFilter}}style=\"font-weight: bold;\"{{/if}} \n             >all</a> \n        </li>\n        <li class=\"starred_filter\">\n         <a {{if app.starredFilter}}style=\"font-weight: bold;\"{{/if}}\n            >★ starred</a>\n        </li>\n        <li>\n          <button class=\"new_feed\"> + new </button>\n        </li>\n        <li>\n           <input id=\"search\" type=\"search\" /><button class=\"search_go\">search</button>\n        </li>\n\t    <li class=\"themes\">\n            <button class=\"light_theme\">light</button>\n            <button class=\"dark_theme\">dark</button>\n            <button class=\"black_theme\">black</button>\n\t\t</li>\n\t  </ul>\n   </script>  \n   \n</body>\n</html>  \n"),  	}  	filed := &embedded.EmbeddedFile{  		Filename:    "ui.js", -		FileModTime: time.Unix(1538242832, 0), -		Content:     string("var templates = {};\n\n$(document).ready(function() {\n    if ( $(window).width() < 1024 ) {\n        $('#filters').addClass('hidden');\n    }\n    document.body.className = localStorage.getItem('theme');\n    boot();\n});\n\nvar AppModel =  Backbone.Model.extend({\n    defaults: {\n        'selectedIndex': 0,\n        'starredFilter': false,\n        'allFilter': false,\n        'unreadFilter': true,\n        'feedFilter': undefined,\n        'searchFilter': undefined\n    },\n\n    initialize: function() {\n        this.bind('change:selectedIndex', this.scroll_to_selected);\n//        this.bind('change:selectedIndex', this.scroll_to_selected)\n    },\n\n    boot: function() {\n        this.items.boot();\n        this.tags.boot();\n        this.feeds.fetch({set: true, remove: false})\n        window.setInterval(function() { App.update_read_status() }, 5000);\n    },\n\n    filterToFeed: function(feed) {\n        if (feed.get('selected')) {\n            feed.set('selected', false);\n            this.set('feedFilter', undefined);\n        }\n        else {\n            App.tags.models.forEach ( function (t) {\n                t.set('selected', false);\n            });\n            App.tag = null;\n            App.feeds.models.forEach ( function (f) {\n                f.set('selected', false);\n            });\n\n            this.set('feedFilter', feed);\n            feed.set('selected', true);\n        }\n        this.items.reboot();\n    },\n\n    filterToTag: function(tag) {\n        App.tag = null;\n        if (tag.get('selected')) {\n            tag.set('selected', false);\n        }\n        else {\n            App.tags.models.forEach ( function (t) {\n                t.set('selected', false);\n            });\n            App.feeds.models.forEach ( function (f) {\n                f.set('selected', false);\n            });\n            this.set('feedFilter', undefined);\n            tag.set('selected', true);\n            App.tag = tag.get('title');\n        }\n        App.items.reboot();\n    },\n\n    filterToStarred: function() {\n        this.set('starredFilter', true);\n        this.set('allFilter', false);\n        this.set('unreadFilter', false);\n        this.items.reboot();\n    },\n\n    filterToAll: function() {\n        this.set('searchFilter', undefined);\n        this.set('starredFilter', false);\n        this.set('allFilter', true);\n        this.set('unreadFilter', false);\n        this.items.reboot();\n    },\n\n    filterToUnread: function() {\n        this.set('starredFilter', false);\n        this.set('allFilter', false);\n        this.set('unreadFilter', true);\n        this.items.reboot();\n    },\n\n    filterToSearch: function() {\n        this.set('searchFilter', $('#search').val());\n        this.set('starredFilter', false);\n        this.set('allFilter', true);\n        this.set('unreadFilter', false);\n        this.items.reboot();\n    },\n\n    update_read_status: function() {\n        var screen_top = $(window).scrollTop();\n        var screen_bottom = $(window).scrollTop() +  $(window).height();\n\n        // // mark all visible items as read\n        $.each($('.item'), function(i,v) {\n            var item_top = $(v).offset().top;\n            // console.log(\"i \", i, \"item_top \", item_top, \"screen_top \", screen_top, \"screen_bottom \", screen_bottom);\n\n            if( (item_top < screen_top)) {\n                App.items.at(i).markRead();\n            //    console.log('marking as read: ', i);\n            }\n        });\n//        window.setTimeout(App.update_read_status, 5000);\n    },\n\n    scroll_to_selected: function() {\n        var item = $('.item').eq(this.get('selectedIndex'));\n        if(item.offset()) {\n            var item_top = item.offset().top;\n            $('.item').removeClass('selected');\n            item.addClass('selected');\n            $(window).scrollTop(item_top);\n        }\n        App.items.at(this.get('selectedIndex')).markRead();\n        if(App.items.models.length>1) {\n            if(this.get('selected')>=App.items.models.length-1) {\n                App.items.boot();\n            }\n        }\n    },\n\n    next: function() {\n        if(this.get('selectedIndex') < this.items.models.length-1) {\n            this.set('selectedIndex', this.get('selectedIndex')+1);\n        }\n        if(this.get('selectedIndex') == this.items.models.length-1) {\n            App.items.boot();\n        }\n    },\n\n    previous: function() {\n        if(this.get('selectedIndex') > 0) {\n            this.set('selectedIndex', this.get('selectedIndex')-1);\n        }\n    },\n\n    star: function() {\n        if(this.get('selectedIndex') >= 0) {\n            App.items.at(this.get('selectedIndex')).toggleStar();\n        }\n    },\n\n    full: function() {\n        if(this.get('selectedIndex') >= 0) {\n            App.items.at(this.get('selectedIndex')).full();\n        }\n    }\n\n});\nvar App = new AppModel();\n\nvar ControlsView = Backbone.View.extend({\n    className: 'controls',\n\n    events: {\n        'click .starred_filter': 'filterToStarred',\n        'click .all_filter': 'filterToAll',\n        'click .unread_filter': 'filterToUnread',\n        'click .new_feed': 'newFeed',\n        'click .search_go': 'filterToSearch',\n        'click .light_theme': 'lightTheme',\n        'click .dark_theme': 'darkTheme',\n        'click .black_theme': 'blackTheme',\n    },\n\n    initialize: function() {\n        _.bindAll(this, 'render');\n        this.model.bind('change', this.render);\n    },\n\n    filterToStarred: function() {\n        App.filterToStarred();\n    },\n\n    filterToAll: function() {\n        App.filterToAll();\n    },\n\n    filterToUnread: function() {\n        App.filterToUnread();\n    },\n\n    filterToSearch: function() {\n        App.filterToSearch();\n    },\n\n    newFeed: function() {\n        var feed_url = prompt('New url to subscribe to');\n        var feed = new Feed({'url': feed_url});\n        App.feeds.add(feed);\n        feed.save();\n    },\n\n    render: function() {\n        var h = $.tmpl(templates.controls_template, { 'app': this.model.toJSON() });\n        $(this.el).html(h);\n        return this;\n    },\n\n    lightTheme: function() {\n        document.body.className = \"light\";\n        localStorage.setItem(\"theme\", \"light\");\n    },\n\n    darkTheme: function() {\n        document.body.className = \"dark\";\n        localStorage.setItem(\"theme\", \"dark\");\n    },\n\n    blackTheme: function() {\n        document.body.className = \"black\";\n        localStorage.setItem(\"theme\", \"black\");\n    },\n    \n});\n\n\n\nvar Item = Backbone.Model.extend({\n    idAttribute: \"_id\",\n    url: '/item/',\n\n    initialize: function() {\n        var p_url = this.get('url');\n        p_url = p_url.replace('https://', '');\n        p_url = p_url.replace('http://', '');\n        this.set('p_url', p_url);\n        this.bind('change', this.maybeSave);\n    },\n\n    maybeSave: function() {\n        if(this.hasChanged()) {\n            this.save();\n        }\n    },\n\n    markRead: function() {\n        // recover if not tag\n        if(this.get('read')) {\n            return;\n        }\n\n        // var t = this.get('feed').tag;\n        // var tag = App.tags.find(function(x){ return x.get('name') == t });\n        this.set('read', true);\n        // if(tag) {\n        //     tag.set('unread', tag.get('unread')-1);\n        // }\n    },\n\n    toggleStar: function() {\n        this.set({'starred': !(this.get('starred'))} );\n    },\n\n    star: function() {\n        this.set({'starred': true});\n    },\n\n    unstar: function() {\n        this.set({'starred': false});\n    },\n\n    full: function() {\n        this.set({'full': !(this.get('full'))} );\n        // this should just use this.fetch() but\n        // it kept GETing from /item instead of /item/id\n        // so just hacking this in for now\n\n        if(this.get('full_content') == \"\") {\n            $.getJSON('/item/' + this.get('_id'), function(data) {\n                var i = App.items.get(data['_id'])\n                i.set('full_content', data['full_content']);\n            });\n        }\n    }\n\n});\n\n\nvar ItemCollection = Backbone.Collection.extend({\n    model: Item,\n\n    initialize: function() {\n        _.bindAll(this, 'boot', 'reboot');\n    },\n\n    boot: function() {\n        if(App.loading) {\n            return;\n        }\n        if(App.noMore) {\n            return;\n        }\n\n        App.loading = true;\n        url = '/stream/';\n        url=url+'?foo=bar'\n        if(App.get('searchFilter')) {\n            url = url + '&q=' + App.get('searchFilter');\n        }\n        if(App.get('feedFilter')) {\n            url = url + '&feed_url=' + App.get('feedFilter').get('url');\n        }\n        if(App.get('starredFilter')) {\n            url = url + '&starred=1';\n        }\n        if(App.tag != undefined) {\n            url = url + '&tag=' + App.tag;\n        }\n        if(App.items.last()) {\n             url = url + '&max_id=' + App.items.last().get('_id');\n        }\n\n        if(App.get('allFilter') || App.get('starredFilter')) {\n            url = url + '&read_filter=all';\n        }\n\n        console.log('fetching from ', url);\n        var t = this;\n        $.getJSON(url, function(data) {\n            var items = [];\n            $.each(data, function(i,v) {\n                var item = new Item(v);\n                t.add(item);\n                items.push(item);\n                if(t.models.length==1){\n                    App.set('selectedIndex', 0);\n                }\n            });\n            // console.log(\"items \", items)\n            if(items.length == 0) {\n                // console.log(\"no more items\");\n                App.noMore = true;\n                // App.loading = true;\n            }\n            else {\n                App.loading = false;\n            }\n            // we wait and add them all at once for performance on mobile\n            App.itemListView.addAll(items);\n\n        });\n    },\n\n    reboot: function() {\n        App.noMore = false;\n        App.loading = false;\n        this.reset();\n        this.boot();\n    },\n\n\n});\nApp.items = new ItemCollection();\n\n\nvar ItemView = Backbone.View.extend({\n    tagName: \"div\",\n    className: \"item\",\n    template: templates.item_template,\n    events: {\n        \"click .star\": \"star\",\n        \"click .unstar\": \"unstar\",\n        \"click .full\": \"full\",\n    },\n\n    initialize: function() {\n        _.bindAll(this, 'render', 'star');\n        this.model.bind('change', this.render);\n    },\n\n    star: function() {\n        this.model.star();\n        this.render();\n    },\n\n    unstar: function() {\n        this.model.unstar();\n        this.render();\n    },\n\n    full: function() {\n        this.model.full();\n        this.render();\n    },\n\n    render: function() {\n        var h = $.tmpl(templates.item_template, { 'item': this.model.toJSON() });\n        $(this.el).html(h);\n        return this;\n    },\n});\n\nvar ItemListView = Backbone.View.extend( {\n    initialize: function() {\n        _.bindAll(this, 'addOne', 'addAll', 'change', 'render', 'reset');\n        // App.items.bind('add', this.addOne);\n        App.items.bind('reset', this.reset);\n    },\n    addOne: function(item) {\n        var view = new ItemView({'model': item});\n        this.$el.append(view.render().el);\n    },\n    addAll: function(items) {\n        // Posts.each(this.addOne);\n        for(i in items) {\n            item = items[i];\n            var view = new ItemView({'model': item});\n            this.$el.append(view.render().el);\n        };\n    },\n    change: function() {\n    },\n    render: function() {\n    },\n    reset: function() {\n        this.$el.children().remove();\n    }\n});\n\nvar Tag = Backbone.Model.extend({\n});\n\nvar TagCollection = Backbone.Collection.extend({\n    model: Tag,\n    initialize: function() {\n        _.bindAll(this, 'boot');\n    },\n\n    boot: function() {\n        var t = this;\n        $.getJSON('/tag/', function(data) {\n            $.each(data, function(i,v) {\n                var tag = new Tag(v);\n                t.add(tag);\n            });\n        });\n    }\n});\nApp.tags = new TagCollection();\n\n\nvar TagView = Backbone.View.extend({\n    tagName: \"li\",\t\n    className: \"tag\",\n    events: {\n        \"click\": \"filterTo\",\n    },\n    initialize: function() {\n        _.bindAll(this, 'render', 'filterTo');\n        this.model.bind('change', this.render);\n    },\n    render: function() {\n        var h = $.tmpl(templates.tag_template, { 'tag': this.model.toJSON() });\n        $(this.el).html(h);\n        return this;\n    },\n    filterTo: function() {\n        App.filterToTag(this.model);\n    }\n});\n\n\nvar TagListView = Backbone.View.extend( {\n\n    initialize: function() {\n        _.bindAll(this, 'addOne', 'addAll', 'change', 'render');\n        App.tags.bind('add', this.addOne);\n        App.tags.bind('refresh', this.addAll);\n        App.tags.bind('change', this.render);\n    },\n    addOne: function(tag) {\n        var view = new TagView({'model': tag});\n        this.$el.append(view.render().el);\n    },\n    addAll: function() {\n        App.tags.each(this.addOne);\n    },\n    change: function() {\n    },\n    render: function() {\n    },\n});\n\nApp.tag = undefined;\nApp.page = 0;\nApp.read_filter = 'unread';\n\n\nvar Feed = Backbone.Model.extend({\n    idAttribute: \"_id\",\n});\n\nvar FeedCollection = Backbone.Collection.extend({\n    model: Feed,\n    url: '/feed/',\n\n    initialize: function() {\n        ///    _.bindAll(this, 'boot');\n        //console.log('initialized');\n    },\n});\nApp.feeds = new FeedCollection();\n\nvar FeedView = Backbone.View.extend({\n    tagName: \"li\",\n    className: \"feed\",\n    events: {\n        \"click .txt\": \"filterTo\",\n        \"click .delete\": \"del\",\n        \"click .edit\": \"edit\",\n    },\n    initialize: function() {\n        _.bindAll(this, 'render', 'filterTo', \"del\");\n        this.model.bind('change', this.render);\n    },\n    render: function() {\n        var h = $.tmpl(templates.feed_template, { 'feed': this.model.toJSON() });\n        $(this.el).html(h);\n        return this;\n    },\n    filterTo: function() {\n        //        console.log('filtering to feed ', this.model);\n        App.filterToFeed(this.model);\n    },\n    del: function() {\n        if( window.confirm(\"Unsubscribe from \" + this.model.get(\"url\") + \"?\" ) ) {\n            this.model.destroy();\n            this.$el.remove();\n        }\n    },\n    edit: function() {\n        var cat = window.prompt(\"Category for this feed?\", this.model.get(\"category\"));\n        if (cat != null) {\n            this.model.set(\"category\", cat);\n            this.model.save();\n        }\n    },\n});\n\n\nvar FeedListView = Backbone.View.extend( {\n    initialize: function() {\n        _.bindAll(this, 'addOne', 'addAll', 'change', 'render');\n        App.feeds.bind('add', this.addOne);\n        App.feeds.bind('refresh', this.addAll);\n        App.feeds.bind('change', this.render);\n    },\n    addOne: function(feed) {\n        // console.log('adding a feed...', feed);\n        var view = new FeedView({'model': feed});\n        this.$el.append(view.render().el);\n    },\n    addAll: function() {\n        // console.log('feed add all...');\n        App.feeds.each(this.addOne);\n    },\n    change: function() {\n        // console.log('feeds changed add all...');\n    },\n    render: function() {\n    },\n});\n\n\n// var page = 0;\n// var read_filter = 'unread';\n\nvar selected_item = 0;\n\nfunction boot() {\n    templates['item_template'] = $('#item_template').html();\n    templates['tag_template'] = $('#tag_template').html();\n    templates['feed_template'] = $('#feed_template').html();\n    templates['controls_template'] = $('#controls_template').html();\n\n    App.itemListView = new ItemListView();\n    App.itemListView.setElement($('#items'));\n    App.tagListView = new TagListView();\n    App.tagListView.setElement($('#tags'));\n    App.feedListView = new FeedListView();\n    App.feedListView.setElement($('#feeds'));\n    App.controlsView = new ControlsView({model: App});\n    App.controlsView.setElement($('#controls'));\n    App.controlsView.render();\n\n    infini_scroll();\n\n    $('#unread_filter').on('click', function() {\n        App.read_filter = 'unread';\n        App.items.reboot();\n    });\n\n    $('#all_filter').on('click', function() {\n        App.read_filter = 'all';\n        App.items.reboot();\n    });\n\n//    $('.logo').on('click', function() {\n        //        App.set('feedFilter', undefined);\n        //        App.items.reboot();\n\n//    });\n\n    // keyboard shortcuts\n    $('body').keydown(function(event) {\n        if(document.activeElement.id == \"search\") {\n            return;\n        }\n        if (event.which == 74) {\n            event.preventDefault();\n            App.next();\n        }\n        if (event.which == 75) {\n            event.preventDefault();\n            App.previous();\n        }\n        if (event.which == 83) {\n            event.preventDefault();\n            App.star();\n        }\n        if (event.which == 70) {\n            event.preventDefault();\n            App.full();\n        }\n    });\n\n    App.boot();\n}\n\n\n// // this is legacy code\n\nfunction infini_scroll() {\n    if(App.loading) {\n    }\n    else {\n        var dh = $('#items').height() - $(window).height();\n        var st = $(window).scrollTop();\n        if  ( (dh-st) < 100   ){\n            App.items.boot();\n        }\n    }\n    window.setTimeout(infini_scroll, 1000);\n}\n\nvar ItemSelector =  {\n    selected_index: 0,\n}\n"), +		FileModTime: time.Unix(1538709815, 0), +		Content:     string("var templates = {};\n\n$(document).ready(function() {\n    if ( $(window).width() < 1024 ) {\n        $('#filters').addClass('hidden');\n    }\n    document.body.className = localStorage.getItem('theme');\n    boot();\n});\n\nvar AppModel =  Backbone.Model.extend({\n    defaults: {\n        'selectedIndex': 0,\n        'starredFilter': false,\n        'allFilter': false,\n        'unreadFilter': true,\n        'feedFilter': undefined,\n        'searchFilter': undefined\n    },\n\n    initialize: function() {\n        this.bind('change:selectedIndex', this.scroll_to_selected);\n//        this.bind('change:selectedIndex', this.scroll_to_selected)\n    },\n\n    boot: function() {\n        this.items.boot();\n        this.tags.boot();\n        this.feeds.fetch({set: true, remove: false})\n        window.setInterval(function() { App.update_read_status() }, 5000);\n    },\n\n    filterToFeed: function(feed) {\n        if (feed.get('selected')) {\n            feed.set('selected', false);\n            this.set('feedFilter', undefined);\n        }\n        else {\n            App.tags.models.forEach ( function (t) {\n                t.set('selected', false);\n            });\n            App.tag = null;\n            App.feeds.models.forEach ( function (f) {\n                f.set('selected', false);\n            });\n\n            this.set('feedFilter', feed);\n            feed.set('selected', true);\n        }\n        this.items.reboot();\n    },\n\n    filterToTag: function(tag) {\n        App.tag = null;\n        if (tag.get('selected')) {\n            tag.set('selected', false);\n        }\n        else {\n            App.tags.models.forEach ( function (t) {\n                t.set('selected', false);\n            });\n            App.feeds.models.forEach ( function (f) {\n                f.set('selected', false);\n            });\n            this.set('feedFilter', undefined);\n            tag.set('selected', true);\n            App.tag = tag.get('title');\n        }\n        App.items.reboot();\n    },\n\n    filterToStarred: function() {\n        this.set('starredFilter', true);\n        this.set('allFilter', false);\n        this.set('unreadFilter', false);\n        this.items.reboot();\n    },\n\n    filterToAll: function() {\n        this.set('searchFilter', undefined);\n        this.set('starredFilter', false);\n        this.set('allFilter', true);\n        this.set('unreadFilter', false);\n        this.items.reboot();\n    },\n\n    filterToUnread: function() {\n        this.set('starredFilter', false);\n        this.set('allFilter', false);\n        this.set('unreadFilter', true);\n        this.items.reboot();\n    },\n\n    filterToSearch: function() {\n        this.set('searchFilter', $('#search').val());\n        this.set('starredFilter', false);\n        this.set('allFilter', true);\n        this.set('unreadFilter', false);\n        this.items.reboot();\n    },\n\n    update_read_status: function() {\n        var screen_top = $(window).scrollTop();\n        var screen_bottom = $(window).scrollTop() +  $(window).height();\n\n        // // mark all visible items as read\n        $.each($('.item'), function(i,v) {\n            var item_top = $(v).offset().top;\n            // console.log(\"i \", i, \"item_top \", item_top, \"screen_top \", screen_top, \"screen_bottom \", screen_bottom);\n\n            if( (item_top < screen_top)) {\n                App.items.at(i).markRead();\n            //    console.log('marking as read: ', i);\n            }\n        });\n//        window.setTimeout(App.update_read_status, 5000);\n    },\n\n    scroll_to_selected: function() {\n        var item = $('.item').eq(this.get('selectedIndex'));\n        if(item.offset()) {\n            var item_top = item.offset().top;\n            $('.item').removeClass('selected');\n            item.addClass('selected');\n            $(window).scrollTop(item_top);\n        }\n        App.items.at(this.get('selectedIndex')).markRead();\n        if(App.items.models.length>1) {\n            if(this.get('selected')>=App.items.models.length-1) {\n                App.items.boot();\n            }\n        }\n    },\n\n    next: function() {\n        if(this.get('selectedIndex') < this.items.models.length-1) {\n            this.set('selectedIndex', this.get('selectedIndex')+1);\n        }\n        if(this.get('selectedIndex') == this.items.models.length-1) {\n            App.items.boot();\n        }\n    },\n\n    previous: function() {\n        if(this.get('selectedIndex') > 0) {\n            this.set('selectedIndex', this.get('selectedIndex')-1);\n        }\n    },\n\n    star: function() {\n        if(this.get('selectedIndex') >= 0) {\n            App.items.at(this.get('selectedIndex')).toggleStar();\n        }\n    },\n\n    full: function() {\n        if(this.get('selectedIndex') >= 0) {\n            App.items.at(this.get('selectedIndex')).full();\n        }\n    }\n\n});\nvar App = new AppModel();\n\nvar ControlsView = Backbone.View.extend({\n    className: 'controls',\n\n    events: {\n        'click .starred_filter': 'filterToStarred',\n        'click .all_filter': 'filterToAll',\n        'click .unread_filter': 'filterToUnread',\n        'click .new_feed': 'newFeed',\n        'click .search_go': 'filterToSearch',\n        'click .light_theme': 'lightTheme',\n        'click .dark_theme': 'darkTheme',\n        'click .black_theme': 'blackTheme',\n    },\n\n    initialize: function() {\n        _.bindAll(this, 'render');\n        this.model.bind('change', this.render);\n    },\n\n    filterToStarred: function() {\n        App.filterToStarred();\n    },\n\n    filterToAll: function() {\n        App.filterToAll();\n    },\n\n    filterToUnread: function() {\n        App.filterToUnread();\n    },\n\n    filterToSearch: function() {\n        App.filterToSearch();\n    },\n\n    newFeed: function() {\n        var feed_url = prompt('New url to subscribe to');\n        var feed = new Feed({'url': feed_url});\n        App.feeds.add(feed);\n        feed.save();\n    },\n\n    render: function() {\n        var h = $.tmpl(templates.controls_template, { 'app': this.model.toJSON() });\n        $(this.el).html(h);\n        return this;\n    },\n\n    lightTheme: function() {\n        document.body.className = \"light\";\n        localStorage.setItem(\"theme\", \"light\");\n    },\n\n    darkTheme: function() {\n        document.body.className = \"dark\";\n        localStorage.setItem(\"theme\", \"dark\");\n    },\n\n    blackTheme: function() {\n        document.body.className = \"black\";\n        localStorage.setItem(\"theme\", \"black\");\n    },\n    \n});\n\n\n\nvar Item = Backbone.Model.extend({\n    idAttribute: \"_id\",\n    url: '/item/',\n\n    initialize: function() {\n        var p_url = this.get('url');\n        p_url = p_url.replace('https://', '');\n        p_url = p_url.replace('http://', '');\n        this.set('p_url', p_url);\n        this.bind('change', this.maybeSave);\n    },\n\n    maybeSave: function() {\n        if(this.hasChanged()) {\n            this.save();\n        }\n    },\n\n    markRead: function() {\n        // recover if not tag\n        if(this.get('read')) {\n            return;\n        }\n\n        // var t = this.get('feed').tag;\n        // var tag = App.tags.find(function(x){ return x.get('name') == t });\n        this.set('read', true);\n        // if(tag) {\n        //     tag.set('unread', tag.get('unread')-1);\n        // }\n    },\n\n    toggleStar: function() {\n        this.set({'starred': !(this.get('starred'))} );\n    },\n\n    star: function() {\n        this.set({'starred': true});\n    },\n\n    unstar: function() {\n        this.set({'starred': false});\n    },\n\n    full: function() {\n        this.set({'full': !(this.get('full'))} );\n        // this should just use this.fetch() but\n        // it kept GETing from /item instead of /item/id\n        // so just hacking this in for now\n\n        if(this.get('full_content') == \"\") {\n            $.getJSON('/item/' + this.get('_id'), function(data) {\n                var i = App.items.get(data['_id'])\n                i.set('full_content', data['full_content']);\n            });\n        }\n    }\n\n});\n\n\nvar ItemCollection = Backbone.Collection.extend({\n    model: Item,\n\n    initialize: function() {\n        _.bindAll(this, 'boot', 'reboot');\n    },\n\n    boot: function() {\n        if(App.loading) {\n            return;\n        }\n        if(App.noMore) {\n            return;\n        }\n\n        App.loading = true;\n        url = '/stream/';\n        url=url+'?foo=bar'\n        if(App.get('searchFilter')) {\n            url = url + '&q=' + App.get('searchFilter');\n        }\n        if(App.get('feedFilter')) {\n            url = url + '&feed_url=' + App.get('feedFilter').get('url');\n        }\n        if(App.get('starredFilter')) {\n            url = url + '&starred=1';\n        }\n        if(App.tag != undefined) {\n            url = url + '&tag=' + App.tag;\n        }\n        if(App.items.last()) {\n             url = url + '&max_id=' + App.items.last().get('_id');\n        }\n\n        if(App.get('allFilter') || App.get('starredFilter')) {\n            url = url + '&read_filter=all';\n        }\n\n        console.log('fetching from ', url);\n        var t = this;\n        $.getJSON(url, function(data) {\n            var items = [];\n            $.each(data, function(i,v) {\n                var item = new Item(v);\n                t.add(item);\n                items.push(item);\n                if(t.models.length==1){\n                    App.set('selectedIndex', 0);\n                }\n            });\n            // console.log(\"items \", items)\n            if(items.length == 0) {\n                // console.log(\"no more items\");\n                App.noMore = true;\n                // App.loading = true;\n            }\n            else {\n                App.loading = false;\n            }\n            // we wait and add them all at once for performance on mobile\n            App.itemListView.addAll(items);\n\n        });\n    },\n\n    reboot: function() {\n        App.noMore = false;\n        App.loading = false;\n        this.reset();\n        this.boot();\n    },\n\n\n});\nApp.items = new ItemCollection();\n\n\nvar ItemView = Backbone.View.extend({\n    tagName: \"div\",\n    className: \"item\",\n    template: templates.item_template,\n    events: {\n        \"click .star\": \"star\",\n        \"click .unstar\": \"unstar\",\n        \"click .full\": \"full\",\n    },\n\n    initialize: function() {\n        _.bindAll(this, 'render', 'star');\n        this.model.bind('change', this.render);\n    },\n\n    star: function() {\n        this.model.star();\n        this.render();\n    },\n\n    unstar: function() {\n        this.model.unstar();\n        this.render();\n    },\n\n    full: function() {\n        this.model.full();\n        this.render();\n    },\n\n    render: function() {\n        var h = $.tmpl(templates.item_template, { 'item': this.model.toJSON() });\n        $(this.el).html(h);\n        return this;\n    },\n});\n\nvar ItemListView = Backbone.View.extend( {\n    initialize: function() {\n        _.bindAll(this, 'addOne', 'addAll', 'change', 'render', 'reset');\n        // App.items.bind('add', this.addOne);\n        App.items.bind('reset', this.reset);\n    },\n    addOne: function(item) {\n        var view = new ItemView({'model': item});\n        this.$el.append(view.render().el);\n    },\n    addAll: function(items) {\n        // Posts.each(this.addOne);\n        for(i in items) {\n            item = items[i];\n            var view = new ItemView({'model': item});\n            this.$el.append(view.render().el);\n        };\n    },\n    change: function() {\n    },\n    render: function() {\n    },\n    reset: function() {\n        this.$el.children().remove();\n    }\n});\n\nvar Tag = Backbone.Model.extend({\n});\n\nvar TagCollection = Backbone.Collection.extend({\n    model: Tag,\n    initialize: function() {\n        _.bindAll(this, 'boot');\n    },\n\n    boot: function() {\n        var t = this;\n        $.getJSON('/tag/', function(data) {\n            $.each(data, function(i,v) {\n                var tag = new Tag(v);\n                t.add(tag);\n            });\n        });\n    }\n});\nApp.tags = new TagCollection();\n\n\nvar TagView = Backbone.View.extend({\n    tagName: \"li\",\t\n    className: \"tag\",\n    events: {\n        \"click\": \"filterTo\",\n    },\n    initialize: function() {\n        _.bindAll(this, 'render', 'filterTo');\n        this.model.bind('change', this.render);\n    },\n    render: function() {\n        var h = $.tmpl(templates.tag_template, { 'tag': this.model.toJSON() });\n        $(this.el).html(h);\n        return this;\n    },\n    filterTo: function() {\n        App.filterToTag(this.model);\n    }\n});\n\n\nvar TagListView = Backbone.View.extend( {\n\n    initialize: function() {\n        _.bindAll(this, 'addOne', 'addAll', 'change', 'render');\n        App.tags.bind('add', this.addOne);\n        App.tags.bind('refresh', this.addAll);\n        App.tags.bind('change', this.render);\n    },\n    addOne: function(tag) {\n        var view = new TagView({'model': tag});\n        this.$el.append(view.render().el);\n    },\n    addAll: function() {\n        App.tags.each(this.addOne);\n    },\n    change: function() {\n    },\n    render: function() {\n    },\n});\n\nApp.tag = undefined;\nApp.page = 0;\nApp.read_filter = 'unread';\n\n\nvar Feed = Backbone.Model.extend({\n    idAttribute: \"_id\",\n});\n\nvar FeedCollection = Backbone.Collection.extend({\n    model: Feed,\n    url: '/feed/',\n\n    initialize: function() {\n        ///    _.bindAll(this, 'boot');\n        //console.log('initialized');\n    },\n});\nApp.feeds = new FeedCollection();\n\nvar FeedView = Backbone.View.extend({\n    tagName: \"li\",\n    className: \"feed\",\n    events: {\n        \"click .txt\": \"filterTo\",\n        \"click .delete\": \"del\",\n        \"click .edit\": \"edit\",\n    },\n    initialize: function() {\n        _.bindAll(this, 'render', 'filterTo', \"del\");\n        this.model.bind('change', this.render);\n    },\n    render: function() {\n        var h = $.tmpl(templates.feed_template, { 'feed': this.model.toJSON() });\n        $(this.el).html(h);\n        return this;\n    },\n    filterTo: function() {\n        //        console.log('filtering to feed ', this.model);\n        App.filterToFeed(this.model);\n    },\n    del: function() {\n        if( window.confirm(\"Unsubscribe from \" + this.model.get(\"url\") + \"?\" ) ) {\n            this.model.destroy();\n            this.$el.remove();\n        }\n    },\n    edit: function() {\n        var cat = window.prompt(\"Category for this feed?\", this.model.get(\"category\"));\n        if (cat != null) {\n            this.model.set(\"category\", cat);\n            this.model.save();\n        }\n    },\n});\n\n\nvar FeedListView = Backbone.View.extend( {\n    initialize: function() {\n        _.bindAll(this, 'addOne', 'addAll', 'change', 'render');\n        App.feeds.bind('add', this.addOne);\n        App.feeds.bind('refresh', this.addAll);\n        App.feeds.bind('change', this.render);\n    },\n    addOne: function(feed) {\n        // console.log('adding a feed...', feed);\n        var view = new FeedView({'model': feed});\n        this.$el.append(view.render().el);\n    },\n    addAll: function() {\n        // console.log('feed add all...');\n        App.feeds.each(this.addOne);\n    },\n    change: function() {\n        // console.log('feeds changed add all...');\n    },\n    render: function() {\n    },\n});\n\n\n// var page = 0;\n// var read_filter = 'unread';\n\nvar selected_item = 0;\n\nfunction boot() {\n    templates['item_template'] = $('#item_template').html();\n    templates['tag_template'] = $('#tag_template').html();\n    templates['feed_template'] = $('#feed_template').html();\n    templates['controls_template'] = $('#controls_template').html();\n\n    App.itemListView = new ItemListView();\n    App.itemListView.setElement($('#items'));\n    App.tagListView = new TagListView();\n    App.tagListView.setElement($('#tags'));\n    App.feedListView = new FeedListView();\n    App.feedListView.setElement($('#feeds'));\n    App.controlsView = new ControlsView({model: App});\n    App.controlsView.setElement($('#controls'));\n    App.controlsView.render();\n\n    infini_scroll();\n\n    $('#unread_filter').on('click', function() {\n        App.read_filter = 'unread';\n        App.items.reboot();\n    });\n\n    $('#all_filter').on('click', function() {\n        App.read_filter = 'all';\n        App.items.reboot();\n    });\n\n//    $('.logo').on('click', function() {\n        //        App.set('feedFilter', undefined);\n        //        App.items.reboot();\n\n//    });\n\n    // keyboard shortcuts\n    $('body').keydown(function(event) {\n        if(document.activeElement.id == \"search\") {\n            return;\n        }\n        if (event.which == 74) {\n            event.preventDefault();\n            App.next();\n        }\n        if (event.which == 75) {\n            event.preventDefault();\n            App.previous();\n        }\n        if (event.which == 83) {\n            event.preventDefault();\n            App.star();\n        }\n    });\n\n    App.boot();\n}\n\n\n// // this is legacy code\n\nfunction infini_scroll() {\n    if(App.loading) {\n    }\n    else {\n        var dh = $('#items').height() - $(window).height();\n        var st = $(window).scrollTop();\n        if  ( (dh-st) < 100   ){\n            App.items.boot();\n        }\n    }\n    window.setTimeout(infini_scroll, 1000);\n}\n\nvar ItemSelector =  {\n    selected_index: 0,\n}\n"),  	}  	filee := &embedded.EmbeddedFile{  		Filename:    "underscore-1.8.3.min.js", @@ -82,7 +82,7 @@ func init() {  	// define dirs  	dir1 := &embedded.EmbeddedDir{  		Filename:   "", -		DirModTime: time.Unix(1538242832, 0), +		DirModTime: time.Unix(1538709815, 0),  		ChildFiles: []*embedded.EmbeddedFile{  			file2, // ".DS_Store"  			file3, // "backbone-1.3.3.min.js" @@ -108,7 +108,7 @@ func init() {  	// register embeddedBox  	embedded.RegisterEmbeddedBox(`../static`, &embedded.EmbeddedBox{  		Name: `../static`, -		Time: time.Unix(1538242832, 0), +		Time: time.Unix(1538709815, 0),  		Dirs: map[string]*embedded.EmbeddedDir{  			"": dir1,  		},  | 
