PMA.UI Examples 2.43.3by Pathomation

Annotations Context

basicviewportannotationscontextfilter
Console
PMA.UI version: 2.43.3
Annotations context filter
annotations_context.html
1<!doctype html>
2<html lang="en">
3
4<head>
5    <meta charset="utf-8">
6    <meta http-equiv="X-UA-Compatible" content="IE=10">
7    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
8
9    <!-- Include PMA.UI required libraries downloaded or from CDN -->
10    <script src="./pma.ui/jquery-3.1.0.js"></script>
11    <link href="./pma.ui/font-awesome.min.css" type="text/css" rel="stylesheet">
12
13    <!-- Include optional libraries downloaded or from CDN -->
14    <link rel="stylesheet" href="./pma.ui/bootstrap5.min.css">
15    <script src="./pma.ui/bootstrap5.bundle.min.js"></script>
16
17    <!-- Include PMA.UI script & css -->
18    <script src="./pma.ui/pma.ui.js"></script>
19    <link href="./pma.ui/pma.ui.css" type="text/css" rel="stylesheet">
20
21    <!-- Include custom script & css -->
22    <script src="./js/annotations_context.js"></script>
23    <link href="./css/annotations_context.css" type="text/css" rel="stylesheet">
24
25    <title>Annotations Context</title>
26</head>
27
28<body>
29    <div class="container-fluid">
30        <div class="row">
31            <div class="col d-flex flex-column vh-100">
32                <div class="row top-row p-1 g-2 align-items-center">
33                    <div class="col-auto" style="padding-left: 0px;">
34                        <div class="btn-group btn-group-sm">
35                            <button type="button" class="btn btn-light" disabled data-action="draw" data-type="Freehand" title="Freehand">
36                                <i class="fas fa-pencil-alt"></i>
37                            </button>
38                        </div>
39                    </div>
40                    <div class="col-auto">
41                        <div class="btn-group btn-group-sm">
42                            <button type="button" class="btn btn-secondary" disabled data-action="delete" data-type title="Delete">
43                                <i class="fas fa-times"></i>
44                            </button>
45                            <button type="button" class="btn btn-secondary" disabled data-action="deleteall" data-type title="Delete all">
46                                <i class="fas fa-times-circle"></i>
47                            </button>
48                            <button type="button" class="btn btn-secondary" disabled data-action="save" data-type title="Save">
49                                <i class="fas fa-save"></i>
50                            </button>
51                        </div>
52                    </div>
53                    <div class="col-auto">
54                        <div style="padding-left: 10px;">
55                            <label class="custom-control-label" for="context-names">
56                                <i class="fa fa-address-book-o"></i>&nbsp;Context:
57                            </label>
58                            <select name="context-names" id="context-names">
59                                <option value="all"><i class="fas fa-font"></i> All</option>
60                                <option value="case1"><i class="fas fa-font"></i> Case 1</option>
61                                <option value="case2"><i class="fas fa-font"></i> Case 2</option>
62                            </select>
63                        </div>
64                    </div>
65                    <div class="col-auto">
66                        <div class="form-group mb-0">
67                            <div class="col-auto d-flex justify-content-center align-items-center">
68                                <span class="annotation-helper-icon">
69                                    <i class="fas fa-spinner fa-spin"></i>
70                                    <span class="badge bg-success"><i class="fas fa-check"></i> Saved</span>
71                                    <span class="badge bg-danger"><i class="fas fa-times"></i> Error</span>
72                                </span>
73                            </div>
74                        </div>
75                    </div>
76                </div>
77                <div class="row viewer-row flex-grow-1">
78                    <div class="col p-0">
79                        <!-- The element that will host the slide loader -->
80                        <div id="viewer"></div>
81                    </div>
82                </div>
83            </div>
84        </div>
85</body>
86
87</html>
annotations_context.css
1html,
2body {
3    height: 100%;
4    padding: 0px;
5    margin: 0px;
6}
7
8body .pma-ui-viewport-container {
9    height: 100%;
10    width: 100%;
11}
12
13.annotation-helper-icon i,
14.annotation-helper-icon span {
15    display: none;
16}
17
18.annotation-helper-icon.loading i {
19    display: initial;
20}
21
22.annotation-helper-icon.loading span {
23    display: none;
24}
25
26.annotation-helper-icon.saved i,
27.annotation-helper-icon.saved span.badge.bg-danger {
28    display: none;
29}
30
31.annotation-helper-icon.saved span.badge.bg-success {
32    display: initial;
33}
34
35.annotation-helper-icon.error i,
36.annotation-helper-icon.error span.badge.bg-success {
37    display: none;
38}
39
40.annotation-helper-icon.error span.badge.bg-danger {
41    display: block;
42}
43
44.top-row {
45    background-color: #f8f9fa;
46    min-height: 74px;
47}
48
49.btn.btn-light.active {
50    background-color: #d2d2d2;
51}
52
53.btn:disabled {
54    cursor: not-allowed;
55    pointer-events: unset;
56}
annotations_context.js
1var serverUrl = "https://host.pathomation.com/pma.core.3/";
2var serverUsername = "PMA_UI_demo";
3var serverPassword = "PMA_UI_demo";
4var caller = "DemoPortal";
5var imageBaseUrl = "Reference/Annotations/CMU-1/CMU-1.svs";
6var context = null;
7var slideLoader = null;
8var annotationManager = null;
9
10console.log(`PMA.UI version: ${PMA.UI.getVersion()}`);
11
12function drawCommands(action, type) {
13    if (action) {
14        if (action === "draw") {
15            var f = annotationManager.getSelection();
16
17            annotationManager.startDrawing({
18                type: type,
19                color: '#78eb10',
20                fillColor: "rgba(0,0,0,0)",
21                lineThickness: Math.floor(Math.random() * 4) + 1,
22                iconRelativePath: null,
23                feature: type === undefined,
24                notes: $("#annotation-text").val() ? $("#annotation-text").val() : ""
25            });
26        }
27        else if (action === "save") {
28            saveAnnotations();
29        }
30        else if (action === "delete") {
31            var ann = annotationManager.getSelection();
32
33            if (ann && ann.length > 0) {
34                annotationManager.deleteAnnotation(ann[0].getId());
35            }
36        }
37        else if (action === "deleteall") {
38            var allAnnotations = slideLoader.mainViewport.getAnnotations();
39
40            for (var i = 0; i < allAnnotations.length; i++) {
41                annotationManager.deleteAnnotation(allAnnotations[i].getId());
42            }
43        }
44    }
45}
46
47function saveAnnotations() {
48    $(".annotation-helper-icon").addClass("loading");
49    annotationManager.saveAnnotations();
50}
51
52jQuery(function () {
53    $("button[data-action][data-type], a[data-action][data-type]").on("click", function (e) {
54        var shouldReturn = $("button.active").data("type") === $(this).data("type");
55        e.preventDefault();
56
57        if ($("button.active")?.data("action")?.toLowerCase() === "draw") {
58            annotationManager.finishDrawing(false, $("button.active").data("type"));
59        }
60
61        $("button.active").removeClass("active");
62        if (shouldReturn) return;
63
64        drawCommands($(this).data("action"), $(this).data("type"));
65
66        if ($(this).data("type")) {
67            $(this).addClass("active");
68        }
69    });
70
71    $("#context-names").change(function () {
72        let selectedContext = $("#context-names").val();
73
74        if (selectedContext === 'all') {
75            selectedContext = null;
76        }
77
78        slideLoader.setOption("annotations", {
79            imageBaseUrl: imageBaseUrl,
80            imageScale: 1,
81            labels: true,
82            contexts: [selectedContext],
83        });
84
85        slideLoader.load(serverUrl, imageBaseUrl);
86    });
87
88    // Create a context per case selection
89    context = new PMA.UI.Components.Context({ caller: caller });
90
91    // Add an autologin authentication provider
92    new PMA.UI.Authentication.AutoLogin(context, [{ serverUrl: serverUrl, username: serverUsername, password: serverPassword }]);
93
94    // Create an image loader that will allow us to load images easily
95    slideLoader = new PMA.UI.Components.SlideLoader(context, {
96        element: "#viewer",
97        overview: true,
98        channels: {
99            collapsed: true
100        },
101        dimensions: true,
102        filename: false,
103        scaleLine: true,
104        annotations: {
105            imageBaseUrl: imageBaseUrl,
106            imageScale: 1,
107            labels: true,
108            contexts: null,
109        },
110        annotationsLayers: false,
111        colorAdjustments: false,
112        digitalZoomLevels: 2,
113        loadingBar: true,
114        highQuality: true,
115        barcode: {
116            collapsed: true
117        },
118        snapshot: false,
119        theme: "modern",
120        magnifier: false,
121        flip: {
122            horizontally: false,
123            vertically: false,
124        }
125    });
126
127    // Listen for the slide loaded event by the slide loader
128    slideLoader.listen(PMA.UI.Components.Events.SlideLoaded, function (args) {
129        $("button, input").removeAttr("disabled");
130        $("button[data-action='delete']").attr("disabled", true);
131
132        annotationManager = new PMA.UI.Components.Annotations({
133            context: context,
134            element: null,
135            viewport: slideLoader.mainViewport,
136            serverUrl: args.serverUrl,
137            path: args.path,
138            enabled: true
139        });
140
141        window.annotationManager.listen(PMA.UI.Components.Events.AnnotationsSelectionChanged, function (e) {
142            if (e) {
143                $("button[data-action='delete']").attr("disabled", false);
144
145                var metadata = e.hasOwnProperty("feature") ? e.feature.metaData : (e.hasOwnProperty("length") && e.length !== 0 ? e[0].metaData : null);
146                if (metadata) {
147                    $("#annotation-text").val(metadata.Notes);
148                } else {
149                    $("button[data-action='delete']").attr("disabled", true);
150                }
151            }
152        });
153
154        annotationManager.listen(PMA.UI.Components.Events.AnnotationAdded, function (e) {
155            let selectedContext = $("#context-names").val();
156
157            if (selectedContext === 'all') {
158                return;
159            }
160
161            e.feature.metaData.Context = selectedContext;
162        });
163
164        window.annotationManager.listen(PMA.UI.Components.Events.AnnotationDeleted, function (e) {
165            $("button[data-action='delete']").attr("disabled", true);
166        });
167
168        annotationManager.listen(PMA.UI.Components.Events.AnnotationsSaved, function (e) {
169            if (e && e.success) {
170                $(".annotation-helper-icon").removeClass("loading").addClass("saved");
171            }
172            else {
173                $(".annotation-helper-icon").removeClass("loading").addClass("error");
174            }
175
176            $("button[data-action=save][data-type]").removeClass("active");
177
178            setTimeout(function () {
179                $(".annotation-helper-icon").removeClass("loading error saved");
180            }, 1000);
181        });
182    });
183
184    $("body").keydown(function (event) {
185        if (!$(event.target).is('input') && (event.key == "Delete" || event.key == "Del")) {
186            drawCommands("delete", null);
187        }
188    });
189
190    // Load the image with the context
191    slideLoader.load(serverUrl, imageBaseUrl);
192});