Compare commits
	
		
			5 Commits
		
	
	
		
			cherry-pic
			...
			dialog-ser
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | be5da7fa72 | ||
|   | 5e3acc4363 | ||
|   | 196f49fca6 | ||
|   | f9cb753fce | ||
|   | 9bc1630cb4 | 
| @@ -1,7 +1,7 @@ | ||||
| <div class="c-ne__embed"> | ||||
|     <div class="c-ne__embed__snap-thumb" | ||||
|         v-if="embed.snapshot" | ||||
|         v-on:click="openSnapshot"> | ||||
|         v-on:click="openSnapshot(domainObject, entry, embed)"> | ||||
|         <img v-bind:src="embed.snapshot.src"> | ||||
|     </div> | ||||
|     <div class="c-ne__embed__info"> | ||||
|   | ||||
| @@ -15,7 +15,7 @@ | ||||
|         <div class="flex-elem holder flex-can-shrink s-snapshot-datetime"> | ||||
|             SNAPSHOT {{formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss')}} | ||||
|         </div> | ||||
|         <a class="s-button icon-pencil" title="Annotate"> | ||||
|         <a class="s-button icon-pencil" title="Annotate" v-on:click="annotateSnapshot"> | ||||
|             <span class="title-label">Annotate</span> | ||||
|         </a> | ||||
|     </div> | ||||
|   | ||||
| @@ -23,16 +23,16 @@ | ||||
| define([ | ||||
|     'moment', | ||||
|     'zepto', | ||||
|     '../utils/SnapshotOverlay', | ||||
|     '../../res/templates/snapshotTemplate.html', | ||||
|     'vue' | ||||
|     'vue', | ||||
|     'painterro' | ||||
| ], | ||||
| function ( | ||||
|     Moment, | ||||
|     $, | ||||
|     SnapshotOverlay, | ||||
|     SnapshotTemplate, | ||||
|     Vue | ||||
|     Vue, | ||||
|     Painterro | ||||
| ) { | ||||
|     function EmbedController (openmct, domainObject) { | ||||
|         this.openmct = openmct; | ||||
| @@ -52,11 +52,102 @@ function ( | ||||
|  | ||||
|     EmbedController.prototype.navigate = function (embedType) { | ||||
|         this.objectService.getObjects([embedType]).then(function (objects) { | ||||
|             this.navigationService.setNavigation(objects[embedType]);    | ||||
|             this.navigationService.setNavigation(objects[embedType]); | ||||
|         }.bind(this)); | ||||
|     }; | ||||
|  | ||||
|     EmbedController.prototype.openSnapshot = function () { | ||||
|     EmbedController.prototype.openSnapshot = function (domainObject, entry, embed) { | ||||
|  | ||||
|         function annotateSnapshot(openmct) { | ||||
|             return function () { | ||||
|  | ||||
|                 var save = false, | ||||
|                     painterroInstance = {}, | ||||
|                     annotateOverlay = new Vue({ | ||||
|                         template: '<div id="snap-annotation"></div>' | ||||
|                     }), | ||||
|                     self = this; | ||||
|  | ||||
|                 var options = { | ||||
|                     cssClass: 'l-large-view', | ||||
|                     onDestroy: function () { | ||||
|                         annotateOverlay.$destroy(true); | ||||
|                     }, | ||||
|                     buttons: [ | ||||
|                         { | ||||
|                             label: 'Cancel', | ||||
|                             callback: function () { | ||||
|                                 save = false; | ||||
|                                 painterroInstance.save(); | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             label: 'Save', | ||||
|                             callback: function () { | ||||
|                                 save = true; | ||||
|                                 painterroInstance.save(); | ||||
|                             } | ||||
|                         } | ||||
|                     ] | ||||
|                 }; | ||||
|  | ||||
|                 openmct.OverlayService.show(annotateOverlay.$mount().$el, options); | ||||
|  | ||||
|                 painterroInstance = Painterro({ | ||||
|                     id: 'snap-annotation', | ||||
|                     activeColor: '#ff0000', | ||||
|                     activeColorAlpha: 1.0, | ||||
|                     activeFillColor: '#fff', | ||||
|                     activeFillColorAlpha: 0.0, | ||||
|                     backgroundFillColor: '#000', | ||||
|                     backgroundFillColorAlpha: 0.0, | ||||
|                     defaultFontSize: 16, | ||||
|                     defaultLineWidth: 2, | ||||
|                     defaultTool: 'ellipse', | ||||
|                     hiddenTools: ['save', 'open', 'close', 'eraser', 'pixelize', 'rotate', 'settings', 'resize'], | ||||
|                     translation: { | ||||
|                         name: 'en', | ||||
|                         strings: { | ||||
|                             lineColor: 'Line', | ||||
|                             fillColor: 'Fill', | ||||
|                             lineWidth: 'Size', | ||||
|                             textColor: 'Color', | ||||
|                             fontSize: 'Size', | ||||
|                             fontStyle: 'Style' | ||||
|                         } | ||||
|                     }, | ||||
|                     saveHandler: function (image, done) { | ||||
|                         if (save) { | ||||
|                             var entryPos = self.findInArray(domainObject.entries, entry.id), | ||||
|                                 embedPos = self.findInArray(entry.embeds, embed.id); | ||||
|  | ||||
|                             if (entryPos !== -1 && embedPos !== -1) { | ||||
|                                 var url = image.asBlob(), | ||||
|                                     reader = new window.FileReader(); | ||||
|  | ||||
|                                 reader.readAsDataURL(url); | ||||
|                                 reader.onloadend = function () { | ||||
|                                     var snapshot = reader.result, | ||||
|                                         snapshotObject = { | ||||
|                                             src: snapshot, | ||||
|                                             type: url.type, | ||||
|                                             size: url.size, | ||||
|                                             modified: Date.now() | ||||
|                                         }, | ||||
|                                         dirString = 'entries[' + entryPos + '].embeds[' + embedPos + '].snapshot'; | ||||
|  | ||||
|                                     openmct.objects.mutate(domainObject, dirString, snapshotObject); | ||||
|                                 }; | ||||
|                             } | ||||
|                         } else { | ||||
|                             console.log('You cancelled the annotation!!!'); | ||||
|                         } | ||||
|                         done(true); | ||||
|                     } | ||||
|                 }).show(embed.snapshot.src); | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         var self = this, | ||||
|             snapshot = new Vue({ | ||||
|                 template: SnapshotTemplate, | ||||
| @@ -66,15 +157,22 @@ function ( | ||||
|                     }; | ||||
|                 }, | ||||
|                 methods: { | ||||
|                     formatTime: self.formatTime | ||||
|                     formatTime: self.formatTime, | ||||
|                     annotateSnapshot: annotateSnapshot(self.openmct), | ||||
|                     findInArray: self.findInArray | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         function onDestroyCallback() { | ||||
|             snapshot.$destroy(true); | ||||
|         } | ||||
|         var options = { | ||||
|             onDestroy: onDestroyCallback, | ||||
|             cssClass: 'l-large-view' | ||||
|         }; | ||||
|  | ||||
|         this.openmct.OverlayService.show(snapshot.$mount().$el, {onDestroy: onDestroyCallback, cssClass: 'l-large-view'}); | ||||
|  | ||||
|         this.openmct.OverlayService.show(snapshot.$mount().$el, options); | ||||
|     }; | ||||
|  | ||||
|     EmbedController.prototype.formatTime = function (unixTime, timeFormat) { | ||||
| @@ -125,23 +223,20 @@ function ( | ||||
|                 var entryPosition = self.findInArray(self.domainObject.entries, entry.id), | ||||
|                     embedPosition = self.findInArray(entry.embeds, embed.id); | ||||
|  | ||||
|                 var warningDialog = self.dialogService.showBlockingMessage({ | ||||
|                 self.openmct.OverlayService.showBlockingMessage({ | ||||
|                     severity: "error", | ||||
|                     title: "This action will permanently delete this embed. Do you wish to continue?", | ||||
|                     options: [{ | ||||
|                         label: "OK", | ||||
|                     actionText: 'This Action will permanently delete this embed. Do you wish to continue?', | ||||
|                     buttons: [{ | ||||
|                         label: "No", | ||||
|                         callback: function () {} | ||||
|                     }, | ||||
|                     { | ||||
|                         label: "Yes", | ||||
|                         callback: function () { | ||||
|                             entry.embeds.splice(embedPosition, 1); | ||||
|                             var dirString = 'entries[' + entryPosition + '].embeds'; | ||||
|  | ||||
|                             self.openmct.objects.mutate(self.domainObject, dirString, entry.embeds); | ||||
|  | ||||
|                             warningDialog.dismiss(); | ||||
|                         } | ||||
|                     },{ | ||||
|                         label: "Cancel", | ||||
|                         callback: function () { | ||||
|                             warningDialog.dismiss(); | ||||
|                         } | ||||
|                     }] | ||||
|                 }); | ||||
| @@ -207,7 +302,8 @@ function ( | ||||
|             openSnapshot: self.openSnapshot, | ||||
|             formatTime: self.formatTime, | ||||
|             toggleActionMenu: self.toggleActionMenu, | ||||
|             actionToMenuDecorator: self.actionToMenuDecorator | ||||
|             actionToMenuDecorator: self.actionToMenuDecorator, | ||||
|             findInArray: self.findInArray | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -81,21 +81,18 @@ function ( | ||||
|  | ||||
|         if (entryPos !== -1) { | ||||
|  | ||||
|             var errorDialog = this.dialogService.showBlockingMessage({ | ||||
|             this.openmct.OverlayService.showBlockingMessage({ | ||||
|                 severity: "error", | ||||
|                 title: "This action will permanently delete this Notebook entry. Do you wish to continue?", | ||||
|                 options: [{ | ||||
|                     label: "OK", | ||||
|                 actionText: "This action will permanently delete this Notebook entry. Do you wish to continue?", | ||||
|                 buttons: [{ | ||||
|                     label: "No", | ||||
|                     callback: function () {} | ||||
|                 }, | ||||
|                 { | ||||
|                     label: "Yes", | ||||
|                     callback: function () { | ||||
|                         domainObject.entries.splice(entryPos, 1); | ||||
|                         openmct.objects.mutate(domainObject, 'entries', domainObject.entries); | ||||
|  | ||||
|                         errorDialog.dismiss(); | ||||
|                     } | ||||
|                 },{ | ||||
|                     label: "Cancel", | ||||
|                     callback: function () { | ||||
|                         errorDialog.dismiss(); | ||||
|                     } | ||||
|                 }] | ||||
|             }); | ||||
|   | ||||
| @@ -60,7 +60,7 @@ function ( | ||||
|         this.container = container; | ||||
|  | ||||
|         var notebookEmbed = { | ||||
|             inject:['openmct'], | ||||
|             inject:['openmct', 'domainObject'], | ||||
|             props:['embed', 'entry'], | ||||
|             template: EmbedTemplate, | ||||
|             data: embedController.exposedData, | ||||
| @@ -81,7 +81,7 @@ function ( | ||||
|  | ||||
|         var notebookVue = Vue.extend({ | ||||
|             template: NotebookTemplate, | ||||
|             provide: {openmct: self.openmct}, | ||||
|             provide: {openmct: self.openmct, domainObject: self.domainObject}, | ||||
|             components: { | ||||
|                 'notebook-entry': entryComponent, | ||||
|                 'search': search.default | ||||
|   | ||||
| @@ -40,7 +40,8 @@ $inputTextP: $inputTextPTopBtm $inputTextPLeftRight; | ||||
| $menuLineH: 1.5rem; | ||||
| $treeItemIndent: 16px; | ||||
| $treeTypeIconW: 18px; | ||||
| $overlayOuterMargin: 5%; | ||||
| $overlayOuterMarginLg: 5%; | ||||
| $overlayOuterMarginDialog: 20%; | ||||
| $overlayInnerMargin: 25px; | ||||
|  | ||||
| /*************** Items */ | ||||
|   | ||||
							
								
								
									
										91
									
								
								src/ui/overlayService/blockingMessage.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/ui/overlayService/blockingMessage.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| <template> | ||||
|     <div class="c-message" | ||||
|         v-bind:class=""> | ||||
|         <!-- This element is displayed within the overlay service as well as in the list of messages | ||||
|         Uses flex-row --> | ||||
|         <div class="c-message__icon" | ||||
|              :class="['message-severity-' + model.severity]"></div> | ||||
|         <div class="c-message__text"> | ||||
|             <!-- Uses flex-column --> | ||||
|             <div class="c-message__title" | ||||
|                 v-if="model.title"> | ||||
|                 {{model.title}} | ||||
|             </div> | ||||
|             <div class="c-message__hint" | ||||
|                  v-if="model.hint"> | ||||
|                 {{model.hint}} | ||||
|                 <span v-if="model.timestamp">[{{model.timestamp}}]</span> | ||||
|             </div> | ||||
|             <div class="c-message__action-text" | ||||
|                 v-if="model.actionText"> | ||||
|                 {{model.actionText}} | ||||
|             </div> | ||||
|  | ||||
|             <div class="c-message__actions" | ||||
|                  v-if="model.primaryOption"> | ||||
|                 <a class="c-button c-button--major" | ||||
|                    @click="model.primaryOption.callback()"> | ||||
|                     {{model.primaryOption.label}} | ||||
|                 </a> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|  | ||||
|     .c-message { | ||||
|         display: flex; | ||||
|         padding: $interiorMarginLg; | ||||
|  | ||||
|         > * + * { | ||||
|             @include test(); | ||||
|             margin-left: $interiorMarginLg; | ||||
|         } | ||||
|  | ||||
|         &__icon { | ||||
|             $s: 50px; | ||||
|             flex: 0 0 auto; | ||||
|             min-width: $s; | ||||
|             min-height: $s; | ||||
|  | ||||
|             &.message-severity { | ||||
|                 // TEMP: TODO: replace with SVG background assets | ||||
|                 &-alert { | ||||
|                     background: $colorAlert; | ||||
|                 } | ||||
|  | ||||
|                 &-error { | ||||
|                     background: $colorFormError; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &__text { | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|             flex: 1 1 auto; | ||||
|  | ||||
|             > * + * { | ||||
|                 @include test(); | ||||
|                 margin-top: $interiorMargin; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // __text elements | ||||
|         &__title, | ||||
|         &__action-text { | ||||
|             font-size: 1.2em; // TEMP | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|     inject:['model'] | ||||
| } | ||||
| </script> | ||||
| @@ -8,9 +8,19 @@ | ||||
|                 v-on:click="destroy"> | ||||
|             </button> | ||||
|             <div class="c-overlay__contents" ref="element"></div> | ||||
|             <div class="c-overlay__button-bar"> | ||||
|             <div class="c-overlay__button-bar" v-if="!buttons"> | ||||
|                 <button class="c-button c-button--major" | ||||
|                         v-on:click="destroy">Done</button> | ||||
|                         v-on:click="destroy"> | ||||
|                     Done | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div class="c-overlay__button-bar" v-if="buttons"> | ||||
|                 <button class="c-button c-button--major" | ||||
|                         v-for="(button, index) in buttons" | ||||
|                         :key="index" | ||||
|                         @click="buttonClickHandler(button.callback)"> | ||||
|                     {{button.label}} | ||||
|                 </button> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| @@ -19,6 +29,13 @@ | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|  | ||||
|     @mixin overlaySizing($marginTB: 5%, $marginLR: $marginTB, $width: auto, $height: auto) { | ||||
|         position: absolute; | ||||
|         top: $marginTB; right: $marginLR; bottom: $marginTB; left: $marginLR; | ||||
|         width: $width; | ||||
|         height: $height; | ||||
|     } | ||||
|  | ||||
|     .l-overlay-wrapper { | ||||
|         // Created by overlayService.js, contains this template. | ||||
|         // Acts as an anchor for one or more overlays. | ||||
| @@ -62,7 +79,26 @@ | ||||
|             margin-top: $interiorMargin; | ||||
|         } | ||||
|  | ||||
|         body.desktop & { | ||||
|  | ||||
|         // Overlay types, styling independent of platform. | ||||
|         .l-large-view & { | ||||
|             // Default | ||||
|         } | ||||
|  | ||||
|         .l-dialog & { | ||||
|             // | ||||
|         } | ||||
|  | ||||
|         .l-message & { | ||||
|             &__outer { | ||||
|              //   background: orange; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     body.desktop { | ||||
|         .c-overlay { | ||||
|             &__blocker { | ||||
|                 @include abs(); | ||||
|                 background: $colorOvrBlocker; | ||||
| @@ -71,21 +107,49 @@ | ||||
|             } | ||||
|  | ||||
|             &__outer { | ||||
|                 $m: $overlayOuterMargin; | ||||
|                 top: $m; right: $m; bottom: $m; left: $m; | ||||
|                 border-radius: $overlayCr; | ||||
|                 box-shadow: rgba(black, 0.5) 0 2px 25px; | ||||
|                 // Defaults to l-large-view | ||||
|                 @include overlaySizing($overlayOuterMarginLg); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Overlay types, styling for desktop. | ||||
|         .l-large-view { | ||||
|             // Default | ||||
|         } | ||||
|  | ||||
|         .l-dialog { | ||||
|             .c-overlay__outer { | ||||
|                 @include overlaySizing($overlayOuterMarginDialog); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         .l-message { | ||||
|             .c-overlay__outer { | ||||
|                 @include overlaySizing(auto); | ||||
|                 top: 50%; | ||||
|                 left: 50%; | ||||
|                 transform: translate(-50%, -50%); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
|     export default { | ||||
|         inject: ['destroy', 'element'], | ||||
|         inject: ['destroy', 'element', 'buttons'], | ||||
|         mounted() { | ||||
|             this.$refs.element.appendChild(this.element); | ||||
|         }, | ||||
|         methods: { | ||||
|             buttonClickHandler: function (method) { | ||||
|                 method(); | ||||
|                 this.destroy(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|   | ||||
| @@ -22,15 +22,19 @@ | ||||
|  | ||||
| define([ | ||||
|     './overlay.vue', | ||||
|     './blockingMessage.vue', | ||||
|     'vue' | ||||
| ], function ( | ||||
|     OverlayComponent, | ||||
|     BlockingMessage, | ||||
|     Vue | ||||
| ) { | ||||
|  | ||||
|     function OverlayService() { | ||||
|         this.activeOverlays = []; | ||||
|         this.overlayId = 0; | ||||
|  | ||||
|         this.showBlockingMessage = this.showBlockingMessage.bind(this); | ||||
|     } | ||||
|  | ||||
|     OverlayService.prototype.show  = function (element, options) { | ||||
| @@ -43,45 +47,96 @@ define([ | ||||
|             component = new Vue({ | ||||
|                 provide: { | ||||
|                     destroy: this.destroy.bind(this), | ||||
|                     element: element | ||||
|                     element: element, | ||||
|                     buttons: options.buttons | ||||
|                 }, | ||||
|                 components: { | ||||
|                     OverlayComponent: OverlayComponent.default | ||||
|                 }, | ||||
|                 template: '<overlay-component></overlay-component>' | ||||
|             }); | ||||
|             }), | ||||
|             dialog = {}; | ||||
|  | ||||
|  | ||||
|         overlay.classList.add('l-overlay-wrapper', overlayTypeCssClass); | ||||
|         document.body.appendChild(overlay); | ||||
|  | ||||
|         overlay.appendChild(component.$mount().$el); | ||||
|  | ||||
|         this.activeOverlays.push({ | ||||
|         var overlayObject = { | ||||
|             overlay: overlay, | ||||
|             component: component, | ||||
|             onDestroy: options.onDestroy, | ||||
|             id: this.overlayId | ||||
|         }); | ||||
|             id: this.overlayId, | ||||
|             dialog: dialog | ||||
|         }; | ||||
|  | ||||
|         dialog.dismiss = function () { | ||||
|             let pos = findInArray(overlayObject.id, this.activeOverlays); | ||||
|  | ||||
|             if (pos !== -1) { | ||||
|                 if (overlayObject.onDestroy && typeof overlayObject.onDestroy === 'function') { | ||||
|                     overlayObject.onDestroy(); | ||||
|                 } | ||||
|  | ||||
|                 overlayObject.component.$destroy(true); | ||||
|                 document.body.removeChild(overlayObject.overlay); | ||||
|                 this.activeOverlays.splice(pos, 1); | ||||
|  | ||||
|                 if (this.activeOverlays.length) { | ||||
|                     this.activeOverlays[this.activeOverlays.length - 1].overlay.classList.remove('invisible'); | ||||
|                 } | ||||
|             } | ||||
|         }.bind(this); | ||||
|  | ||||
|         this.activeOverlays.push(overlayObject); | ||||
|         this.overlayId++; | ||||
|  | ||||
|         return dialog; | ||||
|     }; | ||||
|  | ||||
|     OverlayService.prototype.destroy = function () { | ||||
|         var lastActiveOverlayObject = this.activeOverlays.pop(), | ||||
|             lastActiveOverlay = lastActiveOverlayObject.overlay, | ||||
|             lastActiveComponent = lastActiveOverlayObject.component; | ||||
|         var lastActiveOverlayObject = this.activeOverlays[this.activeOverlays.length - 1]; | ||||
|  | ||||
|         if (lastActiveOverlayObject.onDestroy && typeof lastActiveOverlayObject.onDestroy === 'function') { | ||||
|             lastActiveOverlayObject.onDestroy(); | ||||
|         } | ||||
|  | ||||
|         lastActiveComponent.$destroy(true); | ||||
|         document.body.removeChild(lastActiveOverlay); | ||||
|  | ||||
|         if (this.activeOverlays.length) { | ||||
|             this.activeOverlays[this.activeOverlays.length - 1].overlay.classList.remove('invisible'); | ||||
|         } | ||||
|         lastActiveOverlayObject.dialog.dismiss(lastActiveOverlayObject.id); | ||||
|     }; | ||||
|  | ||||
|     OverlayService.prototype.showBlockingMessage = function (model) { | ||||
|         let component = new Vue({ | ||||
|             provide: { | ||||
|                 model: model | ||||
|             }, | ||||
|             components: { | ||||
|                 BlockingMessage: BlockingMessage.default | ||||
|             }, | ||||
|             template: '<blocking-message></blocking-message>' | ||||
|         }); | ||||
|  | ||||
|         function destroy() { | ||||
|             component.$destroy(true); | ||||
|         } | ||||
|  | ||||
|         let options = { | ||||
|             cssClass: 'l-message', | ||||
|             onDestroy: destroy, | ||||
|             buttons: model.buttons | ||||
|         }; | ||||
|  | ||||
|         return this.show(component.$mount().$el, options); | ||||
|     }; | ||||
|  | ||||
|     function findInArray(id, array) { | ||||
|         var found = -1; | ||||
|  | ||||
|         array.forEach(function (o,i) { | ||||
|             if (o.id === id) { | ||||
|                 found = i; | ||||
|                 return; | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         return found; | ||||
|     } | ||||
|  | ||||
|     return OverlayService; | ||||
| }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user