From e2e9a79aa3acb0d3a02d81ddbfbdcacc676db2d2 Mon Sep 17 00:00:00 2001 From: Bernhard Posselt Date: Sun, 18 May 2014 18:51:16 +0200 Subject: add model --- js/Gruntfile.js | 18 ++-- js/build/app.js | 153 +++++++++++++++++++++++++++++++++ js/service/model.js | 77 +++++++++++++++++ js/service/publisher.js | 35 ++++++++ js/tests/unit/service/ModelSpec.js | 103 ++++++++++++++++++++++ js/tests/unit/service/PublisherSpec.js | 26 ++++++ 6 files changed, 407 insertions(+), 5 deletions(-) create mode 100644 js/build/app.js create mode 100644 js/service/model.js create mode 100644 js/service/publisher.js create mode 100644 js/tests/unit/service/ModelSpec.js create mode 100644 js/tests/unit/service/PublisherSpec.js (limited to 'js') diff --git a/js/Gruntfile.js b/js/Gruntfile.js index 491a4922a..df0afc2ba 100644 --- a/js/Gruntfile.js +++ b/js/Gruntfile.js @@ -9,19 +9,27 @@ */ var globals = [ + // libs '$', 'angular', + // app 'app', 'OC', + // angular + 'inject', + 'module', + + // protractor 'protractor', + 'browser', + 'By', + // jasmine + 'jasmine', + 'it', 'describe', 'beforeEach', - 'module', - 'it', - 'browser', 'expect', - 'By', - 'inject', + // js 'console', 'exports' ]; diff --git a/js/build/app.js b/js/build/app.js new file mode 100644 index 000000000..206114cd4 --- /dev/null +++ b/js/build/app.js @@ -0,0 +1,153 @@ +(function(angular, $, OC, undefined){ + +'use strict'; + + +var app = angular.module('News', [ + 'ngRoute', + 'ngSanitize', + 'ngAnimate' + ]); +app.config([ + '$routeProvider', + '$provide', + function ($routeProvider, $provide) { + 'use strict'; + $provide.constant('baseUrl', OC.generateUrl('')); + $routeProvider.when('/items', { + controller: 'AllItemsController', + templateUrl: 'content.html', + resolve: {} + }).when('/items/starred', { + controller: 'StarredItemsController', + templateUrl: 'content.html', + resolve: {} + }).when('/items/feeds/:id', { + controller: 'FeedItemsController', + templateUrl: 'content.html', + resolve: {} + }).when('/items/folders/:id', { + controller: 'FolderItemsController', + templateUrl: 'content.html', + resolve: {} + }).otherwise({ redirectTo: '/items' }); + } +]); +app.run([ + '$rootScope', + '$location', + 'Loading', + 'Setup', + function ($rootScope, $location, Loading, Setup) { + 'use strict'; + // load feeds, settings and last read feed + Setup.load(); + $rootScope.$on('$routeChangeStart', function () { + Loading.setLoading('content', true); + }); + $rootScope.$on('$routeChangeSuccess', function () { + Loading.setLoading('content', false); + }); + // in case of wrong id etc show all items + $rootScope.$on('$routeChangeError', function () { + $location.path('/items'); + }); + } +]); +app.service('Loading', function () { + 'use strict'; + this.loading = { + global: false, + content: false + }; + this.setLoading = function (area, isLoading) { + this.loading[area] = isLoading; + }; + this.isLoading = function (area) { + return this.loading[area]; + }; +}); +app.factory('Model', function () { + 'use strict'; + var Model = function (id) { + this.id = id; + this.values = []; + this.hashMap = {}; + }; + Model.prototype = { + receive: function (values) { + var self = this; + values.forEach(function (value) { + self.add(value); + }); + }, + add: function (value) { + var key, existing; + existing = this.hashMap[value[this.id]]; + if (existing === undefined) { + this.values.push(value); + this.hashMap[value[this.id]] = value; + } else { + // copy values from new to old object if it exists already + for (key in value) { + if (value.hasOwnProperty(key)) { + existing[key] = value[key]; + } + } + } + }, + size: function () { + return this.values.length; + }, + get: function (id) { + return this.hashMap[id]; + }, + delete: function (id) { + // find index of object that should be deleted + var i, deleteAtIndex; + for (i = 0; i < this.values.length; i += 1) { + if (this.values[i][this.id] === id) { + deleteAtIndex = i; + break; + } + } + if (deleteAtIndex !== undefined) { + this.values.splice(deleteAtIndex, 1); + } + if (this.hashMap[id] !== undefined) { + delete this.hashMap[id]; + } + } + }; + return Model; +}); +app.service('Publisher', function () { + 'use strict'; + var self = this; + this.channels = {}; + this.subscribe = function (object) { + return { + toChannel: function (channel) { + self.channels[channel] = self.channels[channel] || []; + self.channels[channel].push(object); + } + }; + }; + this.publish = function (value) { + return { + onChannel: function (channel) { + self.channels[channel].forEach(function (object) { + object.receive(value); + }); + } + }; + }; +}); +app.service('Setup', function () { + 'use strict'; + this.load = function () { + console.log('init'); + }; +}); + +})(angular, jQuery, OC); \ No newline at end of file diff --git a/js/service/model.js b/js/service/model.js new file mode 100644 index 000000000..a28608329 --- /dev/null +++ b/js/service/model.js @@ -0,0 +1,77 @@ +/** + * ownCloud - News + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Bernhard Posselt + * @copyright Bernhard Posselt 2014 + */ +app.factory('Model', function () { + 'use strict'; + + var Model = function (id) { + this.id = id; + this.values = []; + this.hashMap = {}; + }; + + Model.prototype = { + receive: function (values) { + var self = this; + values.forEach(function (value) { + self.add(value); + }); + }, + + add: function (value) { + var key, + existing; + + existing = this.hashMap[value[this.id]]; + + if (existing === undefined) { + this.values.push(value); + this.hashMap[value[this.id]] = value; + } else { + // copy values from new to old object if it exists already + for (key in value) { + if (value.hasOwnProperty(key)) { + existing[key] = value[key]; + } + } + } + }, + + size: function () { + return this.values.length; + }, + + get: function (id) { + return this.hashMap[id]; + }, + + delete: function (id) { + // find index of object that should be deleted + var i, + deleteAtIndex; + + for (i = 0; i < this.values.length; i += 1) { + if (this.values[i][this.id] === id) { + deleteAtIndex = i; + break; + } + } + + if (deleteAtIndex !== undefined) { + this.values.splice(deleteAtIndex, 1); + } + + if (this.hashMap[id] !== undefined) { + delete this.hashMap[id]; + } + } + }; + + return Model; +}); \ No newline at end of file diff --git a/js/service/publisher.js b/js/service/publisher.js new file mode 100644 index 000000000..185e60047 --- /dev/null +++ b/js/service/publisher.js @@ -0,0 +1,35 @@ +/** + * ownCloud - News + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Bernhard Posselt + * @copyright Bernhard Posselt 2014 + */ +app.service('Publisher', function () { + 'use strict'; + + var self = this; + this.channels = {}; + + this.subscribe = function (object) { + return { + toChannel: function (channel) { + self.channels[channel] = self.channels[channel] || []; + self.channels[channel].push(object); + } + }; + }; + + this.publish = function (value) { + return { + onChannel: function (channel) { + self.channels[channel].forEach(function (object) { + object.receive(value); + }); + } + }; + }; + +}); \ No newline at end of file diff --git a/js/tests/unit/service/ModelSpec.js b/js/tests/unit/service/ModelSpec.js new file mode 100644 index 000000000..d8aba1520 --- /dev/null +++ b/js/tests/unit/service/ModelSpec.js @@ -0,0 +1,103 @@ +/** + * ownCloud - News + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Bernhard Posselt + * @copyright Bernhard Posselt 2014 + */ +describe('Model', function () { + 'use strict'; + + var childModel; + + beforeEach(module('News')); + + beforeEach(inject(function (Model) { + var ChildModel = function () { + Model.call(this, 'id'); + }; + ChildModel.prototype = Object.create(Model.prototype); + + childModel = new ChildModel(); + })); + + + it('should receive an object', function () { + var objects = [ + { + id: 2 + }, + { + id: 3 + } + ]; + + childModel.receive(objects); + + expect(childModel.size()).toBe(2); + }); + + + it('should add an object', function () { + var object = { + id: 3, + name: 'test' + }; + childModel.add(object); + + expect(childModel.get(3)).toBe(object); + }); + + + it('should overwrite an object if it already exists', function () { + var object1, + object2; + + object1 = { + id: 3, + name: 'test', + test: 'ho' + }; + + object2 = { + id: 3, + name: 'test2' + }; + + childModel.add(object1); + childModel.add(object2); + + expect(childModel.get(3).name).toBe('test2'); + expect(childModel.get(3).test).toBe('ho'); + expect(childModel.size()).toBe(1); + }); + + + it('should delete a model', function () { + var object1, + object2; + + object1 = { + id: 3, + name: 'test', + test: 'ho' + }; + + object2 = { + id: 4, + name: 'test2' + }; + + childModel.add(object1); + childModel.add(object2); + + childModel.delete(3); + + expect(childModel.get(3)).not.toBeDefined(); + expect(childModel.get(4).name).toBe('test2'); + expect(childModel.size()).toBe(1); + }); + +}); \ No newline at end of file diff --git a/js/tests/unit/service/PublisherSpec.js b/js/tests/unit/service/PublisherSpec.js new file mode 100644 index 000000000..8a019f0db --- /dev/null +++ b/js/tests/unit/service/PublisherSpec.js @@ -0,0 +1,26 @@ +/** + * ownCloud - News + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Bernhard Posselt + * @copyright Bernhard Posselt 2014 + */ +describe('Publisher', function () { + 'use strict'; + + beforeEach(module('News')); + + it('should subscribe an object and publish a message', inject(function (Publisher) { + var obj = { + receive: jasmine.createSpy('receive') + }; + Publisher.subscribe(obj).toChannel('test'); + + Publisher.publish('tom').onChannel('test'); + expect(obj.receive).toHaveBeenCalledWith('tom'); + })); + + +}); \ No newline at end of file -- cgit v1.2.3