Rearrange client mediafile app.

This commit is contained in:
Norman Jäckel 2016-12-14 16:32:24 +01:00
parent deb5c5f4b3
commit 1221ef4f02
8 changed files with 502 additions and 430 deletions

View File

@ -0,0 +1,38 @@
(function () {
'use strict';
angular.module('OpenSlidesApp.mediafiles.create', [
'OpenSlidesApp.mediafiles.forms',
//TODO: Add deps for User
])
.controller('MediafileCreateCtrl', [
'$scope',
'User',
'MediafileForm',
function ($scope, User, MediafileForm) {
User.bindAll({}, $scope, 'users');
$scope.mediafile = {};
$scope.alert = {};
$scope.users = User.getAll();
// upload and save mediafile
$scope.save = function (mediafile) {
MediafileForm.uploadFile(mediafile).then(
function (success) {
$scope.closeThisDialog();
},
function (error) {
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
}
]);
}());

View File

@ -0,0 +1,57 @@
(function () {
'use strict';
angular.module('OpenSlidesApp.mediafiles.forms', [
'gettext',
'ngFileUpload',
'ui.router',
//TODO: Add deps for operator, User
])
// Service for mediafile form
.factory('MediafileForm', [
'gettextCatalog',
'$state',
'Upload',
'operator',
'User',
function (gettextCatalog, $state, Upload, operator, User) {
return {
// ngDialog for mediafile form
getDialog: function (mediafile) {
var resolve;
if (mediafile) {
resolve = {
mediafile: function(Assignment) {return mediafile;}
};
}
return {
template: 'static/templates/mediafiles/mediafile-form.html',
controller: (mediafile) ? 'MediafileUpdateCtrl' : 'MediafileCreateCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
resolve: (resolve) ? resolve : null
};
},
// upload selected file (used by create view only)
uploadFile: function (mediafile) {
if (!mediafile.title) {
mediafile.title = mediafile.newFile.name;
}
if (!mediafile.uploader_id) {
mediafile.uploader_id = operator.user.id;
}
return Upload.upload({
url: '/rest/mediafiles/mediafile/',
method: 'POST',
data: {mediafile: mediafile.newFile, title: mediafile.title, uploader_id: mediafile.uploader_id, hidden: mediafile.hidden}
});
}
};
}
]);
}());

View File

@ -0,0 +1,274 @@
(function () {
'use strict';
angular.module('OpenSlidesApp.mediafiles.list', [
'gettext',
'ngDialog',
'OpenSlidesApp.mediafiles.forms',
'OpenSlidesApp.mediafiles.resources',
//TODO: Add deps for operator, User, Projector, ProjectionDefault, osTableFilter, osTableSort,
])
.controller('MediafileListCtrl', [
'$http',
'$scope',
'gettext',
'ngDialog',
'osTableFilter',
'osTableSort',
'ProjectionDefault',
'Projector',
'User',
'Mediafile',
'MediafileForm',
function ($http, $scope, gettext, ngDialog, osTableFilter, osTableSort,
ProjectionDefault, Projector, User, Mediafile, MediafileForm) {
Mediafile.bindAll({}, $scope, 'mediafiles');
User.bindAll({}, $scope, 'users');
$scope.$watch(function() {
return Projector.lastModified();
}, function() {
$scope.projectors = Projector.getAll();
updatePresentedMediafiles();
});
$scope.$watch(function () {
return Projector.lastModified();
}, function () {
var projectiondefault = ProjectionDefault.filter({name: 'mediafiles'})[0];
if (projectiondefault) {
$scope.defaultProjectorId = projectiondefault.projector_id;
}
});
function updatePresentedMediafiles () {
$scope.presentedMediafiles = [];
Projector.getAll().forEach(function (projector) {
var projectorElements = _.map(projector.elements, function(element) { return element; });
var mediaElements = _.filter(projectorElements, function (element) {
return element.name === 'mediafiles/mediafile';
});
mediaElements.forEach(function (element) {
$scope.presentedMediafiles.push(element);
});
});
if ($scope.presentedMediafiles.length) {
$scope.isMeta = false;
} else {
$scope.isMeta = true;
}
}
updatePresentedMediafiles();
// Filtering
$scope.filter = osTableFilter.createInstance('MediafilesTableFilter');
if (!$scope.filter.existsStorageEntry()) {
$scope.filter.booleanFilters = {
isHidden: {
value: undefined,
displayName: gettext('Hidden'),
choiceYes: gettext('Is hidden'),
choiceNo: gettext('Is not hidden'),
needExtraPermission: true,
},
isPdf: {
value: undefined,
displayName: gettext('Is PDF'),
choiceYes: gettext('Is PDF file'),
choiceNo: gettext('Is no PDF file'),
},
};
}
$scope.filter.propertyList = ['title_or_filename'];
$scope.filter.propertyFunctionList = [
function (mediafile) {return mediafile.uploader.get_short_name();},
function (mediafile) {return mediafile.mediafile.type;},
function (mediafile) {return mediafile.mediafile.name;},
];
// Sorting
$scope.sort = osTableSort.createInstance();
$scope.sort.column = 'title_or_filename';
$scope.sortOptions = [
{name: 'title_or_filename',
display_name: gettext('Title')},
{name: 'mediafile.type',
display_name: gettext('Type')},
{name: 'filesize',
display_name: gettext('File size')},
{name: 'timestamp',
display_name: gettext('Upload time')},
{name: 'uploader.get_short_name()',
display_name: gettext('Uploaded by')},
];
// open new/edit dialog
$scope.openDialog = function (mediafile) {
ngDialog.open(MediafileForm.getDialog(mediafile));
};
// *** select mode functions ***
$scope.isSelectMode = false;
// check all checkboxes
$scope.checkAll = function () {
angular.forEach($scope.mediafiles, function (mediafile) {
mediafile.selected = $scope.selectedAll;
});
};
// uncheck all checkboxes if SelectMode is closed
$scope.uncheckAll = function () {
if (!$scope.isSelectMode) {
$scope.selectedAll = false;
angular.forEach($scope.mediafiles, function (mediafile) {
mediafile.selected = false;
});
}
};
// delete all selected mediafiles
$scope.deleteMultiple = function () {
angular.forEach($scope.mediafiles, function (mediafile) {
if (mediafile.selected)
Mediafile.destroy(mediafile.id);
});
$scope.isSelectMode = false;
$scope.uncheckAll();
};
// delete single mediafile
$scope.delete = function (mediafile) {
Mediafile.destroy(mediafile.id);
};
// ** PDF presentation functions **/
// show document on projector
$scope.showMediafile = function (projectorId, mediafile) {
var isProjectedIds = mediafile.isProjected();
_.forEach(isProjectedIds, function (id) {
$http.post('/rest/core/projector/' + id + '/clear_elements/');
});
if (_.indexOf(isProjectedIds, projectorId) == -1) {
var postUrl = '/rest/core/projector/' + projectorId + '/prune_elements/';
var data = [{
name: 'mediafiles/mediafile',
id: mediafile.id,
numPages: mediafile.mediafile.pages,
page: 1,
scale: 'page-fit',
rotate: 0,
visible: true,
playing: false,
fullscreen: mediafile.is_pdf
}];
$http.post(postUrl, data);
}
};
var sendMediafileCommand = function (mediafile, data) {
var updateData = _.extend({}, mediafile);
_.extend(updateData, data);
var postData = {};
postData[mediafile.uuid] = updateData;
// Find Projector where the mediafile is projected
$scope.projectors.forEach(function (projector) {
if (_.find(projector.elements, function (e) {return e.uuid == mediafile.uuid;})) {
$http.post('/rest/core/projector/' + projector.id + '/update_elements/', postData);
}
});
};
$scope.getTitle = function (mediafile) {
return Mediafile.get(mediafile.id).title;
};
$scope.getType = function (presentedMediafile) {
var mediafile = Mediafile.get(presentedMediafile.id);
return mediafile.is_pdf ? 'pdf' : mediafile.is_image ? 'image' : 'video';
};
$scope.mediafileGoToPage = function (mediafile, page) {
if (parseInt(page) > 0) {
sendMediafileCommand(
mediafile,
{page: parseInt(page)}
);
}
};
$scope.mediafileZoomIn = function (mediafile) {
var scale = 1;
if (parseFloat(mediafile.scale)) {
scale = mediafile.scale;
}
sendMediafileCommand(
mediafile,
{scale: scale + 0.2}
);
};
$scope.mediafileFit = function (mediafile) {
sendMediafileCommand(
mediafile,
{scale: 'page-fit'}
);
};
$scope.mediafileZoomOut = function (mediafile) {
var scale = 1;
if (parseFloat(mediafile.scale)) {
scale = mediafile.scale;
}
sendMediafileCommand(
mediafile,
{scale: scale - 0.2}
);
};
$scope.mediafileChangePage = function (mediafile, pageNum) {
sendMediafileCommand(
mediafile,
{pageToDisplay: pageNum}
);
};
$scope.mediafileRotate = function (mediafile) {
var rotation = mediafile.rotate;
if (rotation === 270) {
rotation = 0;
} else {
rotation = rotation + 90;
}
sendMediafileCommand(
mediafile,
{rotate: rotation}
);
};
$scope.mediafileToggleFullscreen = function (mediafile) {
sendMediafileCommand(
mediafile,
{fullscreen: !mediafile.fullscreen}
);
};
$scope.mediafileTogglePlaying = function (mediafile) {
sendMediafileCommand(
mediafile,
{playing: !mediafile.playing}
);
};
}
])
/*
* Special filter only for mediafile list view.
*/
.filter('hiddenFilter', [
'$filter',
'operator',
function ($filter, operator) {
return function (array) {
if (operator.hasPerms('mediafiles.can_see_hidden')) {
return array;
}
return Array.prototype.filter.call(array, function (item) {
return !item.hidden;
});
};
}
]);
}());

View File

@ -2,7 +2,10 @@
'use strict'; 'use strict';
angular.module('OpenSlidesApp.mediafiles.projector', ['OpenSlidesApp.mediafiles']) angular.module('OpenSlidesApp.mediafiles.projector', [
'OpenSlidesApp.mediafiles.resources',
//TODO: Add deps for slidesProvider
])
.config([ .config([
'slidesProvider', 'slidesProvider',
@ -40,4 +43,4 @@ angular.module('OpenSlidesApp.mediafiles.projector', ['OpenSlidesApp.mediafiles'
} }
]); ]);
})(); }());

View File

@ -2,13 +2,17 @@
'use strict'; 'use strict';
angular.module('OpenSlidesApp.mediafiles', []) angular.module('OpenSlidesApp.mediafiles.resources', [
'gettext',
'js-data',
//TODO: Add deps for jsDataModel
])
.factory('Mediafile', [ .factory('Mediafile', [
'DS', 'DS',
'jsDataModel',
'gettext', 'gettext',
function(DS, jsDataModel, gettext) { 'jsDataModel',
function (DS, gettext, jsDataModel) {
var name = 'mediafiles/mediafile'; var name = 'mediafiles/mediafile';
return DS.defineResource({ return DS.defineResource({
name: name, name: name,

View File

@ -2,428 +2,11 @@
'use strict'; 'use strict';
angular.module('OpenSlidesApp.mediafiles.site', ['ngFileUpload', 'OpenSlidesApp.mediafiles']) angular.module('OpenSlidesApp.mediafiles.site', [
'OpenSlidesApp.mediafiles.create',
.config([ 'OpenSlidesApp.mediafiles.list',
'mainMenuProvider', 'OpenSlidesApp.mediafiles.states',
'gettext', 'OpenSlidesApp.mediafiles.update',
function (mainMenuProvider, gettext) {
mainMenuProvider.register({
'ui_sref': 'mediafiles.mediafile.list',
'img_class': 'paperclip',
'title': gettext('Files'),
'weight': 600,
'perm': 'mediafiles.can_see',
});
}
])
.config([
'$stateProvider',
'gettext',
function($stateProvider, gettext) {
$stateProvider
.state('mediafiles', {
url: '/mediafiles',
abstract: true,
template: "<ui-view/>",
data: {
title: gettext('Files'),
},
})
.state('mediafiles.mediafile', {
abstract: true,
template: "<ui-view/>",
})
.state('mediafiles.mediafile.list', {
resolve: {
mediafiles: function(Mediafile) {
return Mediafile.findAll();
},
users: function(User) {
return User.findAll().catch(
function () {
return null;
}
);
},
}
});
}
])
.controller('MediafileListCtrl', [
'$scope',
'$http',
'ngDialog',
'Mediafile',
'MediafileForm',
'User',
'Projector',
'ProjectionDefault',
'osTableFilter',
'osTableSort',
'gettext',
function($scope, $http, ngDialog, Mediafile, MediafileForm, User, Projector, ProjectionDefault, osTableFilter, osTableSort, gettext) {
Mediafile.bindAll({}, $scope, 'mediafiles');
User.bindAll({}, $scope, 'users');
$scope.$watch(function() {
return Projector.lastModified();
}, function() {
$scope.projectors = Projector.getAll();
updatePresentedMediafiles();
});
$scope.$watch(function () {
return Projector.lastModified();
}, function () {
var projectiondefault = ProjectionDefault.filter({name: 'mediafiles'})[0];
if (projectiondefault) {
$scope.defaultProjectorId = projectiondefault.projector_id;
}
});
function updatePresentedMediafiles () {
$scope.presentedMediafiles = [];
Projector.getAll().forEach(function (projector) {
var projectorElements = _.map(projector.elements, function(element) { return element; });
var mediaElements = _.filter(projectorElements, function (element) {
return element.name === 'mediafiles/mediafile';
});
mediaElements.forEach(function (element) {
$scope.presentedMediafiles.push(element);
});
});
if ($scope.presentedMediafiles.length) {
$scope.isMeta = false;
} else {
$scope.isMeta = true;
}
}
updatePresentedMediafiles();
// Filtering
$scope.filter = osTableFilter.createInstance('MediafilesTableFilter');
if (!$scope.filter.existsStorageEntry()) {
$scope.filter.booleanFilters = {
isHidden: {
value: undefined,
displayName: gettext('Hidden'),
choiceYes: gettext('Is hidden'),
choiceNo: gettext('Is not hidden'),
needExtraPermission: true,
},
isPdf: {
value: undefined,
displayName: gettext('Is PDF'),
choiceYes: gettext('Is PDF file'),
choiceNo: gettext('Is no PDF file'),
},
};
}
$scope.filter.propertyList = ['title_or_filename'];
$scope.filter.propertyFunctionList = [
function (mediafile) {return mediafile.uploader.get_short_name();},
function (mediafile) {return mediafile.mediafile.type;},
function (mediafile) {return mediafile.mediafile.name;},
];
// Sorting
$scope.sort = osTableSort.createInstance();
$scope.sort.column = 'title_or_filename';
$scope.sortOptions = [
{name: 'title_or_filename',
display_name: gettext('Title')},
{name: 'mediafile.type',
display_name: gettext('Type')},
{name: 'filesize',
display_name: gettext('File size')},
{name: 'timestamp',
display_name: gettext('Upload time')},
{name: 'uploader.get_short_name()',
display_name: gettext('Uploaded by')},
];
// open new/edit dialog
$scope.openDialog = function (mediafile) {
ngDialog.open(MediafileForm.getDialog(mediafile));
};
// *** select mode functions ***
$scope.isSelectMode = false;
// check all checkboxes
$scope.checkAll = function () {
angular.forEach($scope.mediafiles, function (mediafile) {
mediafile.selected = $scope.selectedAll;
});
};
// uncheck all checkboxes if SelectMode is closed
$scope.uncheckAll = function () {
if (!$scope.isSelectMode) {
$scope.selectedAll = false;
angular.forEach($scope.mediafiles, function (mediafile) {
mediafile.selected = false;
});
}
};
// delete all selected mediafiles
$scope.deleteMultiple = function () {
angular.forEach($scope.mediafiles, function (mediafile) {
if (mediafile.selected)
Mediafile.destroy(mediafile.id);
});
$scope.isSelectMode = false;
$scope.uncheckAll();
};
// delete single mediafile
$scope.delete = function (mediafile) {
Mediafile.destroy(mediafile.id);
};
// ** PDF presentation functions **/
// show document on projector
$scope.showMediafile = function (projectorId, mediafile) {
var isProjectedIds = mediafile.isProjected();
_.forEach(isProjectedIds, function (id) {
$http.post('/rest/core/projector/' + id + '/clear_elements/');
});
if (_.indexOf(isProjectedIds, projectorId) == -1) {
var postUrl = '/rest/core/projector/' + projectorId + '/prune_elements/';
var data = [{
name: 'mediafiles/mediafile',
id: mediafile.id,
numPages: mediafile.mediafile.pages,
page: 1,
scale: 'page-fit',
rotate: 0,
visible: true,
playing: false,
fullscreen: mediafile.is_pdf
}];
$http.post(postUrl, data);
}
};
var sendMediafileCommand = function (mediafile, data) {
var updateData = _.extend({}, mediafile);
_.extend(updateData, data);
var postData = {};
postData[mediafile.uuid] = updateData;
// Find Projector where the mediafile is projected
$scope.projectors.forEach(function (projector) {
if (_.find(projector.elements, function (e) {return e.uuid == mediafile.uuid;})) {
$http.post('/rest/core/projector/' + projector.id + '/update_elements/', postData);
}
});
};
$scope.getTitle = function (mediafile) {
return Mediafile.get(mediafile.id).title;
};
$scope.getType = function(presentedMediafile) {
var mediafile = Mediafile.get(presentedMediafile.id);
return mediafile.is_pdf ? 'pdf' : mediafile.is_image ? 'image' : 'video';
};
$scope.mediafileGoToPage = function (mediafile, page) {
if (parseInt(page) > 0) {
sendMediafileCommand(
mediafile,
{page: parseInt(page)}
);
}
};
$scope.mediafileZoomIn = function (mediafile) {
var scale = 1;
if (parseFloat(mediafile.scale)) {
scale = mediafile.scale;
}
sendMediafileCommand(
mediafile,
{scale: scale + 0.2}
);
};
$scope.mediafileFit = function (mediafile) {
sendMediafileCommand(
mediafile,
{scale: 'page-fit'}
);
};
$scope.mediafileZoomOut = function (mediafile) {
var scale = 1;
if (parseFloat(mediafile.scale)) {
scale = mediafile.scale;
}
sendMediafileCommand(
mediafile,
{scale: scale - 0.2}
);
};
$scope.mediafileChangePage = function(mediafile, pageNum) {
sendMediafileCommand(
mediafile,
{pageToDisplay: pageNum}
);
};
$scope.mediafileRotate = function (mediafile) {
var rotation = mediafile.rotate;
if (rotation === 270) {
rotation = 0;
} else {
rotation = rotation + 90;
}
sendMediafileCommand(
mediafile,
{rotate: rotation}
);
};
$scope.mediafileToggleFullscreen = function(mediafile) {
sendMediafileCommand(
mediafile,
{fullscreen: !mediafile.fullscreen}
);
};
$scope.mediafileTogglePlaying = function(mediafile) {
sendMediafileCommand(
mediafile,
{playing: !mediafile.playing}
);
};
}
])
.controller('MediafileCreateCtrl', [
'$scope',
'MediafileForm',
'User',
function($scope, MediafileForm, User) {
User.bindAll({}, $scope, 'users');
$scope.mediafile = {};
$scope.alert = {};
$scope.users = User.getAll();
// upload and save mediafile
$scope.save = function (mediafile) {
MediafileForm.uploadFile(mediafile).then(
function (success) {
$scope.closeThisDialog();
},
function (error) {
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
}
])
.controller('MediafileUpdateCtrl', [
'$scope',
'operator',
'Mediafile',
'User',
'mediafile',
function($scope, operator, Mediafile, User, mediafile) {
User.bindAll({}, $scope, 'users');
$scope.alert = {};
$scope.users = User.getAll();
// set initial values for form model by create deep copy of motion object
// so list/detail view is not updated while editing
$scope.mediafile = angular.copy(mediafile);
// save mediafile
$scope.save = function (mediafile) {
// reset title and uploader_id if empty
if (!mediafile.title) {
mediafile.title = mediafile.filename;
}
if (!mediafile.uploader_id) {
mediafile.uploader_id = operator.user.id;
}
// inject the changed mediafile (copy) object back into DS store
Mediafile.inject(mediafile);
// save change mediafile object on server
Mediafile.save(mediafile, { method: 'PATCH' }).then(
function (success) {
$scope.closeThisDialog();
},
function (error) {
Mediafile.refresh(mediafile);
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
}
])
// Service for mediafile form
.factory('MediafileForm', [
'$state',
'operator',
'Upload',
'gettextCatalog',
'User',
function ($state, operator, Upload, gettextCatalog, User) {
return {
// ngDialog for mediafile form
getDialog: function (mediafile) {
var resolve;
if (mediafile) {
resolve = {
mediafile: function(Assignment) {return mediafile;}
};
}
return {
template: 'static/templates/mediafiles/mediafile-form.html',
controller: (mediafile) ? 'MediafileUpdateCtrl' : 'MediafileCreateCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
resolve: (resolve) ? resolve : null
};
},
// upload selected file (used by create view only)
uploadFile: function (mediafile) {
if (!mediafile.title) {
mediafile.title = mediafile.newFile.name;
}
if (!mediafile.uploader_id) {
mediafile.uploader_id = operator.user.id;
}
return Upload.upload({
url: '/rest/mediafiles/mediafile/',
method: 'POST',
data: {mediafile: mediafile.newFile, title: mediafile.title, uploader_id: mediafile.uploader_id, hidden: mediafile.hidden}
});
}
};
}
])
.filter('hiddenFilter', [
'$filter',
'operator',
function ($filter, operator) {
return function (array) {
if (operator.hasPerms('mediafiles.can_see_hidden')) {
return array;
}
return Array.prototype.filter.call(array, function (item) {
return !item.hidden;
});
};
}
]); ]);
}()); }());

View File

@ -0,0 +1,59 @@
(function () {
'use strict';
angular.module('OpenSlidesApp.mediafiles.states', [
'gettext',
'ui.router',
//TODO: Add deps for mainMenuProvider
])
.config([
'gettext',
'mainMenuProvider',
function (gettext, mainMenuProvider) {
mainMenuProvider.register({
'ui_sref': 'mediafiles.mediafile.list',
'img_class': 'paperclip',
'title': gettext('Files'),
'weight': 600,
'perm': 'mediafiles.can_see',
});
}
])
.config([
'gettext',
'$stateProvider',
function (gettext, $stateProvider) {
$stateProvider
.state('mediafiles', {
url: '/mediafiles',
abstract: true,
template: "<ui-view/>",
data: {
title: gettext('Files'),
},
})
.state('mediafiles.mediafile', {
abstract: true,
template: "<ui-view/>",
})
.state('mediafiles.mediafile.list', {
resolve: {
mediafiles: function (Mediafile) {
return Mediafile.findAll();
},
users: function (User) {
return User.findAll().catch(
function () {
return null;
}
);
},
}
});
}
]);
}());

View File

@ -0,0 +1,54 @@
(function () {
'use strict';
angular.module('OpenSlidesApp.mediafiles.update', [
'OpenSlidesApp.mediafiles.resources',
//TODO: Add deps for operator, User
])
.controller('MediafileUpdateCtrl', [
'$scope',
'operator',
'User',
'Mediafile',
'mediafile',
function ($scope, operator, User, Mediafile, mediafile) {
User.bindAll({}, $scope, 'users');
$scope.alert = {};
$scope.users = User.getAll();
// set initial values for form model by create deep copy of motion object
// so list/detail view is not updated while editing
$scope.mediafile = angular.copy(mediafile);
// save mediafile
$scope.save = function (mediafile) {
// reset title and uploader_id if empty
if (!mediafile.title) {
mediafile.title = mediafile.filename;
}
if (!mediafile.uploader_id) {
mediafile.uploader_id = operator.user.id;
}
// inject the changed mediafile (copy) object back into DS store
Mediafile.inject(mediafile);
// save change mediafile object on server
Mediafile.save(mediafile, { method: 'PATCH' }).then(
function (success) {
$scope.closeThisDialog();
},
function (error) {
Mediafile.refresh(mediafile);
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
}
]);
}());