PageSourceSearch

https://qi.gg/js/qi.js

js qi.gg collected 2026-10-02 01:54:28 UTC 40,110 bytes, 1,358 lines download raw bytes

1
2
3//Class Defintions ******************************************
4class Coord{
5    lat = 0.000000;
6    lng = 0.000000;
7    constructor(lat,lng){
8        this.lat = lat;
9        this.lng= lng;
10    }
11}
12
13class Island{
14    Name = "";
15    TopLeftCoord = new Coord(0,0);
16    BottomRightCoord = new Coord(0,0);
17    XIncrement = 0.000000;
18    YIncrement = 0.000000;
19    AliasMatrix = [];
20    QiCodeMatrix = [];
21    CodePrefix = "";
22    constructor(name, mapTopLCoord, mapbottomRCoord, xIncr, yIncr){
23        this.Name= name;
24        this.TopLeftCoord = mapTopLCoord;
25        this.BottomRightCoord = mapbottomRCoord;
26        this.XIncrement = xIncr;
27        this.YIncrement = yIncr;
28    }
29}
30
31class QiPosition {     
32    GridCoord = new Coord(0.000000,0.000000);
33	ActualCoord = new Coord(0.000000,0.000000);
34    QiCode = "";
35    QiCodeURL = "";
36    QiCodeAlias=  "";
37    Island = new Island();
38    isValid = true; 
39    constructor(){        
40    }
41}
42
43class MatrixPosition{    
44    Word1 = "";         //x-axis word
45    Word2 = "";         //y-axis word
46    Word1Index = -1;
47    Word2Index = -1;    
48    AliasPhrase = "";
49	CopyTo(toObject) {
50		toObject.Word1 = this.Word1;
51		toObject.Word2 = this.Word2;   
52		toObject.Word1Index = this.Word1Index;
53		toObject.Word2Index = this.Word2Index;    
54		toObject.AliasPhrase = this.AliasPhrase;
55	}
56    constructor(word1, word2, word1Index, word2Index){        
57        this.Word1 = word1;         
58        this.Word2 = word2;         
59        this.Word1Index = word1Index;
60        this.Word2Index = word2Index; 
61    }
62}
63//***********************************************************
64 
65
66var strCurrentQiCode = "";		//Current QiCode entered or selected (blank for invalid direct entry)
67var elmOutputMap;				//Element Container for the Map
68var objOutputMap;				//The Map
69var objOutputMapMarker;			//Map Marker
70//var objGeolocationOptions = {maximumAge:10000, timeout:5000, enableHighAccuracy: true};
71var objGeolocationOptions = {maximumAge:6000, timeout:5000, enableHighAccuracy: true};
72var objLastKnownLocation;		//Coord() class - Last Known co-ordinates (lat/lng) of the user - used to helpe determine the intended island  
73
74//Navigation Variables
75var objMapDirectionsService;	//The Map's Direction service (google.maps.DirectionsService)
76var objMapDirectionsRenderer;	//The Map's Renderer service (google.maps.DirectionsService)
77var objNavigateStartPoint;
78
79var aryIslands;					//Global Array of all Islands Covered
80
81var objGeoCoder;
82
83//Grid Overlays
84var roadmapOverlay;				//Map Grid overlay for Roadmaps
85var satelliteOverlay;			//Map Grid overlay for Satellite
86var blnGridShown = false;
87
88
89
90
91//Sharing 
92var strDomainName = window.location.hostname;
93var strDomainAddress = "https://" + strDomainName;
94//var strDomainName = window.location.hostname + ":8000";
95//var strDomainAddress = "http://" + strDomainName;
96
97
98const strFB_href = "http://www.facebook.com/sharer.php?u={url_to_qi_code}";
99const strTwitter_href = "http://twitter.com/intent/tweet?url=&original_referer&=hashtags=qi_code&text={url_to_qi_code}" ;
100const strWhatsApp_href="https://api.whatsapp.com/send?text={url_to_qi_code}";
101//const strEmail_href="mailto:[email protected]?&subject=Find%20Me%20at&body=Click%20on%20this%20link%20to%20find%20the%20location%20'{qi_code}'%20I%20found%20at%20qi.gg%20:%20{url_to_qi_code}";
102var strEmail_href="mailto:[email protected]?&subject=Find%20Me%20at&body=Click%20on%20this%20link%20to%20find%20the%20location%20'{qi_code}'%20I%20found%20at%20" + strDomainName +"%20:%20{url_to_qi_code}";
103
104
105//Default Positions (per Island)
106//const strPlaceHolderText = "Enter a Qi code";
107const objPosnStPP =  new Coord(49.455284, -2.534757);
108const objPosnStHlr = new Coord(49.182747,-2.109142);
109
110const mapType_Satellite = 'satellite';
111// const mapType_Hybrid = 'hybrid';
112// const markerLatOffset = 0.000058762911;
113// const markerLngOffset = 0.000059902668;
114
115const blnUseQiMapToNavigate = false;	//system setting to specify of navigation occurs in our own map or is delegated
116
117
118
119function findRowInArray(array, searchFor, lookupColNo) {
120	//Tranverse an Array Table
121	//Note : colIndex -1 means match any column value
122
123	var rowIndex = 0;
124	var colIndex = 0;
125	var row;
126	var colToLookup;
127	
128	
129	//if (lookupColNo == null){
130	if (IsSet(lookupColNo) == false){
131		colToLookup = 0;
132	} else {
133		colToLookup = lookupColNo;
134	}
135	
136	if(colToLookup==-1){
137		//Check All Columns for a match
138		for (rowIndex = 0; rowIndex < array.length; rowIndex++ ) {
139			row = array[rowIndex];
140			for (colIndex = 0; colIndex < row.length; colIndex++ ){
141				if (row[colIndex] == searchFor) {
142					return row;                    
143			   }			
144			}
145		}
146	} else {
147		//Check a Specific column
148		for (rowIndex = 0; rowIndex < array.length; rowIndex++ ) {
149			row = array[rowIndex];
150			if (row[colToLookup] == searchFor) {
151				return row;                    
152			}
153		}
154	}
155	
156    row = null;
157    return row; // value not found in array
158 }
159
160 function findRowInAliasArray(array, searchFor, lookupColNo, matchToMainSquareOnly) {
161	//Tranverse an Alias Array Table
162	//Array : QiCode  | Alias | isMainSquare <-- used in case 1 alias covers multiple squares
163	//Note : colIndex -1 means match any column value
164
165	var rowIndex = 0;
166	var colIndex = 0;
167	var row;
168	var colToLookup;
169	var mustMatchMain;
170		
171	if (IsSet(lookupColNo) == false){
172		colToLookup = 0;
173	} else {
174		colToLookup = lookupColNo;
175	}
176
177	if (IsSet(matchToMainSquareOnly) == false){
178		mustMatchMain = false;
179	} else {
180		mustMatchMain = matchToMainSquareOnly;
181	}
182	
183	if (array.length>0){
184		if(colToLookup==-1){
185			//Check All Columns for a match
186			for (rowIndex = 0; rowIndex < array.length; rowIndex++ ) {
187				row = array[rowIndex];
188				for (colIndex = 0; colIndex < row.length; colIndex++ ){
189					if (row[colIndex] == searchFor.trim()){
190						if (mustMatchMain){
191							if (row[2] == true){
192								return row;   
193							}
194						} else {
195							return row;  
196						}
197				}			
198				}
199			}
200		} else {
201			//Check a Specific column
202			for (rowIndex = 0; rowIndex < array.length; rowIndex++ ) {
203				row = array[rowIndex];
204				if (row[colToLookup] == searchFor.trim()){
205					if (mustMatchMain){
206						if (row[2] == true){
207							return row;   
208						}
209					} else {
210						return row;  
211					}                    
212				}
213			}
214		}
215	}
216    row = null;
217    return row; // value not found in array
218 }
219
220 function valueInRange(val, bottomVal, topVal){
221	var blnIsInRange = false;
222
223	if (val<=topVal && val>=bottomVal){
224		blnIsInRange = true;		
225	}
226
227	return blnIsInRange;
228 }
229
230 /* EXAMPLE ONLY 
231
232function getQiCodeMatrixForGuernsey (){
233	var matrix = [];
234	matrix.push('man');
235	matrix.push('any');
236	matrix.push('dinner');
237	matrix.push('put');
238	matrix.push('big');
239	return matrix;
240}
241
242
243function getQiCodeAliasMatrixForGuernsey(){
244	//Array : QiCode  | Alias | isMainSquare <-- used in case 1 alias covers multiple squares	
245	var matrix = [];
246	
247	
248	matrix.push(['happy place','mr joker',true]);	
249	matrix.push(['dutch donkey','wibble', true]);
250	matrix.push(['dutch swim','wibble', false]);
251	matrix.push(['sneak donkey','wibble', false]);
252	
253
254	return matrix;
255}
256
257function getIslandsList(){
258	//Initialise a list of All the islands, their map co-ords, QiCode Matrices etc
259	var matrix = [];
260	var island;
261	var tlCoord;
262	var brCoord;	
263	var xIncrement = .000075;
264	var yIncrement = .00005;
265
266	//Guernsey & Herm
267	tlCoord = new Coord(49.52,-2.69);
268	brCoord = new Coord(49.4,-2.44);
269	island = new Island("Guernsey", tlCoord, brCoord, xIncrement, yIncrement);
270	island.QiCodeMatrix = getQiCodeMatrixForGuernsey();
271	island.AliasMatrix = getQiCodeAliasMatrixForGuernsey();
272	island.CodePrefix = "gy";
273	matrix.push(island);
274
275
276	// //Alderney
277	// tlCoord = new Coord(49.78,-2.39);
278	// brCoord = new Coord(49.66,-2.14);
279	// island = new Island("Alderney", tlCoord, brCoord, xIncrement, yIncrement);
280	// island.QiCodeMatrix = getQiCodeMatrixForAlderney();
281	// island.AliasMatrix = getQiCodeAliasMatrixForAlderney();
282	// island.CodePrefix = "ay";
283	// matrix.push(island);
284
285	// //Sark
286	// tlCoord = new Coord(49.49,-2.42);
287	// brCoord = new Coord(49.37,-2.17);
288	// island = new Island("Sark", tlCoord, brCoord, xIncrement, yIncrement);
289	// island.QiCodeMatrix = getQiCodeMatrixForSark();
290	// island.AliasMatrix = getQiCodeAliasMatrixForSark();
291	// island.CodePrefix = "sk";
292	// matrix.push(island);
293
294	return matrix;
295}
296*/
297
298function initGlobalVariables(){
299	//Initialise Global Variables
300
301	//Set Global HTML Element to hold the map	
302	elmOutputMap = document.getElementById("outputMap");
303
304	//Global Array of all Islands Covered (incl their Co-ordinates and Matrices)
305	aryIslands = getIslandsList();
306
307}
308
309function createUnknownIsland(){
310	var island = new Island("unknown");
311	return island;
312}
313
314function isUnknownIsland(island){
315	var isUknown = false;
316
317	if (IsSet(island)){
318		isUknown = (island.Name =="unknown");
319	} else {
320		isUknown = true;
321	}
322
323	return isUknown;
324}
325
326function getIslandFromCoords(posn, out_MatrixPosition){
327	//Get an Island from the aryIslands Array based on a set of co-ordinates
328
329	var i;
330	var island;
331	var islandFound = createUnknownIsland();
332	
333	
334	
335	for (i=0; i<aryIslands.length; i++){
336		island = aryIslands[i];
337		
338		if (valueInRange(posn.lat, island.BottomRightCoord.lat,	island.TopLeftCoord.lat) &&
339			valueInRange(Math.abs(posn.lng), Math.abs(island.BottomRightCoord.lng),	Math.abs(island.TopLeftCoord.lng))){
340	
341				//Island has been Found
342				//Get Matrix Position
343				var matrixPosn = createMatrixPositionFromCoordObject(posn, island);
344				matrixPosn.CopyTo(out_MatrixPosition);
345
346				//Check the Alias Matrix (matching the Main Square)		
347				//AliasArray = QiCode  | Alias | isMainSquare <-- used in case 1 alias covers multiple squares
348				var row = findRowInAliasArray(island.AliasMatrix, out_MatrixPosition.Word1 + " " + out_MatrixPosition.Word2,0, false);
349				if (IsSet(row)){
350					//There is an Alias
351					out_MatrixPosition.AliasPhrase = row[1];
352				}
353				islandFound = island;
354				break;
355			}
356	}
357	
358	return islandFound;	
359}
360
361
362function getIslandFromQiCode(rawQiCode, out_MatrixPosition){
363	//Get an Island from the aryIslands Array based on a passed qiCode
364	//out param out_MatrixPosition returns the actual underlying Matrix words and any Alias
365	
366	var i;
367	var matrixPosn;		
368	var strQiWord1;
369	var strQiWord2;
370	var intSpacePosn = rawQiCode.indexOf(' ');
371	var island;
372	var islandFound = createUnknownIsland();
373	var matrixPosn;
374
375	
376	//Split the Code into 2 words
377	rawQiCode = rawQiCode.trim();
378	strQiWord1 = rawQiCode.substr(0,intSpacePosn);
379	strQiWord2 = rawQiCode.substr(intSpacePosn+1);
380	strQiWord1 = strQiWord1.replace(/ /g,'');
381	strQiWord2 = strQiWord2.replace(/ /g,'');
382	
383
384	//Check if we have at least 1 word if not return UnknownIsland
385	if (strQiWord1=="" && strQiWord2==""){				
386        return islandFound;
387	}
388
389	for (i=0; i<aryIslands.length; i++){
390		island = aryIslands[i];
391
392		//Check the QiCodeMatrix & Exit Loop if Found
393		matrixPosn = createMatrixPositionFromQiCode(strQiWord1, strQiWord2, island);
394		if (IsSet(matrixPosn)){
395			matrixPosn.CopyTo(out_MatrixPosition);
396			islandFound = island;
397			break;
398		}
399
400
401		//Check the Alias Matrix (matching the Main Square)		
402		//AliasArray = QiCode  | Alias | isMainSquare <-- used in case 1 alias covers multiple squares
403		var row = findRowInAliasArray(island.AliasMatrix, rawQiCode,1, true);
404		if (IsSet(row)) {	
405			//This is an Alias so create teh MatrixPosition based on the 'real' 2 words			
406			intSpacePosn = row[0].indexOf(' ');
407			strQiWord1 = row[0].substr(0,intSpacePosn);
408			strQiWord2 = row[0].substr(intSpacePosn+1);			
409			matrixPosn = createMatrixPositionFromQiCode(strQiWord1, strQiWord2, island)			
410			matrixPosn.AliasPhrase = rawQiCode;		
411			//out_MatrixPosition = matrixPosn;	
412			matrixPosn.CopyTo(out_MatrixPosition);		
413			islandFound = island;
414			break;
415		}
416	}
417
418	return islandFound;	
419}
420
421function createMatrixPositionFromQiCode(word1, word2, forIsland){
422	//Create a MatrixPosition() object based on know Coord() object
423
424	var matrixPosn = null;
425	var intCodeIndex1;
426	var intCodeIndex2;
427
428
429	intCodeIndex1 = forIsland.QiCodeMatrix.indexOf(word1);
430	if (intCodeIndex1 != -1) {
431		intCodeIndex2 = forIsland.QiCodeMatrix.indexOf(word2);
432		if (intCodeIndex2 != -1) {
433			matrixPosn = new MatrixPosition(word1,word2,intCodeIndex1,intCodeIndex2);			
434			matrixPosn.AliasPhrase = "";	
435			//Do a check to see if an Alias does exist and an old code has been supplied
436			var row = findRowInAliasArray(forIsland.AliasMatrix, word1 + " " + word2,0, false);
437			if (IsSet(row)) {	
438				matrixPosn.AliasPhrase = row[1];
439			}				
440
441		}
442	}
443
444
445	return matrixPosn;
446}
447
448function createMatrixPositionFromCoordObject(coord, forIsland){
449	//Create a MatrixPosition() object based on know Coord() object
450
451	var matrixPosn = new MatrixPosition();
452	
453	//Zero based array
454    //matrixPosn.Word1Index =  Math.floor((Math.abs(coord.lng) - Math.abs(forIsland.BottomRightCoord.lng) )/forIsland.XIncrement) + 1;	
455	//matrixPosn.Word2Index =  Math.floor((Math.abs(coord.lat) - Math.abs(forIsland.BottomRightCoord.lat) )/forIsland.YIncrement) + 1;
456	matrixPosn.Word1Index =  Math.floor((Math.abs(coord.lng) - Math.abs(forIsland.BottomRightCoord.lng) )/forIsland.XIncrement);	
457	matrixPosn.Word2Index =  Math.floor((Math.abs(coord.lat) - Math.abs(forIsland.BottomRightCoord.lat) )/forIsland.YIncrement);
458
459	matrixPosn.Word1 = forIsland.QiCodeMatrix[matrixPosn.Word1Index];
460	matrixPosn.Word2 = forIsland.QiCodeMatrix[matrixPosn.Word2Index];
461
462	return matrixPosn;
463}
464
465function createCoordObjectFromMatrixPositionObject(matrixPosn, forIsland){
466	//Create a Coord() object based on know word positions in a matrix
467
468	var coord = new Coord();
469    coord.lng = forIsland.BottomRightCoord.lng - (matrixPosn.Word1Index * forIsland.XIncrement);
470	coord.lat = forIsland.BottomRightCoord.lat + (matrixPosn.Word2Index * forIsland.YIncrement);
471	return coord;
472}
473
474
475
476
477// function getUsersIslandChoice(){
478// 	//Determine which Island the User is 'using'
479// 	//Based on either current location or 'intention' based on any markers layed dowm
480
481// 	var island = createUnknownIsland();
482
483// 	if (IsSet(objOutputMapMarker)){
484// 		//A Marker is set so use that 
485// 		var posn = new Coord(objOutputMapMarker.getPosition.lat(), objOutputMapMarker.getPosition.lng())
486// 		island = getIslandFromCoords(posn);
487// 	}
488
489// 	if (IsSet(objLastKnownLocation) && isUnknownIsland(island)){
490// 		//A last known Position is set so use that
491// 		island = getIslandFromCoords(objLastKnownLocation);
492// 	}
493
494// 	return island;
495// }
496
497
498function processUserEntry(posn, qiCode, setZoom, setCentre, setPlaceHolderText, markerType){
499    //Process a User Action of either entering a QiCode or Clicking on the Map for a Posn
500	
501	var blnEntryByQiCode = false;
502
503
504    //Calc Location Values
505	if (IsSet(qiCode)){
506		if (qiCode == "") return; //don't process place holder text
507		blnEntryByQiCode = true;
508		qiCode = qiCode.toLowerCase();		
509	}
510    var objQiPositionObject = createQiPositionObject(qiCode, posn);
511
512    try{
513
514        if (objQiPositionObject.isValid){
515            //Code is Valid (either a QiCode entered directly or a Lat/Long is inside a recognized 'Island Area')
516
517			//Set Global Variable recording the current QiCode
518			if (objQiPositionObject.QiCodeAlias != ""){
519				strCurrentQiCode = objQiPositionObject.QiCodeAlias;
520			} else {
521				strCurrentQiCode = objQiPositionObject.QiCode;
522			}
523		
524            //Set Zoom & Position  
525            if (IsSet(setZoom)){
526                if (setZoom == true){
527                    objOutputMap.setZoom(18);
528                }
529            }
530			if (IsSet(setCentre)){
531				if (setCentre == true){
532					var objPosn = {lat: objQiPositionObject.ActualCoord.lat, lng: objQiPositionObject.ActualCoord.lng};
533					objOutputMap.setCenter(objPosn);
534				}
535			}
536        
537
538            //Display the Marker      			
539            ClearMapMarker(objOutputMapMarker);
540			createMarker(objQiPositionObject, markerType, blnEntryByQiCode, objQiPositionObject.Island);
541
542		
543            //Display the QiCode
544			setCodeInputTextBoxValues(objQiPositionObject, setPlaceHolderText);
545			
546		
547        } else {
548            //Code is Invalid
549
550			//Reset Global Variable recording the current QiCode
551			strCurrentQiCode = "";
552
553			//Still show the marker if entry was by clicking outside the area
554			if (!blnEntryByQiCode){
555				//Display the Marker      			
556				ClearMapMarker(objOutputMapMarker);
557				createMarker(objQiPositionObject, "std", false, objQiPositionObject.Island);
558			}
559
560            setCodeInputTextBoxValues(objQiPositionObject);
561        }
562
563    } catch (err){
564        //Display the values **********  
565		objQiPositionObject.isValid = false;	
566        setCodeInputTextBoxValues(objQiPositionObject);
567        //******************************
568    }
569
570}
571
572
573// function setControls(mode){
574// 	switch (mode) {
575// 		case "std":			
576// 			//Standard Map view and Marker clicks						
577// 			$('#divMainMenu').removeClass('qi-toggle-off');
578// 			$('#divNavigateMenu').addClass('qi-toggle-off');
579// 			break;
580
581// 		case "nav":			
582// 			//Navigate from Current Location to a QiCode
583// 			$('#divMainMenu').addClass('qi-toggle-off');
584// 			$('#divNavigateMenu').removeClass('qi-toggle-off');
585			
586// 			//Also Hide any other pop-ups displayed
587// 			if (!$('#divCodeEntryPopUp').hasClass('qi-toggle-off')){
588// 				toggleCodeEntryPopUp();
589// 			}
590// 			if (!$('#divSharePopUp').hasClass('qi-toggle-off')){
591// 			  toggleSharePopUp();
592// 			}			
593// 			break;
594
595// 		default:
596// 			break;
597// 	}	
598// }
599
600
601function createQiPositionObject(strQiCode, posn){
602    //Create the QiPosition Object from parameters passed in 
603	
604	var blnPosnSupplied = false;
605    //var qiPositionObject = {PlusCode: "", lat: 0, lng: 0, QiCode: "", QiCodeURL: "", QiCodeAlias: "", isValid: true };
606	var qiPositionObject = new QiPosition();	
607	var out_MatrixPosn = new MatrixPosition();
608	
609
610    if (IsSet(strQiCode)){ 
611        //Create Object from a passed QiCode **********
612		//Find Which Island this Code refers to
613		qiPositionObject.Island = getIslandFromQiCode(strQiCode, out_MatrixPosn);
614        //*********************************************
615
616    }  else {
617        //Create Object from a Lat/Long ***************
618		blnPosnSupplied = true;
619		//Find Which Island this Coord refers to
620		qiPositionObject.Island = getIslandFromCoords(posn, out_MatrixPosn);		
621        //*********************************************
622
623    }
624
625
626	//Return if the Island (& QiCode therfore) is Unknown 
627	if (isUnknownIsland(qiPositionObject.Island)){
628		qiPositionObject.isValid = false; 
629		qiPositionObject.QiCode="Invalid QiCode";
630		if (blnPosnSupplied){
631			qiPositionObject.ActualCoord = new Coord(posn.lat,posn.lng);
632		}
633		return qiPositionObject;
634	}	
635
636
637	//Island and QiCode/Coords passed in are Valid
638	qiPositionObject.isValid = true;
639	qiPositionObject.GridCoord = createCoordObjectFromMatrixPositionObject(out_MatrixPosn, qiPositionObject.Island); //this will create a Grid Postion of Co-ordinates rather tnas any clicked on in the passed in posn	
640	if (blnPosnSupplied){
641		qiPositionObject.ActualCoord = new Coord(posn.lat,posn.lng);
642	} else {
643		qiPositionObject.ActualCoord = new Coord(qiPositionObject.GridCoord.lat,qiPositionObject.GridCoord.lng);
644	}	
645	qiPositionObject.QiCode = out_MatrixPosn.Word1 + " "  + out_MatrixPosn.Word2;
646	qiPositionObject.QiCodeAlias = out_MatrixPosn.AliasPhrase;
647	if (qiPositionObject.QiCodeAlias !=""){			
648		qiPositionObject.QiCodeURL = getQIEncodedURL(qiPositionObject.QiCodeAlias,true);
649	} else {
650		qiPositionObject.QiCodeURL = getQIEncodedURL(qiPositionObject.QiCode,true);
651	}		
652
653
654    return qiPositionObject;
655}
656
657
658function getQIEncodedURL(strQICode, escapeSpaces){
659
660	//var siteURL ="https://qi.gg";
661	var siteURL = strDomainAddress;
662
663	if (IsSet(strQICode)){// != null
664		if (!IsSet(escapeSpaces)){
665			escapeSpaces = true;
666		}
667		if (escapeSpaces == true){
668			siteURL = siteURL + "/?qi=" + strQICode.replace(" ", "%20");
669		} else {
670			siteURL = siteURL + "/?qi=" + strQICode;
671		}
672    }
673	return siteURL;
674}
675
676
677function initMap() {	
678	//Main initialisation point : Call back from Google Maps 
679		
680	//Reset global variables
681	strCurrentQiCode = "";
682	elmOutputMap = null;
683	objOutputMap = null;
684	objOutputMapMarker = null;
685	objLastKnownLocation = null;
686	objMapDirectionsService = null;
687	objMapDirectionsRenderer= null;
688	objNavigateStartPoint = null;
689	objGeoCoder = null;
690	roadmapOverlay = null;
691	satelliteOverlay = null;
692	blnGridShown = false;
693	
694	
695
696	//Initialise the Global Environment Variables
697	initGlobalVariables();
698
699	    
700    //Create the Map & Set Global Variable
701    createMap(mapType_Satellite);
702  
703
704	//Initialise Grid Overlays
705    initialise_GridOverlays();
706
707
708	try{
709		//Check if Parameters were passed in 
710		var queryString = window.location.search;
711		var parms = new URLSearchParams(queryString);
712		var action = parms.get('a');
713		var qiCode = parms.get('qi');		
714				
715
716		if (IsSet(qiCode)){// != null
717			//A Code Parameter was passed so run it 			
718			
719			processUserEntry(null,qiCode, true, true, false);
720			if (IsSet(action)){
721				//An Action Parameter was passed
722				switch (action) {
723					// case "nav":
724					// 	//Display the Navigation bar
725					// 	setViewMode('nav');						
726					// 	break;
727
728					case "navgo":
729						//Trigger a Navigation Action
730						//setViewMode('nav');
731						navigateGO(qiCode);
732						break;
733					default:
734						break;
735				}
736				
737			}
738			
739		} else {
740			//No Code Parameter passed so try to use Geolocation to Initialise it			
741			if (navigator.geolocation) {	
742				navigator.geolocation.getCurrentPosition(initialiseMap_foundGeolocationPosn ,initialiseMap_noGeolocationPosn, objGeolocationOptions);
743			} else {
744				initialiseMap_noGeolocationPosn(null);
745			}	
746		}
747	} catch (err){
748		
749		initialiseMap_noGeolocationPosn(null);
750	}
751}
752
753function createMap(mapType){
754	//Create the Map for the 1st Time
755 
756	var blnShowZoomControls =  !IsMobileDevice();	
757	var defaultPosn = getDefaultPosition();
758	
759
760	//Create Map, centered in St Peter (hide streetview)
761    objOutputMap = new google.maps.Map(elmOutputMap, {
762		zoomControl: blnShowZoomControls,
763        zoom: 12,
764        center: defaultPosn,
765		streetViewControl: false,
766        mapTypeId: mapType,
767    });
768
769
770
771	//Add Custom Buttons
772	addMapCustomButtons();
773
774	//Configure the click listener.
775	try{
776		objOutputMap.addListener("click", ouputMap_Click);
777	} catch(listenErr){
778       
779	}
780	
781}
782
783
784function getDefaultPosition(){
785	//Get a Default Island Position 
786	//Depends on the site being accessed
787
788	var defaultPosn;
789
790	switch (strDomainName){
791		case "qicodes.je":
792			defaultPosn = objPosnStHlr;
793			case "qi.gg":
794			case "qicodes.gg":
795			default:
796				defaultPosn = objPosnStPP
797				break;
798	}
799	
800	return defaultPosn;
801}
802function toggleMapZoomControls(toggleOn){
803	if (toggleOn){
804		map.setOptions("zoomControl", true);
805	} else {
806		map.setOptions("zoomControl", false);
807	}
808}
809
810function addMapCustomButtons(){
811	//Add Cutom Buttons to the Map	
812	/*
813	objOutputMap.controls[google.maps.ControlPosition.TOP_LEFT].push(controlMarkerUI);
814
815	Options : google.maps.ControlPosition.TOP_CENTER ; 
816										TOP_CENTER
817										TOP_LEFT
818										TOP_RIGHT
819										LEFT_TOP
820										RIGHT_TOP
821										LEFT_CENTER
822										RIGHT_CENTER
823										LEFT_BOTTOM
824										RIGHT_BOTTOM
825										BOTTOM_CENTER
826										BOTTOM_LEFT
827										BOTTOM_RIGHT
828	*/
829
830	if (navigator.geolocation) {	
831		//### Add a Current Position button on Google Maps ...
832		var controlPositionUI = document.createElement('DIV');	
833		controlPositionUI.className = "qi-current-location-icon";
834		controlPositionUI.title = 'Select current location';		
835		controlPositionUI.onclick = function() {
836			navigator.geolocation.getCurrentPosition(initialiseMap_foundGeolocationPosn ,initialiseMap_noGeolocationPosn, objGeolocationOptions);
837		};	
838		objOutputMap.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(controlPositionUI);
839
840		//### Add a Navigate button on Google Maps ...
841		var controlNavigateUI = document.createElement('DIV');
842		controlNavigateUI.className = "qi-navigate-icon";
843		controlNavigateUI.title = 'Navigate from current location';		
844		controlNavigateUI.onclick = function() {
845			//toggleNavigateTo('on');
846			performNavigate('');			
847		};	
848		objOutputMap.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(controlNavigateUI);
849
850		//### Add a Clear Navigate button on Google Maps ...
851		// var controlClearNavigateUI = document.createElement('DIV');
852		// controlClearNavigateUI.className = "qi-clear-navigate-icon";
853		// controlClearNavigateUI.title = 'Clear Navigation';		
854		// controlClearNavigateUI.onclick = function() {			
855		// 	resetNavigationServices();			
856		// };	
857		// objOutputMap.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(controlClearNavigateUI);
858
859	}
860}
861
862function ClearMapMarker(marker){
863	try{
864		marker.setMap(null);  
865	} catch (errr){
866		//do nothing
867	}		
868}
869
870//function createMarker(posn, objQiPositionObject, markerType, applyRectangleOffset, island){
871function createMarker(objQiPositionObject, markerType, applyRectangleOffset, island){
872
873	var googlePosn = {lat: objQiPositionObject.ActualCoord.lat, lng: objQiPositionObject.ActualCoord.lng};
874
875
876
877	if (!IsSet(markerType)){
878		markerType='std';
879	}
880
881	//var iconBounceURL="https://qi.gg/images/location-bounce64.gif";	
882	//var iconRedMarkerDot = "https://qi.gg/images/marker-red-whitedot1-32x45.png";
883	var iconBounceURL= strDomainAddress + "/images/location-bounce64.gif";	
884	var iconRedMarkerDot = strDomainAddress + "/images/marker-red-whitedot1-32x45.png";			
885	var isOptimized = false;
886	//animation : google.maps.Animation.BOUNCE,
887	var qiCode;
888
889
890
891	//Get QiCode Text to display
892	//if (objQiPositionObject.isValid){
893		if (objQiPositionObject.QiCodeAlias !=""){
894			qiCode = objQiPositionObject.QiCodeAlias;
895		} else {
896			qiCode = objQiPositionObject.QiCode;
897		}
898	//}
899
900
901	//If Rectangle Offset is to be applied then use GridCoords NOT Actual Position
902	if (IsSet(applyRectangleOffset)){
903		if (applyRectangleOffset==true){			
904			googlePosn.lat = objQiPositionObject.GridCoord.lat + ((island.YIncrement)/2);			
905			googlePosn.lng =  objQiPositionObject.GridCoord.lng - ((island.XIncrement)/2);
906		}
907	} 
908
909
910	switch (markerType) {
911		case "bounce":
912			//Blue Pulse
913			if (IsSet(qiCode)){
914				objOutputMapMarker = new google.maps.Marker({
915					position: googlePosn,
916					map: objOutputMap,
917					label: {text: qiCode, color:'white', className: 'qiCode-MapMarker-Label',},
918					title: qiCode,
919					optimized : isOptimized,					
920					icon: iconBounceURL,
921					});
922			} else {
923				objOutputMapMarker = new google.maps.Marker({
924					position: googlePosn,
925					map: objOutputMap,					
926					optimized : isOptimized,					
927					icon: iconBounceURL,
928					});
929			}
930			break;
931
932		case "std":
933		default:
934			//Standard Marker
935			if (IsSet(qiCode)){
936				objOutputMapMarker = new google.maps.Marker({
937					position: googlePosn,
938					map: objOutputMap,
939					label: {text: qiCode, color:'white', className: 'qiCode-MapMarker-Label'},
940					title: qiCode,
941					icon: iconRedMarkerDot, //have to use this as it removes the dot when setting the label
942					});
943			
944			} else {
945				objOutputMapMarker = new google.maps.Marker({
946					position: googlePosn,
947					map: objOutputMap,
948					});
949			}
950			break;
951	}
952	//marker.set('labelContent', 'sdda');
953	
954}
955
956function initialise_GridOverlays(){
957	//Create the 2 overlays for use in the map
958
959	// Define an image map overlay in black.	 
960	roadmapOverlay = new google.maps.ImageMapType({
961        getTileUrl: function(coord, zoom) {
962            // Using WMS tile numbering.
963			//window.alert(['https://grid.plus.codes/grid/wms/', zoom, '/', coord.x, '/', coord.y, '.png'].join(''));
964            return ['https://grid.plus.codes/grid/wms/', zoom, '/', coord.x, '/', coord.y, '.png'].join('');
965        },
966        tileSize: new google.maps.Size(256, 256)
967	});
968
969	// Define an image map overlay in white.
970	satelliteOverlay = new google.maps.ImageMapType({
971        getTileUrl: function(coord, zoom) {
972            // Using WMS tile numbering.
973			//window.alert(['https://grid.plus.codes/grid/wms/', zoom, '/', coord.x, '/', coord.y, '.png?col=white'].join(''));
974            return ['https://grid.plus.codes/grid/wms/', zoom, '/', coord.x, '/', coord.y, '.png?col=white'].join('');			
975        },
976        tileSize: new google.maps.Size(256, 256)
977	});
978
979	//Add a Listener to Change the overlay when the map type changes
980	objOutputMap.addListener('maptypeid_changed', outputMap_MapTypeChanged);
981	  
982}
983
984function getMapType(){
985	var mapType;
986	try{	
987		mapType = objOutputMap.getMapTypeId(); 
988	} catch (err){
989		mapType = 'satellite';
990	}			
991	return mapType;
992}
993
994function toggleGrid(){
995	
996	if (blnGridShown == false){
997		//Set Toggle value
998		blnGridShown = true;
999
1000		//Shoe the Grid
1001		showGrid(getMapType());
1002	} else {
1003		//Set Toggle value
1004		blnGridShown = false;
1005		
1006		//Remove Overlays
1007		hideGrid();
1008
1009	}
1010
1011	//showTheGrid
1012	//grid = new Graticule(objOutputMap, true);
1013}
1014
1015function showGrid(mapTypeId){
1016	//Display the Grid according to the MapTypeId
1017
1018	hideGrid();
1019
1020	if (mapTypeId == google.maps.MapTypeId.SATELLITE ||
1021		mapTypeId == google.maps.MapTypeId.HYBRID) {
1022		objOutputMap.overlayMapTypes.push(satelliteOverlay);
1023	} else {
1024		objOutputMap.overlayMapTypes.push(roadmapOverlay);
1025	}
1026
1027}
1028
1029function hideGrid(){
1030	objOutputMap.overlayMapTypes.pop();	
1031}
1032
1033function copyURLToClipboard(callerID, copyMessageID) {
1034   
1035	//var strQiCode = document.getElementById('txtNAVQICodeInput').value;
1036	var strQiCode = strCurrentQiCode;
1037
1038	var copyText = getQIEncodedURL(strQiCode, true);
1039
1040    const el = document.createElement('textarea');
1041    el.value = copyText;
1042    el.setAttribute('readonly', '');
1043    el.style.position = 'absolute';
1044    el.style.left = '-9999px';
1045    document.body.appendChild(el);
1046    el.select();
1047    document.execCommand('copy');
1048    document.body.removeChild(el);
1049
1050	if (IsSet(copyMessageID)){	
1051		$('#'+copyMessageID).show();				
1052		$('#'+copyMessageID).fadeOut(3500);		
1053	}
1054}
1055
1056function setCodeInputTextBoxValues(objQiPositionObject, setPlaceHolderText){
1057	//Set the Various QiCode input TextBoxes
1058
1059	var elmBoxQiCodeInput = document.getElementById("txtPOPQICodeInput");	
1060	var elmNavQiCodeInput = document.getElementById("txtNAVQICodeInput");		
1061	var qiCodeToDisplay = "";
1062
1063	if (!IsSet(setPlaceHolderText)){
1064		setPlaceHolderText = false;
1065	}
1066
1067	if (setPlaceHolderText){
1068		elmBoxQiCodeInput.value = "";		
1069		elmNavQiCodeInput.value = "";		
1070
1071		//elmBoxQiCodeInput.attr("placeholder", strPlaceHolderText);	
1072		//elmNavQiCodeInput.attr("placeholder", strPlaceHolderText);			
1073	} else {
1074
1075		if (objQiPositionObject.isValid){
1076			if (objQiPositionObject.QiCodeAlias !=""){
1077				qiCodeToDisplay = objQiPositionObject.QiCodeAlias;
1078			} else {
1079				qiCodeToDisplay = objQiPositionObject.QiCode;
1080			}
1081		} else {
1082			qiCodeToDisplay = "Invalid QiCode";
1083		}
1084		
1085		elmBoxQiCodeInput.value = qiCodeToDisplay;	
1086		elmNavQiCodeInput.value = qiCodeToDisplay;		
1087
1088		// elmBoxQiCodeInput.attr("placeholder", "");	
1089		// elmNavQiCodeInput.attr("placeholder", "");	
1090	}
1091
1092	//Set the Share Buttons also
1093	setShareButtonsTextValues(objQiPositionObject);
1094}
1095
1096function setShareButtonsTextValues(objQiPositionObject){
1097
1098
1099	//const strFB_href = "http://www.facebook.com/sharer.php?u={url_to_qi_code}";
1100	//const strTwitter_href = "http://twitter.com/intent/tweet?url=&original_referer&=hashtags=qi_code&text={url_to_qi_code}" ;
1101	//const strWhatsApp_href="https://api.whatsapp.com/send?text={url_to_qi_code}";
1102	//const strEmail_href="mailto:[email protected]?&subject=Find%20Me%20at&body=Click%20on%20this%20link%20to%20find%20the%20location%20'{qi_code}'%20I%20found%20at%20qi.gg%20:%20{url_to_qi_code}";
1103	
1104	if (!objQiPositionObject.isValid){
1105		return;
1106	}
1107
1108
1109	var strQiCode;
1110
1111	if (objQiPositionObject.QiCodeAlias !=""){
1112		strQiCode = objQiPositionObject.QiCodeAlias;
1113	} else {
1114		strQiCode = objQiPositionObject.QiCode;
1115	}
1116
1117	var elm;
1118	var strhref;
1119	var siteURL = getQIEncodedURL(strQiCode,true);
1120
1121	elm = document.getElementById("lnkShareFacebook");
1122	strhref = strFB_href.replace("{url_to_qi_code}", encodeURIComponent(siteURL));
1123	strhref = strhref.replace("{qi_code}", encodeURIComponent(strQiCode));
1124	elm.href = strhref;
1125
1126	elm = document.getElementById("lnkShareTwitter");
1127	strhref = strTwitter_href.replace("{url_to_qi_code}", encodeURIComponent(siteURL));
1128	strhref = strhref.replace("{qi_code}", encodeURIComponent(strQiCode));
1129	elm.href = strhref;
1130
1131	elm = document.getElementById("lnkShareWhatsApp");
1132	strhref = strWhatsApp_href.replace("{url_to_qi_code}", encodeURIComponent(siteURL));
1133	strhref = strhref.replace("{qi_code}", encodeURIComponent(strQiCode));
1134	elm.href = strhref;
1135
1136	elm = document.getElementById("lnkShareEmail");
1137	strhref = strEmail_href.replace("{url_to_qi_code}", encodeURIComponent(siteURL));
1138	strhref = strhref.replace("{qi_code}", encodeURIComponent(strQiCode));
1139	elm.href = strhref;
1140
1141}
1142
1143//Start : Events *****************************************
1144function ouputMap_Click(pos){
1145	//Map Click Event
1146
1147    var posn = {
1148        lat: pos.latLng.lat(),
1149        lng: pos.latLng.lng()   
1150    };
1151 
1152	
1153    //Trigger the Calculation
1154    processUserEntry(posn, null, false, false, false);
1155}
1156
1157function outputMap_MapTypeChanged(){
1158	//User selected a different Map Type
1159
1160	//Show a different Overlay (if visible) Depending on the Maptype 
1161	if (blnGridShown){
1162		showGrid(getMapType());
1163	}
1164
1165}
1166
1167function initialiseMap_foundGeolocationPosn(pos){	
1168	//Initialise the Map based on current location
1169	
1170	const posn = {
1171            lat: pos.coords.latitude,
1172            lng: pos.coords.longitude,
1173		};
1174        
1175	
1176    //Process as if a UserAction
1177	processUserEntry(posn, null, true, true, true, "bounce");
1178}
1179
1180function initialiseMap_noGeolocationPosn(err){
1181	//Geolocation Failed 
1182	
1183	//Check if the Error was due to a High Accuracy timeout
1184	if (err.code == err.TIMEOUT){
1185        // Attempt to get GPS loc timed out after 5 seconds, 
1186        // try low accuracy location
1187		navigator.geolocation.getCurrentPosition(initialiseMap_foundGeolocationPosn ,initialiseMap_errorCallback_lowAccuracy,  {maximumAge:600000, timeout:10000, enableHighAccuracy: fal
1187se});
1188        return;
1189    }
1190
1191	//Initialise the Map without reference to current location
1192    //Process as if a UserAction in St PP
1193    //processUserEntry(objPosnStPP, null, false, true, true);
1194	var defaultPosn = getDefaultPosition();
1195	processUserEntry(defaultPosn, null, false, true, true);
1196}
1197
1198
1199
1200function initialiseMap_errorCallback_lowAccuracy(err) {
1201	//A Low Accuracy attempt to get geolocation was tried and has also failed 
1202
1203	//Process as if a UserAction in St PP
1204	//processUserEntry(objPosnStPP, null, false, true, true);
1205	var defaultPosn = getDefaultPosition();
1206	processUserEntry(defaultPosn, null, false, true, true);
1207}
1208
1209//End : Events ******************************************
1210
1211
1212function IsSet(variable){
1213	var blnIsSet = true;
1214	if (typeof variable === 'undefined') {
1215		blnIsSet=false;
1216	} else if (variable === null){
1217		blnIsSet=false;
1218	}
1219	return blnIsSet;
1220}
1221
1222function IsMobileDevice(){
1223	if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)){
1224		//document.write("mobile");
1225		return true;
1226	}else{
1227		//document.write("not mobile");
1228		return false;
1229	}
1230}
1231
1232function navigateGO(strNavToQiCode){
1233	//Calculate & Display the Navigation between Current Position & and End Point
1234
1235	var blnEndPointIsValid = false;
1236
1237
1238	//Check a Value has been entered
1239	if (strNavToQiCode == ""){
1240		if (strCurrentQiCode ==""){
1241			return;
1242		} else {
1243			strNavToQiCode = strCurrentQiCode;
1244		}
1245	}
1246
1247	//Check the Endpoint is valid	
1248	strNavToQiCode = strNavToQiCode.toLowerCase();		
1249	//Set Global Variable Start Point
1250	objNavigateStartPoint = createQiPositionObject(strNavToQiCode, null);		
1251	blnEndPointIsValid = objNavigateStartPoint.isValid;
1252	
1253	//Keep TextBoxes Synchronized (will aslo swap in any alias discovered)
1254	setCodeInputTextBoxValues(objNavigateStartPoint);
1255
1256	//Process if invalid
1257	if (blnEndPointIsValid){		
1258
1259		if (blnUseQiMapToNavigate==true){
1260			//Handle the Navigation ourselves : Show Navigation using our Own Map
1261			//Get Current Location (& process in the reply events)
1262			if (navigator.geolocation) {	
1263				navigator.geolocation.getCurrentPosition(showNavigate_foundGeolocationPosn, showNavigate_noGeolocationPosn, objGeolocationOptions);
1264			} else {
1265				window.alert("Cannot calculate route. This browser doesn't support geolocation, or permission hasn't been given");
1266			}	
1267		} else {
1268			//Delegate the Navigation to another service
1269			/*Use Google Maps
1270				https://www.google.com/maps/dir/?api=1&parameters
1271				Example : https://www.google.com/maps/dir/?api=1&destination=49.449695,-2.533354
1272			Optional params : 
1273				travelmode = driving | walking | bicycling | transit
1274				dir_action = navigate - Launches either turn-by-turn navigation or route preview to the specified destination, based on whether the origin is available.
1275			*/
1276			var googleNavURL = "https://www.google.com/maps/dir/?api=1&destination=" + objNavigateStartPoint.ActualCoord.lat + "," + objNavigateStartPoint.ActualCoord.lng;
1277			googleNavURL += "&travelmode=driving";
1278			googleNavURL += "&dir_action=navigate";
1279			window.open(googleNavURL, '_blank');
1280		}
1281	}
1282
1283}
1284
1285function resetNavigationServices(){
1286	//Clear any Existing Directions
1287	if(objMapDirectionsService != null) {
1288		objMapDirectionsRenderer.setMap(null);
1289		objMapDirectionsRenderer = null;
1290
1291		objMapDirectionsService = null;
1292	}
1293}
1294
1295function showNavigate_noGeolocationPosn(err){
1296	//Geolocation not found (or user gave no permission)
1297    window.alert("Cannot calculate route. This browser doesn't support geolocation, or permission hasn't been given.");
1298}
1299
1300function showNavigate_foundGeolocationPosn(pos){	
1301	//Show the route based on the values of Current location and objNavigateStartPoint
1302	
1303	const posn = {
1304            lat: pos.coords.latitude,
1305            lng: pos.coords.longitude,
1306		};
1307        
1308
1309	//Clear any Existing Directions
1310	resetNavigationServices();
1311
1312	//Create the Directions Service & Renderer		
1313	objMapDirectionsService = new google.maps.DirectionsService();
1314	objMapDirectionsRenderer = new google.maps.DirectionsRenderer();		
1315	objMapDirectionsRenderer.setMap(objOutputMap);
1316	
1317	//Define the Request
1318	var startPoint = new google.maps.LatLng(posn.lat, posn.lng);
1319	var endPoint = new google.maps.LatLng(objNavigateStartPoint.lat, objNavigateStartPoint.lng);
1320	
1321	var request = {
1322		origin: startPoint,
1323		destination: endPoint,
1324		travelMode: 'DRIVING'
1325		};
1326
1327	//Call for Directions & render it on the map
1328	objMapDirectionsService.route(request, function(result, status) {
1329		//window.alert(status);
1330		if (status == 'OK') {
1331			objMapDirectionsRenderer.setDirections(result);
1332		}
1333	});
1334
1335}
1336
1337function createGeoCoder(){
1338	objGeoCoder = new google.maps.Geocoder();
1339}
1340
1341function createMarkerFromAddress(address) {
1342	address="GY1 1BE";
1343
1344	objGeoCoder.geocode({ 'address': address }, function (results, status) {
1345		if (status == 'OK') {
1346				var marker = new google.maps.Marker({
1347				position: address,
1348				map : objOutputMap,
1349			});
1350		} else {
1351			alert('Geocode was not successful for the following reason: ' + status);
1352		}
1353
1354	});
1355}
1356
1357
1358

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.