/** * ownCloud - News app * * @author Bernhard Posselt * Copyright (c) 2012 - Bernhard Posselt * * This file is licensed under the Affero General Public License version 3 or later. * See the COPYING-README file * */ /** * This file includes the cache and access for feed items */ var News = News || {}; (function(){ /*########################################################################## * Items *########################################################################*/ /** * Creates a new item instance and tells it to put the items into * the selected div * @param cssSelector the selector of the div which holds the ul for the feeds */ var Items = function(cssSelector){ var self = this; this._$articleList = $(cssSelector); this._$articleList.scrollTop(0); this._$articleList.children('ul').children('.feed_item:eq(0)').addClass('viewed'); this._itemCache = new ItemCache(); this._setScrollBottom(); $(window).resize(function(){ self._setScrollBottom(); }); // mark items whose title was hid under the top edge as read this._scrollTimeoutMiliSecs = 100; this._markReadTimeoutMiliSecs = 1000; this._isScrolling = false; this._$articleList.scroll(function(){ // prevent too many scroll requests; if(!self._isScrolling){ self._isScrolling = true; setTimeout(function(){ self._isScrolling = false; }, self._scrollTimeoutMiliSecs); $(this).children('ul').children('.feed_item:not(.read)').each(function(){ var item = this; var itemOffset = $(item).position().top; if(itemOffset <= 0){ setTimeout(function(){ self._markItemAsReadTimeout(item); }, self._markReadTimeoutMiliSecs); } }); self._markCurrentlyViewed(); } }); this._itemCache.populate(this._$articleList.children('ul')); } /** * Marks an item as read which is called by the timeout * @param item the dom item */ Items.prototype._markItemAsReadTimeout = function(item) { var itemId = parseInt($(item).data('id')); var itemOffset = $(item).position().top; var item = this._itemCache.getItem(itemId); if(itemOffset < 0){ if(!item.isLocked()){ // lock item to prevent massive request when scrolling item.setLocked(true); item.setRead(true); } } }; /** * Loads the feeds into the righ view * @param type the type (MenuNodeType) * @param id the id * @param onSuccessCallback a callback that is executed when the loading succeeded */ Items.prototype.load = function(type, id, onSuccessCallback) { var self = this; var data = { id: id, type: type, mostRecentItemId: this._itemCache.getMostRecentItemId(type, id) }; this._$articleList.addClass('loading'); this._$articleList.children('ul').hide(); $.post(OC.filePath('news', 'ajax', 'loadfeed.php'), data, function(jsonData) { if(jsonData.status == 'success'){ self._$articleList.empty() // FIXME: does this also removed cached items? self._itemCache.populate(jsonData.data.feedItems); var $items = self._itemCache.getFeedHtml(type, id); self._$articleList.append($items); self._$articleList.scrollTop(0); onSuccessCallback(); } else { OC.dialogs.alert(t('news', 'Error while loading the feed'), t('news', 'Error')); self._$articleList.children('ul').show(); } self._$articleList.removeClass('loading'); self._setScrollBottom(); }); }; /** * Jumps to the next visible element */ Items.prototype.jumpToNext = function() { var self = this; var notJumped = true; $('.feed_item').each(function(){ if(notJumped && $(this).position().top > 1){ var id = parseInt($(this).data('id')); self._jumpToElemenId(id); notJumped = false; } }); }; /** * Jumps to the previous visible element */ Items.prototype.jumpToPrevious = function() { var self = this; var notJumped = true; $('.feed_item').each(function(){ if(notJumped && $(this).position().top >= 0){ var previous = $(this).prev(); if(previous.length > 0){ var id = parseInt(previous.data('id')); self._jumpToElemenId(id); } notJumped = false; } }); // in case we scroll more than the last element, just jump back to the // last one if(notJumped){ var $items = $('.feed_item'); if($items.length > 0){ var id = parseInt($items.last().data('id')); self._jumpToElemenId(id); } } }; /** * Empties the item cache */ Items.prototype.emptyItemCache = function() { this._itemCache.empty(); }; /** * Marks all items of a feed as read * @param type the type (MenuNodeType) * @param id the id */ Items.prototype.markAllRead = function(type, id) { this._itemCache.markAllRead(type, id); }; /** * Returns the most recent id of a feed from the cache * @param type the type (MenuNodeType) * @param id the id * @return the most recent id that is loaded on the page or 0 */ Items.prototype.getMostRecentItemId = function(type, id) { return this._itemCache.getMostRecentItemId(type, id); }; /** * Jumps to an element in the article list * @param number the number of the item starting with 0 */ Items.prototype._jumpToElemenId = function(id) { $elem = $('.feed_item[data-id=' + id + ']'); this._$articleList.scrollTop( $elem.offset().top - this._$articleList.offset().top + this._$articleList.scrollTop()); this._markCurrentlyViewed(); }; /** * Adds padding to the bottom to be able to scroll the last element beyond * the top area */ Items.prototype._setScrollBottom = function() { var padding = this._$articleList.height() - 80; this._$articleList.children('ul').css('padding-bottom', padding + 'px'); }; /** * Returns a jquery node by searching for its id * @param id the id of the node * @return the jquery node */ Items.prototype._findNodeById = function(id) { id = parseInt(id); return this._$articleList.find('.feed_item[data-id="' + id + '"]'); }; /** * Marks the currently viewed element as viewed */ Items.prototype._markCurrentlyViewed = function() { var self = this; $('.viewed').removeClass('viewed'); var notFound = true; $('.feed_item').each(function(){ var visiblePx = Math.ceil($(this).position().top + $(this).outerHeight()); if(notFound && visiblePx > 90){ $(this).addClass('viewed'); notFound = false; } }); }; News.Items = Items; /*########################################################################## * ItemCache *########################################################################*/ /** * A cache which holds the items of all loaded feeds */ var ItemCache = function() { this._items = {}; this._feeds = {}; } /** * Returns an item from the cache */ ItemCache.prototype.getItem = function(itemId) { itemId = parseInt(itemId); return this._items[itemId]; }; /** * Marks all items of a feed as read * @param type the type (MenuNodeType) * @param id the id */ ItemCache.prototype.markAllRead = function(type, id) { var ids = this._getItemIdTimestampPairs(type, id); for(var i=0; i'); for(var i=0; i