From ae7393db3d99a7ac223ae917129cccd9f49888e3 Mon Sep 17 00:00:00 2001 From: Bernhard Posselt Date: Sun, 27 Jan 2013 04:15:53 +0100 Subject: merged the angularjs branch --- coffee/Cakefile | 135 +++++++++++++ coffee/app.coffee | 38 ++++ coffee/controllers/controller.coffee | 14 ++ coffee/controllers/controllers.coffee | 42 ++++ coffee/controllers/feedcontroller.coffee | 223 +++++++++++++++++++++ coffee/controllers/itemcontroller.coffee | 93 +++++++++ coffee/controllers/settingscontroller.coffee | 127 ++++++++++++ coffee/directives/draggable.coffee | 23 +++ coffee/directives/droppable.coffee | 33 +++ coffee/directives/feednavigation.coffee | 66 ++++++ coffee/directives/hidesettingswhenfocuslost.coffee | 25 +++ coffee/directives/onenter.coffee | 20 ++ coffee/directives/readfile.coffee | 33 +++ coffee/directives/whenscrolled.coffee | 52 +++++ coffee/filters/feedInFolder.coffee | 18 ++ coffee/services/activefeed.coffee | 24 +++ coffee/services/cache.coffee | 149 ++++++++++++++ coffee/services/feedmodel.coffee | 31 +++ coffee/services/feedtype.coffee | 18 ++ coffee/services/foldermodel.coffee | 21 ++ coffee/services/garbageregistry.coffee | 38 ++++ coffee/services/itemmodel.coffee | 97 +++++++++ coffee/services/loading.coffee | 17 ++ coffee/services/model.coffee | 80 ++++++++ coffee/services/opmlparser.coffee | 67 +++++++ coffee/services/persistence.coffee | 73 +++++++ coffee/services/persitencenews.coffee | 150 ++++++++++++++ coffee/services/publisher.coffee | 30 +++ coffee/services/services.coffee | 90 +++++++++ coffee/services/showall.coffee | 23 +++ coffee/services/starredcount.coffee | 24 +++ 31 files changed, 1874 insertions(+) create mode 100644 coffee/Cakefile create mode 100644 coffee/app.coffee create mode 100644 coffee/controllers/controller.coffee create mode 100644 coffee/controllers/controllers.coffee create mode 100644 coffee/controllers/feedcontroller.coffee create mode 100644 coffee/controllers/itemcontroller.coffee create mode 100644 coffee/controllers/settingscontroller.coffee create mode 100644 coffee/directives/draggable.coffee create mode 100644 coffee/directives/droppable.coffee create mode 100644 coffee/directives/feednavigation.coffee create mode 100644 coffee/directives/hidesettingswhenfocuslost.coffee create mode 100644 coffee/directives/onenter.coffee create mode 100644 coffee/directives/readfile.coffee create mode 100644 coffee/directives/whenscrolled.coffee create mode 100644 coffee/filters/feedInFolder.coffee create mode 100644 coffee/services/activefeed.coffee create mode 100644 coffee/services/cache.coffee create mode 100644 coffee/services/feedmodel.coffee create mode 100644 coffee/services/feedtype.coffee create mode 100644 coffee/services/foldermodel.coffee create mode 100644 coffee/services/garbageregistry.coffee create mode 100644 coffee/services/itemmodel.coffee create mode 100644 coffee/services/loading.coffee create mode 100644 coffee/services/model.coffee create mode 100644 coffee/services/opmlparser.coffee create mode 100644 coffee/services/persistence.coffee create mode 100644 coffee/services/persitencenews.coffee create mode 100644 coffee/services/publisher.coffee create mode 100644 coffee/services/services.coffee create mode 100644 coffee/services/showall.coffee create mode 100644 coffee/services/starredcount.coffee (limited to 'coffee') diff --git a/coffee/Cakefile b/coffee/Cakefile new file mode 100644 index 000000000..878a4f821 --- /dev/null +++ b/coffee/Cakefile @@ -0,0 +1,135 @@ +### +# ownCloud +# +# @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 +# +### + +# imports +fs = require 'fs' +util = require 'util' +{exec} = require 'child_process' + + +### Configuration ### + +# path that contains your coffee files +sourceDirectory = __dirname + +# path and name of the compiled js file relative to this script without .js suffix +compileToFile = '../js/app' + +# These files will be compiled in order before any other files +compileFirst = [ + 'app.coffee' +] + + +### Functions ### + +# Recursively searches for coffee files +# @param string path: the path to search for coffee files +# @param array coffeeFiles: a hashmap with existing files that will be ignored +getCoffeeFiles = (path, coffeeFiles) -> + entries = fs.readdirSync(path) + + for entry in entries + do (entry) -> + entryPath = path + '/' + entry + entryStats = fs.statSync(entryPath) + + if entryStats.isFile() + if entryPath.indexOf('.coffee') > 1 and coffeeFiles[entryPath] == undefined + coffeeFiles[entryPath] = true + + else if entryStats.isDirectory() + getCoffeeFiles(entryPath, coffeeFiles) + + +# returns an array with all coffeefiles in order +getOrderedCoffeeFiles = (directory) -> + unorderedFiles = {} + getCoffeeFiles(directory, unorderedFiles) + + # create data structures for quick prioritized files lookup + orderedFilesHashMap = {} + for file in compileFirst + filePath = directory + '/' + file + orderedFilesHashMap[filePath] = true + + # prepend prioritized files + orderedFiles = [] + for file in compileFirst + orderedFiles.push(directory + '/' + file) + + # order files + for file, exists of unorderedFiles + if orderedFilesHashMap[file] == undefined + orderedFiles.push(file) + + util.log "#{orderedFiles.length} coffee files found." + return orderedFiles + + +# compiles an array with file content to a js file +compile = (content, toFile) -> + toFile += '.coffee' + + fs.writeFile toFile, content.join('\n\n'), 'utf8', (error) -> + if error + throw error + + exec 'coffee --compile ' + toFile, (error, stdout, stderr) -> + if error + util.log 'Error compiling coffee file.' + util.error error + else + fs.unlink toFile, (error) -> + if error + util.log 'Couldn\'t delete the compile cache file ' + toFile + util.log 'Finished building coffee file.' + + +# register a callback on an array of files and remove already bound ones +watchFiles = (files, callback) -> + files = {} + getCoffeeFiles(sourceDirectory, files) + for file, exists of files + fs.unwatchFile(file) + fs.watchFile(file, callback) + + +### Tasks ### + +task 'watch', 'Watch and rebuild on changes', -> + invoke 'build' + util.log "Watching for changes" + + watchFiles getOrderedCoffeeFiles(sourceDirectory), -> + invoke 'build' + + fs.watchFile sourceDirectory, (current, previous) -> + watchFiles getOrderedCoffeeFiles(sourceDirectory), -> + invoke 'build' + + +task 'build', 'Build and compress CoffeeScript into single JavaScript file', -> + files = getOrderedCoffeeFiles(sourceDirectory) + content = [] + remaining = files.length + + # read out content of files and compile them afterwards + for file, index in files then do (file, index) -> + fs.readFile file, 'utf8', (error, fileContent) -> + if error + throw error + + content[index] = fileContent + remaining -= 1 + + if remaining <= 0 + compile(content, compileToFile) diff --git a/coffee/app.coffee b/coffee/app.coffee new file mode 100644 index 000000000..fb3589dbd --- /dev/null +++ b/coffee/app.coffee @@ -0,0 +1,38 @@ +### +# 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 +# +### + +app = angular.module('News', []).config ($provide) -> + # enter your config values in here + config = + MarkReadTimeout: 500 + ScrollTimeout: 500 + initialLoadedItemsNr: 20 + FeedUpdateInterval: 6000000 + + $provide.value('Config', config) + + +app.run ['PersistenceNews', (PersistenceNews) -> + PersistenceNews.loadInitial() +] + + +$(document).ready -> + # this is used to forces browser to reload content after refreshing + # and thus clearing the scroll cache + $(this).keyup (e) -> + if (e.which == 116) || (e.which == 82 && e.ctrlKey) + document.location.reload(true); + return false + + # click on upload button should trigger the file input + $('#browselink').click -> + $('#file_upload_start').trigger('click'); diff --git a/coffee/controllers/controller.coffee b/coffee/controllers/controller.coffee new file mode 100644 index 000000000..456f60675 --- /dev/null +++ b/coffee/controllers/controller.coffee @@ -0,0 +1,14 @@ +### +# 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 +# +### + +angular.module('News').factory 'Controller', -> + class Controller + constructor: () -> diff --git a/coffee/controllers/controllers.coffee b/coffee/controllers/controllers.coffee new file mode 100644 index 000000000..7e33962b5 --- /dev/null +++ b/coffee/controllers/controllers.coffee @@ -0,0 +1,42 @@ +### +# 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 +# +### + +angular.module('News').controller 'SettingsController', +['_SettingsController', '$scope', '$rootScope', 'ShowAll', 'PersistenceNews', +'FolderModel', 'FeedModel', 'OPMLParser', +(_SettingsController, $scope, $rootScope, ShowAll, PersistenceNews, +FolderModel, FeedModel, OPMLParser) -> + return new _SettingsController($scope, $rootScope, ShowAll, + PersistenceNews, FolderModel, FeedModel, + OPMLParser) +] + +angular.module('News').controller 'ItemController', +['_ItemController', '$scope', 'ItemModel', 'ActiveFeed', 'PersistenceNews', 'FeedModel', +'StarredCount', 'GarbageRegistry', 'ShowAll', 'Loading', '$rootScope', 'FeedType', +(_ItemController, $scope, ItemModel, ActiveFeed, PersistenceNews, FeedModel, +StarredCount, GarbageRegistry, ShowAll, Loading, $rootScope, FeedType) -> + return new _ItemController($scope, ItemModel, ActiveFeed, PersistenceNews + FeedModel, StarredCount, GarbageRegistry, + ShowAll, Loading, $rootScope, FeedType) +] + +angular.module('News').controller 'FeedController', +['_FeedController', '$scope', 'FeedModel', 'FeedType', 'FolderModel', 'ActiveFeed', 'PersistenceNews', +'StarredCount', 'ShowAll', 'ItemModel', 'GarbageRegistry', '$rootScope', 'Loading', +'Config', +(_FeedController, $scope, FeedModel, FeedType, FolderModel, ActiveFeed, PersistenceNews +StarredCount, ShowAll, ItemModel, GarbageRegistry, $rootScope, Loading, Config) -> + return new _FeedController($scope, FeedModel, FolderModel, FeedType, + ActiveFeed, PersistenceNews, StarredCount, ShowAll, + ItemModel, GarbageRegistry, $rootScope, Loading, + Config) +] \ No newline at end of file diff --git a/coffee/controllers/feedcontroller.coffee b/coffee/controllers/feedcontroller.coffee new file mode 100644 index 000000000..a54f4451d --- /dev/null +++ b/coffee/controllers/feedcontroller.coffee @@ -0,0 +1,223 @@ +### +# 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 +# +### + +angular.module('News').factory '_FeedController', ['Controller', (Controller) -> + + class FeedController extends Controller + + constructor: (@$scope, @feedModel, @folderModel, @feedType, @activeFeed, + @persistence, @starredCount, @showAll, @itemModel, + @garbageRegistry, @$rootScope, @loading, @config) -> + + @showSubscriptions = true + + @$scope.feeds = @feedModel.getItems() + @$scope.folders = @folderModel.getItems() + @$scope.feedType = @feedType + + @$scope.toggleFolder = (folderId) => + folder = @folderModel.getItemById(folderId) + folder.open = !folder.open + @persistence.collapseFolder(folder.id, folder.open) + + + @$scope.isFeedActive = (type, id) => + if type == @activeFeed.type && id == @activeFeed.id + return true + else + return false + + + @$scope.loadFeed = (type, id) => + @loadFeed(type, id) + + + @$scope.getUnreadCount = (type, id) => + count = @getUnreadCount(type, id) + if count > 999 + return "999+" + else + return count + + + @$scope.renameFolder = -> + alert 'not implemented yet, needs better solution' + + + @$scope.triggerHideRead = => + @triggerHideRead() + + + @$scope.isShown = (type, id) => + switch type + when @feedType.Subscriptions then return @showSubscriptions + when @feedType.Starred then return @starredCount.count > 0 + + + @$scope.delete = (type, id) => + switch type + when @feedType.Folder + @folderModel.removeById(id) + @persistence.deleteFolder(id) + + when @feedType.Feed + @feedModel.removeById(id) + @persistence.deleteFeed(id) + + + @$scope.markAllRead = (type, id) => + switch type + when @feedType.Feed + for itemId, item of @itemModel.getItemsByTypeAndId(type, id) + item.isRead = true + feed = @feedModel.getItemById(id) + feed.unreadCount = 0 + mostRecentItemId = @itemModel.getHighestId(type, id) + @persistence.setAllItemsRead(feed.id, mostRecentItemId) + + when @feedType.Folder + for itemId, item of @itemModel.getItemsByTypeAndId(type, id) + item.isRead = true + for feedId in @itemModel.getFeedsOfFolderId(id) + feed = @feedModel.getItemById(feedId) + feed.unreadCount = 0 + mostRecentItemId = @itemModel.getHighestId(type, feedId) + @persistence.setAllItemsRead(feedId, mostRecentItemId) + + when @feedType.Subscriptions + for itemId, item of @itemModel.getItemsByTypeAndId(type, id) + item.isRead = true + for feed in @feedModel.getItems() + feed.unreadCount = 0 + mostRecentItemId = @itemModel.getHighestId(type, feed.id) + @persistence.setAllItemsRead(feed.id, mostRecentItemId) + + @$scope.$on 'triggerHideRead', => + @itemModel.clearCache() + @triggerHideRead() + @loadFeed(activeFeed.type, activeFeed.id) + + @$scope.$on 'loadFeed', (scope, params) => + @loadFeed(params.type, params.id) + + @$scope.$on 'moveFeedToFolder', (scope, params) => + @moveFeedToFolder(params.feedId, params.folderId) + + setInterval => + @updateFeeds() + , @config.FeedUpdateInterval + + + + updateFeeds: -> + for feed in @feedModel.getItems() + @persistence.updateFeed(feed.id) + + + moveFeedToFolder: (feedId, folderId) -> + feed = @feedModel.getItemById(feedId) + if feed.folderId != folderId + feed.folderId = folderId + @feedModel.markAccessed() + @persistence.moveFeedToFolder(feedId, folderId) + + + loadFeed: (type, id) -> + # to not go crazy with autopaging, clear the caches if we switch the + # type of the feed. if the caches only contain seperate feeds, the + # cache and autopage logic works fine. if the feed contains more than + # one + if type != @activeFeed.type or id != @activeFeed.id + if not (type == @feedType.Feed && @activeFeed.type == @feedType.Feed) + @itemModel.clearCache() + + @activeFeed.id = id + @activeFeed.type = type + @$scope.triggerHideRead() + @persistence.loadFeed(type, id, + @itemModel.getHighestId(type, id), + @itemModel.getHighestTimestamp(type, id), @config.initialLoadedItemsNr) + + + triggerHideRead: () -> + preventParentFolder = 0 + + # feeds + for feed in @feedModel.getItems() + if @showAll.showAll == false && @getUnreadCount(@feedType.Feed, feed.id) == 0 + + # we dont hide the selected feed and folder. But we also dont hide + # the parent folder of the selcted feed + if @activeFeed.type == @feedType.Feed && @activeFeed.id == feed.id + feed.show = true + preventParentFolder = feed.folderId + else + feed.show = false + else + feed.show = true + + # folders + for folder in @folderModel.getItems() + if @showAll.showAll == false && @getUnreadCount(@feedType.Folder, folder.id) == 0 + # prevent hiding when childfeed is active + if (@activeFeed.type == @feedType.Folder && @activeFeed.id == folder.id) || preventParentFolder == folder.id + folder.show = true + else + folder.show = false + else + folder.show = true + + # subscriptions + if @showAll.showAll == false && @getUnreadCount(@feedType.Subscriptions, 0) == 0 + if @activeFeed.type == @feedType.Subscriptions + @showSubscriptions = true + else + @showSubscriptions = false + else + @showSubscriptions = true + + # starred + if @showAll.showAll == false && @getUnreadCount(@feedType.Starred, 0) == 0 + if @activeFeed.type == @feedType.Starred + @showStarred = true + else + @showStarred = false + else + @showStarred = true + + @garbageRegistry.clear() + + + + getUnreadCount: (type, id) -> + switch type + when @feedType.Feed + return @feedModel.getItemById(id).unreadCount + + when @feedType.Folder + counter = 0 + for feed in @feedModel.getItems() + if feed.folderId == id + counter += feed.unreadCount + return counter + + when @feedType.Starred + return @starredCount.count + + when @feedType.Subscriptions + counter = 0 + for feed in @feedModel.getItems() + counter += feed.unreadCount + return counter + + + return FeedController +] \ No newline at end of file diff --git a/coffee/controllers/itemcontroller.coffee b/coffee/controllers/itemcontroller.coffee new file mode 100644 index 000000000..b3a7c9fe9 --- /dev/null +++ b/coffee/controllers/itemcontroller.coffee @@ -0,0 +1,93 @@ +### +# 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 +# +### + +angular.module('News').factory '_ItemController', ['Controller', (Controller) -> + + class ItemController extends Controller + + constructor: (@$scope, @itemModel, @activeFeed, @persistence, @feedModel, + @starredCount, @garbageRegistry, @showAll, @loading + @$rootScope, @feedType) -> + + @batchSize = 4 + @loaderQueue = 0 + + @$scope.getItems = (type, id) => + return @itemModel.getItemsByTypeAndId(type, id) + + @$scope.items = @itemModel.getItems() + @$scope.loading = @loading + + + @$scope.scroll = => + + @$scope.activeFeed = @activeFeed + + @$scope.$on 'read', (scope, params) => + @$scope.markRead(params.id, params.feed) + + + @$scope.loadFeed = (feedId) => + params = + id: feedId + type: @feedType.Feed + @$rootScope.$broadcast 'loadFeed', params + + + @$scope.markRead = (itemId, feedId) => + item = @itemModel.getItemById(itemId) + feed = @feedModel.getItemById(feedId) + + if not item.keptUnread && !item.isRead + item.isRead = true + feed.unreadCount -= 1 + + # this item will be completely deleted if showAll is false + if not @showAll.showAll + @garbageRegistry.register(item) + + @persistence.markRead(itemId, true) + + + @$scope.keepUnread = (itemId, feedId) => + item = @itemModel.getItemById(itemId) + feed = @feedModel.getItemById(feedId) + + + item.keptUnread = !item.keptUnread + + if item.isRead + item.isRead = false + feed.unreadCount += 1 + + @persistence.markRead(itemId, false) + + + @$scope.isKeptUnread = (itemId) => + return @itemModel.getItemById(itemId).keptUnread + + + @$scope.toggleImportant = (itemId) => + item = @itemModel.getItemById(itemId) + + # cache + @itemModel.setImportant(itemId, !item.isImportant) + + if item.isImportant + @starredCount.count += 1 + else + @starredCount.count -= 1 + + @persistence.setImportant(itemId, item.isImportant) + + + return ItemController +] \ No newline at end of file diff --git a/coffee/controllers/settingscontroller.coffee b/coffee/controllers/settingscontroller.coffee new file mode 100644 index 000000000..ba72bb718 --- /dev/null +++ b/coffee/controllers/settingscontroller.coffee @@ -0,0 +1,127 @@ +### +# 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 +# +### + +angular.module('News').factory '_SettingsController', ['Controller', (Controller) -> + + class SettingsController extends Controller + + constructor: (@$scope, @$rootScope, @showAll, @persistence, @folderModel, + @feedModel, @opmlParser) -> + + @add = false + @settings = false + @addingFeed = false + @addingFolder = false + + @$scope.getFolders = => + return @folderModel.getItems() + + @$scope.getShowAll = => + return @showAll.showAll + + @$scope.setShowAll = (value) => + @showAll.showAll = value + @persistence.showAll(value) + @$rootScope.$broadcast('triggerHideRead') + + @$scope.toggleSettings = => + @settings = !@settings + + @$scope.toggleAdd = => + @add = !@add + + @$scope.addIsShown = => + return @add + + @$scope.settingsAreShown = => + return @settings + + @$scope.isAddingFeed = => + return @addingFeed + + @$scope.isAddingFolder = => + return @addingFolder + + @$scope.addFeed = (url, folder) => + @$scope.feedEmptyError = false + @$scope.feedExistsError = false + @$scope.feedError = false + + if url == undefined or url.trim() == '' + @$scope.feedEmptyError = true + else + url = url.trim() + for feed in @feedModel.getItems() + if url == feed.url # FIXME: can we really compare this + @$scope.feedExistsError = true + + if not (@$scope.feedEmptyError or @$scope.feedExistsError) + if folder == undefined + folderId = 0 + else + folderId = folder.id + @addingFeed = true + onSuccess = => + @$scope.feedUrl = '' + @addingFeed = false + onError = => + @$scope.feedError = true + @addingFeed = false + @persistence.createFeed(url, folderId, onSuccess, onError) + + + @$scope.addFolder = (name) => + @$scope.folderEmptyError = false + @$scope.folderExistsError = false + + if name == undefined or name.trim() == '' + @$scope.folderEmptyError = true + else + name = name.trim() + for folder in @folderModel.getItems() + if name.toLowerCase() == folder.name.toLowerCase() + @$scope.folderExistsError = true + + if not (@$scope.folderEmptyError or @$scope.folderExistsError) + @addingFolder = true + onSuccess = => + @$scope.folderName = '' + @addingFolder = false + @persistence.createFolder(name, onSuccess) + + @$scope.$on 'readFile', (scope, fileContent) => + structure = @opmlParser.parseXML(fileContent) + @parseOPMLStructure(structure) + + + @$scope.$on 'hidesettings', => + @add = false + @settings = false + + + # recursively create folders + parseOPMLStructure: (structure, folderId=0) -> + for item in structure.getItems() + if item.isFolder() + onSuccess = (data) => + console.log data + folderId = data.folders[0].id + @parseOPMLStructure(item, folderId) + @persistence.createFolder(item.getName(), onSuccess) + else + # FIXME: handle errors + onSuccess = -> + onError = -> + @persistence.createFeed(item.getUrl(), folderId, onSuccess, onError) + + + return SettingsController +] \ No newline at end of file diff --git a/coffee/directives/draggable.coffee b/coffee/directives/draggable.coffee new file mode 100644 index 000000000..9ebd9d30e --- /dev/null +++ b/coffee/directives/draggable.coffee @@ -0,0 +1,23 @@ +### +# 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 +# +### + +angular.module('News').directive 'draggable', -> + + return (scope, elm, attr) -> + + details = + revert: true + stack: '> li' + zIndex: 1000 + axis: 'y' + + $(elm).draggable(details) + diff --git a/coffee/directives/droppable.coffee b/coffee/directives/droppable.coffee new file mode 100644 index 000000000..37b29fca1 --- /dev/null +++ b/coffee/directives/droppable.coffee @@ -0,0 +1,33 @@ +### +# 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 +# +### + +angular.module('News').directive 'droppable', ['$rootScope', ($rootScope) -> + + return (scope, elm, attr) -> + $elem = $(elm) + + details = + accept: '.feed' + hoverClass: 'dnd_over' + greedy: true + drop: (event, ui) -> + # in case jquery ui did something weird + $('.dnd_over').removeClass('dnd_over') + + data = + folderId: parseInt($elem.data('id'), 10) + feedId: parseInt($(ui.draggable).data('id'), 10) + + $rootScope.$broadcast('moveFeedToFolder', data) + scope.$apply attr.droppable + + $elem.droppable(details) +] \ No newline at end of file diff --git a/coffee/directives/feednavigation.coffee b/coffee/directives/feednavigation.coffee new file mode 100644 index 000000000..58b0d6c24 --- /dev/null +++ b/coffee/directives/feednavigation.coffee @@ -0,0 +1,66 @@ +### +# 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 +# +### + +angular.module('News').directive 'feedNavigation', -> + + return (scope, elm, attr) -> + + jumpTo = ($scrollArea, $item) -> + position = $item.offset().top - $scrollArea.offset().top + $scrollArea.scrollTop() + $scrollArea.scrollTop(position) + + jumpToPreviousItem = (scrollArea) -> + $scrollArea = $(scrollArea) + $items = $scrollArea.find('.feed_item') + notJumped = true + for item in $items + $item = $(item) + if $item.position().top >= 0 + $previous = $item.prev() + # if there are no items before the current one + if $previous.length > 0 + jumpTo($scrollArea, $previous) + + notJumped = false + break + + # in case we didnt jump + if $items.length > 0 and notJumped + jumpTo($scrollArea, $items.last()) + + + jumpToNextItem = (scrollArea) -> + $scrollArea = $(scrollArea) + $items = $scrollArea.find('.feed_item') + for item in $items + $item = $(item) + if $item.position().top > 1 + jumpTo($scrollArea, $item) + break + + + $(document).keydown (e) -> + # only activate if no input elements is focused + focused = $(':focus') + + if not (focused.is('input') or focused.is('select') or + focused.is('textarea') or focused.is('checkbox') or focused.is('button')) + + scrollArea = elm + # j or right + if e.keyCode == 74 or e.keyCode == 39 + jumpToNextItem(scrollArea) + + # k or left + else if e.keyCode == 75 or e.keyCode == 37 + jumpToPreviousItem(scrollArea) + + diff --git a/coffee/directives/hidesettingswhenfocuslost.coffee b/coffee/directives/hidesettingswhenfocuslost.coffee new file mode 100644 index 000000000..0d7220c4a --- /dev/null +++ b/coffee/directives/hidesettingswhenfocuslost.coffee @@ -0,0 +1,25 @@ +### +# 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 is used to signal the settings bar that the app has been focused and that +# it should hide +### +angular.module('News').directive 'hideSettingsWhenFocusLost', ['$rootScope', ($rootScope) -> + + return (scope, elm, attr) -> + $(document.body).click -> + $rootScope.$broadcast('hidesettings') + scope.$apply attr.hideSettingsWhenFocusLost + + $(elm).click (e) -> + e.stopPropagation() +] diff --git a/coffee/directives/onenter.coffee b/coffee/directives/onenter.coffee new file mode 100644 index 000000000..a9b0ab95a --- /dev/null +++ b/coffee/directives/onenter.coffee @@ -0,0 +1,20 @@ +### +# 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 +# +### + +angular.module('News').directive 'onEnter', -> + + return (scope, elm, attr) -> + + elm.bind 'keyup', (e) -> + if e.keyCode == 13 + e.preventDefault() + scope.$apply attr.onEnter + diff --git a/coffee/directives/readfile.coffee b/coffee/directives/readfile.coffee new file mode 100644 index 000000000..32a113b82 --- /dev/null +++ b/coffee/directives/readfile.coffee @@ -0,0 +1,33 @@ +### +# 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 +# +### + +### +Thise directive can be bound on an input element with type file and name files [] +When a file is input, the content will be broadcasted as a readFile event +### +angular.module('News').directive 'readFile', ['$rootScope', ($rootScope) -> + + return (scope, elm, attr) -> + $(elm).change -> + if window.File and window.FileReader and window.FileList + file = elm[0].files[0] + + reader = new FileReader() + + reader.onload = (e) -> + content = e.target.result + $rootScope.$broadcast 'readFile', content + + reader.readAsText(file) + + else + alert 'Your browser does not support the FileReader API!' +] \ No newline at end of file diff --git a/coffee/directives/whenscrolled.coffee b/coffee/directives/whenscrolled.coffee new file mode 100644 index 000000000..ef3215874 --- /dev/null +++ b/coffee/directives/whenscrolled.coffee @@ -0,0 +1,52 @@ +### +# 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 +# +### + +scrolling = true +markingRead = true + +angular.module('News').directive 'whenScrolled', +['$rootScope', 'Config', +($rootScope, Config) -> + + return (scope, elm, attr) -> + + elm.bind 'scroll', -> + # prevent from doing to many scroll actions + # the first timeout prevents accidental and too early marking as read + if scrolling + scrolling = false + setTimeout -> + scrolling = true + , Config.ScrollTimeout + + if markingRead + markingRead = false + setTimeout -> + markingRead = true + # only broadcast elements that are not already read + # and that are beyond the top border + $elems = $(elm).find('.feed_item:not(.read)') + + for feedItem in $elems + offset = $(feedItem).position().top + if offset <= -50 + id = parseInt($(feedItem).data('id'), 10) + feed = parseInt($(feedItem).data('feed'), 10) + $rootScope.$broadcast('read', {id: id, feed: feed}) + else + break + + , Config.MarkReadTimeout + + scope.$apply attr.whenScrolled + +] + diff --git a/coffee/filters/feedInFolder.coffee b/coffee/filters/feedInFolder.coffee new file mode 100644 index 000000000..24247ce62 --- /dev/null +++ b/coffee/filters/feedInFolder.coffee @@ -0,0 +1,18 @@ +### +# 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 +# +### + +angular.module('News').filter 'feedInFolder', -> + return (feeds, folderId) -> + result = [] + for feed in feeds + if feed.folderId == folderId + result.push(feed) + return result \ No newline at end of file diff --git a/coffee/services/activefeed.coffee b/coffee/services/activefeed.coffee new file mode 100644 index 000000000..3ebb34634 --- /dev/null +++ b/coffee/services/activefeed.coffee @@ -0,0 +1,24 @@ +### +# 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 +# +### + +angular.module('News').factory '_ActiveFeed', -> + + class ActiveFeed + + constructor: -> + @id = 0 + @type = 3 + + handle: (data) -> + @id = data.id + @type = data.type + + return ActiveFeed diff --git a/coffee/services/cache.coffee b/coffee/services/cache.coffee new file mode 100644 index 000000000..194fca3f1 --- /dev/null +++ b/coffee/services/cache.coffee @@ -0,0 +1,149 @@ +angular.module('News').factory '_Cache', -> + + class Cache + + constructor: (@feedType, @feedModel, @folderModel) -> + @clear() + + + clear: -> + @feedCache = [] + @folderCache = {} + @folderCacheLastModified = 0 + @importantCache = [] + @highestId = 0 + @lowestId = 0 + @highestTimestamp = 0 + @lowestTimestamp = 0 + @highestIds = {} + @lowestIds = {} + @highestTimestamps = {} + @lowestTimestamps = {} + + + add: (item) -> + # cache for feed access + if not @feedCache[item.feedId] + @feedCache[item.feedId] = [] + @feedCache[item.feedId].push(item) + + # cache for non feeds + if @highestTimestamp < item.date + @highestTimestamp = item.date + if @lowestTimestamp > item.date + @lowestTimestamp = item.date + if @highestId < item.id + @highestId = item.id + if @lowestId > item.id + @lowestId = item.id + + + # cache for important + if item.isImportant + @importantCache.push(item) + + # cache lowest and highest ids and timestamps for only fetching new + # items + if @highestTimestamps[item.feedId] == undefined or item.id > @highestTimestamps[item.feedId] + @highestTimestamps[item.feedId] = item.date + if @lowestTimestamps[item.feedId] == undefined or item.id > @lowestTimestamps[item.feedId] + @lowestTimestamps[item.feedId] = item.date + if @highestIds[item.feedId] == undefined or item.id > @highestIds[item.feedId] + @highestIds[item.feedId] = item.id + if @lowestIds[item.feedId] == undefined or item.id > @lowestIds[item.feedId] + @lowestIds[item.feedId] = item.id + + + getItemsOfFeed: (feedId) -> + return @feedCache[feedId] + + + getFeedIdsOfFolder: (folderId) -> + @buildFolderCache(folderId) + return @folderCache[folderId] + + + getImportantItems: -> + return @importantCache + + + buildFolderCache: (id) -> + # invalidate the foldercache if the last modified date is + # not the currently used one + if @folderCacheLastModified != @feedModel.getLastModified() + @folderCache = {} + @folderCacheLastModified = @feedModel.getLastModified() + + # if the folderarray does not yet exist, build it + # otherwise use the last generated one + if @folderCache[id] == undefined + @folderCache[id] = [] + for feed in @feedModel.getItems() + if feed.folderId == id + @folderCache[id].push(feed.id) + + + getFeedsOfFolderId: (id) -> + @buildFolderCache(id) + return @folderCache[id] + + + removeItemInArray: (id, array) -> + removeItemIndex = null + counter = 0 + for element in array + if element.id == id + removeItemIndex = counter + break + counter += 1 + + if removeItemIndex != null + array.splice(removeItemIndex, 1) + + + remove: (item) -> + @removeItemInArray(item.id, @feedCache[item.feedId]) + @removeItemInArray(item.id, @importantCache) + + + setImportant: (item, isImportant) -> + if isImportant + @importantCache.push(item) + else + @removeItemInArray(item.id, @importantCache) + + getHighestId: (type, id) -> + if @isFeed(type) + return @highestIds[id] || 0 + else + return @highestId + + + getHighestTimestamp: (type, id) -> + if @isFeed(type) + return @highestTimestamps[id] || 0 + else + return @highestTimestamp + + + getLowestId: (type, id) -> + if @isFeed(type) + return @lowestIds[id] || 0 + else + return @lowestId + + + getLowestTimestamp: (type, id) -> + if @isFeed(type) + return @lowestTimestamps[id] || 0 + else + return @lowestTimestamp + + + isFeed: (type) -> + return type == @feedType.Feed + + + return Cache + + diff --git a/coffee/services/feedmodel.coffee b/coffee/services/feedmodel.coffee new file mode 100644 index 000000000..94af1727f --- /dev/null +++ b/coffee/services/feedmodel.coffee @@ -0,0 +1,31 @@ +### +# 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 +# +### + +angular.module('News').factory '_FeedModel', ['Model', (Model) -> + + class FeedModel extends Model + + constructor: () -> + super() + + + add: (item) -> + super(@bindAdditional(item)) + + + bindAdditional: (item) -> + if item.icon == "url()" + item.icon = 'url(' + OC.imagePath('news', 'rss.svg') + ')' + return item + + + return FeedModel +] \ No newline at end of file diff --git a/coffee/services/feedtype.coffee b/coffee/services/feedtype.coffee new file mode 100644 index 000000000..a14970649 --- /dev/null +++ b/coffee/services/feedtype.coffee @@ -0,0 +1,18 @@ +### +# 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 +# +### + +angular.module('News').factory 'FeedType', -> + feedType = + Feed: 0 + Folder: 1 + Starred: 2 + Subscriptions: 3 + Shared: 4 \ No newline at end of file diff --git a/coffee/services/foldermodel.coffee b/coffee/services/foldermodel.coffee new file mode 100644 index 000000000..279caffd9 --- /dev/null +++ b/coffee/services/foldermodel.coffee @@ -0,0 +1,21 @@ +### +# 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 +# +### + +angular.module('News').factory '_FolderModel', ['Model', (Model, $rootScope) -> + + class FolderModel extends Model + + constructor: -> + super() + + + return FolderModel +] \ No newline at end of file diff --git a/coffee/services/garbageregistry.coffee b/coffee/services/garbageregistry.coffee new file mode 100644 index 000000000..5d52d81ad --- /dev/null +++ b/coffee/services/garbageregistry.coffee @@ -0,0 +1,38 @@ +### +# 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 +# +### + +angular.module('News').factory '_GarbageRegistry', -> + + class GarbageRegistry + + constructor: (@itemModel) -> + @registeredItemIds = {} + + register: (item) -> + itemId = item.id + @registeredItemIds[itemId] = item + + + unregister: (item) -> + itemId = item.id + delete @registeredItemIds[itemId] + + + clear: () -> + # delete read items for performance reasons when showAll == false + for id, item of @registeredItemIds + if not item.keptUnread + @itemModel.removeById(parseInt(id, 10)) + item.keptUnread = false + @registeredItemIds = {} + + + return GarbageRegistry diff --git a/coffee/services/itemmodel.coffee b/coffee/services/itemmodel.coffee new file mode 100644 index 000000000..e7b87f9ec --- /dev/null +++ b/coffee/services/itemmodel.coffee @@ -0,0 +1,97 @@ +### +# 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 +# +### + +angular.module('News').factory '_ItemModel', ['Model', (Model) -> + + class ItemModel extends Model + + constructor: (@cache, @feedType) -> + super() + + + clearCache: () -> + @cache.clear() + super() + + + add: (item) -> + item = @bindAdditional(item) + if super(item) + @cache.add(@getItemById(item.id)) + + + bindAdditional: (item) -> + item.getRelativeDate = -> + return moment.unix(this.date).fromNow(); + + item.getAuthorLine = -> + if this.author != null and this.author.trim() != "" + return "by " + this.author + else + return "" + return item + + + removeById: (itemId) -> + item = @getItemById(itemId) + if item != undefined + @cache.remove(item) + super(itemId) + + + getHighestId: (type, id) -> + @cache.getHighestId(type, id) + + + getHighestTimestamp: (type, id) -> + @cache.getHighestTimestamp(type, id) + + + getLowestId: (type, id) -> + @cache.getLowestId(type, id) + + + getLowestTimestamp: (type, id) -> + @cache.getLowestTimestamp(type, id) + + + getFeedsOfFolderId: (id) -> + @cache.getFeedsOfFolderId(id) + + + getItemsByTypeAndId: (type, id) -> + switch type + when @feedType.Feed + items = @cache.getItemsOfFeed(id) || [] + return items + + when @feedType.Subscriptions + return @getItems() + + when @feedType.Folder + items = [] + for feedId in @cache.getFeedIdsOfFolder(id) + items = items.concat(@cache.getItemsOfFeed(feedId) || []) + return items + + when @feedType.Starred + return @cache.getImportantItems() + + + setImportant: (itemId, isImportant) -> + item = @getItemById(itemId) + @cache.setImportant(item, isImportant) + item.isImportant = isImportant + + + return ItemModel + +] \ No newline at end of file diff --git a/coffee/services/loading.coffee b/coffee/services/loading.coffee new file mode 100644 index 000000000..041170e76 --- /dev/null +++ b/coffee/services/loading.coffee @@ -0,0 +1,17 @@ +### +# 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 +# +### + +angular.module('News').factory '_Loading', -> + + class Loading + + constructor: -> + @loading = 0 diff --git a/coffee/services/model.coffee b/coffee/services/model.coffee new file mode 100644 index 000000000..7094f8a83 --- /dev/null +++ b/coffee/services/model.coffee @@ -0,0 +1,80 @@ +### +# 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 +# +### + +angular.module('News').factory 'Model', -> + + class Model + + constructor: () -> + @clearCache() + + handle: (data) -> + for item in data + @add(item) + + + clearCache: () -> + @items = [] + @itemIds = {} + @markAccessed() + + + markAccessed: () -> + @lastAccessed = new Date().getTime() + + + getLastModified: () -> + return @lastAccessed + + + add: (item) -> + if @itemIds[item.id] == undefined + @items.push(item) + @itemIds[item.id] = item + @markAccessed() + return true + else + @update(item) + return false + + + update: (item) -> + updatedItem = @itemIds[item.id] + for key, value of item + if key != 'id' + updatedItem[key] = value + @markAccessed() + + + removeById: (id) -> + removeItemIndex = null + counter = 0 + for item in @items + if item.id == id + removeItemIndex = counter + break + counter += 1 + + if removeItemIndex != null + @items.splice(removeItemIndex, 1) + delete @itemIds[id] + @markAccessed() + + + getItemById: (id) -> + return @itemIds[id] + + + getItems: () -> + return @items + + + return Model \ No newline at end of file diff --git a/coffee/services/opmlparser.coffee b/coffee/services/opmlparser.coffee new file mode 100644 index 000000000..085a18ae5 --- /dev/null +++ b/coffee/services/opmlparser.coffee @@ -0,0 +1,67 @@ +### +# 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 +# +### + +angular.module('News').factory '_OPMLParser', -> + + class Feed + + constructor: (@name, @url) -> + + getName: -> + return @name + + getUrl: -> + return @url + + isFolder: -> + return false + + + class Folder + + constructor: (@name) -> + @items = [] + + add: (feed) -> + @items.push(feed) + + getItems: -> + return @items + + getName: -> + return @name + + isFolder: -> + return true + + + class OPMLParser + + parseXML: (xml) -> + $xml = $($.parseXML(xml)) + $root = $xml.find('body') + structure = new Folder('root') + @_recursivelyParse($root, structure) + return structure + + _recursivelyParse: ($xml, structure) -> + for outline in $xml.children('outline') + $outline = $(outline) + if $outline.attr('type') != undefined + feed = new Feed($outline.attr('text'), $outline.attr('xmlUrl')) + structure.add(feed) + else + folder = new Folder($outline.attr('text')) + structure.add(folder) + @_recursivelyParse($outline, folder) + + + return OPMLParser diff --git a/coffee/services/persistence.coffee b/coffee/services/persistence.coffee new file mode 100644 index 000000000..2a00b1f69 --- /dev/null +++ b/coffee/services/persistence.coffee @@ -0,0 +1,73 @@ +### +# 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 +# +### + +angular.module('News').factory 'Persistence', -> + + class Persistence + + constructor: (@appName, @$http) -> + @appInitialized = false + @shelvedRequests = [] + + + setInitialized: (isInitialized) -> + if isInitialized + @executePostRequests() + @appInitialized = isInitialized + + + executePostRequests: () -> + for request in @shelvedRequests + @post(request.route, request.data, request.callback) + console.log request + @shelvedRequests = [] + + + isInitialized: -> + return @appInitialized + + + post: (route, data={}, callback, errorCallback, init=false, contentType='application/x-www-form-urlencoded') -> + if @isInitialized == false && init == false + request = + route: route + data: data + callback: callback + @shelvedRequests.push(request) + return + + if not callback + callback = -> + if not errorCallback + errorCallback = -> + + url = OC.Router.generate("news_ajax_" + route) + + data = $.param(data) + + # csrf token + headers = + requesttoken: oc_requesttoken + 'Content-Type': 'application/x-www-form-urlencoded' + + @$http.post(url, data, {headers: headers}). + success((data, status, headers, config) -> + if data.status == "error" + errorCallback(data.msg) + else + callback(data) + ). + error (data, status, headers, config) -> + console.warn('Error occured: ') + console.warn(status) + console.warn(headers) + console.warn(config) + diff --git a/coffee/services/persitencenews.coffee b/coffee/services/persitencenews.coffee new file mode 100644 index 000000000..b83e9a0c1 --- /dev/null +++ b/coffee/services/persitencenews.coffee @@ -0,0 +1,150 @@ +### +# 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 +# +### + +angular.module('News').factory '_PersistenceNews', ['Persistence', (Persistence) -> + + class PersistenceNews extends Persistence + + constructor: ($http, @$rootScope, @loading, @publisher) -> + super('news', $http) + + + updateModels: (data) -> + for type, value of data + @publisher.publish(type, value) + + + loadInitial: () -> + @loading.loading += 1 + OC.Router.registerLoadedCallback => + @post 'init', {}, (json) => + @loading.loading -= 1 + @updateModels(json.data) + @$rootScope.$broadcast('triggerHideRead') + @setInitialized(true) + , null, true + + + loadFeed: (type, id, latestFeedId, latestTimestamp, limit=20) -> + data = + type: type + id: id + latestFeedId: latestFeedId + latestTimestamp: latestTimestamp + limit: limit + + @loading.loading += 1 + @post 'loadfeed', data, (json) => + @loading.loading -= 1 + @updateModels(json.data) + + + createFeed: (feedUrl, folderId, onSuccess, onError) -> + data = + feedUrl: feedUrl + folderId: folderId + @post 'createfeed', data, (json) => + onSuccess(json.data) + @updateModels(json.data) + , onError + + + deleteFeed: (feedId, onSuccess) -> + data = + feedId: feedId + @post 'deletefeed', data, onSuccess + + + moveFeedToFolder: (feedId, folderId) -> + data = + feedId: feedId + folderId: folderId + @post 'movefeedtofolder', data + + + createFolder: (folderName, onSuccess) -> + data = + folderName: folderName + @post 'createfolder', data, (json) => + onSuccess(json.data) + @updateModels(json.data) + + + deleteFolder: (folderId) -> + data = + folderId: folderId + @post 'deletefolder', data + + + changeFolderName: (folderId, newFolderName) -> + data = + folderId: folderId + newFolderName: newFolderName + @post 'folderName', data + + + showAll: (isShowAll) -> + data = + showAll: isShowAll + @post 'setshowall', data + + + markRead: (itemId, isRead) -> + if isRead + status = 'read' + else + status = 'unread' + + data = + itemId: itemId + status: status + + @post 'setitemstatus', data + + + setImportant: (itemId, isImportant) -> + if isImportant + status = 'important' + else + status = 'unimportant' + + data = + itemId: itemId + status: status + + @post 'setitemstatus', data + + + collapseFolder: (folderId, value) -> + data = + folderId: folderId + opened: value + @post 'collapsefolder', data + + + updateFeed: (feedId) -> + data = + feedId: feedId + + @post 'updatefeed', data, (json) => + @updateModels(json.data) + + + setAllItemsRead: (feedId, mostRecentItemId) -> + data = + feedId: feedId + mostRecentItemId: mostRecentItemId + @post 'setallitemsread', data + + + + return PersistenceNews +] \ No newline at end of file diff --git a/coffee/services/publisher.coffee b/coffee/services/publisher.coffee new file mode 100644 index 000000000..6794c586d --- /dev/null +++ b/coffee/services/publisher.coffee @@ -0,0 +1,30 @@ +### +# 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 +# +### + +angular.module('News').factory '_Publisher', -> + + class Publisher + + constructor: () -> + @subscriptions = {} + + + subscribeTo: (type, object) -> + @subscriptions[type] or= [] + @subscriptions[type].push(object) + + + publish: (type, message) -> + for subscriber in @subscriptions[type] || [] + subscriber.handle(message) + + + return Publisher \ No newline at end of file diff --git a/coffee/services/services.coffee b/coffee/services/services.coffee new file mode 100644 index 000000000..25879a5b6 --- /dev/null +++ b/coffee/services/services.coffee @@ -0,0 +1,90 @@ +### +# 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 +# +### + +angular.module('News').factory 'Loading', +['_Loading', (_Loading) -> + return new _Loading() +] + +# Models +angular.module('News').factory 'ActiveFeed', +['_ActiveFeed', 'Publisher', (_ActiveFeed, Publisher) -> + model = new _ActiveFeed() + Publisher.subscribeTo('activeFeed', model) + return model +] + +angular.module('News').factory 'ShowAll', +['_ShowAll', 'Publisher', (_ShowAll, Publisher) -> + model = new _ShowAll() + Publisher.subscribeTo('showAll', model) + return model +] + +angular.module('News').factory 'StarredCount', +['_StarredCount', 'Publisher', (_StarredCount, Publisher) -> + model = new _StarredCount() + Publisher.subscribeTo('starredCount', model) + return model +] + +angular.module('News').factory 'FeedModel', +['_FeedModel', 'Publisher', +(_FeedModel, Publisher) -> + model = new _FeedModel() + Publisher.subscribeTo('feeds', model) + return model +] + +angular.module('News').factory 'FolderModel', +['_FolderModel', 'Publisher', +(_FolderModel, Publisher) -> + model = new _FolderModel() + Publisher.subscribeTo('folders', model) + return model +] + +angular.module('News').factory 'ItemModel', +['_ItemModel', 'Publisher', 'Cache', 'FeedType', +(_ItemModel, Publisher, Cache, FeedType) -> + model = new _ItemModel(Cache, FeedType) + Publisher.subscribeTo('items', model) + return model +] + +# Classes +angular.module('News').factory 'Cache', +['_Cache', 'FeedType', 'FeedModel', 'FolderModel', +(_Cache, FeedType, FeedModel, FolderModel) -> + return new _Cache(FeedType, FeedModel, FolderModel) +] + +angular.module('News').factory 'PersistenceNews', +['_PersistenceNews', '$http', '$rootScope', 'Loading', 'Publisher', +(_PersistenceNews, $http, $rootScope, Loading, Publisher) -> + return new _PersistenceNews($http, $rootScope, Loading, Publisher) +] + +angular.module('News').factory 'GarbageRegistry', +['_GarbageRegistry', 'ItemModel', +(_GarbageRegistry, ItemModel) -> + return new _GarbageRegistry(ItemModel) +] + +angular.module('News').factory 'Publisher', +['_Publisher', (_Publisher) -> + return new _Publisher() +] + +angular.module('News').factory 'OPMLParser', +['_OPMLParser', (_OPMLParser) -> + return new _OPMLParser() +] \ No newline at end of file diff --git a/coffee/services/showall.coffee b/coffee/services/showall.coffee new file mode 100644 index 000000000..a707b245c --- /dev/null +++ b/coffee/services/showall.coffee @@ -0,0 +1,23 @@ +### +# 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 +# +### + +angular.module('News').factory '_ShowAll', -> + + class ShowAll + + constructor: -> + @showAll = false + + handle: (data) -> + @showAll = data + + + return ShowAll diff --git a/coffee/services/starredcount.coffee b/coffee/services/starredcount.coffee new file mode 100644 index 000000000..e0b55a99a --- /dev/null +++ b/coffee/services/starredcount.coffee @@ -0,0 +1,24 @@ +### +# 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 +# +### + +angular.module('News').factory '_StarredCount', -> + + class StarredCount + + constructor: -> + @count = 0 + + + handle: (data) -> + @count = data + + + return StarredCount -- cgit v1.2.3