diff --git a/src/mw-modal/directives/mw_modal.js b/src/mw-modal/directives/mw_modal.js
index 4453f9b1..ba418c26 100644
--- a/src/mw-modal/directives/mw_modal.js
+++ b/src/mw-modal/directives/mw_modal.js
@@ -1,6 +1,6 @@
angular.module('mwUI.Modal')
- .directive('mwModal', function (mwModalTmpl) {
+ .directive('mwModal', function (mwModalTmpl, Loading) {
return {
restrict: 'A',
scope: {
@@ -8,21 +8,25 @@ angular.module('mwUI.Modal')
},
transclude: true,
templateUrl: 'uikit/mw-modal/directives/templates/mw_modal.html',
- controller: function($scope){
- this.addClass = function(styleClass){
+ controller: function ($scope) {
+ this.addClass = function (styleClass) {
$scope.addClass(styleClass);
};
},
link: function (scope, el) {
scope.$emit('COMPILE:FINISHED');
scope.mwModalTmpl = mwModalTmpl;
- scope.addClass = function(styleClass){
+ scope.addClass = function (styleClass) {
el.addClass(styleClass);
};
- if(scope.title){
+ if (scope.title) {
scope.addClass('has-header');
}
+
+ scope.showLoadingSpinner = function () {
+ return Loading.isLoading();
+ };
}
};
});
\ No newline at end of file
diff --git a/src/mw-modal/directives/templates/mw_modal.html b/src/mw-modal/directives/templates/mw_modal.html
index 3cc74e7b..b62670d5 100644
--- a/src/mw-modal/directives/templates/mw_modal.html
+++ b/src/mw-modal/directives/templates/mw_modal.html
@@ -6,6 +6,10 @@
ng-src="{{mwModalTmpl.getLogoPath()}}"
class="pull-left logo"/>
diff --git a/src/mw-modal/styles/_mw_modal.scss b/src/mw-modal/styles/_mw_modal.scss
index c85a376d..eabbb275 100644
--- a/src/mw-modal/styles/_mw_modal.scss
+++ b/src/mw-modal/styles/_mw_modal.scss
@@ -125,6 +125,9 @@ $windowPadding: 60;
box-shadow: 0 -1px 4px rgba(122,122,122,.5);
position: relative;
z-index: 1000;
+ display: flex;
+ overflow: hidden;
+ white-space: nowrap;
.logo{
margin-top: 4px;
@@ -132,6 +135,12 @@ $windowPadding: 60;
height: 16px;
}
+ h4{
+ flex-grow: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
}
.body-holder{
diff --git a/src/mw-utils/mw_utils.js b/src/mw-utils/mw_utils.js
index ee5e6415..a36b9cb5 100644
--- a/src/mw-utils/mw_utils.js
+++ b/src/mw-utils/mw_utils.js
@@ -26,6 +26,7 @@ angular.module('mwUI.Utils', ['mwUI.i18n','mwUI.Modal']);
// @include ./services/mw_bootstrap_breakpoint.js
// @include ./services/mw_browser_title_handler.js
// @include ./services/mw_callback_handler.js
+// @include ./services/mw_loading_service.js
// @include ./services/mw_scheduler.js
// @include ./services/mw_url_storage.js
// @include ./services/mw_runtime_storage.js
@@ -35,6 +36,25 @@ angular.module('mwUI.Utils', ['mwUI.i18n','mwUI.Modal']);
// @include ./shims/route_to_regex.js
// @include ./shims/deprecation_warning.js
-angular.module('mwUI.Utils').config(function(i18nProvider){
+angular.module('mwUI.Utils').config(function($provide, $httpProvider, i18nProvider){
i18nProvider.addResource('mw-utils/i18n', 'uikit');
+
+ $provide.factory('requestInterceptorForLoadingService', function ($q, Loading) {
+ return {
+ request: function(request){
+ Loading.start();
+ return request;
+ },
+ response: function (response) {
+ Loading.done();
+ return response;
+ },
+ responseError: function (response) {
+ Loading.done();
+ return $q.reject(response);
+ }
+ };
+ });
+
+ $httpProvider.interceptors.push('requestInterceptorForLoadingService');
});
\ No newline at end of file
diff --git a/src/mw-utils/services/mw_loading_service.js b/src/mw-utils/services/mw_loading_service.js
new file mode 100644
index 00000000..52da84ec
--- /dev/null
+++ b/src/mw-utils/services/mw_loading_service.js
@@ -0,0 +1,142 @@
+'use strict';
+
+angular.module('mwUI.Utils')
+
+/**
+ * @ngdoc service
+ * @name mwUI.Utils.service:Loading
+ *
+ * @description
+ * This service manages loading processes. It can be used e.g. by a httpInterceptor to register a loading process
+ */
+ .service('Loading', function ($timeout) {
+
+ var itemsToLoad = 0,
+ itemsAlreadyLoaded = 0,
+ loading = false,
+ keysToLoad = {},
+ waitUntilDoneTimout,
+ doneCallbacks = [],
+ startCallbacks = [];
+
+ var reset = function () {
+ itemsToLoad = 0;
+ itemsAlreadyLoaded = 0;
+ };
+
+ var executeCallbacks = function (callbacks, args, scope) {
+ scope = scope || this;
+ callbacks.forEach(function (callback) {
+ callback.apply(scope, args);
+ });
+ };
+
+ var setToDone = function () {
+ if (waitUntilDoneTimout) {
+ $timeout.cancel(waitUntilDoneTimout);
+ }
+ waitUntilDoneTimout = $timeout(function () {
+ loading = false;
+ executeCallbacks(doneCallbacks);
+ reset();
+ }, 100);
+ };
+
+ var registerCallback = function (array, callback) {
+ if (typeof callback === 'function') {
+ array.push(callback);
+ } else {
+ throw new Error('Callback has to be a function');
+ }
+ };
+
+ /**
+ * @ngdoc function
+ * @name start
+ * @methodOf mwUI.Utils.service:Loading
+ * @description
+ * Starts a loading process. When a key is passed a specified loading process is registered otherwise
+ * a global loading process is registered
+ * @param {String} key a unique key to start the loading process
+ */
+ this.start = function (key) {
+ if (key) {
+ keysToLoad[key] = true;
+ } else {
+ itemsToLoad++;
+ if (!loading) {
+ executeCallbacks(startCallbacks);
+ loading = true;
+ $timeout.cancel(waitUntilDoneTimout);
+ } else {
+ $timeout.cancel(waitUntilDoneTimout);
+ }
+ }
+ };
+
+ /**
+ * @ngdoc function
+ * @name done
+ * @methodOf mwUI.Utils.service:Loading
+ * @description
+ * Stops a loading process identified by a unique key.
+ * When no key is specified a global loading process counter is increased. As soon as the global loading
+ * process counter is the same as the length of registered global loading processes a debounced callback is called
+ * @param {String} key the unique key which belongs to the animation (optional)
+ */
+ this.done = function (key) {
+ if (key) {
+ delete keysToLoad[key];
+ } else {
+ if (itemsToLoad !== 0) {
+ itemsAlreadyLoaded++;
+ if (itemsToLoad === itemsAlreadyLoaded) {
+ setToDone();
+ }
+ }
+ }
+ };
+
+ /**
+ * @ngdoc function
+ * @name isLoading
+ * @methodOf mwUI.Utils.service:Loading
+ * @description
+ * When a key is specified it returns whether the loading process for that key is active
+ * otherwise it returns whether at least one process is active or none at all
+ * @param {String} key the unique key which belongs to the loading process (optional)
+ * @return {Boolean} Returns true if a loading is currently active for the given key
+ */
+ this.isLoading = function (key) {
+ if (key) {
+ return keysToLoad[key] || false;
+ } else {
+ return loading;
+ }
+ };
+
+ /**
+ * @ngdoc function
+ * @name registerDoneCallback
+ * @methodOf mwUI.Utils.service:Loading
+ * @description
+ * Registers a callback function which gets called when all loading processes are done
+ * @param {Function} callback the callback function
+ */
+ this.registerDoneCallback = function (callback) {
+ registerCallback(doneCallbacks, callback);
+ };
+
+ /**
+ * @ngdoc function
+ * @name registerStartCallback
+ * @methodOf mwUI.Utils.service:Loading
+ * @description
+ * Registers a callback function which gets called when the loading starts
+ * @param {Function} callback the callback function
+ */
+ this.registerStartCallback = function (callback) {
+ registerCallback(startCallbacks, callback);
+ };
+
+ });
\ No newline at end of file
diff --git a/src/mw-utils/services/mw_loading_service_test.js b/src/mw-utils/services/mw_loading_service_test.js
new file mode 100644
index 00000000..d8da58f7
--- /dev/null
+++ b/src/mw-utils/services/mw_loading_service_test.js
@@ -0,0 +1,93 @@
+'use strict';
+
+describe('Loading', function () {
+ var service, rootScope;
+ var $timeout;
+ beforeEach(module('mwUI.Utils'));
+
+ afterEach(function () {
+ $timeout.verifyNoPendingTasks();
+ service = null;
+ rootScope = null;
+ $timeout = null;
+ });
+
+ beforeEach(inject(function (_$rootScope_, _Loading_, _$timeout_) {
+ rootScope = _$rootScope_;
+ service = _Loading_;
+ $timeout = _$timeout_;
+ }));
+
+ it('can be initialized', function () {
+ expect(service).toBeDefined();
+ });
+
+ it('stores a loading key', function () {
+ service.start('test-key');
+ expect(service.isLoading('test-key')).toBeTruthy();
+ service.done('test-key');
+ expect(service.isLoading('test-key')).toBeFalsy();
+ });
+
+ it('isLoading can handle undefined as key', function () {
+ expect(service.isLoading(undefined)).toBeFalsy();
+ });
+
+ it('isLoading can handle undefined as key and returns true when global loading process is in progress', function () {
+ service.start();
+ expect(service.isLoading()).toBeTruthy();
+ });
+
+ it('isLoading can handle undefined as key and returns false when no global loading process is in progress anymore', function () {
+ service.start();
+ service.done();
+ $timeout.flush();
+
+ expect(service.isLoading()).toBeFalsy();
+ });
+
+ it('isLoading can handle undefined as key and returns true when at least one global loading process is in progress', function () {
+ service.start();
+ service.start();
+
+ service.done();
+
+ expect(service.isLoading()).toBeTruthy();
+ });
+
+ it('isLoading can handle undefined as key and returns false when all global loading process are done', function () {
+ service.start();
+ service.start();
+
+ service.done();
+ service.done();
+ $timeout.flush();
+
+ expect(service.isLoading()).toBeFalsy();
+ });
+
+ it('isLoading can handle null as key', function () {
+ expect(service.isLoading(null)).toBeFalsy();
+ });
+
+ it('isLoading can unknown key', function () {
+ expect(service.isLoading('something')).toBeFalsy();
+ });
+
+ it('sets a loading session with callbacks when no key is provided', function () {
+ var startCallback = jasmine.createSpy('startSpy');
+ var doneCallback = jasmine.createSpy('doneSpy');
+ service.registerStartCallback(startCallback);
+ service.registerDoneCallback(doneCallback);
+ service.start();
+ rootScope.$digest();
+ expect(service.isLoading()).toBeTruthy();
+ service.done();
+ rootScope.$digest();
+ $timeout.flush();
+ expect(service.isLoading()).toBeFalsy();
+
+ expect(startCallback).toHaveBeenCalled();
+ expect(doneCallback).toHaveBeenCalled();
+ });
+});
\ No newline at end of file