d9447f6224
EmojiMenu click scroll fix EmojiMenu tail Emojiarea improve paste hadling Emoji, attach icons styles (opacity + color on active state) Message better placeholder styles Notifications click improve (now focus field) Send message from modal now closes all modals Send message now scrolls down Message drafts now support line breaks Correct display ‘f’, ’n’, ‘no’ messages Group info click causes double modal window fixed Document is now downloaded on icon click
354 lines
11 KiB
JavaScript
354 lines
11 KiB
JavaScript
angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.modal"]);
|
|
angular.module("ui.bootstrap.tpls", ["template/modal/backdrop.html","template/modal/window.html"]);
|
|
angular.module('ui.bootstrap.modal', [])
|
|
|
|
/**
|
|
* A helper, internal data structure that acts as a map but also allows getting / removing
|
|
* elements in the LIFO order
|
|
*/
|
|
.factory('$$stackedMap', function () {
|
|
return {
|
|
createNew: function () {
|
|
var stack = [];
|
|
|
|
return {
|
|
add: function (key, value) {
|
|
stack.push({
|
|
key: key,
|
|
value: value
|
|
});
|
|
},
|
|
get: function (key) {
|
|
for (var i = 0; i < stack.length; i++) {
|
|
if (key == stack[i].key) {
|
|
return stack[i];
|
|
}
|
|
}
|
|
},
|
|
keys: function() {
|
|
var keys = [];
|
|
for (var i = 0; i < stack.length; i++) {
|
|
keys.push(stack[i].key);
|
|
}
|
|
return keys;
|
|
},
|
|
top: function () {
|
|
return stack[stack.length - 1];
|
|
},
|
|
remove: function (key) {
|
|
var idx = -1;
|
|
for (var i = 0; i < stack.length; i++) {
|
|
if (key == stack[i].key) {
|
|
idx = i;
|
|
break;
|
|
}
|
|
}
|
|
return stack.splice(idx, 1)[0];
|
|
},
|
|
removeTop: function () {
|
|
return stack.splice(stack.length - 1, 1)[0];
|
|
},
|
|
length: function () {
|
|
return stack.length;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
})
|
|
|
|
/**
|
|
* A helper directive for the $modal service. It creates a backdrop element.
|
|
*/
|
|
.directive('modalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
|
|
return {
|
|
restrict: 'EA',
|
|
replace: true,
|
|
templateUrl: 'template/modal/backdrop.html',
|
|
link: function (scope, element, attrs) {
|
|
|
|
//trigger CSS transitions
|
|
$timeout(function () {
|
|
scope.animate = true;
|
|
});
|
|
|
|
scope.close = function (evt) {
|
|
var modal = $modalStack.getTop();
|
|
if (modal && modal.value.backdrop && modal.value.backdrop != 'static') {
|
|
evt.preventDefault();
|
|
evt.stopPropagation();
|
|
$modalStack.dismiss(modal.key, 'backdrop click');
|
|
}
|
|
};
|
|
}
|
|
};
|
|
}])
|
|
|
|
.directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
|
|
return {
|
|
restrict: 'EA',
|
|
scope: {
|
|
index: '@'
|
|
},
|
|
replace: true,
|
|
transclude: true,
|
|
templateUrl: 'template/modal/window.html',
|
|
link: function (scope, element, attrs) {
|
|
scope.windowClass = attrs.windowClass || '';
|
|
|
|
//trigger CSS transitions
|
|
$timeout(function () {
|
|
scope.animate = true;
|
|
});
|
|
|
|
scope.close = function (evt) {
|
|
var modal = $modalStack.getTop();
|
|
if (modal) {
|
|
evt.preventDefault();
|
|
evt.stopPropagation();
|
|
$modalStack.dismiss(modal.key, 'backdrop click');
|
|
}
|
|
};
|
|
}
|
|
};
|
|
}])
|
|
|
|
.factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap',
|
|
function ($document, $compile, $rootScope, $$stackedMap) {
|
|
|
|
var backdropjqLiteEl, backdropDomEl;
|
|
var backdropScope = $rootScope.$new(true);
|
|
var body = $document.find('body').eq(0);
|
|
var openedWindows = $$stackedMap.createNew();
|
|
var $modalStack = {};
|
|
|
|
function backdropIndex() {
|
|
var topBackdropIndex = -1;
|
|
var opened = openedWindows.keys();
|
|
for (var i = 0; i < opened.length; i++) {
|
|
if (openedWindows.get(opened[i]).value.backdrop) {
|
|
topBackdropIndex = i;
|
|
}
|
|
}
|
|
return topBackdropIndex;
|
|
}
|
|
|
|
$rootScope.$watch(backdropIndex, function(newBackdropIndex){
|
|
backdropScope.index = newBackdropIndex;
|
|
});
|
|
|
|
function removeModalWindow(modalInstance) {
|
|
|
|
var modalWindow = openedWindows.get(modalInstance).value;
|
|
|
|
//clean up the stack
|
|
openedWindows.remove(modalInstance);
|
|
|
|
//remove window DOM element
|
|
modalWindow.modalDomEl.remove();
|
|
|
|
//remove backdrop if no longer needed
|
|
if (backdropDomEl && backdropIndex() == -1) {
|
|
backdropDomEl.remove();
|
|
backdropDomEl = undefined;
|
|
}
|
|
|
|
//destroy scope
|
|
modalWindow.modalScope.$destroy();
|
|
}
|
|
|
|
$document.bind('keydown', function (evt) {
|
|
var modal;
|
|
|
|
if (evt.which === 27) {
|
|
modal = openedWindows.top();
|
|
if (modal && modal.value.keyboard) {
|
|
$rootScope.$apply(function () {
|
|
$modalStack.dismiss(modal.key);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
$modalStack.open = function (modalInstance, modal) {
|
|
|
|
openedWindows.add(modalInstance, {
|
|
deferred: modal.deferred,
|
|
modalScope: modal.scope,
|
|
backdrop: modal.backdrop,
|
|
keyboard: modal.keyboard
|
|
});
|
|
|
|
var angularDomEl = angular.element('<div modal-window></div>');
|
|
angularDomEl.attr('window-class', modal.windowClass);
|
|
angularDomEl.attr('index', openedWindows.length() - 1);
|
|
angularDomEl.html(modal.content);
|
|
|
|
var modalDomEl = $compile(angularDomEl)(modal.scope);
|
|
openedWindows.top().value.modalDomEl = modalDomEl;
|
|
body.append(modalDomEl);
|
|
|
|
if (backdropIndex() >= 0 && !backdropDomEl) {
|
|
backdropjqLiteEl = angular.element('<div modal-backdrop></div>');
|
|
backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
|
|
body.append(backdropDomEl);
|
|
}
|
|
};
|
|
|
|
$modalStack.close = function (modalInstance, result) {
|
|
var modal = openedWindows.get(modalInstance);
|
|
if (modal) {
|
|
modal.value.deferred.resolve(result);
|
|
removeModalWindow(modalInstance);
|
|
}
|
|
};
|
|
|
|
$modalStack.dismiss = function (modalInstance, reason) {
|
|
var modalWindow = openedWindows.get(modalInstance).value;
|
|
if (modalWindow) {
|
|
modalWindow.deferred.reject(reason);
|
|
removeModalWindow(modalInstance);
|
|
}
|
|
};
|
|
|
|
$modalStack.dismissAll = function () {
|
|
var modal;
|
|
while (openedWindows.length()) {
|
|
if (modal = openedWindows.top()) {
|
|
$modalStack.dismiss(modal.key);
|
|
}
|
|
}
|
|
}
|
|
|
|
$modalStack.getTop = function () {
|
|
return openedWindows.top();
|
|
};
|
|
|
|
return $modalStack;
|
|
}])
|
|
|
|
.provider('$modal', function () {
|
|
|
|
var $modalProvider = {
|
|
options: {
|
|
backdrop: true, //can be also false or 'static'
|
|
keyboard: true
|
|
},
|
|
$get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
|
|
function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
|
|
|
|
var $modal = {};
|
|
|
|
function getTemplatePromise(options) {
|
|
return options.template ? $q.when(options.template) :
|
|
$http.get(options.templateUrl, {cache: $templateCache}).then(function (result) {
|
|
return result.data;
|
|
});
|
|
}
|
|
|
|
function getResolvePromises(resolves) {
|
|
var promisesArr = [];
|
|
angular.forEach(resolves, function (value, key) {
|
|
if (angular.isFunction(value) || angular.isArray(value)) {
|
|
promisesArr.push($q.when($injector.invoke(value)));
|
|
}
|
|
});
|
|
return promisesArr;
|
|
}
|
|
|
|
$modal.open = function (modalOptions) {
|
|
|
|
var modalResultDeferred = $q.defer();
|
|
var modalOpenedDeferred = $q.defer();
|
|
|
|
//prepare an instance of a modal to be injected into controllers and returned to a caller
|
|
var modalInstance = {
|
|
result: modalResultDeferred.promise,
|
|
opened: modalOpenedDeferred.promise,
|
|
close: function (result) {
|
|
$modalStack.close(modalInstance, result);
|
|
},
|
|
dismiss: function (reason) {
|
|
$modalStack.dismiss(modalInstance, reason);
|
|
}
|
|
};
|
|
|
|
//merge and clean up options
|
|
modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
|
|
modalOptions.resolve = modalOptions.resolve || {};
|
|
|
|
//verify options
|
|
if (!modalOptions.template && !modalOptions.templateUrl) {
|
|
throw new Error('One of template or templateUrl options is required.');
|
|
}
|
|
|
|
var templateAndResolvePromise =
|
|
$q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
|
|
|
|
|
|
templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
|
|
|
|
var modalScope = (modalOptions.scope || $rootScope).$new();
|
|
modalScope.$close = modalInstance.close;
|
|
modalScope.$dismiss = modalInstance.dismiss;
|
|
|
|
var ctrlInstance, ctrlLocals = {};
|
|
var resolveIter = 1;
|
|
|
|
//controllers
|
|
if (modalOptions.controller) {
|
|
ctrlLocals.$scope = modalScope;
|
|
ctrlLocals.$modalInstance = modalInstance;
|
|
angular.forEach(modalOptions.resolve, function (value, key) {
|
|
ctrlLocals[key] = tplAndVars[resolveIter++];
|
|
});
|
|
|
|
ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
|
|
}
|
|
|
|
$modalStack.open(modalInstance, {
|
|
scope: modalScope,
|
|
deferred: modalResultDeferred,
|
|
content: tplAndVars[0],
|
|
backdrop: modalOptions.backdrop,
|
|
keyboard: modalOptions.keyboard,
|
|
windowClass: modalOptions.windowClass
|
|
});
|
|
|
|
}, function resolveError(reason) {
|
|
modalResultDeferred.reject(reason);
|
|
});
|
|
|
|
templateAndResolvePromise.then(function () {
|
|
modalOpenedDeferred.resolve(true);
|
|
}, function () {
|
|
modalOpenedDeferred.reject(false);
|
|
});
|
|
|
|
return modalInstance;
|
|
};
|
|
|
|
return $modal;
|
|
}]
|
|
};
|
|
|
|
return $modalProvider;
|
|
});
|
|
|
|
angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
|
|
$templateCache.put("template/modal/backdrop.html",
|
|
"<div class=\"modal-backdrop fade\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1040 + index*10}\" ng-click=\"close($event)\"></div>");
|
|
}]);
|
|
|
|
angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
|
|
$templateCache.put("template/modal/window.html",
|
|
"<div class=\"modal fade {{ windowClass }}\" tabindex=\"-1\" role=\"dialog\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\
|
|
<div class=\"modal_close_wrap\" ng-click=\"close($event)\">\
|
|
<div class=\"modal_close\"></div>\
|
|
</div>\
|
|
<div class=\"modal-dialog\">\
|
|
<div class=\"modal-content\" ng-transclude>\
|
|
</div>\
|
|
</div>\
|
|
</div>");
|
|
}]);
|