diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index 1e68f82e69..feeb0d9e12 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -4,7 +4,12 @@ { "key": "dialogService", "implementation": "DialogService.js", - "depends": [ "$document", "$compile", "$rootScope", "$timeout", "$q", "$log" ] + "depends": [ "overlayService", "$q", "$log" ] + }, + { + "key": "overlayService", + "implementation": "OverlayService.js", + "depends": [ "$document", "$compile", "$rootScope" ] } ], "templates": [ diff --git a/platform/commonUI/dialog/res/templates/overlay.html b/platform/commonUI/dialog/res/templates/overlay.html index ee11c5df06..bae3b906d9 100644 --- a/platform/commonUI/dialog/res/templates/overlay.html +++ b/platform/commonUI/dialog/res/templates/overlay.html @@ -1,8 +1,8 @@ -
+
x
diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 2157e0d00b..a694bbd613 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -7,33 +7,91 @@ define( [], function () { "use strict"; - - // Template to inject into the DOM to show the dialog; really just points to - // the overlay-dialog template. - var TEMPLATE = ""; - /** * The dialog service is responsible for handling window-modal * communication with the user, such as displaying forms for user * input. * @constructor */ - function DialogService($document, $compile, $rootScope, $timeout, $q, $log) { - var scope; + function DialogService(overlayService, $q, $log) { + var overlay, + dialogVisible = false; - // Inject the dialog at the top of the body; this is necessary to - // ensure that the dialog is positioned appropriately and can fill - // the screen to block other interactions. - function addContent() { - scope = $rootScope.$new(); - $document.find('body').prepend($compile(TEMPLATE)(scope)); - scope.dialog = { visible: false, value: {} }; + // Stop showing whatever overlay is currently active + // (e.g. because the user hit cancel) + function dismiss() { + if (overlay) { + overlay.dismiss(); + } + dialogVisible = false; } - // Dismiss the dialog; just stop showing it, and release any - // form information for garbage collection. - function dismiss() { - scope.dialog = { visible: false, value: {} }; + function getUserInput(formModel, value) { + // We will return this result as a promise, because user + // input is asynchronous. + var deferred = $q.defer(), + overlayModel; + + // Confirm function; this will be passed in to the + // overlay-dialog template and associated with a + // OK button click + function confirm() { + var resultingValue; + + // Temporary workaround, in the absence of a + // forms package. + try { + resultingValue = JSON.parse(overlayModel.value); + } catch (e) { + resultingValue = {}; + } + + // Pass along the result + deferred.resolve(resultingValue); + + // Stop showing the dialog + dismiss(); + } + + // Cancel function; this will be passed in to the + // overlay-dialog template and associated with a + // Cancel or X button click + function cancel() { + deferred.reject(); + dismiss(); + } + + if (dialogVisible) { + // Only one dialog should be shown at a time. + // The application design should be such that + // we never even try to do this. + $log.warn([ + "Dialog already showing; ", + "unable to show ", + formModel.name + ].join("")); + deferred.reject(); + } else { + // To be passed to the overlay-dialog template, + // via ng-model + overlayModel = { + title: formModel.name, + message: formModel.message, + formModel: formModel, + value: JSON.stringify(value), + confirm: confirm, + cancel: cancel + }; + + // Add the overlay using the OverlayService, which + // will handle actual insertion into the DOM + overlay = overlayService.createOverlay( + overlayModel, + "overlay-dialog" + ); + } + + return deferred.promise; } return { @@ -48,49 +106,7 @@ define( * user input cannot be obtained (for instance, * because the user cancelled the dialog) */ - getUserInput: function (formModel, value) { - var deferred = $q.defer(); - - if (!scope) { - addContent(); - } - - $timeout(function () { - if (scope.dialog.visible) { - $log.warn([ - "Dialog already showing; ", - "unable to show ", - formModel.name - ].join("")); - deferred.reject(); - return; - } - - scope.dialog.visible = true; - scope.dialog.title = formModel.name; - scope.dialog.message = formModel.message; - scope.dialog.formModel = formModel; - scope.dialog.value = JSON.stringify(value); - - scope.dialog.confirm = function () { - var resultingValue; - - try { - resultingValue = JSON.parse(scope.dialog.value); - } catch (e) { - resultingValue = {}; - } - deferred.resolve(resultingValue); - dismiss(); - }; - scope.dialog.cancel = function () { - deferred.reject(); - dismiss(); - }; - }); - - return deferred.promise; - } + getUserInput: getUserInput }; } diff --git a/platform/commonUI/dialog/src/OverlayService.js b/platform/commonUI/dialog/src/OverlayService.js new file mode 100644 index 0000000000..580595162d --- /dev/null +++ b/platform/commonUI/dialog/src/OverlayService.js @@ -0,0 +1,72 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + // Template to inject into the DOM to show the dialog; really just points to + // the a specific template that can be included via mct-include + var TEMPLATE = ''; + + + /** + * The OverlayService is responsible for pre-pending templates to + * the body of the document, which is useful for displaying templates + * which need to block the full screen. + * + * This is intended to be used by the DialogService; by design, it + * does not have any protections in place to prevent multiple overlays + * from being shown at once. (The DialogService does have these + * protections, and should be used for most overlay-type interactions, + * particularly where a multiple-overlay effect is not specifically + * desired). + * + * @constructor + */ + function OverlayService($document, $compile, $rootScope) { + function createOverlay(overlayModel, key) { + // Create a new scope for this overlay + var scope = $rootScope.$new(), + element; + + // Populate the scope; will be passed directly to the template + scope.overlay = overlayModel; + scope.key = key; + + // Create the overlay element and add it to the document's body + element = $compile(TEMPLATE)(scope); + $document.find('body').prepend(element); + + // Stop showing the overlay; additionally, release the scope + // that it uses. + function dismiss() { + scope.$destroy(); + element.remove(); + } + + return { + dismiss: dismiss + }; + } + + return { + /** + * Add a new overlay to the document. This will be + * prepended to the document body; the overlay's + * template (as pointed to by the `key` argument) is + * responsible for having a useful z-order, and for + * blocking user interactions if appropriate. + * @param {object} overlayModel the model to pass to the + * included overlay template (this will be passed + * in via ng-model) + * @param {string} key the symbolic key which identifies + * the template of the overlay to be shown + */ + createOverlay: createOverlay + }; + } + + return OverlayService; + } +); \ No newline at end of file