diff --git a/app/assets/javascripts/app/services/componentManager.js b/app/assets/javascripts/app/services/componentManager.js index 3bd723017..46b71f3c6 100644 --- a/app/assets/javascripts/app/services/componentManager.js +++ b/app/assets/javascripts/app/services/componentManager.js @@ -606,7 +606,14 @@ class ComponentManager { } component.window = componentWindow; component.sessionKey = Neeto.crypto.generateUUID(); - this.sendMessageToComponent(component, {action: "component-registered", sessionKey: component.sessionKey, componentData: component.componentData}); + this.sendMessageToComponent(component, { + action: "component-registered", + sessionKey: component.sessionKey, + componentData: component.componentData, + data: { + environment: isDesktopApplication() ? "desktop" : "web" + } + }); this.postThemeToComponent(component); } diff --git a/app/assets/javascripts/app/services/directives/functional/click-outside.js b/app/assets/javascripts/app/services/directives/functional/click-outside.js index 10e40e1da..b30e749da 100644 --- a/app/assets/javascripts/app/services/directives/functional/click-outside.js +++ b/app/assets/javascripts/app/services/directives/functional/click-outside.js @@ -1,27 +1,102 @@ +// via https://github.com/IamAdamJowett/angular-click-outside + angular .module('app.frontend') - .directive('clickOutside', ['$document', function($document) { + .directive('clickOutside', ['$document', '$parse', '$timeout', function($document, $parse, $timeout) { return { restrict: 'A', replace: false, - link : function($scope, $element, attrs) { + link: function($scope, elem, attr) { + $timeout(() => { + // postpone linking to next digest to allow for unique id generation + var classList = (attr.outsideIfNot !== undefined) ? attr.outsideIfNot.split(/[ ,]+/) : [], + fn; - var didApplyClickOutside = false; + function eventHandler(e) { + var i, + element, + r, + id, + classNames, + l; - $element.bind('click', function(e) { - didApplyClickOutside = false; - if (attrs.isOpen) { - e.stopPropagation(); + // check if our element already hidden and abort if so + if (angular.element(elem).hasClass("ng-hide")) { + return; + } + + // if there is no click target, no point going on + if (!e || !e.target) { + return; + } + + // loop through the available elements, looking for classes in the class list that might match and so will eat + for (element = e.target; element; element = element.parentNode) { + // check if the element is the same element the directive is attached to and exit if so (props @CosticaPuntaru) + if (element === elem[0]) { + return; + } + + // now we have done the initial checks, start gathering id's and classes + id = element.id, + classNames = element.className, + l = classList.length; + + // Unwrap SVGAnimatedString classes + if (classNames && classNames.baseVal !== undefined) { + classNames = classNames.baseVal; + } + + // if there are no class names on the element clicked, skip the check + if (classNames || id) { + + // loop through the elements id's and classnames looking for exceptions + for (i = 0; i < l; i++) { + //prepare regex for class word matching + r = new RegExp('\\b' + classList[i] + '\\b'); + + // check for exact matches on id's or classes, but only if they exist in the first place + if ((id !== undefined && id === classList[i]) || (classNames && r.test(classNames))) { + // now let's exit out as it is an element that has been defined as being ignored for clicking outside + return; + } + } + } + } + + // if we have got this far, then we are good to go with processing the command passed in via the click-outside attribute + $timeout(function() { + fn = $parse(attr['clickOutside']); + fn($scope, { event: e }); + }); } - }); - $document.bind('click', function() { - if(!didApplyClickOutside) { - $scope.$apply(attrs.clickOutside); - didApplyClickOutside = true; + // if the devices has a touchscreen, listen for this event + if (_hasTouch()) { + $document.on('touchstart', eventHandler); } - }) - } + // still listen for the click event even if there is touch to cater for touchscreen laptops + $document.on('click', eventHandler); + + // when the scope is destroyed, clean up the documents event handlers as we don't want it hanging around + $scope.$on('$destroy', function() { + if (_hasTouch()) { + $document.off('touchstart', eventHandler); + } + + $document.off('click', eventHandler); + }); + + /** + * @description Private function to attempt to figure out if we are on a touch device + * @private + **/ + function _hasTouch() { + // works on most browsers, IE10/11 and Surface + return 'ontouchstart' in window || navigator.maxTouchPoints; + }; + }); } - }]); + } +}]); diff --git a/app/assets/javascripts/app/services/directives/views/componentModal.js b/app/assets/javascripts/app/services/directives/views/componentModal.js index d1235fea9..ee20de0d2 100644 --- a/app/assets/javascripts/app/services/directives/views/componentModal.js +++ b/app/assets/javascripts/app/services/directives/views/componentModal.js @@ -33,7 +33,6 @@ class ComponentModal { if(component.active) { $timeout(function(){ var iframe = componentManager.iframeForComponent(component); - console.log("iframe", iframe, component); if(iframe) { iframe.onload = function() { componentManager.registerComponentWindow(component, iframe.contentWindow); @@ -44,7 +43,6 @@ class ComponentModal { }, actionHandler: function(component, action, data) { if(action == "set-size") { - console.log("componentModalReceivedAction SetSize", component); componentManager.handleSetSizeEvent(component, data); } }.bind(this)}); diff --git a/app/assets/javascripts/app/services/directives/views/editorMenu.js b/app/assets/javascripts/app/services/directives/views/editorMenu.js index 2092c08f1..57652641c 100644 --- a/app/assets/javascripts/app/services/directives/views/editorMenu.js +++ b/app/assets/javascripts/app/services/directives/views/editorMenu.js @@ -22,7 +22,7 @@ class EditorMenu { if(editor) { editor.conflict_of = null; // clear conflict if applicable if(editor.local && !isDesktopApplication()) { - alert("This editor is installed ") + alert("This editor is installed locally and is available only through Standard Notes for Desktop.") return; } } diff --git a/app/assets/javascripts/app/services/directives/views/permissionsModal.js b/app/assets/javascripts/app/services/directives/views/permissionsModal.js index e2a1f0a07..3007f362f 100644 --- a/app/assets/javascripts/app/services/directives/views/permissionsModal.js +++ b/app/assets/javascripts/app/services/directives/views/permissionsModal.js @@ -26,12 +26,19 @@ class PermissionsModal { $scope.callback(false); $scope.dismiss(); } + } + controller($scope, modelManager) { $scope.formattedPermissions = $scope.permissions.map(function(permission){ if(permission.name === "stream-items") { var title = "Access to "; var types = permission.content_types.map(function(type){ - return (type + "s").toLowerCase(); + var desc = modelManager.humanReadableDisplayForContentType(type); + if(desc) { + return desc + "s"; + } else { + return "items of type " + type; + } }) var typesString = ""; var separator = ", "; diff --git a/app/assets/javascripts/app/services/directives/views/roomBar.js b/app/assets/javascripts/app/services/directives/views/roomBar.js index b03eab93c..63f3d89e4 100644 --- a/app/assets/javascripts/app/services/directives/views/roomBar.js +++ b/app/assets/javascripts/app/services/directives/views/roomBar.js @@ -47,10 +47,14 @@ class RoomBar { if(room.show) { this.componentManager.activateComponent(room); } else { - this.componentManager.deactivateComponent(room); + $scope.hideRoom(room); } } + $scope.hideRoom = function(room) { + room.show = false; + this.componentManager.deactivateComponent(room); + } } diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index 73a49d80b..70438bc0b 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -400,6 +400,25 @@ class ModelManager { return JSON.stringify(data, null, 2 /* pretty print */); } + + + /* + Misc + */ + + humanReadableDisplayForContentType(contentType) { + return { + "Note" : "note", + "Tag" : "tag", + "Extension" : "action-based extension", + "SN|Component" : "component", + "SN|Editor" : "editor", + "SN|Theme" : "theme", + "SF|Extension" : "server extension", + "SF|MFA" : "server multi-factor authentication setting" + }[contentType]; + } + } angular.module('app.frontend').service('modelManager', ModelManager); diff --git a/app/assets/templates/frontend/directives/room-bar.html.haml b/app/assets/templates/frontend/directives/room-bar.html.haml index 08cb5059b..d941c3f29 100644 --- a/app/assets/templates/frontend/directives/room-bar.html.haml +++ b/app/assets/templates/frontend/directives/room-bar.html.haml @@ -1,4 +1,4 @@ -.room-item{"ng-repeat" => "room in rooms", "ng-click" => "selectRoom(room)"} +.room-item{"ng-repeat" => "room in rooms", "ng-click" => "selectRoom(room)", "click-outside" => "hideRoom(room)"} %img.icon{"ng-src" => "{{room.package_info.icon_bar}}"} .label {{room.name}} .room-container.panel-right{"ng-if" => "room.show && room.active", "ng-attr-id" => "component-{{room.uuid}}"} diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index 9b9ddf714..0a812b61b 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -13,7 +13,7 @@ %input.tags-input{"ng-if" => "!(ctrl.tagsComponent && ctrl.tagsComponent.active)", "type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();", "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"} %ul.section-menu-bar{"ng-if" => "ctrl.note"} - %li{"ng-class" => "{'selected' : ctrl.showMenu}", "click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"} + %li{"ng-class" => "{'selected' : ctrl.showMenu}", "click-outside" => "ctrl.showMenu = false;"} %label{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false; ctrl.showEditorMenu = false;"} Menu %ul.dropdown-menu.sectioned-menu{"ng-if" => "ctrl.showMenu"} @@ -37,11 +37,11 @@ %li{"ng-if" => "ctrl.hasDisabledStackComponents()"} %label{"ng-click" => "ctrl.selectedMenuItem($event); ctrl.restoreDisabledStackComponents()"} Restore Disabled Components - %li{"ng-class" => "{'selected' : ctrl.showEditorMenu}", "click-outside" => "ctrl.showEditorMenu = false;", "is-open" => "ctrl.showEditorMenu"} + %li{"ng-class" => "{'selected' : ctrl.showEditorMenu}", "click-outside" => "ctrl.showEditorMenu = false;"} %label{"ng-click" => "ctrl.showEditorMenu = !ctrl.showEditorMenu; ctrl.showMenu = false; ctrl.showExtensions = false;"} Editor %editor-menu{"ng-if" => "ctrl.showEditorMenu", "callback" => "ctrl.selectedEditor", "selected-editor" => "ctrl.editorComponent"} - %li{"ng-class" => "{'selected' : ctrl.showExtensions}", "ng-if" => "ctrl.hasAvailableExtensions()", "click-outside" => "ctrl.showExtensions = false;", "is-open" => "ctrl.showExtensions"} + %li{"ng-class" => "{'selected' : ctrl.showExtensions}", "ng-if" => "ctrl.hasAvailableExtensions()", "click-outside" => "ctrl.showExtensions = false;"} %label{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false; ctrl.showEditorMenu = false;"} Actions %contextual-extensions-menu{"ng-if" => "ctrl.showExtensions", "item" => "ctrl.note"} diff --git a/app/assets/templates/frontend/footer.html.haml b/app/assets/templates/frontend/footer.html.haml index b0943a8f9..42452128b 100644 --- a/app/assets/templates/frontend/footer.html.haml +++ b/app/assets/templates/frontend/footer.html.haml @@ -1,10 +1,10 @@ #footer-bar .pull-left - .footer-bar-link{"click-outside" => "ctrl.showAccountMenu = false;", "is-open" => "ctrl.showAccountMenu"} + .footer-bar-link{"click-outside" => "ctrl.showAccountMenu = false;"} %a{"ng-click" => "ctrl.accountMenuPressed()", "ng-class" => "{red: ctrl.error}"} Account %account-menu{"ng-if" => "ctrl.showAccountMenu", "on-successful-auth" => "ctrl.onAuthSuccess"} - .footer-bar-link{"click-outside" => "ctrl.showExtensionsMenu = false;", "is-open" => "ctrl.showExtensionsMenu"} + .footer-bar-link{"click-outside" => "ctrl.showExtensionsMenu = false;"} %a{"ng-click" => "ctrl.toggleExtensions()"} Extensions %global-extensions-menu{"ng-if" => "ctrl.showExtensionsMenu"} diff --git a/app/assets/templates/frontend/notes.html.haml b/app/assets/templates/frontend/notes.html.haml index 810763227..76c75fa52 100644 --- a/app/assets/templates/frontend/notes.html.haml +++ b/app/assets/templates/frontend/notes.html.haml @@ -13,7 +13,7 @@ %label Options .subtitle {{ctrl.optionsSubtitle()}} - .sectioned-menu.dropdown-menu{"ng-if" => "ctrl.showMenu"} + .sectioned-menu.dropdown-menu{"ng-if" => "ctrl.showMenu", "click-outside" => "ctrl.showMenu = false;"} %ul .header .title Sort by