Index: /gs3-extensions/structured-image/trunk/src/js/document_extra.js
===================================================================
--- /gs3-extensions/structured-image/trunk/src/js/document_extra.js	(revision 37113)
+++ /gs3-extensions/structured-image/trunk/src/js/document_extra.js	(revision 37113)
@@ -0,0 +1,479 @@
+function load_gv_dococr_json(json_file,doc_sec_id,callback) {
+
+    var http_assocfilepath = gs.collectionMetadata["httpPath"]+"/index/assoc/"+gs.documentMetadata["assocfilepath"];
+    var json_url = http_assocfilepath +"/"+json_file;
+
+    $.ajax({
+	method: "GET",
+	url: json_url
+    })
+        .fail(function(jqXHR,textStatus) {
+            console.error("load_gv_dococr_json(): failed to retrieve url '" + json_url +"'");
+	    console.error(textStatus);
+        })
+        .done(function(gv_ocr_json_result) {
+	    callback(gv_ocr_json_result,doc_sec_id);
+
+        });
+    
+}
+
+function gv_ocr_paras_to_text(paras)
+{
+    var ocr_text = "";
+
+    var num_paras = paras.length;
+    for (var p=0; p<num_paras; p++) {
+
+	var para = paras[p];	
+	var words = para.words;
+	var num_words = words.length;
+	
+	for (var w=0; w<num_words; w++) {
+	    var word = words[w];
+	    
+	    var symbols = word.symbols;
+	    var num_symbols = symbols.length;
+	    
+	    for (var s=0; s<num_symbols; s++) {
+		
+		var symbol = symbols[s];
+
+		ocr_text += symbol.text;
+
+		// Enumerated fields listed at:
+		//   http://googleapis.github.io/googleapis/java/grpc-google-cloud-vision-v1/0.1.5/apidocs/com/google/cloud/vision/v1/TextAnnotation.DetectedBreak.BreakType.html
+		if (symbol.property) {
+		    var detected_break = symbol.property.detectedBreak;
+		    if ((detected_break.type == "SPACE" || detected_break.type == "EOL_SURE_SPACE")) {
+			ocr_text += ' ';
+		    }
+		    else if (detected_break.type == "SURE_SPACE") { // very wide space
+			ocr_text += '\t';
+		    }
+		    // "HYPHEN" at end end of line => assume word continues onto next line
+		    else if (detected_break.type == "LINE_BREAK" ) {
+			ocr_text += '\n\n';
+		    }
+		}
+	    }
+	}
+    }
+    
+    return ocr_text;
+}
+
+function display_gv_ocr_bounding_boxes(gv_ocr_json,doc_sec_id)
+{
+    //console.log(gv_ocr_json);
+
+    
+    var full_text_annotation = gv_ocr_json.fullTextAnnotation;
+    var pages = full_text_annotation.pages;
+    var num_pages = pages.length;
+    
+    if (num_pages == 1) {
+	var page = pages[0];
+
+	var bounding_box_rects = [];
+	var page_x_dim = page.width;
+	var page_y_dim = page.height;
+
+	var blocks = page.blocks;
+	var num_blocks = blocks.length;
+
+	for (var b=0; b<num_blocks; b++) {
+	    var block = blocks[b];
+
+	    var boundingBox = block.boundingBox;
+
+	    var min_x = Number.MAX_SAFE_INTEGER;
+	    var min_y = Number.MAX_SAFE_INTEGER;
+	    var max_x = Number.MIN_SAFE_INTEGER;
+	    var max_y = Number.MIN_SAFE_INTEGER;
+
+	    var vertices = boundingBox.vertices;
+	    var num_vertices = vertices.length;
+
+	    for (var v=0; v<num_vertices; v++) {
+		var x = vertices[v].x;
+		var y = vertices[v].y;
+		
+		min_x = Math.min(min_x,x);
+		min_y = Math.min(min_y,y);
+		max_x = Math.max(max_x,x);
+		max_y = Math.max(max_y,y);
+	    }
+
+	    var x_org = min_x;
+	    var y_org = min_y;
+	    var x_dim = max_x - min_x +1;
+	    var y_dim = max_y - min_y +1;
+
+	    var rect = { "x_org": x_org, "y_org": y_org, "x_dim": x_dim, "y_dim": y_dim};
+
+	    var paras = block.paragraphs;
+	    var ocr_text = gv_ocr_paras_to_text(paras);
+	    rect.text = ocr_text;
+
+	    bounding_box_rects.push(rect);	    
+	}
+	
+	    
+	display_scaled_div_bounding_boxes(doc_sec_id, bounding_box_rects, page_x_dim,page_y_dim);
+	
+    }
+    else {
+	console.error("display_gv_ocr_bounding_boxes(): incorrect number of pages found.")
+	console.error("  Expected 1 page, found " + num_pages +" page(s)");
+    }
+
+}
+
+
+function display_scaled_div_bounding_boxes(doc_sec_id, bounding_box_rects,fullsize_x_dim,fullsize_y_dim)
+{
+    var screen_x_dim = gs.variables.screenImageWidth;
+    var screen_y_dim = gs.variables.screenImageHeight;
+
+    var scale_x = screen_x_dim / fullsize_x_dim;
+    var scale_y = screen_y_dim / fullsize_y_dim;
+
+    //console.log("scale x = " + scale_x);
+    //console.log("scale y = " + scale_y);
+    
+    var screen_image_id = "small"+doc_sec_id;
+
+    var $boundingbox_overlay = $("<div>")
+	.attr("id","ocr-boundingbox-overlay-"+doc_sec_id)
+	.attr("class","ocr-boundingbox-overlay");
+
+    var $screen_div = $('#'+screen_image_id);
+    
+    $screen_div.append($boundingbox_overlay);
+    
+    var num_bb_rects = bounding_box_rects.length;
+
+    //console.log("Block bounding boxes:")
+
+    for (var r=0; r<num_bb_rects; r++) {
+	var rect = bounding_box_rects[r];
+    	//console.log("  " + JSON.stringify(rect));
+
+	var scaled_x_org = rect.x_org * scale_x;
+	var scaled_y_org = rect.y_org * scale_y;
+	var scaled_x_dim = rect.x_dim * scale_x;
+	var scaled_y_dim = rect.y_dim * scale_y;
+		
+	var $boundingbox_div = $("<div>")
+	    .attr("class","ocr-boundingbox")
+	    .css("left",  scaled_x_org)
+	    .css("top",   scaled_y_org)
+	    .css("width", scaled_x_dim)
+	    .css("height",scaled_y_dim);
+
+	var text = rect.text;
+	// //console.log("text = " + text);
+	// // Could use 'data' here, but then does not appear in the element itself
+	// // => for now do this 'old school' with an explicit 'data-text' attribute
+	// $boundingbox_div.attr("data-text",text);
+
+	$boundingbox_div.attr("text",text);
+	
+	$boundingbox_overlay.append($boundingbox_div)	
+    }
+}
+
+/*
+
+https://stackoverflow.com/questions/68395710/building-a-bounding-box-surrounding-text-in-google-vision-api-to-extract-the-tex
+
+def get_text_within(document, x1, y1, x2, y2):
+text = ""
+for page in document.pages:
+    for block in page.blocks:
+        for paragraph in block.paragraphs:
+            for word in paragraph.words:
+                for symbol in word.symbols:
+                    min_x = min(symbol.bounding_box.vertices[0].x, symbol.bounding_box.vertices[1].x,
+                                symbol.bounding_box.vertices[2].x, symbol.bounding_box.vertices[3].x)
+                    max_x = max(symbol.bounding_box.vertices[0].x, symbol.bounding_box.vertices[1].x,
+                                symbol.bounding_box.vertices[2].x, symbol.bounding_box.vertices[3].x)
+                    min_y = min(symbol.bounding_box.vertices[0].y, symbol.bounding_box.vertices[1].y,
+                                symbol.bounding_box.vertices[2].y, symbol.bounding_box.vertices[3].y)
+                    max_y = max(symbol.bounding_box.vertices[0].y, symbol.bounding_box.vertices[1].y,
+                                symbol.bounding_box.vertices[2].y, symbol.bounding_box.vertices[3].y)
+                    if (min_x >= x1 and max_x <= x2 and min_y >= y1 and max_y <= y2):
+                        text += symbol.text
+                    if (symbol.property.detected_break.type == 1 or
+                            symbol.property.detected_break.type == 3):
+                        text += ' '
+                    if (symbol.property.detected_break.type == 2):
+                        text += '\t'
+                    if (symbol.property.detected_break.type == 5):
+                        text += '\n'
+return text
+
+*/
+
+
+
+
+function createMirador3Viewer()
+{
+    var doc_url = new URL(window.location);
+    var goto_page = doc_url.searchParams.get('gotoPage') || 1;
+
+    var canvas_index = goto_page -1;
+
+    var httpDocument = gs.xsltParams.library_name + "/collection/" + gs.cgiParams.c + "/document/" + gs.cgiParams.d;
+    var httpDocumentIIIFManifest = httpDocument + "?sa=iiif-manifest&ed=1&excerptid-text=iiif-manifest";
+
+    //const endpointUrl = 'https://intermuse.sowemustthink.space/simpleAnnotationStore/annotation';
+    //const endpointUrl = '/simpleAnnotationStore/annotation';
+    
+    var mirador3_config = {
+	"manifests": {
+	    httpDocumentIIIFManifest: {
+		"provider": "InterMusE"
+	    }
+	},
+	"window" : { "panels": { "attribution": true, "search": true } },
+
+	//"annotations": {
+	//  "htmlSanitizationRuleSet": 'iiif', // See src/lib/htmlRules.js for acceptable values
+	//  "filteredMotivations": ['oa:commenting', 'oa:tagging', 'sc:painting', 'commenting', 'tagging'],
+	//},
+	/*
+	"requests": {
+	    "postprocessors": [
+		(url, action) => {
+		    if (action.type === "mirador/RECEIVE_MANIFEST") {
+			//console.log(action)
+			//console.log(iiif_manifest);
+			return {
+			    ...action ,
+			    "manifestJson": iiif_manifest
+			};
+		    }
+		}
+	    ]
+	},*/
+    
+	"windows": [
+	    {
+		"loadedManifest": httpDocumentIIIFManifest,
+		"canvasIndex": canvas_index,
+		"thumbnailNavigationPosition": 'far-bottom'
+	    }
+	]
+    };
+
+    var includeFileAssocOpenAnnotations = gs.variables.optionIncludeFileAssocOpenAnnotations;
+    var includeEditableOpenAnnotations  = gs.variables.optionIncludeEditableOpenAnnotations;
+
+    if ((includeFileAssocOpenAnnotations == 'true') || (includeEditableOpenAnnotations == 'true')) {
+	// Activate the annotations plugin
+
+	mirador3_config.window.panels.annotations = true;
+	
+	mirador3_config.window.defaultSideBarPanel = "annotations";
+	mirador3_config.window.sideBarOpenByDefault = true;	  
+
+	if (includeEditableOpenAnnotations == 'true') {
+
+	    var endpointURL  = gs.variables.optionIncludeEditableOpenAnnotationsEndpointURL;
+	    
+	    mirador3_config.annotation = {};
+	
+	    if ((endpointURL == "") || (endpointURL == "localStorage")) {
+		mirador3_config.annotation.adapter = (canvasId) => new mirador3WithAnnotations.LocalStorageAdapter(`localStorage://?canvasId=${canvasId}`)
+	    }
+	    else {
+		mirador3_config.annotation.adapter = (canvasId) => new mirador3WithAnnotations.SimpleAnnotationServerV2Adapter(canvasId, endpointURL);
+	    }
+
+	    // **** add in a further options for this ????	
+	    mirador3_config.annotation.exportLocalStorageAnnotations = true;  // display annotation JSON export button
+	
+	}
+    }
+    
+    var mirador3_viewer = mirador3WithAnnotations.initMirador3Viewer('mirador3-viewer',mirador3_config);
+    
+    return mirador3_viewer;
+}
+
+function initIIIFAnnotations()
+{
+    var iiif_annotations_example = {
+	"@context": "http://www.shared-canvas.org/ns/context.json",
+	"@id": "https://iiif.harvardartmuseums.org/manifests/object/299843/list/47174896",
+	"@type": "sc:AnnotationList",
+	"resources": [
+            {
+		"@context": "http://iiif.io/api/presentation/2/context.json",
+		"@id": "https://iiif.harvardartmuseums.org/annotations/9641482",
+		"@type": "oa:Annotation",
+		"motivation": [
+                    "oa:commenting"
+		],
+		"on": {
+                    "@type": "oa:SpecificResource",
+                    "full": "https://iiif.harvardartmuseums.org/manifests/object/299843/canvas/canvas-47174896",
+                    "selector": {
+			"@type": "oa:FragmentSelector",
+			"value": "xywh=622,591,642,940"
+                    },
+                    "within": {
+			"@id": "https://iiif.harvardartmuseums.org/manifests/object/299843",
+			"@type": "sc:Manifest"
+                    }
+		},
+		"resource": [
+                    {
+			"@type": "dctypes:Text",
+			"chars": "<p>age: 35-52<br/>gender: Female(66.337677%)<br/>CALM: 55.438412%<br/>CONFUSED: 3.949288%<br/>SURPRISED: 2.33092%<br/>DISGUSTED: 0.545727%<br/>HAPPY: 1.549943%<br/>ANGRY: 2.082294%<br/>SAD: 34.103416%<br/></p><p>Generated by AWS Rekognition</p>",
+			"format": "text/html"
+                    }
+		]
+            }
+	    // More ...
+	]
+    };
+
+    document.open("application/json");
+    document.write(JSON.stringify(iiif_annotations_example));
+    document.close();
+}
+
+	    
+function initIIIFManifest(iiifpres_root_id,iiifpres_label, iiif_doc_images)
+{
+    console.log("IIIF_Doc_Images");
+    console.log(iiif_doc_images);
+    // Developed with reference to the following manifest example:
+    //  https://iiif.archivelab.org/iiif/McGillLibrary-rbsc_ms-medieval-073-18802/manifest.json
+    
+    var iiif_manifest = {
+        "@context": "http://iiif.io/api/presentation/2/context.json",
+	"@id": iiifpres_root_id + "/manifest",
+	"@type": "sc:Manifest",
+	"label": iiifpres_label,
+	"attribution": "The Internet of Musical Events (InterMusE): Digital Scholarship, Community, and the Archiving of Performance",
+	"license": "https://**** To Be Determined ****",
+	"logo": "https://intermuse.datatodata.org/wp-content/uploads/sites/4/2021/03/cropped-cropped-cropped-1.png"
+    };
+
+    /*
+    var iiif_metadata = [
+	{ "label": "creator", "value": ["aaa1","aaa2"] },
+	{ "label": "title", "value": ["ttt"] }
+    ];
+
+    iiif_manifest.metadata = iiif_metadata;
+    */
+   
+	
+    // trival IIIF sequences => has one IIIF canvas in it
+    var iiif_sequence = {
+	"@context": "http://iiif.io/api/image/2/context.json",
+        "@id": iiifpres_root_id + "/sequence/normal",
+        "@type": "sc:Sequence"
+    };
+
+
+    var iiif_canvases = [];
+
+    var assocfilepath = gs.documentMetadata['assocfilepath'];
+
+    var simple_doc_type = (gs.documentMetadata.docType == "simple");
+    
+    for (var i=0; i<iiif_doc_images.length; i++) {
+	var section = i+1; // assumes flat (i.e. non-hierarchical) image document structure
+	
+	var iiif_doc_image = iiif_doc_images[i];
+	console.log(iiif_doc_image);
+	var iiifimg_root_id = iiif_doc_image.id;
+
+	var iiifimg_x_dim = iiif_doc_image.x_dim;
+	var iiifimg_y_dim = iiif_doc_image.y_dim;
+	var iiifimg_label = iiif_doc_image.chosen_title;
+
+	var opt_section = (simple_doc_type) ? "" : section;
+	
+	var iiif_canvas = {
+	    "@context": "http://iiif.io/api/presentation/2/context.json",
+	    //"@id": "https://iiif.lib.harvard.edu/manifests/drs:48309543/canvas/canvas-48309544.json",
+	    "@id": iiifpres_root_id +"/canvas/" + section,
+            "@type": "sc:Canvas",
+            "width":  iiifimg_x_dim,
+	    "height": iiifimg_y_dim,
+	    "label":  iiifimg_label,
+
+	    // **** Annotation JSON File openannotation-list.json *****
+	    /*
+	    "otherContent": [
+                {
+                    //"@id": "https://iiif.harvardartmuseums.org/manifests/object/299843/list/47174896",
+		    "@id": "https://intermuse.sowemustthink.space/greenstone3/library/sites/intermuse/collect/programmes/index/assoc/"+assocfilepath+"/openannotation-list"+opt_section+".json",
+                    "@type": "sc:AnnotationList"
+                }
+            ]
+	    */
+	    
+	    // **** Annotation JSON File webannotation-list.json *****
+	    /*
+	    "otherContent": [
+                {
+		    "@id": "https://intermuse.sowemustthink.space/greenstone3/library/sites/intermuse/collect/programmes/index/assoc/"+assocfilepath+"/webannotation-list"+opt_section+".json",
+                    "@type": "sc:AnnotationList"
+                }
+            ]
+	   */
+	};
+	
+	var iiif_image = {
+	    "@context": "http://iiif.io/api/image/2/context.json",
+            //"@id": "https://iiif.lib.harvard.edu/manifests/drs:48309543/annotation/anno-48309544.json",
+            "@id": iiifpres_root_id +"/annotation/" + section,
+            "@type": "oa:Annotation",
+            "motivation": "sc:painting",
+            //"on": "https://iiif.lib.harvard.edu/manifests/drs:48309543/canvas/canvas-48309544.json",
+            "on": iiifpres_root_id +"/canvas",
+            "resource": {
+		//"@id": "https://intermuse.sowemustthink.space/gs-cantaloupe/iiif/2/intermuse:programmes:HASH012cd965c3e83d504f4a78cd//full/full/0/default.jpg",
+		"@id": iiifimg_root_id+"/full/full/0/default.jpg",
+		"@type": "dctypes:Image",
+		"format": "image/jpeg",
+		//"width":  1700,
+		//"height": 2338,
+		"width":  iiifimg_x_dim,
+		"height": iiifimg_y_dim,
+		"service": {
+                    "@context": "http://iiif.io/api/image/2/context.json",
+                    //"@id": "https://intermuse.sowemustthink.space/gs-cantaloupe/iiif/2/intermuse:programmes:HASH012cd965c3e83d504f4a78cd",
+                    "@id": iiifimg_root_id,
+                    "profile": "https://iiif.io/api/image/2/profiles/level2.json"
+		}
+            }
+	};
+
+
+	var iiif_thumbnail = {
+            //"@id": "https://intermuse.sowemustthink.space/gs-cantaloupe/iiif/2/intermuse:programmes:HASH012cd965c3e83d504f4a78cd/full/,150/0/default.jpg",
+            "@id": iiifimg_root_id + "/full/,150/0/default.jpg",
+            "@type": "dctypes:Image"
+	};
+
+	iiif_canvas.images    = [ iiif_image ];
+	iiif_canvas.thumbnail = iiif_thumbnail;
+
+	iiif_canvases.push(iiif_canvas);
+    }
+    
+    iiif_sequence.canvases = iiif_canvases;
+    iiif_manifest.sequences = [ iiif_sequence ];
+	    
+    return iiif_manifest;
+}
