<commit_before>(function() {
    'use strict';

    angular.module('chaise.viewer')

    .controller('AnnotationsController', ['AuthService', 'annotations', 'comments', 'anatomies', 'AnnotationsService', '$window', '$scope', '$uibModal', 'AlertsService', function AnnotationsController(AuthService, annotations, comments, anatomies, AnnotationsService, $window, $scope, $uibModal, AlertsService) {
        var vm = this;
        vm.annotations = annotations;
        vm.anatomies = anatomies;
        vm.colors = ['red', 'orange', 'gold', 'green', 'blue', 'purple'];
        vm.defaultColor = chaiseConfig.defaultAnnotationColor || 'red';
        vm.annotationTypes = ['rectangle', 'arrow']; // 'section' excluded b/c once you set an annotation as a section, it can't be changed to other types
        vm.filterByType = {section: true, rectangle: true, arrow: true}; // show all annotation types by default

        vm.filterAnnotations = filterAnnotations;

        vm.createMode = false;
        vm.newAnnotation = {config:{color: vm.defaultColor}};
        vm.drawAnnotation = drawAnnotation;
        vm.createAnnotation = createAnnotation;
        vm.cancelNewAnnotation = cancelNewAnnotation;

        vm.editedAnnotation = null; // Track which annotation is being edited right now
        var originalAnnotation = null; // Holds the original contents of annotation in the event that a user cancels an edit
        vm.editAnnotation = editAnnotation;
        vm.cancelEdit = cancelEdit;
        vm.updateAnnotation = updateAnnotation;

        vm.deleteAnnotation = deleteAnnotation;

        vm.highlightedAnnotation = null;
        vm.centerAnnotation = centerAnnotation;

        vm.getNumComments = getNumComments;
        vm.authorName = authorName;

        vm.allowCreate = AuthService.createAnnotation;
        vm.allowEdit = AuthService.editAnnotation;
        vm.allowDelete = AuthService.deleteAnnotation;

        // Listen to events of type 'message' (from Annotorious)
        $window.addEventListener('message', function annotationControllerListener(event) {
            // TODO: Check if origin is valid first; if not, return and exit.
            // Do this for the other listeners as well.
            if (event.origin === window.location.origin) {
                var data = event.data;
                var messageType = data.messageType;

                switch (messageType) {
                    case 'annotoriousReady':
                        // annotoriousReady case handled in viewer.app.js.
                        // Repeating the case here to avoid triggering default case
                        break;
                    case 'annotationDrawn':
                        vm.newAnnotation.shape = data.content.shape;
                        $scope.$apply(function() {
                            vm.createMode = true;
                        });
                        break;
                    case 'onHighlighted':
                    // On-hover highlighting behavior no longer needed
                    // OSD still sends this message out on hover though, so the
                    // is case here to avoid triggering default case
                        break;
                    case 'onUnHighlighted':
                    // On-hover highlighting behavior no longer needed
                    // OSD still sends this message out on hover though, so the
                    // is case here to avoid triggering default case
                        break;
                    case 'onClickAnnotation':
                        var content = JSON.parse(data.content);
                        var annotation = findAnnotation(content.data.shapes[0].geometry);
                        if (annotation) {
                            var annotationId = annotation.table.name + '-' + annotation.data.id;
                            $scope.$apply(function() {
                                vm.highlightedAnnotation = annotationId;
                            });
                            scrollIntoView(annotationId);
                        }
                        break;
                    default:
                        console.log('Invalid event message type "' + messageType + '"');
                }
            } else {
                console.log('Invalid event origin. Event origin: ', event.origin, '. Expected origin: ', window.location.origin);
            }
        });

        function filterAnnotations(annotation) {
            if (!vm.query) {
                return true;
            }

            vm.query = vm.query.toLowerCase();

            annotation = annotation.data;
            var author = annotation.author;
            var props = [annotation.anatomy, annotation.description, author.display_name, author.full_name, author.email, annotation.created];
            var numProps = props.length;
            for (var i = 0; i < numProps; i++) {
                if (props[i] && props[i].toLowerCase().indexOf(vm.query) > -1) {
                    return true;
                }
            }

            var commentsArr = comments[annotation.id];
            if (commentsArr) {
                var numComments = commentsArr.length;
                for (var c = 0; c < numComments; c++) {
                    var comment = commentsArr[c].data;
                    var commentAuthor = comment.author;
                    var commentProps = [comment.comment, comment.created, commentAuthor.display_name, commentAuthor.full_name, commentAuthor.email];
                    var numCommentProps = commentProps.length;
                    for (var p = 0; p < numCommentProps; p++) {
                        if (commentProps[p] && commentProps[p].toLowerCase().indexOf(vm.query) > -1) {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        function drawAnnotation(type) {
            vm.newAnnotation.type = type;
            return AnnotationsService.drawAnnotation();
        }

        function createAnnotation() {
            vm.createMode = false;
            AnnotationsService.createAnnotation(vm.newAnnotation);
            vm.newAnnotation = {config:{color: vm.defaultColor}};
        }

        function cancelNewAnnotation() {
            vm.createMode = false;
            return AnnotationsService.cancelNewAnnotation();
        }

        function editAnnotation(annotation) {
            // Must make a copy instead of assigning to remove original annotation's
            // references. Otherwise, changing something in editedAnnotation will
            // also change the original annotation.
            vm.editedAnnotation = angular.copy(annotation);

            vm.editedAnnotation.domId = annotation.table.name + '-' + annotation.data.id;
            setHighlightedAnnotation(annotation);
            annotation = annotation.data;
            originalAnnotation = {
                description: annotation.description,
                anatomy: annotation.anatomy,
                config: annotation.config,
                type: annotation.type
            };
        }

        function cancelEdit(annotation) {
            vm.editedAnnotation = null;
            var data = annotation.data;
            data.description = originalAnnotation.description;
            data.anatomy = originalAnnotation.anatomy;
            data.config = originalAnnotation.config;
            data.type = originalAnnotation.type;
        }

        function updateAnnotation(annotation) {
            annotation.data = vm.editedAnnotation.data;
            AnnotationsService.updateAnnotation(annotation);
            vm.editedAnnotation = null;
        }

        function deleteAnnotation(annotation) {
            // if annotation has comments, allow it to be deleted
            if (!hasComments(annotation)) {
                if (chaiseConfig.confirmDelete == undefined ||  chaiseConfig.confirmDelete){
                    var modalInstance = $uibModal.open({
                        templateUrl: 'annotations/confirm_delete.html',
                        controller: 'ConfirmDeleteController',
                        size: 'sm'
                    });

                    modalInstance.result.then(function () {
                        AnnotationsService.deleteAnnotation(annotation);
                        console.log('annotation deleted');
                    }, function () {
                        console.log('Modal dismissed');
                    });
                } else {
                    AnnotationsService.deleteAnnotation(annotation);
                    console.log('annotation deleted')
                }
            } else {
                AlertsService.addAlert({
                    type: 'error',
                    message: 'Sorry, this annotation cannot be deleted because there is at least 1 comment on it. Please delete the comments before trying to delete the annotation.'
                });
            }
        };

        function setHighlightedAnnotation(annotation) {
            vm.highlightedAnnotation = annotation.table.name + '-' + annotation.data.id;
        }

        // Centers and zooms to the annotation inside Annotorious
        function centerAnnotation(annotation) {
            setHighlightedAnnotation(annotation);
            return AnnotationsService.centerAnnotation(annotation);
        }

        function getNumComments(annotation) {
            return AnnotationsService.getNumComments(annotation.data.id);
        }

        // Returns boolean
        function hasComments(annotation) {
            // if there are comments return true
            return getNumComments(annotation) > 0 ? true : false;
        }

        // Return an annotation/section that matches an object of coordinates
        function findAnnotation(coordinates) {
            var length = vm.annotations.length;
            for (var i = 0; i < length; i++) {
                var annotationCoords = vm.annotations[i].data.coords;
                if (coordinates.x == annotationCoords[0] && coordinates.y == annotationCoords[1] && coordinates.width == annotationCoords[2] && coordinates.height == annotationCoords[3]) {
                    return vm.annotations[i];
                }
            }
        }

        // Scroll a DOM element into visible part of the browser
        function scrollIntoView(elementId) {
            // Using native JS b/c angular.element returns a jQuery/jqLite object,
            // which is incompatible with .scrollIntoView()
            document.getElementById(elementId).scrollIntoView({
                block: 'start',
                behavior: 'smooth'
            });
        }

        // Used to set the author based on the info object from the user object (user.info) that is set on every annotation
        // The info object is the session.client object and may contain a combination of display_name, full_name, and email
        function authorName(client) {
            return (client.display_name ? client.display_name : (client.full_name ? client.full_name : client.email ));
        }
    }]);
})();
<commit_msg>Fix typo<commit_after>(function() {
    'use strict';

    angular.module('chaise.viewer')

    .controller('AnnotationsController', ['AuthService', 'annotations', 'comments', 'anatomies', 'AnnotationsService', '$window', '$scope', '$uibModal', 'AlertsService', function AnnotationsController(AuthService, annotations, comments, anatomies, AnnotationsService, $window, $scope, $uibModal, AlertsService) {
        var vm = this;
        vm.annotations = annotations;
        vm.anatomies = anatomies;
        vm.colors = ['red', 'orange', 'gold', 'green', 'blue', 'purple'];
        vm.defaultColor = chaiseConfig.defaultAnnotationColor || 'red';
        vm.annotationTypes = ['rectangle', 'arrow']; // 'section' excluded b/c once you set an annotation as a section, it can't be changed to other types
        vm.filterByType = {section: true, rectangle: true, arrow: true}; // show all annotation types by default

        vm.filterAnnotations = filterAnnotations;

        vm.createMode = false;
        vm.newAnnotation = {config:{color: vm.defaultColor}};
        vm.drawAnnotation = drawAnnotation;
        vm.createAnnotation = createAnnotation;
        vm.cancelNewAnnotation = cancelNewAnnotation;

        vm.editedAnnotation = null; // Track which annotation is being edited right now
        var originalAnnotation = null; // Holds the original contents of annotation in the event that a user cancels an edit
        vm.editAnnotation = editAnnotation;
        vm.cancelEdit = cancelEdit;
        vm.updateAnnotation = updateAnnotation;

        vm.deleteAnnotation = deleteAnnotation;

        vm.highlightedAnnotation = null;
        vm.centerAnnotation = centerAnnotation;

        vm.getNumComments = getNumComments;
        vm.authorName = authorName;

        vm.allowCreate = AuthService.createAnnotation;
        vm.allowEdit = AuthService.editAnnotation;
        vm.allowDelete = AuthService.deleteAnnotation;

        // Listen to events of type 'message' (from Annotorious)
        $window.addEventListener('message', function annotationControllerListener(event) {
            // TODO: Check if origin is valid first; if not, return and exit.
            // Do this for the other listeners as well.
            if (event.origin === window.location.origin) {
                var data = event.data;
                var messageType = data.messageType;

                switch (messageType) {
                    case 'annotoriousReady':
                        // annotoriousReady case handled in viewer.app.js.
                        // Repeating the case here to avoid triggering default case
                        break;
                    case 'annotationDrawn':
                        vm.newAnnotation.shape = data.content.shape;
                        $scope.$apply(function() {
                            vm.createMode = true;
                        });
                        break;
                    case 'onHighlighted':
                    // On-hover highlighting behavior no longer needed
                    // OSD still sends this message out on hover though, so the
                    // is case here to avoid triggering default case
                        break;
                    case 'onUnHighlighted':
                    // On-hover highlighting behavior no longer needed
                    // OSD still sends this message out on hover though, so the
                    // is case here to avoid triggering default case
                        break;
                    case 'onClickAnnotation':
                        var content = JSON.parse(data.content);
                        var annotation = findAnnotation(content.data.shapes[0].geometry);
                        if (annotation) {
                            var annotationId = annotation.table.name + '-' + annotation.data.id;
                            $scope.$apply(function() {
                                vm.highlightedAnnotation = annotationId;
                            });
                            scrollIntoView(annotationId);
                        }
                        break;
                    default:
                        console.log('Invalid event message type "' + messageType + '"');
                }
            } else {
                console.log('Invalid event origin. Event origin: ', event.origin, '. Expected origin: ', window.location.origin);
            }
        });

        function filterAnnotations(annotation) {
            if (!vm.query) {
                return true;
            }

            vm.query = vm.query.toLowerCase();

            annotation = annotation.data;
            var author = annotation.author;
            var props = [annotation.anatomy, annotation.description, author.display_name, author.full_name, author.email, annotation.created];
            var numProps = props.length;
            for (var i = 0; i < numProps; i++) {
                if (props[i] && props[i].toLowerCase().indexOf(vm.query) > -1) {
                    return true;
                }
            }

            var commentsArr = comments[annotation.id];
            if (commentsArr) {
                var numComments = commentsArr.length;
                for (var c = 0; c < numComments; c++) {
                    var comment = commentsArr[c].data;
                    var commentAuthor = comment.author;
                    var commentProps = [comment.comment, comment.created, commentAuthor.display_name, commentAuthor.full_name, commentAuthor.email];
                    var numCommentProps = commentProps.length;
                    for (var p = 0; p < numCommentProps; p++) {
                        if (commentProps[p] && commentProps[p].toLowerCase().indexOf(vm.query) > -1) {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        function drawAnnotation(type) {
            vm.newAnnotation.type = type;
            return AnnotationsService.drawAnnotation();
        }

        function createAnnotation() {
            vm.createMode = false;
            AnnotationsService.createAnnotation(vm.newAnnotation);
            vm.newAnnotation = {config:{color: vm.defaultColor}};
        }

        function cancelNewAnnotation() {
            vm.createMode = false;
            return AnnotationsService.cancelNewAnnotation();
        }

        function editAnnotation(annotation) {
            // Must make a copy instead of assigning to remove original annotation's
            // references. Otherwise, changing something in editedAnnotation will
            // also change the original annotation.
            vm.editedAnnotation = angular.copy(annotation);

            vm.editedAnnotation.domId = annotation.table.name + '-' + annotation.data.id;
            setHighlightedAnnotation(annotation);
            annotation = annotation.data;
            originalAnnotation = {
                description: annotation.description,
                anatomy: annotation.anatomy,
                config: annotation.config,
                type: annotation.type
            };
        }

        function cancelEdit(annotation) {
            vm.editedAnnotation = null;
            var data = annotation.data;
            data.description = originalAnnotation.description;
            data.anatomy = originalAnnotation.anatomy;
            data.config = originalAnnotation.config;
            data.type = originalAnnotation.type;
        }

        function updateAnnotation(annotation) {
            annotation.data = vm.editedAnnotation.data;
            AnnotationsService.updateAnnotation(annotation);
            vm.editedAnnotation = null;
        }

        function deleteAnnotation(annotation) {
            // if annotation has comments, allow it to be deleted
            if (!hasComments(annotation)) {
                if (chaiseConfig.confirmDelete == undefined || chaiseConfig.confirmDelete){
                    var modalInstance = $uibModal.open({
                        templateUrl: 'annotations/confirm_delete.html',
                        controller: 'ConfirmDeleteController',
                        size: 'sm'
                    });

                    modalInstance.result.then(function () {
                        AnnotationsService.deleteAnnotation(annotation);
                        console.log('annotation deleted');
                    }, function () {
                        console.log('Modal dismissed');
                    });
                } else {
                    AnnotationsService.deleteAnnotation(annotation);
                    console.log('annotation deleted')
                }
            } else {
                AlertsService.addAlert({
                    type: 'error',
                    message: 'Sorry, this annotation cannot be deleted because there is at least 1 comment on it. Please delete the comments before trying to delete the annotation.'
                });
            }
        };

        function setHighlightedAnnotation(annotation) {
            vm.highlightedAnnotation = annotation.table.name + '-' + annotation.data.id;
        }

        // Centers and zooms to the annotation inside Annotorious
        function centerAnnotation(annotation) {
            setHighlightedAnnotation(annotation);
            return AnnotationsService.centerAnnotation(annotation);
        }

        function getNumComments(annotation) {
            return AnnotationsService.getNumComments(annotation.data.id);
        }

        // Returns boolean
        function hasComments(annotation) {
            // if there are comments return true
            return getNumComments(annotation) > 0 ? true : false;
        }

        // Return an annotation/section that matches an object of coordinates
        function findAnnotation(coordinates) {
            var length = vm.annotations.length;
            for (var i = 0; i < length; i++) {
                var annotationCoords = vm.annotations[i].data.coords;
                if (coordinates.x == annotationCoords[0] && coordinates.y == annotationCoords[1] && coordinates.width == annotationCoords[2] && coordinates.height == annotationCoords[3]) {
                    return vm.annotations[i];
                }
            }
        }

        // Scroll a DOM element into visible part of the browser
        function scrollIntoView(elementId) {
            // Using native JS b/c angular.element returns a jQuery/jqLite object,
            // which is incompatible with .scrollIntoView()
            document.getElementById(elementId).scrollIntoView({
                block: 'start',
                behavior: 'smooth'
            });
        }

        // Used to set the author based on the info object from the user object (user.info) that is set on every annotation
        // The info object is the session.client object and may contain a combination of display_name, full_name, and email
        function authorName(client) {
            return (client.display_name ? client.display_name : (client.full_name ? client.full_name : client.email ));
        }
    }]);
})();
<|endoftext|>


<commit_before>/*
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import {MatrixClientPeg} from './MatrixClientPeg';
import dis from "./dispatcher/dispatcher";
import Modal from './Modal';
import * as sdk from './index';
import { _t } from './languageHandler';
import {RightPanelPhases} from "./stores/RightPanelStorePhases";
import {findDMForUser} from './createRoom';
import {accessSecretStorage} from './SecurityManager';
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
import {Action} from './dispatcher/actions';

async function enable4SIfNeeded() {
    const cli = MatrixClientPeg.get();
    if (!cli.isCryptoEnabled()) {
        return false;
    }
    const usk = cli.getCrossSigningId("user_signing");
    if (!usk) {
        await accessSecretStorage();
        return false;
    }

    return true;
}

function UntrustedDeviceDialog(props) {
    const {device, user, onFinished} = props;
    const BaseDialog = sdk.getComponent("dialogs.BaseDialog");
    const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
    let askToVerifyText;
    let newSessionText;

    if (MatrixClientPeg.get().getUserId() === user.userId) {
        newSessionText = _t("You signed in to a new session without verifying it:");
        askToVerifyText = _t("Verify your other session using one of the options below.");
    } else {
        newSessionText = _t("%(name)s (%(userId)s) signed in to a new session without verifying it:",
            {name: user.displayName, userId: user.userId});
        askToVerifyText = _t("Ask this user to verify their session, or manually verify it below.");
    }

    return <BaseDialog
        onFinished={onFinished}
        headerImage={require("../res/img/e2e/warning.svg")}
        title={_t("Not Trusted")}>

{newSessionText}

{device.getDisplayName()} ({device.deviceId})

{askToVerifyText}

            <AccessibleButton element="button" kind="secondary" onClick={() => onFinished("legacy")}>{_t("Manually Verify by Text")}</AccessibleButton>
            <AccessibleButton element="button" kind="secondary" onClick={() => onFinished("sas")}>{_t("Interactively verify by Emoji")}</AccessibleButton>
            <AccessibleButton kind="primary" onClick={() => onFinished()}>{_t("Done")}</AccessibleButton>

    </BaseDialog>;
}

export async function verifyDevice(user, device) {
    const cli = MatrixClientPeg.get();
    // if cross-signing is not explicitly disabled, check if it should be enabled first.
    if (cli.getCryptoTrustCrossSignedDevices()) {
        if (!await enable4SIfNeeded()) {
            return;
        }
    }

    Modal.createTrackedDialog("Verification warning", "unverified session", UntrustedDeviceDialog, {
        user,
        device,
        onFinished: async (action) => {
            if (action === "sas") {
                const verificationRequestPromise = cli.legacyDeviceVerification(
                    user.userId,
                    device.deviceId,
                    verificationMethods.SAS,
                );
                dis.dispatch({
                    action: Action.SetRightPanelPhase,
                    phase: RightPanelPhases.EncryptionPanel,
                    refireParams: {member: user, verificationRequestPromise},
                });
            } else if (action === "legacy") {
                const ManualDeviceKeyVerificationDialog =
                    sdk.getComponent("dialogs.ManualDeviceKeyVerificationDialog");
                Modal.createTrackedDialog("Legacy verify session", "legacy verify session",
                    ManualDeviceKeyVerificationDialog,
                    {
                        userId: user.userId,
                        device,
                    },
                );
            }
        },
    });
}

export async function legacyVerifyUser(user) {
    const cli = MatrixClientPeg.get();
    // if cross-signing is not explicitly disabled, check if it should be enabled first.
    if (cli.getCryptoTrustCrossSignedDevices()) {
        if (!await enable4SIfNeeded()) {
            return;
        }
    }
    const verificationRequestPromise = cli.requestVerification(user.userId);
    dis.dispatch({
        action: Action.SetRightPanelPhase,
        phase: RightPanelPhases.EncryptionPanel,
        refireParams: {member: user, verificationRequestPromise},
    });
}

export async function verifyUser(user) {
    if (!await enable4SIfNeeded()) {
        return;
    }
    const existingRequest = pendingVerificationRequestForUser(user);
    dis.dispatch({
        action: Action.SetRightPanelPhase,
        phase: RightPanelPhases.EncryptionPanel,
        refireParams: {
            member: user,
            verificationRequest: existingRequest,
        },
    });
}

export function pendingVerificationRequestForUser(user) {
    const cli = MatrixClientPeg.get();
    const dmRoom = findDMForUser(cli, user.userId);
    if (dmRoom) {
        return cli.findVerificationRequestDMInProgress(dmRoom.roomId);
    }
}
<commit_msg>Require registration for verification actions<commit_after>/*
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import {MatrixClientPeg} from './MatrixClientPeg';
import dis from "./dispatcher/dispatcher";
import Modal from './Modal';
import * as sdk from './index';
import { _t } from './languageHandler';
import {RightPanelPhases} from "./stores/RightPanelStorePhases";
import {findDMForUser} from './createRoom';
import {accessSecretStorage} from './SecurityManager';
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
import {Action} from './dispatcher/actions';

async function enable4SIfNeeded() {
    const cli = MatrixClientPeg.get();
    if (!cli.isCryptoEnabled()) {
        return false;
    }
    const usk = cli.getCrossSigningId("user_signing");
    if (!usk) {
        await accessSecretStorage();
        return false;
    }

    return true;
}

function UntrustedDeviceDialog(props) {
    const {device, user, onFinished} = props;
    const BaseDialog = sdk.getComponent("dialogs.BaseDialog");
    const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
    let askToVerifyText;
    let newSessionText;

    if (MatrixClientPeg.get().getUserId() === user.userId) {
        newSessionText = _t("You signed in to a new session without verifying it:");
        askToVerifyText = _t("Verify your other session using one of the options below.");
    } else {
        newSessionText = _t("%(name)s (%(userId)s) signed in to a new session without verifying it:",
            {name: user.displayName, userId: user.userId});
        askToVerifyText = _t("Ask this user to verify their session, or manually verify it below.");
    }

    return <BaseDialog
        onFinished={onFinished}
        headerImage={require("../res/img/e2e/warning.svg")}
        title={_t("Not Trusted")}>

{newSessionText}

{device.getDisplayName()} ({device.deviceId})

{askToVerifyText}

            <AccessibleButton element="button" kind="secondary" onClick={() => onFinished("legacy")}>{_t("Manually Verify by Text")}</AccessibleButton>
            <AccessibleButton element="button" kind="secondary" onClick={() => onFinished("sas")}>{_t("Interactively verify by Emoji")}</AccessibleButton>
            <AccessibleButton kind="primary" onClick={() => onFinished()}>{_t("Done")}</AccessibleButton>

    </BaseDialog>;
}

export async function verifyDevice(user, device) {
    const cli = MatrixClientPeg.get();
    if (cli.isGuest()) {
        dis.dispatch({action: 'require_registration'});
        return;
    }
    // if cross-signing is not explicitly disabled, check if it should be enabled first.
    if (cli.getCryptoTrustCrossSignedDevices()) {
        if (!await enable4SIfNeeded()) {
            return;
        }
    }

    Modal.createTrackedDialog("Verification warning", "unverified session", UntrustedDeviceDialog, {
        user,
        device,
        onFinished: async (action) => {
            if (action === "sas") {
                const verificationRequestPromise = cli.legacyDeviceVerification(
                    user.userId,
                    device.deviceId,
                    verificationMethods.SAS,
                );
                dis.dispatch({
                    action: Action.SetRightPanelPhase,
                    phase: RightPanelPhases.EncryptionPanel,
                    refireParams: {member: user, verificationRequestPromise},
                });
            } else if (action === "legacy") {
                const ManualDeviceKeyVerificationDialog =
                    sdk.getComponent("dialogs.ManualDeviceKeyVerificationDialog");
                Modal.createTrackedDialog("Legacy verify session", "legacy verify session",
                    ManualDeviceKeyVerificationDialog,
                    {
                        userId: user.userId,
                        device,
                    },
                );
            }
        },
    });
}

export async function legacyVerifyUser(user) {
    const cli = MatrixClientPeg.get();
    if (cli.isGuest()) {
        dis.dispatch({action: 'require_registration'});
        return;
    }
    // if cross-signing is not explicitly disabled, check if it should be enabled first.
    if (cli.getCryptoTrustCrossSignedDevices()) {
        if (!await enable4SIfNeeded()) {
            return;
        }
    }
    const verificationRequestPromise = cli.requestVerification(user.userId);
    dis.dispatch({
        action: Action.SetRightPanelPhase,
        phase: RightPanelPhases.EncryptionPanel,
        refireParams: {member: user, verificationRequestPromise},
    });
}

export async function verifyUser(user) {
    const cli = MatrixClientPeg.get();
    if (cli.isGuest()) {
        dis.dispatch({action: 'require_registration'});
        return;
    }
    if (!await enable4SIfNeeded()) {
        return;
    }
    const existingRequest = pendingVerificationRequestForUser(user);
    dis.dispatch({
        action: Action.SetRightPanelPhase,
        phase: RightPanelPhases.EncryptionPanel,
        refireParams: {
            member: user,
            verificationRequest: existingRequest,
        },
    });
}

export function pendingVerificationRequestForUser(user) {
    const cli = MatrixClientPeg.get();
    const dmRoom = findDMForUser(cli, user.userId);
    if (dmRoom) {
        return cli.findVerificationRequestDMInProgress(dmRoom.roomId);
    }
}
<|endoftext|>


<commit_before>console.log('download.js loaded')

downloadData=function(div){
    console.log('downloading ...')
    downloadData.getParms()
    // extract limit from url if provided
    if(downloadData.parms.url.match(/limit=\d+/)){
        downloadData.parms.limit=parseInt(downloadData.parms.url.match(/limit=(\d+)/)[1])
    }
    //div.innerHTML='

Maximum number of features to download: <input id="downloadDataLimitInput" size=6> <button class="btn btn-primary" id="downloadButton">Download</button>'
    //downloadDataLimitInput.value=100000
    //downloadData.parms.url=downloadData.parms.url.replace(/limit=\d+&/,'').replace(/"\$gte"\:[0123456789\.]+/,'"$gte":0')
    //downloadData.parms.url=downloadData.parms.url.replace('"randval":{"$gte":0},','')
    // 'properties.scalar_features.nv' :

    downloadData.parms.url=downloadData.parms.url.replace(/limit=\d+&/,'').replace(/"\$gte"\:[0123456789\.]+/,'"$gte":0')
    div.innerHTML='

Maximum number of features to download: <input style="color:blue" id="downloadDataLimitInput" size=6> <button class="btn btn-primary" id="downloadButton" disabled>Download</button>
<button id="saveFileJsonBt" class="btn btn-success" disabled>Save File as JSON</button> <button id="saveFileCsvBt" class="btn btn-success" disabled>Save File as CSV</button> (Chrome browser preferred)'
    downloadDataLimitInput.value=100000
    downloadData.msg=function(m,c){
        downloadDataLimitMsg.style.color=c||'blue'
        downloadDataLimitMsg.innerHTML=m
    }

    var findQuery = function(pre){ //assemble find string where "randval":{"$gte":0} is
        pre=pre||''
        return downloadData.parms.url.replace('"randval":{"$gte":0},',pre+'"$and":[{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.xTitle+'", "value" : {"$gte" : '+downloadData.parms.xmin+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.xTitle+'", "value" : {"$lte" : '+downloadData.parms.xmax+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.yTitle+'", "value" : {"$gte" : '+downloadData.parms.ymin+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.yTitle+'", "value" : {"$lte" : '+downloadData.parms.ymax+'}}}}}}],')
    }

    downloadData.msg('estimating size of dataset ...','blue')
    //$.getJSON(downloadData.parms.url.replace('"$gte":0','"$gte":0.999')+'&limit=1000') // sample 1/1000th of the features
    $.getJSON(findQuery('"randval":{"$gte":0.999},')+'&limit=1000')
     .then(function(x){
         var n=x.length
         if(n>100){
             downloadData.msg('About '+n+',000 expected. That will take a while, for something faster maybe go back to the plot and select a smaller area','red')
         }else if(n>10){
             downloadData.msg('About '+n+',000 expected. It will take a few seconds to retrieve them','orange')
         }else{
             downloadData.msg('fewer than 10,000 features expected, it woudn\'t take long to retrieve them all','green')
         }
         downloadData.parms.pn=n*1000 // predicted n
         downloadData.parms.x=x
         downloadButton.disabled=false
     })
     //downloadData.parms.urlFind=downloadData.parms.url.replace('"randval":{"$gte":0},','"$and":[{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.xTitle+'", "value" : {"$gte" : '+downloadData.parms.xmin+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.xTitle+'", "value" : {"$lte" : '+downloadData.parms.xmax+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.yTitle+'", "value" : {"$gte" : '+downloadData.parms.ymin+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.yTitle+'", "value" : {"$lte" : '+downloadData.parms.ymax+'}}}}}}],')
     downloadData.parms.urlFind=findQuery()

     downloadButton.onclick=function(){
         downloadButton.disabled=true
         downloadData.msg('preparing download ...','blue')
         // first find out what fields are there to retrieve
         /*
         var getFields=function(){
             var x = downloadData.propertiesArray(downloadData.parms.x)
         }
         getFields()
         */
         downloadData.getData()

     }
     saveFileJsonBt.onclick=function(){

         jmat.saveFile(JSON.stringify(downloadData.dt),'features.json')
     }

     saveFileCsvBt.onclick=function(){
         var ff = Object.getOwnPropertyNames(downloadData.dt[0]).sort()
         var csv=ff.join(',')+'\n' // header
         downloadData.dt.forEach(function(r){
             var x = []
             ff.forEach(function(f){
                 x.push(r[f])
             })
             csv+=x.join(',')+'\n'
         })
         jmat.saveFile(csv.slice(0,-1),'features.csv')

     }

}

downloadData.propertiesArray=function(x){ // convert ns in an awway into an arra of ns
    return x.map(function(xi){
        var y = {}
        xi = xi.properties.scalar_features[0].nv.forEach(function(z){
            y[z.name]=z.value
        })
        return y
    })
}

downloadData.getParms=function(){ // extract parameters from url search
    downloadData.parms=downloadData.parms||{}
    location.search.slice(1).split('&')
     .forEach(function(pp){
         pp = pp.split('=')
         downloadData.parms[pp[0]]=decodeURIComponent(decodeURIComponent(pp[1]))
     })
    //var pp = decodeURIComponent(decodeURIComponent(location.search)).slice(1)
}

// get the data

downloadData.getData=function(){
    var n =  1000 // at a time
    if(!downloadData.dt){downloadData.dt=[]}
    //if(!downloadData.parms.n){downloadData.parms.n=0}
    if(!downloadData.parms.lastId){
        var url= downloadData.parms.urlFind+'&limit='+n
    }else{
        //var url=downloadData.parms.urlFind.replace('find={','find={"_id":{"$gt":"'+downloadData.parms.lastId+'"},')+'&limit='+n
        var url= downloadData.parms.urlFind+'&limit='+n+'&offset="'+downloadData.parms.lastId+'"'
    }
    //url = url.replace('http://quip1.uhmc.sunysb.edu','http://129.49.249.191')
    $.getJSON(url)
     .then(function(x){
         try{
             if(!Array.isArray(x)){x=[]}
             //downloadData.parms.n+=x.length
             if(x.length>0){
                 downloadData.parms.lastId=x.slice(-1)[0]._id
                 downloadData.dt=downloadData.dt.concat(downloadData.propertiesArray(x))
                 console.log(Date()+' n = '+downloadData.dt.length+', last _id = '+downloadData.parms.lastId)
             }
             downloadData.msg('features retrieved: '+downloadData.dt.length+' ... out of an estimated total of '+downloadData.parms.pn)
             if((x.length==n)&(x.length<parseInt(downloadDataLimitInput.value))){
                 setTimeout(function(){
                    //downloadData.parms.urlFind = downloadData.parms.urlFind.replace('http://quip1.uhmc.sunysb.edu','http://129.49.249.191')
                    downloadData.getData()
                 },1000)
             }else{
                 downloadData.dt=downloadData.dt.slice(0,Math.min(downloadData.dt.length,parseInt(downloadDataLimitInput.value)))
                 var N=downloadData.dt.length
                 var M=Object.getOwnPropertyNames(downloadData.dt[0]).length
                 downloadData.msg('Data retrieval ended, '+N+' x '+M+' = '+(N*M)+' values retrieved','green') //, it can be resumed by clicking on Download','green')
                 downloadButton.disabled=false
                 downloadButton.textContent='New Download'
                 downloadButton.style.backgroundColor='orange'
                 downloadButton.style.color='green'
                 downloadButton.onclick=function(){
                     window.open(location.href)
                 }
             }
             saveFileJsonBt.disabled=false
             saveFileCsvBt.disabled=false
         }
         catch(err){
             console.log(Date()+' error:',x)
             setTimeout(function(){
                 downloadData.getData()
             },1000)

         }

     })
     .fail(function(err){
         downloadData.msg('Connection Error :-( ','red')
     })

    4
}

// Run
if(document.getElementById('downloadDataDiv')){
    downloadData(downloadDataDiv)
}
window.onload=function(){
    if(document.getElementById('downloadDataDiv')){
        downloadData(downloadDataDiv)
    }
}

/*
example
http://quip1.uhmc.sunysb.edu:3000/?limit=1000&find={%22randval%22:{%22$gte%22:0.17426561033975674},%22provenance.analysis.execution_id%22:%22luad:bg:20160520%22,%22provenance.image.case_id%22:%22TCGA-55-6543-01Z-00-DX1%22}&db=u24_luad

*/<commit_msg>Integrate code 'tabulare data for non-chrome browsers' (author Dr. Almeida)<commit_after>console.log('download.js loaded')

downloadData=function(div){
    console.log('downloading ...')
    downloadData.getParms()
    // extract limit from url if provided
    if(downloadData.parms.url.match(/limit=\d+/)){
        downloadData.parms.limit=parseInt(downloadData.parms.url.match(/limit=(\d+)/)[1])
    }
    //div.innerHTML='

Maximum number of features to download: <input id="downloadDataLimitInput" size=6> <button class="btn btn-primary" id="downloadButton">Download</button>'
    //downloadDataLimitInput.value=100000
    //downloadData.parms.url=downloadData.parms.url.replace(/limit=\d+&/,'').replace(/"\$gte"\:[0123456789\.]+/,'"$gte":0')
    //downloadData.parms.url=downloadData.parms.url.replace('"randval":{"$gte":0},','')
    // 'properties.scalar_features.nv' :

    downloadData.parms.url=downloadData.parms.url.replace(/limit=\d+&/,'').replace(/"\$gte"\:[0123456789\.]+/,'"$gte":0')
    div.innerHTML='

Maximum number of features to download: <input style="color:blue" id="downloadDataLimitInput" size=6> <button class="btn btn-primary" id="downloadButton" disabled>Start download</button>
<button id="saveFileJsonBt" class="btn btn-success" disabled>Save (partial) File as JSON</button> <button id="saveFileCsvBt" class="btn btn-success" disabled>Save (partial) File as CSV</button> (Chrome browser)'
    downloadDataLimitInput.value=100000
    downloadData.msg=function(m,c){
        downloadDataLimitMsg.style.color=c||'blue'
        downloadDataLimitMsg.innerHTML=m
    }

    var findQuery = function(pre){ //assemble find string where "randval":{"$gte":0} is
        pre=pre||''
        return downloadData.parms.url.replace('"randval":{"$gte":0},',pre+'"$and":[{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.xTitle+'", "value" : {"$gte" : '+downloadData.parms.xmin+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.xTitle+'", "value" : {"$lte" : '+downloadData.parms.xmax+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.yTitle+'", "value" : {"$gte" : '+downloadData.parms.ymin+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.yTitle+'", "value" : {"$lte" : '+downloadData.parms.ymax+'}}}}}}],')
    }

    downloadData.msg('estimating size of dataset ...','blue')
    //$.getJSON(downloadData.parms.url.replace('"$gte":0','"$gte":0.999')+'&limit=1000') // sample 1/1000th of the features
    $.getJSON(findQuery('"randval":{"$gte":0.999},')+'&limit=1000')
     .then(function(x){
         var n=x.length
         if(n>100){
             downloadData.msg('About '+n+',000 expected. That will take a while, for something faster maybe go back to the plot and select a smaller area','red')
         }else if(n>10){
             downloadData.msg('About '+n+',000 expected. It will take a few seconds to retrieve them','orange')
         }else{
             downloadData.msg('fewer than 10,000 features expected, it woudn\'t take long to retrieve them all','green')
         }
         downloadData.parms.pn=n*1000 // predicted n
         downloadData.parms.x=x
         downloadButton.disabled=false
     })
     //downloadData.parms.urlFind=downloadData.parms.url.replace('"randval":{"$gte":0},','"$and":[{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.xTitle+'", "value" : {"$gte" : '+downloadData.parms.xmin+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.xTitle+'", "value" : {"$lte" : '+downloadData.parms.xmax+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.yTitle+'", "value" : {"$gte" : '+downloadData.parms.ymin+'}}}}}},{"properties.scalar_features":{"$elemMatch":{"nv":{"$elemMatch":{"name" : "'+downloadData.parms.yTitle+'", "value" : {"$lte" : '+downloadData.parms.ymax+'}}}}}}],')
     downloadData.parms.urlFind=findQuery()

     downloadButton.onclick=function(){
         downloadButton.disabled=true
         downloadButton.textContent='Downloading ...'
         downloadData.msg('preparing download ...','red')
         // first find out what fields are there to retrieve
         /*
         var getFields=function(){
             var x = downloadData.propertiesArray(downloadData.parms.x)
         }
         getFields()
         */
         downloadData.getData()

     }
     saveFileJsonBt.onclick=function(){

         jmat.saveFile(JSON.stringify(downloadData.dt),'features.json')
     }

     saveFileCsvBt.onclick=function(){
         var ff = Object.getOwnPropertyNames(downloadData.dt[0]).sort()
         var csv=ff.join(',')+'\n' // header
         downloadData.dt.forEach(function(r){
             var x = []
             ff.forEach(function(f){
                 x.push(r[f])
             })
             csv+=x.join(',')+'\n'
         })
         jmat.saveFile(csv.slice(0,-1),'features.csv')

     }

}

downloadData.propertiesArray=function(x){ // convert ns in an awway into an arra of ns
    return x.map(function(xi){
        var y = {}
        xi = xi.properties.scalar_features[0].nv.forEach(function(z){
            y[z.name]=z.value
        })
        return y
    })
}

downloadData.getParms=function(){ // extract parameters from url search
    downloadData.parms=downloadData.parms||{}
    location.search.slice(1).split('&')
     .forEach(function(pp){
         pp = pp.split('=')
         downloadData.parms[pp[0]]=decodeURIComponent(decodeURIComponent(pp[1]))
     })
    //var pp = decodeURIComponent(decodeURIComponent(location.search)).slice(1)
}

// get the data

downloadData.dataTabling=function(){
    saveFileJsonBt.textContent='Save File as JSON'
    saveFileCsvBt.textContent='Save File as CSV'
    var div = document.getElementById('dataTabling')|| document.createElement('div')
    div.id='dataTabling'
    div.innerHTML='
Variables:

<button class="btn btn-success" id="showhideData">tabulate data</button>

'
    downloadDataDiv.appendChild(div)
    downloadData.fieldsChecked=[]
    if(downloadData.dt.length>0){
        downloadData.fields=Object.getOwnPropertyNames(downloadData.dt[0]).sort()
        downloadData.fields.forEach(function(p,i){
            var sp = document.createElement('span')
            var ip = document.createElement('input')
            ip.type='radio'
            ip.value=p
            if(i<3){
                ip.checked=true
                ip.nocheck=false
                downloadData.fieldsChecked.push(p)
            } else {
                ip.checked=false
                ip.nocheck=true
            }
            var h = ' '+p
            sp.innerHTML = h
            sp.appendChild(ip)
            sp.style.color='navy'
            tableVars.appendChild(sp)
            ip.onclick=function(){
                this.nocheck=!this.nocheck
                this.checked=!this.nocheck
                if(this.nocheck){
                    downloadData.fieldsChecked.pop(downloadData.fieldsChecked.indexOf(this.value))
                } else {
                    downloadData.fieldsChecked.push(this.value) // add to list of selected fields
                }
                console.log(downloadData.fieldsChecked)
            }
        })
    }
    showhideData.onclick=function(){
        var div = document.getElementById('tableData')
        div.innerHTML='' // clear if something is there
        div.appendChild(downloadData.docs2html(downloadData.dt,downloadData.fieldsChecked))
    }

    4
}

downloadData.docs2html=function(docs,parms){
    parms = parms || Object.getOwnPropertyNames(docs[0])
    var tb = document.createElement('table')
    tb.cellSpacing='5px'
    var th = document.createElement('thead')
    tb.appendChild(th)
    var trh = document.createElement('tr')
    trh.style.fontSize='10px'
    trh.style.color='navy'
    th.appendChild(trh)
    parms.forEach(function(p){
        var td = document.createElement('td')
        td.textContent=p
        td.style.padding='3px'
        trh.appendChild(td)
    })
    var tbd = document.createElement('tbody')
    tb.appendChild(tbd)

    docs.forEach(function(d){
        var tr = document.createElement('tr')
        tr.style.fontSize='10px'
        tbd.appendChild(tr)
        parms.forEach(function(p){
            var td = document.createElement('td')
            td.style.padding='3px'
            td.textContent=d[p]
            tr.appendChild(td)
            4
        })
    })
    return tb
}

downloadData.getData=function(){
    var n =  1000 // at a time
    if(!downloadData.dt){downloadData.dt=[]}
    //if(!downloadData.parms.n){downloadData.parms.n=0}
    if(!downloadData.parms.lastId){
        var url= downloadData.parms.urlFind+'&limit='+n
    }else{
        //var url=downloadData.parms.urlFind.replace('find={','find={"_id":{"$gt":"'+downloadData.parms.lastId+'"},')+'&limit='+n
        var url= downloadData.parms.urlFind+'&limit='+n+'&offset="'+downloadData.parms.lastId+'"'
    }
    //url = url.replace('http://quip1.uhmc.sunysb.edu','http://129.49.249.191')
    $.getJSON(url)
     .then(function(x){
         try{
             if(!Array.isArray(x)){x=[]}
             //downloadData.parms.n+=x.length
             if(x.length>0){
                 downloadData.parms.lastId=x.slice(-1)[0]._id
                 downloadData.dt=downloadData.dt.concat(downloadData.propertiesArray(x))
                 console.log(Date()+' n = '+downloadData.dt.length+', last _id = '+downloadData.parms.lastId)
             }
             downloadData.msg('features retrieved so far: '+downloadData.dt.length+' out of an estimated total of '+downloadData.parms.pn+' ...','orange')
             if((x.length==n)&(x.length<parseInt(downloadDataLimitInput.value))){
                 setTimeout(function(){
                    //downloadData.parms.urlFind = downloadData.parms.urlFind.replace('http://quip1.uhmc.sunysb.edu','http://129.49.249.191')
                    downloadData.getData()
                 },1000)
             }else{
                 downloadData.dt=downloadData.dt.slice(0,Math.min(downloadData.dt.length,parseInt(downloadDataLimitInput.value)))
                 var N=downloadData.dt.length
                 var M=Object.getOwnPropertyNames(downloadData.dt[0]).length
                 downloadData.msg('Data retrieval ended, '+N+' x '+M+' = '+(N*M)+' values retrieved','green') //, it can be resumed by clicking on Download','green')
                 downloadButton.disabled=false
                 downloadButton.textContent='New Download'
                 downloadButton.style.backgroundColor='orange'
                 downloadButton.style.color='green'
                 downloadButton.onclick=function(){
                     window.open(location.href)
                 }
                 // make data tables available
                 downloadData.dataTabling()
             }
             saveFileJsonBt.disabled=false
             saveFileCsvBt.disabled=false
         }
         catch(err){
             console.log(Date()+' error:',x)
             setTimeout(function(){
                 downloadData.getData()
             },1000)

         }

     })
     .fail(function(err){
         downloadData.msg('Connection Error :-( ','red')
     })

    4
}

// Run
if(document.getElementById('downloadDataDiv')){
    downloadData(downloadDataDiv)
}
window.onload=function(){
    if(document.getElementById('downloadDataDiv')){
        downloadData(downloadDataDiv)
    }
}

/*
example
http://quip1.uhmc.sunysb.edu:3000/?limit=1000&find={%22randval%22:{%22$gte%22:0.17426561033975674},%22provenance.analysis.execution_id%22:%22luad:bg:20160520%22,%22provenance.image.case_id%22:%22TCGA-55-6543-01Z-00-DX1%22}&db=u24_luad

*/<|endoftext|>


<commit_before>/*!
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 */

import '@ciscospark/internal-plugin-wdm';
import '@ciscospark/plugin-logger';
import '@ciscospark/plugin-messages';
import '@ciscospark/plugin-rooms';
import CiscoSpark, {SparkHttpError} from '@ciscospark/spark-core';
import {assert} from '@ciscospark/test-helper-chai';
import path from 'path';
import sinon from '@ciscospark/test-helper-sinon';
import testUsers from '@ciscospark/test-helper-test-users';
import fh from '@ciscospark/test-helper-file';
import {browserOnly, flaky, nodeOnly} from '@ciscospark/test-helper-mocha';

const KNOWN_HOSTED_IMAGE_URL = 'https://download.ciscospark.com/test/photo.png';

describe('plugin-messages', function () {
  this.timeout(60000);

  let spark;
  before('create user', () => testUsers.create({count: 1})
    .then(([user]) => {
      spark = new CiscoSpark({credentials: user.token});
    }));

  let server;
  nodeOnly(before)('start yakbak', () => {
    /* eslint-disable global-require */
    const http = require('http');
    const yakbak = require('yakbak');
    /* eslint-enable global-require */
    console.debug('starting yakbak server...');
    // CONVERSATION_SERVICE: process.env.CONVERSATION_SERVICE || process.env.CONVERSATION_SERVICE_URL || 'https://conv-a.wbx2.com/conversation/api/v1',
    server = http.createServer(yakbak('https://api.ciscospark.com', {
      dirname: path.join(__dirname, '/tapes')
    })).listen(3000);
  });

  describe('#messages', () => {
    let room;
    before(() => spark.rooms.create({title: 'Cisco Spark Test Room'})
      .then((r) => {
        room = r;
        return spark.messages.create({
          roomId: room.id,
          text: 'First Message'
        });
      }));

    after(() => {
      if (!room) {
        return Promise.resolve();
      }

      return spark.rooms.remove(room)
        .catch((reason) => {
          console.error('Failed to remove room', reason);
        });
    });

    describe('#create()', () => {
      it('posts a message in a room', () => {
        const text = 'A test message';
        return spark.messages.create({
          roomId: room.id,
          text
        })
          .then((message) => {
            assert.isDefined(message);
            assert.isMessage(message);
            assert.equal(message.text, text);
          });
      });

      it('posts a file to a room by specifying the file\'s url', () => spark.messages.create({
        roomId: room.id,
        file: KNOWN_HOSTED_IMAGE_URL
      })
        .then((message) => {
          assert.property(message, 'files');
          assert.isDefined(message.files);
          assert.isArray(message.files);
          assert.lengthOf(message.files, 1);
        }));

      let blob, buffer;

      browserOnly(before)(() => fh.fetch('sample-image-small-one.png')
        .then((file) => {
          blob = file;

          return new Promise((resolve) => {
            /* global FileReader */
            const fileReader = new FileReader();
            fileReader.onload = function () {
              buffer = this.result;
              resolve();
            };
            fileReader.readAsArrayBuffer(blob);
          });
        }));

      nodeOnly(before)(() => fh.fetchWithoutMagic('sample-image-small-one.png')
        .then((file) => {
          buffer = file;
        }));

      browserOnly(it)('posts a file to a room by directly supplying its blob', () => spark.messages.create({
        roomId: room.id,
        file: blob
      })
        .then((message) => {
          assert.property(message, 'files');
          assert.isDefined(message.files);
          assert.isArray(message.files);
          assert.lengthOf(message.files, 1);
        }));

      // Disabling it gating pipelines because it failes a lot and we get
      // mostly adequate coverage via blob upload
      flaky(it)('posts a file to a room by directly supplying its buffer', () => spark.messages.create({
        roomId: room.id,
        file: buffer
      })
        .then((message) => {
          assert.property(message, 'files');
          assert.isDefined(message.files);
          assert.isArray(message.files);
          assert.lengthOf(message.files, 1);
        }));

      it('posts a file with a message to a room by specifying the file\'s url', () => spark.messages.create({
        roomId: room.id,
        file: KNOWN_HOSTED_IMAGE_URL,
        text: 'A File'
      })
        .then((message) => {
          assert.property(message, 'files');
          assert.isDefined(message.files);
          assert.isArray(message.files);
          assert.lengthOf(message.files, 1);

          assert.property(message, 'text');
        }));
    });

    describe('get()', () => {
      let message;

      before(() => spark.messages.create({
        roomId: room.id,
        text: 'A test message'
      })
        .then((m) => {
          message = m;
        }));

      it('returns a single message', () => spark.messages.get(message)
        .then((m) => {
          assert.isMessage(m);
          assert.deepEqual(m, message);
        }));
    });

    describe('#list()', () => {
      let room;
      before(() => spark.rooms.create({
        title: 'Room List Test'
      })
        .then((r) => {
          room = r;
        }));

      before(() => [1, 2, 3].reduce((promise, value) => promise.then(() => spark.messages.create({
        roomId: room.id,
        text: `message: ${value}`
      })), Promise.resolve()));

      it('returns all messages for a room', () => spark.messages.list({roomId: room.id})
        .then((messages) => {
          assert.isDefined(messages);
          assert.lengthOf(messages, 3);
          for (const message of messages) {
            assert.isMessage(message);
          }
        }));

      it('returns a bounded set of messages for a room', () => {
        const spy = sinon.spy();
        return spark.messages.list({roomId: room.id, max: 2})
          .then((messages) => {
            assert.lengthOf(messages, 2);
            return (function f(page) {
              for (const message of page) {
                spy(message.id);
              }

              if (page.hasNext()) {
                return page.next().then(f);
              }

              return Promise.resolve();
            }(messages));
          })
          .then(() => {
            assert.calledThrice(spy);
          });
      });
    });

    describe('#remove()', () => {
      let message;
      beforeEach(() => spark.messages.create({
        roomId: room.id,
        text: 'A test message'
      })
        .then((m) => {
          message = m;
        }));

      it('deletes a single message', () => spark.messages.remove(message)
        .then((body) => {
          assert.notOk(body);
          return assert.isRejected(spark.messages.get(message));
        })
        .then((reason) => {
          assert.instanceOf(reason, SparkHttpError.NotFound);
        }));
    });
  });

  nodeOnly(after)('stop yakbak', () => {
    if (server) {
      console.log('closing yakbak server...');
      server.close(() => {
        console.log('yakbak server closed');
      });
    }
  });
});
<commit_msg>chore(plugin-messages): remove flawed yakbak implementation<commit_after>/*!
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 */

import '@ciscospark/internal-plugin-wdm';
import '@ciscospark/plugin-logger';
import '@ciscospark/plugin-messages';
import '@ciscospark/plugin-rooms';
import CiscoSpark, {SparkHttpError} from '@ciscospark/spark-core';
import {assert} from '@ciscospark/test-helper-chai';
import sinon from '@ciscospark/test-helper-sinon';
import testUsers from '@ciscospark/test-helper-test-users';
import fh from '@ciscospark/test-helper-file';
import {browserOnly, flaky, nodeOnly} from '@ciscospark/test-helper-mocha';

const KNOWN_HOSTED_IMAGE_URL = 'https://download.ciscospark.com/test/photo.png';

describe('plugin-messages', function () {
  this.timeout(60000);

  let spark;
  before('create user', () => testUsers.create({count: 1})
    .then(([user]) => {
      spark = new CiscoSpark({credentials: user.token});
    }));

  describe('#messages', () => {
    let room;
    before(() => spark.rooms.create({title: 'Cisco Spark Test Room'})
      .then((r) => {
        room = r;
        return spark.messages.create({
          roomId: room.id,
          text: 'First Message'
        });
      }));

    after(() => {
      if (!room) {
        return Promise.resolve();
      }

      return spark.rooms.remove(room)
        .catch((reason) => {
          console.error('Failed to remove room', reason);
        });
    });

    describe('#create()', () => {
      it('posts a message in a room', () => {
        const text = 'A test message';
        return spark.messages.create({
          roomId: room.id,
          text
        })
          .then((message) => {
            assert.isDefined(message);
            assert.isMessage(message);
            assert.equal(message.text, text);
          });
      });

      it('posts a file to a room by specifying the file\'s url', () => spark.messages.create({
        roomId: room.id,
        file: KNOWN_HOSTED_IMAGE_URL
      })
        .then((message) => {
          assert.property(message, 'files');
          assert.isDefined(message.files);
          assert.isArray(message.files);
          assert.lengthOf(message.files, 1);
        }));

      let blob, buffer;

      browserOnly(before)(() => fh.fetch('sample-image-small-one.png')
        .then((file) => {
          blob = file;

          return new Promise((resolve) => {
            /* global FileReader */
            const fileReader = new FileReader();
            fileReader.onload = function () {
              buffer = this.result;
              resolve();
            };
            fileReader.readAsArrayBuffer(blob);
          });
        }));

      nodeOnly(before)(() => fh.fetchWithoutMagic('sample-image-small-one.png')
        .then((file) => {
          buffer = file;
        }));

      browserOnly(it)('posts a file to a room by directly supplying its blob', () => spark.messages.create({
        roomId: room.id,
        file: blob
      })
        .then((message) => {
          assert.property(message, 'files');
          assert.isDefined(message.files);
          assert.isArray(message.files);
          assert.lengthOf(message.files, 1);
        }));

      // Disabling it gating pipelines because it failes a lot and we get
      // mostly adequate coverage via blob upload
      flaky(it)('posts a file to a room by directly supplying its buffer', () => spark.messages.create({
        roomId: room.id,
        file: buffer
      })
        .then((message) => {
          assert.property(message, 'files');
          assert.isDefined(message.files);
          assert.isArray(message.files);
          assert.lengthOf(message.files, 1);
        }));

      it('posts a file with a message to a room by specifying the file\'s url', () => spark.messages.create({
        roomId: room.id,
        file: KNOWN_HOSTED_IMAGE_URL,
        text: 'A File'
      })
        .then((message) => {
          assert.property(message, 'files');
          assert.isDefined(message.files);
          assert.isArray(message.files);
          assert.lengthOf(message.files, 1);

          assert.property(message, 'text');
        }));
    });

    describe('get()', () => {
      let message;

      before(() => spark.messages.create({
        roomId: room.id,
        text: 'A test message'
      })
        .then((m) => {
          message = m;
        }));

      it('returns a single message', () => spark.messages.get(message)
        .then((m) => {
          assert.isMessage(m);
          assert.deepEqual(m, message);
        }));
    });

    describe('#list()', () => {
      let room;
      before(() => spark.rooms.create({
        title: 'Room List Test'
      })
        .then((r) => {
          room = r;
        }));

      before(() => [1, 2, 3].reduce((promise, value) => promise.then(() => spark.messages.create({
        roomId: room.id,
        text: `message: ${value}`
      })), Promise.resolve()));

      it('returns all messages for a room', () => spark.messages.list({roomId: room.id})
        .then((messages) => {
          assert.isDefined(messages);
          assert.lengthOf(messages, 3);
          for (const message of messages) {
            assert.isMessage(message);
          }
        }));

      it('returns a bounded set of messages for a room', () => {
        const spy = sinon.spy();
        return spark.messages.list({roomId: room.id, max: 2})
          .then((messages) => {
            assert.lengthOf(messages, 2);
            return (function f(page) {
              for (const message of page) {
                spy(message.id);
              }

              if (page.hasNext()) {
                return page.next().then(f);
              }

              return Promise.resolve();
            }(messages));
          })
          .then(() => {
            assert.calledThrice(spy);
          });
      });
    });

    describe('#remove()', () => {
      let message;
      beforeEach(() => spark.messages.create({
        roomId: room.id,
        text: 'A test message'
      })
        .then((m) => {
          message = m;
        }));

      it('deletes a single message', () => spark.messages.remove(message)
        .then((body) => {
          assert.notOk(body);
          return assert.isRejected(spark.messages.get(message));
        })
        .then((reason) => {
          assert.instanceOf(reason, SparkHttpError.NotFound);
        }));
    });
  });
});
<|endoftext|>


<commit_before>require('should');
const {agentProvider, fixtureManager, mockManager} = require('../../utils/e2e-framework');
const moment = require('moment');
const ObjectId = require('bson-objectid');
const {_sendEmailJob} = require('../../../core/server/services/mega/mega');
const models = require('../../../core/server/models');
const sinon = require('sinon');
const mailgunProvider = require('../../../core/server/services/bulk-email/mailgun');

let agent;

async function createPublishedPostEmail() {
    const post = {
        title: 'A random test post',
        status: 'draft',
        feature_image_alt: 'Testing sending',
        feature_image_caption: 'Testing feature image caption',
        created_at: moment().subtract(2, 'days').toISOString(),
        updated_at: moment().subtract(2, 'days').toISOString(),
        created_by: ObjectId().toHexString(),
        updated_by: ObjectId().toHexString()
    };

    const res = await agent.post('posts/')
        .body({posts: [post]})
        .expectStatus(201);

    const id = res.body.posts[0].id;

    const updatedPost = {
        status: 'published',
        updated_at: res.body.posts[0].updated_at
    };

    const newsletterSlug = fixtureManager.get('newsletters', 0).slug;
    await agent.put(`posts/${id}/?newsletter=${newsletterSlug}`)
        .body({posts: [updatedPost]})
        .expectStatus(200);

    const emailModel = await models.Email.findOne({
        post_id: id
    });
    should.exist(emailModel);

    return emailModel;
}

describe('MEGA', function () {
    describe('sendEmailJob', function () {
        before(async function () {
            agent = await agentProvider.getAdminAPIAgent();
            await fixtureManager.init('newsletters', 'members:newsletters');
            await agent.loginAsOwner();
        });

        afterEach(function () {
            mockManager.restore();
        });

        it('Can send a scheduled post email', async function () {
            sinon.stub(mailgunProvider, 'getInstance').returns({});
            sinon.stub(mailgunProvider, 'send').callsFake(async () => {
                return {
                    id: 'stubbed-email-id'
                };
            });

            // Prepare a post and email model
            const emailModel = await createPublishedPostEmail();

            // Launch email job
            await _sendEmailJob({emailModel, options: {}});

            await emailModel.refresh();
            emailModel.get('status').should.eql('submitted');
        });

        it('Can handle a failed post email', async function () {
            sinon.stub(mailgunProvider, 'getInstance').returns({});
            sinon.stub(mailgunProvider, 'send').callsFake(async () => {
                throw new Error('Failed to send');
            });

            // Prepare a post and email model
            const emailModel = await createPublishedPostEmail();

            // Launch email job
            await _sendEmailJob({emailModel, options: {}});

            await emailModel.refresh();
            emailModel.get('status').should.eql('failed');
        });
    });
});
<commit_msg>Fixed CI<commit_after>require('should');
const {agentProvider, fixtureManager, mockManager} = require('../../utils/e2e-framework');
const moment = require('moment');
const ObjectId = require('bson-objectid');
const models = require('../../../core/server/models');
const sinon = require('sinon');
const mailgunProvider = require('../../../core/server/services/bulk-email/mailgun');

let agent;

async function createPublishedPostEmail() {
    const post = {
        title: 'A random test post',
        status: 'draft',
        feature_image_alt: 'Testing sending',
        feature_image_caption: 'Testing feature image caption',
        created_at: moment().subtract(2, 'days').toISOString(),
        updated_at: moment().subtract(2, 'days').toISOString(),
        created_by: ObjectId().toHexString(),
        updated_by: ObjectId().toHexString()
    };

    const res = await agent.post('posts/')
        .body({posts: [post]})
        .expectStatus(201);

    const id = res.body.posts[0].id;

    const updatedPost = {
        status: 'published',
        updated_at: res.body.posts[0].updated_at
    };

    const newsletterSlug = fixtureManager.get('newsletters', 0).slug;
    await agent.put(`posts/${id}/?newsletter=${newsletterSlug}`)
        .body({posts: [updatedPost]})
        .expectStatus(200);

    const emailModel = await models.Email.findOne({
        post_id: id
    });
    should.exist(emailModel);

    return emailModel;
}

describe('MEGA', function () {
    let _sendEmailJob;

    describe('sendEmailJob', function () {
        before(async function () {
            agent = await agentProvider.getAdminAPIAgent();
            await fixtureManager.init('newsletters', 'members:newsletters');
            await agent.loginAsOwner();
            _sendEmailJob = require('../../../core/server/services/mega/mega')._sendEmailJob;
        });

        afterEach(function () {
            mockManager.restore();
        });

        it('Can send a scheduled post email', async function () {
            sinon.stub(mailgunProvider, 'getInstance').returns({});
            sinon.stub(mailgunProvider, 'send').callsFake(async () => {
                return {
                    id: 'stubbed-email-id'
                };
            });

            // Prepare a post and email model
            const emailModel = await createPublishedPostEmail();

            // Launch email job
            await _sendEmailJob({emailModel, options: {}});

            await emailModel.refresh();
            emailModel.get('status').should.eql('submitted');
        });

        it('Can handle a failed post email', async function () {
            sinon.stub(mailgunProvider, 'getInstance').returns({});
            sinon.stub(mailgunProvider, 'send').callsFake(async () => {
                throw new Error('Failed to send');
            });

            // Prepare a post and email model
            const emailModel = await createPublishedPostEmail();

            // Launch email job
            await _sendEmailJob({emailModel, options: {}});

            await emailModel.refresh();
            emailModel.get('status').should.eql('failed');
        });
    });
});
<|endoftext|>


<commit_before>/*
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Mick Hakobyan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

// core implementation
(function (global, undefined) {
    "use strict";

    // export
    if (global.module) {
        // Node.js
        global.module.exports = Promise;
    } else {
        // <script />
        (global.chnobean = global.chnobean || {}).Promise = Promise;
    }

    /**
    * @constructor
    * @param {function(function, function)} resolver
    */
    function Promise(resolver) {
        if (resolver) {
            try {
                resolver(this._doResolve.bind(this), this._doReject.bind(this));
            } catch(e) {
                this._doReject(e);
            }
        }
    }

    /**
    * @param {function=} onResolve is executed when this promise is fulfilled
    * @param {function=} onReject is executed when this promise is rejected
    */
    Promise.prototype.then = function Promise_then(onResolve, onReject) {
        var promise = new Promise();
        promise._onResolve = onResolve;
        promise._onReject = onReject;
        this._when(promise);
        return promise;
    };

    /**
    * @param {function} onReject
    *
    * Sugar for promise.then(undefined, onReject)
    */
    Promise.prototype.catch = function Promise_catch(onReject) {
        return this.then(undefined, onReject);
    };

    /**
    * @param {*=} result
    *
    * Creates a promise resolved for result.
    */
    Promise.resolve = function Promise_resolve(result) {
        var promise = new Promise();
        promise._resolved = true;
        promise._result = result;
        return promise;
    };

    /**
    * @param {*=} error
    *
    * Creates a promise rejected with error.
    */
    Promise.reject = function Promise_reject(error) {
        var promise = new Promise();
        promise._resolved = false;
        promise._error = error;
        return promise;
    };

    // --------------------------------------------------------------------------------
    // -- private implementation
    // --------------------------------------------------------------------------------

    /**
    * Used instead of "instanceof" for performance reasons.
    */
    Promise.prototype._isPromise = true;

    /**
    * Resolve this promise and process defferals
    */
    Promise.prototype._doResolve = function Promise_doResolve(result) {
        if (this._resolved === undefined) {
            this._resolved = true;
            this._result = result;
            this._doDeferred();
        }
    };

    /**
    * Reject this promise and process defferals
    */
    Promise.prototype._doReject = function Promise_doReject(error) {
        if (this._resolved === undefined) {
            this._resolved = false;
            this._error = error;
            this._doDeferred();
        }
    };

    /**
    * Settle deffered "when"s
    */
    Promise.prototype._doDeferred = function Promise_doDeferred() {
        // assert(this._resolved !== undefined)
        var deferred = this._deferred,
            deferredLength,
            i;
        if (deferred) {
            this._deferred = undefined;
            deferredLength = deferred.length;
            for(i = 0; i < deferredLength; i++) {
                deferred[i]._settle(this);
            }
        }
    };

    /**
    * If this one is resolved, settle given promise or deffer it's settlement
    */
    Promise.prototype._when = function Promise_when(promise) {
        if (this._resolved !== undefined) {
            promise._settle(this);
        } else {
            this._deferred = this._deferred || [];
            this._deferred.push(promise);
        }
    };

    /**
    * Settle this promise (called by the one before, when it's resolved)
    */
    Promise.prototype._settle = function Promise_settle(settledBy) {
        // assert(settledBy._resolved !== undefined)
        var resolved = settledBy._resolved,
            result = settledBy._result,
            error = settledBy._error,
            onResolve,
            onReject,
            newResult;

        if (result && result._isPromise) {
            // previous promise resolved to a promise, forward this to that promise
            result._when(this);
        } else {
            // settle this promise given result or error
            // cache and kill the onResolve and onReject we got from .then(onResolve, onReject)
            onResolve = this._onResolve;
            onReject = this._onReject;
            this._onResolve = undefined;
            this._onReject = undefined;
            if (resolved) {
                // resolve
                if (onResolve) {
                    try {
                        newResult = onResolve.call(this, result);
                        this._doResolve(newResult);
                    } catch(e) {
                        this._doReject(e);
                    }
                } else {
                    this._doResolve(result);
                }
            } else {
                // reject
                if (onReject) {
                    try {
                        newResult = onReject.call(this, error);
                        this._doResolve(newResult);
                    } catch(e) {
                        this._doReject(e);
                    }
                } else {
                    this._doReject(error);
                }

            }
        }
    };

})(this);<commit_msg>Refactor of _doDeferred to _settleDeferred<commit_after>/*
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Mick Hakobyan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

// core implementation
(function (global, undefined) {
    "use strict";

    // export
    if (global.module) {
        // Node.js
        global.module.exports = Promise;
    } else {
        // <script />
        (global.chnobean = global.chnobean || {}).Promise = Promise;
    }

    /**
    * @constructor
    * @param {function(function, function)} resolver
    */
    function Promise(resolver) {
        if (resolver) {
            try {
                resolver(this._doResolve.bind(this), this._doReject.bind(this));
            } catch(e) {
                this._doReject(e);
            }
        }
    }

    /**
    * @param {function=} onResolve is executed when this promise is fulfilled
    * @param {function=} onReject is executed when this promise is rejected
    */
    Promise.prototype.then = function Promise_then(onResolve, onReject) {
        var promise = new Promise();
        promise._onResolve = onResolve;
        promise._onReject = onReject;
        this._when(promise);
        return promise;
    };

    /**
    * @param {function} onReject
    *
    * Sugar for promise.then(undefined, onReject)
    */
    Promise.prototype.catch = function Promise_catch(onReject) {
        return this.then(undefined, onReject);
    };

    /**
    * @param {*=} result
    *
    * Creates a promise resolved for result.
    */
    Promise.resolve = function Promise_resolve(result) {
        var promise = new Promise();
        promise._resolved = true;
        promise._result = result;
        return promise;
    };

    /**
    * @param {*=} error
    *
    * Creates a promise rejected with error.
    */
    Promise.reject = function Promise_reject(error) {
        var promise = new Promise();
        promise._resolved = false;
        promise._error = error;
        return promise;
    };

    // --------------------------------------------------------------------------------
    // -- private implementation
    // --------------------------------------------------------------------------------

    /**
    * Used instead of "instanceof" for performance reasons.
    */
    Promise.prototype._isPromise = true;

    /**
    * Resolve this promise and process deferals
    */
    Promise.prototype._doResolve = function Promise_doResolve(result) {
        if (this._resolved === undefined) {
            this._resolved = true;
            this._result = result;
            this._settleDefered();
        }
    };

    /**
    * Reject this promise and process deferals
    */
    Promise.prototype._doReject = function Promise_doReject(error) {
        if (this._resolved === undefined) {
            this._resolved = false;
            this._error = error;
            this._settleDefered();
        }
    };

    /**
    * If this one is resolved, settle given promise or defer it's settlement
    */
    Promise.prototype._when = function Promise_when(promise) {
        if (this._resolved !== undefined) {
            // we are resolved, settle the given promise
            promise._settle(this);
        } else {
            // defer the settlement
            this._deferred = this._deferred || [];
            this._deferred.push(promise);
        }
    };

    /**
    * Settle promised whos settlement was defered
    */
    Promise.prototype._settleDefered = function Promise_settleDefered() {
        // assert(this._resolved !== undefined)
        var deferred = this._deferred,
            deferredLength,
            i;
        if (deferred) {
            this._deferred = undefined;
            deferredLength = deferred.length;
            for(i = 0; i < deferredLength; i++) {
                deferred[i]._settle(this);
            }
        }
    };

    /**
    * Settle this promise (called by the one before, when it's resolved)
    */
    Promise.prototype._settle = function Promise_settle(settledBy) {
        // assert(settledBy._resolved !== undefined)
        var resolved = settledBy._resolved,
            result = settledBy._result,
            error = settledBy._error,
            onResolve,
            onReject,
            newResult;

        if (result && result._isPromise) {
            // previous promise resolved to a promise, forward this to that promise
            result._when(this);
        } else {
            // settle this promise given result or error
            // cache and kill the onResolve and onReject we got from .then(onResolve, onReject)
            onResolve = this._onResolve;
            onReject = this._onReject;
            this._onResolve = undefined;
            this._onReject = undefined;
            if (resolved) {
                // resolve
                if (onResolve) {
                    try {
                        newResult = onResolve.call(this, result);
                        this._doResolve(newResult);
                    } catch(e) {
                        this._doReject(e);
                    }
                } else {
                    this._doResolve(result);
                }
            } else {
                // reject
                if (onReject) {
                    try {
                        newResult = onReject.call(this, error);
                        this._doResolve(newResult);
                    } catch(e) {
                        this._doReject(e);
                    }
                } else {
                    this._doReject(error);
                }

            }
        }
    };

})(this);<|endoftext|>


<commit_before>/* global $ */
'use strict';
// ==UserScript==
// @name         MusicBrainz: Lean display
// @namespace    mbz-loujine
// @author       loujine
// @version      2021.9.17
// @downloadURL  https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mbz-display_lean_ui.user.js
// @updateURL    https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mbz-display_lean_ui.user.js
// @supportURL   https://github.com/loujine/musicbrainz-scripts
// @icon         https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/icon.png
// @description  musicbrainz.org: hide unused elements in MB pages
// @compatible   firefox+tampermonkey
// @license      MIT
// @include      http*://*musicbrainz.org/artist/*
// @include      http*://*musicbrainz.org/collection/*
// @include      http*://*musicbrainz.org/recording/*
// @include      http*://*musicbrainz.org/release/*
// @exclude      http*://*musicbrainz.org/*/merge
// @exclude      http*://*musicbrainz.org/*/*/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

function removeTabHeadersUI() {
    const mbid = document.URL.split('/')[4].slice(0,36);
    $(`li > a[href$='${mbid}/details']`).parent().remove();
    $(`li > a[href$='${mbid}/tags']`).parent().remove();
}

function removeRatingsUI() {
    // document.querySelectorAll("div#content table.tbl > * > tr > th.rating, div#content table.tbl > tbody > tr > td.rating, div#sidebar > h2.rating, div#sidebar > h2.rating + p, div#page > div.tabs > ul.tabs > li:not(.sel) > a[href$='/ratings'], div.header ul.menu li.data a[href$='/ratings']");
    $('th.rating').empty();
    $('th.rating').css('width', '1px');
    // $('td.rating').empty();
    $('td.rating span').removeClass(); // needed for mass merge recordings script
    $('td.rating a').empty(); // needed for mass merge recordings script
    $('td.rating').css('width', '1px');
    $('h2.rating + p').empty();
    $('h2.rating').empty();
}

function collapseReleaseSidebar() {
    // would prevent "expand/collapse all mediums" script
    // $('h2.tracklist').empty();

    const summaryTag = `

    `;

    const $infoheader = $('#sidebar h2.release-information');
    if ($infoheader.length) {
        const $infodl = $('#sidebar h2.release-information + dl');
        $infoheader.before('');
        $('details#toggle-release-information').append(
            $(summaryTag).append($infoheader)
        );
        $infoheader.css({display: 'list-item'});
        $('details#toggle-release-information').append($infodl);
    }

    const $detailsheader = $('#sidebar h2.additional-details');
    if ($detailsheader.length) {
        const $detailsdl = $('#sidebar h2.additional-details + dl');
        $detailsheader.before('');
        $('details#toggle-additional-details').append(
            $(summaryTag).append($detailsheader)
        );
        $detailsheader.css({display: 'list-item'});
        $('details#toggle-additional-details').append($detailsdl);
    }

    const $labelheader = $('#sidebar h2.labels');
    if ($labelheader.length) {
        const $labelul = $('#sidebar h2.labels + ul.links');
        $labelheader.before('');
        $('details#toggle-labels').append(
            $(summaryTag).append($labelheader)
        );
        $labelheader.css({display: 'list-item'});
        $('details#toggle-labels').append($labelul);
    }

    const $eventsheader = $('#sidebar h2.release-events');
    if ($eventsheader.length) {
        const $eventsblock = $('#sidebar h2.release-events + script + div');
        $eventsheader.before('');
        $('details#toggle-release-events').append(
            $(summaryTag).append($eventsheader)
        );
        $eventsheader.css({display: 'list-item'});
        $('details#toggle-release-events').append($eventsblock);
    }

    $('h2.reviews + p').empty();
    $('h2.reviews').empty();

    $('div#sidebar-tags').empty();

    let $linksheader = $('#sidebar h2.external-links');
    if ($linksheader.length) {
        $linksheader = $linksheader.first();
        const $linksul = $(
            '#sidebar h2.external-links,#sidebar h2.external-links + ul.external_links'
        ).not(':first');
        $linksheader.before('');
        $('details#toggle-external-links').append(
            $(summaryTag).append($linksheader)
        );
        $linksheader.css({display: 'list-item'});
        $('details#toggle-external-links').append($linksul);
    }

    if ($('dd span.high-data-quality,dd span.low-data-quality').length) {
        $('span.prefix').after($('dd span.high-data-quality,dd span.low-data-quality')[0]);
    }
}

$(document).ready(function () {
    removeTabHeadersUI();
    removeRatingsUI();
    if (document.URL.split('/')[3] === 'release') {
        collapseReleaseSidebar();
    }
    return false;
});
<commit_msg>[leqn display] Hide elements not needed by other scripts<commit_after>/* global $ */
'use strict';
// ==UserScript==
// @name         MusicBrainz: Lean display
// @namespace    mbz-loujine
// @author       loujine
// @version      2021.9.25
// @downloadURL  https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mbz-display_lean_ui.user.js
// @updateURL    https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mbz-display_lean_ui.user.js
// @supportURL   https://github.com/loujine/musicbrainz-scripts
// @icon         https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/icon.png
// @description  musicbrainz.org: hide unused elements in MB pages
// @compatible   firefox+tampermonkey
// @license      MIT
// @include      http*://*musicbrainz.org/artist/*
// @include      http*://*musicbrainz.org/collection/*
// @include      http*://*musicbrainz.org/recording/*
// @include      http*://*musicbrainz.org/release/*
// @exclude      http*://*musicbrainz.org/*/merge
// @exclude      http*://*musicbrainz.org/*/*/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

function removeTabHeadersUI() {
    const mbid = document.URL.split('/')[4].slice(0,36);
    $(`li > a[href$='${mbid}/details']`).parent().remove();
    $(`li > a[href$='${mbid}/tags']`).parent().remove();
}

function removeRatingsUI() {
    // document.querySelectorAll("div#content table.tbl > * > tr > th.rating, div#content table.tbl > tbody > tr > td.rating, div#sidebar > h2.rating, div#sidebar > h2.rating + p, div#page > div.tabs > ul.tabs > li:not(.sel) > a[href$='/ratings'], div.header ul.menu li.data a[href$='/ratings']");
    $('th.rating').empty();
    $('th.rating').css('display', 'none');
    // $('td.rating').empty();
    $('td.rating span').removeClass(); // needed for mass merge recordings script
    $('td.rating a').empty(); // needed for mass merge recordings script
    $('td.rating').css('display', 'none');
    $('h2.rating + p').empty();
    $('h2.rating').empty();
}

function collapseReleaseSidebar() {
    // would prevent "expand/collapse all mediums" script
    // $('h2.tracklist').empty();

    const summaryTag = `

    `;

    const $infoheader = $('#sidebar h2.release-information');
    if ($infoheader.length) {
        const $infodl = $('#sidebar h2.release-information + dl');
        $infoheader.before('');
        $('details#toggle-release-information').append(
            $(summaryTag).append($infoheader)
        );
        $infoheader.css({display: 'list-item'});
        $('details#toggle-release-information').append($infodl);
    }

    const $detailsheader = $('#sidebar h2.additional-details');
    if ($detailsheader.length) {
        const $detailsdl = $('#sidebar h2.additional-details + dl');
        $detailsheader.before('');
        $('details#toggle-additional-details').append(
            $(summaryTag).append($detailsheader)
        );
        $detailsheader.css({display: 'list-item'});
        $('details#toggle-additional-details').append($detailsdl);
    }

    const $labelheader = $('#sidebar h2.labels');
    if ($labelheader.length) {
        const $labelul = $('#sidebar h2.labels + ul.links');
        $labelheader.before('');
        $('details#toggle-labels').append(
            $(summaryTag).append($labelheader)
        );
        $labelheader.css({display: 'list-item'});
        $('details#toggle-labels').append($labelul);
    }

    const $eventsheader = $('#sidebar h2.release-events');
    if ($eventsheader.length) {
        const $eventsblock = $('#sidebar h2.release-events + script + div');
        $eventsheader.before('');
        $('details#toggle-release-events').append(
            $(summaryTag).append($eventsheader)
        );
        $eventsheader.css({display: 'list-item'});
        $('details#toggle-release-events').append($eventsblock);
    }

    $('h2.reviews + p').empty();
    $('h2.reviews').empty();

    $('div#sidebar-tags').empty();

    let $linksheader = $('#sidebar h2.external-links');
    if ($linksheader.length) {
        $linksheader = $linksheader.first();
        const $linksul = $(
            '#sidebar h2.external-links,#sidebar h2.external-links + ul.external_links'
        ).not(':first');
        $linksheader.before('');
        $('details#toggle-external-links').append(
            $(summaryTag).append($linksheader)
        );
        $linksheader.css({display: 'list-item'});
        $('details#toggle-external-links').append($linksul);
    }

    if ($('dd span.high-data-quality,dd span.low-data-quality').length) {
        $('span.prefix').after($('dd span.high-data-quality,dd span.low-data-quality')[0]);
    }
}

$(document).ready(function () {
    removeTabHeadersUI();
    removeRatingsUI();
    if (document.URL.split('/')[3] === 'release') {
        collapseReleaseSidebar();
    }
    return false;
});
<|endoftext|>