From 6474b2b0451d25ec008097a906e0e07a43f6c1d1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 14 Nov 2013 13:27:53 +1100 Subject: [PATCH] Updates eventsService to actually be able to handle async callbacks with promises - though i'm really not sure when this will effectively be used. It no longer returns a promise - since that was a 'false promise' anyways . lol. --- .../src/common/services/events.service.js | 81 ++++++++++--------- .../dialogs/contentpicker.controller.js | 51 ++++++------ .../common/dialogs/linkpicker.controller.js | 62 +++++++------- .../common/dialogs/mediapicker.controller.js | 35 ++++---- .../dialogs/membergrouppicker.controller.js | 41 +++++----- .../common/dialogs/memberpicker.controller.js | 35 ++++---- .../common/dialogs/treepicker.controller.js | 56 ++++++------- .../views/content/content.copy.controller.js | 35 ++++---- .../views/content/content.move.controller.js | 36 ++++----- .../src/views/media/media.move.controller.js | 25 +++--- .../common/services/events-service.spec.js | 74 +++++++++++------ 11 files changed, 283 insertions(+), 248 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js index 8ce699a537..542b454f2c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js @@ -1,45 +1,54 @@ -/* pubsub - based on https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js*/ -function eventsService($q) { - var cache = {}; +/** Used to broadcast and listen for global events and allow the ability to add async listeners to the callbacks */ +function eventsService($q, $rootScope) { + + return { + + /** raise an event with a given name, returns an array of promises for each listener */ + publish: function (name, args) { - return { - publish: function(){ - - var args = [].splice.call(arguments,0); - var topic = args[0]; - var deferred = $q.defer(); - args.splice(0,1); + //there are no listeners + if (!$rootScope.$$listeners[name]) { + return []; + } - if(cache[topic]){ - $.each(cache[topic], function() { - this.apply(null, args || []); - }); - deferred.resolve.apply(null, args); - }else{ - deferred.resolve.apply(null, args); - } + //setup a deferred promise for each listener + var deferred = []; + for (var i = 0; i < $rootScope.$$listeners[name].length; i++) { + deferred.push($q.defer()); + } + //create a new event args object to pass to the + // $broadcast containing methods that will allow listeners + // to return data in an async if required + var eventArgs = { + args: args, + reject: function (a) { + deferred.pop().reject(a); + }, + resolve: function (a) { + deferred.pop().resolve(a); + } + }; + + //send the event + $rootScope.$broadcast(name, eventArgs); + + //return an array of promises + var promises = _.map(deferred, function(p) { + return p.promise; + }); + return promises; + }, - return deferred.promise; - }, - - subscribe: function(topic, callback) { - if(!cache[topic]) { - cache[topic] = []; - } - cache[topic].push(callback); - return [topic, callback]; + /** subscribe to a method, or use scope.$on = same thing */ + subscribe: function(name, callback) { + return $rootScope.$on(name, callback); }, + /** pass in the result of subscribe to this method, or just call the method returned from subscribe to unsubscribe */ unsubscribe: function(handle) { - var t = handle[0]; - - if(cache[t]){ - $.each(cache[t], function(idx){ - if(this === handle[1]){ - cache[t].splice(idx, 1); - } - }); - } + if (angular.isFunction(handle)) { + handle(); + } } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/contentpicker.controller.js index 2dd47ff5cd..3c9ce94bf9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/contentpicker.controller.js @@ -43,34 +43,33 @@ angular.module("umbraco").controller("Umbraco.Dialogs.ContentPickerController", args.event.preventDefault(); args.event.stopPropagation(); - eventsService.publish("Umbraco.Dialogs.ContentPickerController.Select", args).then(function(args){ - if(dialogOptions && dialogOptions.multiPicker){ - - var c = $(args.event.target.parentElement); - if(!args.node.selected){ - args.node.selected = true; - - var temp = ""; - var icon = c.find("i.umb-tree-icon"); - if(icon.length > 0){ - icon.hide().after(temp); - }else{ - c.prepend(temp); - } - - }else{ - args.node.selected = false; - c.find(".temporary").remove(); - c.find("i.umb-tree-icon").show(); - } + eventsService.publish("Umbraco.Dialogs.ContentPickerController.Select", args); + + if (dialogOptions && dialogOptions.multiPicker) { - $scope.select(args.node); + var c = $(args.event.target.parentElement); + if (!args.node.selected) { + args.node.selected = true; - }else{ - $scope.submit(args.node); - } - - }); + var temp = ""; + var icon = c.find("i.umb-tree-icon"); + if (icon.length > 0) { + icon.hide().after(temp); + } else { + c.prepend(temp); + } + + } else { + args.node.selected = false; + c.find(".temporary").remove(); + c.find("i.umb-tree-icon").show(); + } + + $scope.select(args.node); + + } else { + $scope.submit(args.node); + } }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js index a40fb110fb..ff75f595bf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js @@ -30,40 +30,40 @@ angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController", args.event.preventDefault(); args.event.stopPropagation(); - eventsService.publish("Umbraco.Dialogs.LinkPickerController.Select", args).then(function(args){ - var c = $(args.event.target.parentElement); + eventsService.publish("Umbraco.Dialogs.LinkPickerController.Select", args); + + var c = $(args.event.target.parentElement); - //renewing - if(args.node !== $scope.target){ - if($scope.selectedEl){ - $scope.selectedEl.find(".temporary").remove(); - $scope.selectedEl.find("i.umb-tree-icon").show(); - } + //renewing + if (args.node !== $scope.target) { + if ($scope.selectedEl) { + $scope.selectedEl.find(".temporary").remove(); + $scope.selectedEl.find("i.umb-tree-icon").show(); + } - $scope.selectedEl = c; - $scope.target = args.node; - $scope.target.name = args.node.name; + $scope.selectedEl = c; + $scope.target = args.node; + $scope.target.name = args.node.name; - $scope.selectedEl.find("i.umb-tree-icon") - .hide() - .after(""); - - if(args.node.id < 0){ - $scope.target.url = "/"; - }else{ - contentResource.getNiceUrl(args.node.id).then(function(url){ - $scope.target.url = angular.fromJson(url); - }); - } - }else{ - $scope.target = undefined; - //resetting - if($scope.selectedEl){ - $scope.selectedEl.find(".temporary").remove(); - $scope.selectedEl.find("i.umb-tree-icon").show(); - } - } - }); + $scope.selectedEl.find("i.umb-tree-icon") + .hide() + .after(""); + + if (args.node.id < 0) { + $scope.target.url = "/"; + } else { + contentResource.getNiceUrl(args.node.id).then(function (url) { + $scope.target.url = angular.fromJson(url); + }); + } + } else { + $scope.target = undefined; + //resetting + if ($scope.selectedEl) { + $scope.selectedEl.find(".temporary").remove(); + $scope.selectedEl.find("i.umb-tree-icon").show(); + } + } }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js index ba834e5e9e..8e1de23cf9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js @@ -77,15 +77,16 @@ angular.module("umbraco") $scope.gotoFolder(image.id); } else if (image.contentTypeAlias.toLowerCase() == 'image') { - eventsService.publish("Umbraco.Dialogs.MediaPickerController.Select", image).then(function (img) { - if (dialogOptions && dialogOptions.multiPicker) { - $scope.select(img); - img.cssclass = ($scope.dialogData.selection.indexOf(img) > -1) ? "selected" : ""; - } - else { - $scope.submit(img); - } - }); + + eventsService.publish("Umbraco.Dialogs.MediaPickerController.Select", image); + + if (dialogOptions && dialogOptions.multiPicker) { + $scope.select(image); + image.cssclass = ($scope.dialogData.selection.indexOf(image) > -1) ? "selected" : ""; + } + else { + $scope.submit(image); + } } ev.preventDefault(); @@ -98,14 +99,14 @@ angular.module("umbraco") } else if (image.contentTypeAlias.toLowerCase() == 'image') { - eventsService.publish("Umbraco.Dialogs.MediaPickerController.Select", image).then(function(img) { - if (dialogOptions && dialogOptions.multiPicker) { - $scope.select(img); - } - else { - $scope.submit(img); - } - }); + eventsService.publish("Umbraco.Dialogs.MediaPickerController.Select", image); + + if (dialogOptions && dialogOptions.multiPicker) { + $scope.select(image); + } + else { + $scope.submit(image); + } } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.controller.js index df9f7bc729..3b3fac0d11 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/membergrouppicker.controller.js @@ -40,29 +40,28 @@ angular.module("umbraco").controller("Umbraco.Dialogs.MemberGroupPickerControlle args.event.stopPropagation(); - eventsService.publish("Umbraco.Dialogs.MemberGroupPickerController.Select", args).then(function(a) { + eventsService.publish("Umbraco.Dialogs.MemberGroupPickerController.Select", args); + + //This is a tree node, so we don't have an entity to pass in, it will need to be looked up + //from the server in this method. + select(args.node.name, args.node.id); - //This is a tree node, so we don't have an entity to pass in, it will need to be looked up - //from the server in this method. - select(a.node.name, a.node.id); - - if (dialogOptions.multiPicker) { - var c = $(a.event.target.parentElement); - if (!a.node.selected) { - a.node.selected = true; - c.find("i.umb-tree-icon").hide() - .after(""); - } - else { - - remove(a.node.name, a.node.id); - - a.node.selected = false; - c.find(".temporary").remove(); - c.find("i.umb-tree-icon").show(); - } + if (dialogOptions.multiPicker) { + var c = $(args.event.target.parentElement); + if (!args.node.selected) { + args.node.selected = true; + c.find("i.umb-tree-icon").hide() + .after(""); } - }); + else { + + remove(args.node.name, args.node.id); + + args.node.selected = false; + c.find(".temporary").remove(); + c.find("i.umb-tree-icon").show(); + } + } }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.controller.js index 39b03dca57..8731c62e44 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/memberpicker.controller.js @@ -62,27 +62,26 @@ angular.module("umbraco").controller("Umbraco.Dialogs.MemberPickerController", return; } - eventsService.publish("Umbraco.Dialogs.MemberPickerController.Select", args).then(function(a) { + eventsService.publish("Umbraco.Dialogs.MemberPickerController.Select", args); + + //This is a tree node, so we don't have an entity to pass in, it will need to be looked up + //from the server in this method. + select(args.node.name, args.node.id); - //This is a tree node, so we don't have an entity to pass in, it will need to be looked up - //from the server in this method. - select(a.node.name, a.node.id); + if (dialogOptions && dialogOptions.multipicker) { - if (dialogOptions && dialogOptions.multipicker) { - - var c = $(a.event.target.parentElement); - if (!a.node.selected) { - a.node.selected = true; - c.find("i.umb-tree-icon").hide() - .after(""); - } - else { - a.node.selected = false; - c.find(".temporary").remove(); - c.find("i.umb-tree-icon").show(); - } + var c = $(args.event.target.parentElement); + if (!args.node.selected) { + args.node.selected = true; + c.find("i.umb-tree-icon").hide() + .after(""); } - }); + else { + args.node.selected = false; + c.find(".temporary").remove(); + c.find("i.umb-tree-icon").show(); + } + } }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js index 9358ea08b8..6030a52629 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js @@ -167,35 +167,35 @@ angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController", args.event.preventDefault(); args.event.stopPropagation(); - eventsService.publish("Umbraco.Dialogs.TreePickerController.Select", args).then(function (a) { - if (a.node.filtered) { - return; - } + eventsService.publish("Umbraco.Dialogs.TreePickerController.Select", args); + + if (args.node.filtered) { + return; + } - //This is a tree node, so we don't have an entity to pass in, it will need to be looked up - //from the server in this method. - select(a.node.name, a.node.id); + //This is a tree node, so we don't have an entity to pass in, it will need to be looked up + //from the server in this method. + select(args.node.name, args.node.id); - //ui... - if ($scope.multiPicker) { - var c = $(a.event.target.parentElement); - if (!a.node.selected) { - a.node.selected = true; - var temp = ""; - var icon = c.find("i.umb-tree-icon"); - if (icon.length > 0) { - icon.hide().after(temp); - } - else { - c.prepend(temp); - } - } - else { - a.node.selected = false; - c.find(".temporary").remove(); - c.find("i.umb-tree-icon").show(); - } - } - }); + //ui... + if ($scope.multiPicker) { + var c = $(args.event.target.parentElement); + if (!args.node.selected) { + args.node.selected = true; + var temp = ""; + var icon = c.find("i.umb-tree-icon"); + if (icon.length > 0) { + icon.hide().after(temp); + } + else { + c.prepend(temp); + } + } + else { + args.node.selected = false; + c.find(".temporary").remove(); + c.find("i.umb-tree-icon").show(); + } + } }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js index 3b4a2cdbed..486c1dda10 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js @@ -10,24 +10,25 @@ angular.module("umbraco") args.event.preventDefault(); args.event.stopPropagation(); - eventsService.publish("Umbraco.Editors.Content.CopyController.Select", args).then(function(args){ - var c = $(args.event.target.parentElement); - if($scope.selectedEl){ - $scope.selectedEl.find(".temporary").remove(); - $scope.selectedEl.find("i.umb-tree-icon").show(); - } + eventsService.publish("Umbraco.Editors.Content.CopyController.Select", args); + + var c = $(args.event.target.parentElement); + if ($scope.selectedEl) { + $scope.selectedEl.find(".temporary").remove(); + $scope.selectedEl.find("i.umb-tree-icon").show(); + } + + var temp = ""; + var icon = c.find("i.umb-tree-icon"); + if (icon.length > 0) { + icon.hide().after(temp); + } else { + c.prepend(temp); + } + + $scope.target = args.node; + $scope.selectedEl = c; - var temp = ""; - var icon = c.find("i.umb-tree-icon"); - if(icon.length > 0){ - icon.hide().after(temp); - }else{ - c.prepend(temp); - } - - $scope.target = args.node; - $scope.selectedEl = c; - }); }); $scope.copy = function(){ diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js index cedb409117..7283a90073 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js @@ -10,26 +10,26 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController", args.event.preventDefault(); args.event.stopPropagation(); - eventsService.publish("Umbraco.Editors.Content.MoveController.Select", args).then(function(args){ - var c = $(args.event.target.parentElement); - - if($scope.selectedEl){ - $scope.selectedEl.find(".temporary").remove(); - $scope.selectedEl.find("i.umb-tree-icon").show(); - } + eventsService.publish("Umbraco.Editors.Content.MoveController.Select", args); + + var c = $(args.event.target.parentElement); - var temp = ""; - var icon = c.find("i.umb-tree-icon"); - if(icon.length > 0){ - icon.hide().after(temp); - }else{ - c.prepend(temp); - } + if ($scope.selectedEl) { + $scope.selectedEl.find(".temporary").remove(); + $scope.selectedEl.find("i.umb-tree-icon").show(); + } - - $scope.target = args.node; - $scope.selectedEl = c; - }); + var temp = ""; + var icon = c.find("i.umb-tree-icon"); + if (icon.length > 0) { + icon.hide().after(temp); + } else { + c.prepend(temp); + } + + + $scope.target = args.node; + $scope.selectedEl = c; }); $scope.move = function(){ diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.move.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.move.controller.js index 8a7d807c59..a705229221 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.move.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.move.controller.js @@ -10,19 +10,20 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController", args.event.preventDefault(); args.event.stopPropagation(); - eventsService.publish("Umbraco.Editors.Media.MoveController.Select", args).then(function(args){ - var c = $(args.event.target.parentElement); - if($scope.selectedEl){ - $scope.selectedEl.find(".temporary").remove(); - $scope.selectedEl.find("i.umb-tree-icon").show(); - } + eventsService.publish("Umbraco.Editors.Media.MoveController.Select", args); + + var c = $(args.event.target.parentElement); + if ($scope.selectedEl) { + $scope.selectedEl.find(".temporary").remove(); + $scope.selectedEl.find("i.umb-tree-icon").show(); + } - c.find("i.umb-tree-icon").hide() - .after(""); - - $scope.target = args.node; - $scope.selectedEl = c; - }); + c.find("i.umb-tree-icon").hide() + .after(""); + + $scope.target = args.node; + $scope.selectedEl = c; + }); $scope.move = function(){ diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/events-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/events-service.spec.js index 49738bbccd..a696bdd0e4 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/events-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/events-service.spec.js @@ -17,41 +17,67 @@ describe('angular event tests', function () { val: "" }; - eventsService.subscribe("testEvent", function (args) { - args.val = "changed"; + eventsService.subscribe("testEvent", function (e, args) { + args.args.val = "changed"; }); - eventsService.publish("testEvent", eventArgs).then(function (args) { - expect(eventArgs.val).toBe("changed"); - expect(args.val).toBe("changed"); - }); + eventsService.publish("testEvent", eventArgs); + + expect(eventArgs.val).toBe("changed"); $rootScope.$digest(); }); - //NOTE: This will fail because our eventsService doesn't actually wait for async events to finish. + it('will handle 2 non async process', function () { + var eventArgs = { + val: "" + }; - //it('will handle one async process', function () { - // var eventArgs = { - // val: "" - // }; + eventsService.subscribe("testEvent", function (e, args) { + args.args.val = "changed"; + }); + + eventsService.subscribe("testEvent", function (e, args) { + args.args.val = "changed1"; + }); - // eventsService.subscribe("testEvent", function (args) { - // $timeout(function() { - // args.val = "changed"; - // }, 1000); - // }); + eventsService.publish("testEvent", eventArgs); - // eventsService.publish("testEvent", eventArgs).then(function (args) { - // expect(eventArgs.val).toBe("changed"); - // expect(args.val).toBe("changed"); - // }); + expect(eventArgs.val).toBe("changed1"); - // $rootScope.$digest(); - // $timeout.flush(); - //}); + $rootScope.$digest(); + }); + + it('will handle one async process', function () { + var eventArgs = { + val: "" + }; + + eventsService.subscribe("testEvent", function (e, msg) { + $timeout(function () { + msg.args.val = "changed"; + //NOTE: We could resolve anything here + msg.resolve(msg.args); + }, 1000); + }); + + var promises = eventsService.publish("testEvent", eventArgs); + + //this won't be changed yet + expect(eventArgs.val).toBe(""); + + promises[0].then(function (args) { + console.log("WOOT"); + expect(args.val).toBe("changed"); + expect(eventArgs.val).toBe("changed"); + }); + + $rootScope.$digest(); + $timeout.flush(); + }); - it('will wait to execute after all handlers', function () { + //NOTE: This logic has been merged into the eventsService + it('POC will wait to execute after all handlers', function () { //assign multiple listeners