summaryrefslogtreecommitdiffstats
path: root/coffee
diff options
context:
space:
mode:
authorBernhard Posselt <nukeawhale@gmail.com>2013-01-27 04:15:53 +0100
committerBernhard Posselt <nukeawhale@gmail.com>2013-01-27 04:15:53 +0100
commitae7393db3d99a7ac223ae917129cccd9f49888e3 (patch)
tree7f54b72b0d01c38afd1378365a67e4f192922423 /coffee
parent483784caa38bd6131405ac474347a215584e30a5 (diff)
merged the angularjs branch
Diffstat (limited to 'coffee')
-rw-r--r--coffee/Cakefile135
-rw-r--r--coffee/app.coffee38
-rw-r--r--coffee/controllers/controller.coffee14
-rw-r--r--coffee/controllers/controllers.coffee42
-rw-r--r--coffee/controllers/feedcontroller.coffee223
-rw-r--r--coffee/controllers/itemcontroller.coffee93
-rw-r--r--coffee/controllers/settingscontroller.coffee127
-rw-r--r--coffee/directives/draggable.coffee23
-rw-r--r--coffee/directives/droppable.coffee33
-rw-r--r--coffee/directives/feednavigation.coffee66
-rw-r--r--coffee/directives/hidesettingswhenfocuslost.coffee25
-rw-r--r--coffee/directives/onenter.coffee20
-rw-r--r--coffee/directives/readfile.coffee33
-rw-r--r--coffee/directives/whenscrolled.coffee52
-rw-r--r--coffee/filters/feedInFolder.coffee18
-rw-r--r--coffee/services/activefeed.coffee24
-rw-r--r--coffee/services/cache.coffee149
-rw-r--r--coffee/services/feedmodel.coffee31
-rw-r--r--coffee/services/feedtype.coffee18
-rw-r--r--coffee/services/foldermodel.coffee21
-rw-r--r--coffee/services/garbageregistry.coffee38
-rw-r--r--coffee/services/itemmodel.coffee97
-rw-r--r--coffee/services/loading.coffee17
-rw-r--r--coffee/services/model.coffee80
-rw-r--r--coffee/services/opmlparser.coffee67
-rw-r--r--coffee/services/persistence.coffee73
-rw-r--r--coffee/services/persitencenews.coffee150
-rw-r--r--coffee/services/publisher.coffee30
-rw-r--r--coffee/services/services.coffee90
-rw-r--r--coffee/services/showall.coffee23
-rw-r--r--coffee/services/starredcount.coffee24
31 files changed, 1874 insertions, 0 deletions
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 <nukeawhale@gmail.com>
+#
+# 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 <nukeawhale@gmail.com>
+#
+# 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 <nukeawhale@gmail.com>
+#
+# 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 <nukeawhale@gmail.com>
+#
+# 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 <nukeawhale@gmail.com>
+#
+# 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 <nukeawhale@gmail.com>
+#
+# 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 <nukeawhale@gmail.com>
+#
+# 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 <nukeawhale@gmail.com>
+#
+# 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 <nukeawhale@gmail.com>
+#
+# 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/