diff options
author | Bernhard Posselt <nukeawhale@gmail.com> | 2013-01-27 04:15:53 +0100 |
---|---|---|
committer | Bernhard Posselt <nukeawhale@gmail.com> | 2013-01-27 04:15:53 +0100 |
commit | ae7393db3d99a7ac223ae917129cccd9f49888e3 (patch) | |
tree | 7f54b72b0d01c38afd1378365a67e4f192922423 /coffee | |
parent | 483784caa38bd6131405ac474347a215584e30a5 (diff) |
merged the angularjs branch
Diffstat (limited to 'coffee')
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 +# |