Files
raven-client/ayanova/src/api/gzutil.js
2022-12-14 00:14:57 +00:00

995 lines
29 KiB
JavaScript

/////////////////////////////////
// General utility library
//
const icons = {
image: "$ayiFileImage",
pdf: "$ayiFilePdf",
word: "$ayiFileWord",
powerpoint: "$ayiFilePowerpoint",
excel: "$ayiFileExcel",
csv: "$ayiFileCsv",
audio: "$ayiFileAudio",
video: "$ayiFileVidio",
archive: "$ayiFileArchive",
code: "$ayiFileCode",
text: "$ayiFileAlt",
file: "$ayiFile"
};
const mimeTypes = {
"image/gif": icons.image,
"image/jpeg": icons.image,
"image/png": icons.image,
"image/webp": icons.image,
"application/pdf": icons.pdf,
"application/msword": icons.word,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
icons.word,
"application/mspowerpoint": icons.powerpoint,
"application/vnd.openxmlformats-officedocument.presentationml.presentation":
icons.powerpoint,
"application/msexcel": icons.excel,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
icons.excel,
"text/csv": icons.csv,
"audio/aac": icons.audio,
"audio/wav": icons.audio,
"audio/mpeg": icons.audio,
"audio/mp4": icons.audio,
"audio/ogg": icons.audio,
"video/x-msvideo": icons.video,
"video/mpeg": icons.video,
"video/mp4": icons.video,
"video/ogg": icons.video,
"video/quicktime": icons.video,
"video/webm": icons.video,
"application/gzip": icons.archive,
"application/zip": icons.archive,
"application/x-tar": icons.archive,
"text/css": icons.code,
"text/html": icons.code,
"text/javascript": icons.code,
"application/javascript": icons.code,
"text/plain": icons.text,
"text/richtext": icons.text,
"text/rtf": icons.text,
"application/rtf": icons.text,
"application/json": icons.text
};
const extensions = {
gif: icons.image,
jpeg: icons.image,
jpg: icons.image,
png: icons.image,
webp: icons.image,
pdf: icons.pdf,
doc: icons.word,
docx: icons.word,
ppt: icons.powerpoint,
pptx: icons.powerpoint,
xls: icons.excel,
xlsx: icons.excel,
csv: icons.csv,
aac: icons.audio,
mp3: icons.audio,
ogg: icons.audio,
avi: icons.video,
flv: icons.video,
mkv: icons.video,
mp4: icons.video,
gz: icons.archive,
zip: icons.archive,
tar: icons.archive,
"7z": icons.archive,
css: icons.code,
html: icons.code,
js: icons.code,
txt: icons.text,
json: icons.text,
rtf: icons.text
};
export default {
///////////////////////////////
// CLEAN OBJECT
// Clear all properties from object without resorting to assigning a new object (o={})
// which can be problematic in some cases (IE bugs, watched data items in forms etc)
removeAllPropertiesFromObject: function(o) {
for (let variableKey in o) {
if (Object.prototype.hasOwnProperty.call(o, variableKey)) {
delete o[variableKey];
}
}
},
///////////////////////////////
// DEEP COPY FOR API UPDATE
// Deep copy an object skipping all *Viz and named properties from object
//
deepCopySkip: function(source, skipNames) {
if (skipNames == null) {
skipNames = [];
}
let o = {};
for (let key in source) {
if (
!key.endsWith("Viz") &&
!skipNames.some(x => x == key) &&
Object.prototype.hasOwnProperty.call(source, key)
) {
o[key] = source[key];
}
}
return o;
},
/**
* Copy a string to clipboard
* @param {String} string The string to be copied to clipboard
* @return {Boolean} returns a boolean correspondent to the success of the copy operation.
* Modified from an example here: https://stackoverflow.com/a/53951634/8939
* Basically a fallback if navigator.clipboard is not available
*/
copyToClipboard: function(string) {
let textarea;
let result;
if (navigator && navigator.clipboard) {
navigator.clipboard.writeText(string);
} else {
try {
textarea = document.createElement("textarea");
textarea.setAttribute("readonly", true);
textarea.setAttribute("contenteditable", true);
textarea.style.position = "fixed"; // prevent scroll from jumping to the bottom when focus is set.
textarea.value = string;
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
const range = document.createRange();
range.selectNodeContents(textarea);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
textarea.setSelectionRange(0, textarea.value.length);
result = document.execCommand("copy");
} catch (err) {
result = null;
} finally {
document.body.removeChild(textarea);
}
// manual copy fallback using prompt
if (!result) {
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
const copyHotkey = isMac ? "⌘C" : "CTRL+C";
result = prompt(`Press ${copyHotkey}`, string);
if (!result) {
return false;
}
}
}
return true;
},
///////////////////////////////
// ROUNDING
// //https://medium.com/swlh/how-to-round-to-a-certain-number-of-decimal-places-in-javascript-ed74c471c1b8
roundAccurately: function(number, decimalPlaces) {
if (!number || number == 0 || Number.isNaN(number)) {
return number;
}
const wasNegative = number < 0;
if (wasNegative) {
number = Math.abs(number); //make sure it's positive because rounding negative numbers is weird in JS
}
number = Number(
Math.round(number + "e" + decimalPlaces) + "e-" + decimalPlaces
);
if (wasNegative) {
number = 0 - number;
}
return number;
},
///////////////////////////////
// CLEAN TAG NAME
// Clean up a tag with same rules as server
//
normalizeTag: function(tagName) {
if (!tagName || tagName == "") {
return null;
}
tagName = tagName.toLowerCase();
//spaces to dashes
tagName = tagName.replace(/ /gi, "-");
//multiple dashes to single dashes
tagName = tagName.replace(/-+/g, "-");
//ensure doesn't start or end with a dash
tagName = this.trimSpecific(tagName, "-");
//No longer than 255 characters
tagName = tagName.length > 255 ? tagName.substr(0, 255 - 1) : tagName;
return tagName;
},
///////////////////////////////
// Quick hash for trivial purposes
// not cryptographic
// https://stackoverflow.com/a/7616484/8939
//
quickHash: function(theString) {
let hash = 0;
let i;
let chr;
if (theString.length === 0) return hash;
for (i = 0; i < theString.length; i++) {
chr = theString.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
},
////////////////////////////////////////
// Random password / login generator
// https://stackoverflow.com/a/51540480/8939
// using 32 character (128 bit) as default
//
getRandomPassword: function() {
const wishlist = "0123456789abcdefghijkmnopqrstuvwxyz";
return Array.from(crypto.getRandomValues(new Uint32Array(32)))
.map(x => wishlist[x % wishlist.length])
.join("");
},
///////////////////////////////
// CONVERT STRING TO BOOLEAN
// https://stackoverflow.com/a/1414175/8939
//
stringToBoolean: function(string) {
switch (string.toLowerCase().trim()) {
case "true":
case "yes":
case "1":
return true;
case "false":
case "no":
case "0":
case null:
return false;
default:
return Boolean(string);
}
}, ///////////////////////////////
// CONVERT STRING TO FLOAT
// https://stackoverflow.com/a/9409894/8939
//
stringToFloat: function(string) {
//null or empty then zero
if (!string) {
return 0;
}
//A number already then parse and return
if (this.isNumeric(string)) {
if (Number.isNaN(string)) {
return 0;
}
return parseFloat(string);
}
//Not a string at all?
if (!this.isString(string)) {
return 0;
}
const ret = parseFloat(string.replace(/[^\d.-]/g, ""));
if (Number.isNaN(ret)) {
return 0;
}
return ret;
},
///////////////////////////////
// Is negative number
//
//
isNegative: function(v) {
//null or empty then zero
if (!v || v == 0 || Number.isNaN(v)) {
return false;
}
return parseFloat(v) < 0;
},
///////////////////////////////
// Splice a string
//changes the content of a string by removing a range of
// characters and/or adding new characters.
//
// @param {String} source string
// @param {number} start Index at which to start changing the string.
// @param {number} delCount An integer indicating the number of old chars to remove.
// @param {string} newSubStr The String that is spliced in.
// @return {string} A new string with the spliced substring.
stringSplice: function(source, start, delCount, newSubStr) {
if (source == null || source == "") {
if (newSubStr) {
return newSubStr;
}
return "";
}
return (
source.slice(0, start) +
newSubStr +
source.slice(start + Math.abs(delCount))
);
},
///////////////////////////////
// Truncate a string
//truncates and adds ellipses
//
// @param {String} source string
// @param {number} length desired
// @return {string} A new string truncated with ellipses at end
truncateString: function(s, len) {
if (this.stringIsNullOrEmpty(s)) {
return s;
}
if (s.length > len) {
return s.substring(0, len) + "...";
} else {
return s;
}
},
///////////////////////////////
// Format tags for display
//
//
// @param {String} tags raw from server
// @return {string} A new string with the tags formatted or an empty string if no tags
formatTags: function(tags) {
if (tags && tags.length > 0) {
return tags.join(", ");
}
return "";
},
///////////////////////////////
// ICON FOR *ALL* OBJECT TYPES
//(used for search results and event log / history)
//NOTE: Any object type could appear in event log, they all need to be supported where possible
//CoreBizObject add here
iconForType: function(ayaType) {
switch (ayaType) {
case window.$gz.type.NoType:
case null:
return "$ayiGenderless";
case window.$gz.type.Global:
return "$ayiGlobe";
case window.$gz.type.User:
return "$ayiUser";
case window.$gz.type.ServerState:
return "$ayiDoorOpen";
case window.$gz.type.License:
return "$ayiTicket";
case window.$gz.type.LogFile:
return "$ayiGlasses";
case window.$gz.type.PickListTemplate:
return "$ayiPencilRuler";
case window.$gz.type.Customer:
return "$ayiAddressCard";
case window.$gz.type.ServerJob:
return "$ayiRobot";
case window.$gz.type.Contract:
return "$ayiFileContract";
case window.$gz.type.TrialSeeder:
return "$ayiSeedling";
case window.$gz.type.Metrics:
return "$ayiFileMedicalAlt";
case window.$gz.type.Translation:
return "$ayiLanguage";
case window.$gz.type.UserOptions:
return "$ayiUserCog";
case window.$gz.type.HeadOffice:
return "$ayiSitemap";
case window.$gz.type.LoanUnit:
return "$ayiPlug";
case window.$gz.type.FileAttachment:
return "$ayiPaperclip";
case window.$gz.type.DataListSavedFilter:
return "$ayiFilter";
case window.$gz.type.FormCustom:
return "$ayiCustomize";
case window.$gz.type.Part:
return "$ayiBoxes";
case window.$gz.type.PartWarehouse:
return "$ayiWarehouse";
case window.$gz.type.PartAssembly:
return "$ayiObjectGroup";
case window.$gz.type.Project:
return "$ayiProjectDiagram";
case window.$gz.type.PurchaseOrder:
return "$ayiTruckLoading";
case window.$gz.type.Unit:
return "$ayiFan";
case window.$gz.type.UnitModel:
return "$ayiDiceD20";
case window.$gz.type.Vendor:
return "$ayiStore";
case window.$gz.type.Quote:
return "$ayiPencilAlt";
case window.$gz.type.PM:
return "$ayiBusinessTime";
case window.$gz.type.WorkOrder:
return "$ayiTools";
case window.$gz.type.WorkOrderItem:
case window.$gz.type.PMItem:
case window.$gz.type.QuoteItem:
return "$ayiWrench";
case window.$gz.type.WorkOrderItemExpense:
case window.$gz.type.QuoteItemExpense:
case window.$gz.type.PMItemExpense:
return "$ayiMoneyBillWave";
case window.$gz.type.WorkOrderItemLabor:
case window.$gz.type.QuoteItemLabor:
case window.$gz.type.PMItemLabor:
return "$ayiHammer";
case window.$gz.type.WorkOrderItemLoan:
case window.$gz.type.QuoteItemLoan:
case window.$gz.type.PMItemLoan:
return "$ayiPlug";
case window.$gz.type.WorkOrderItemPart:
case window.$gz.type.QuoteItemPart:
case window.$gz.type.PMItemPart:
return "$ayiBoxes";
case window.$gz.type.WorkOrderItemPartRequest:
return "$ayiParachuteBox";
case window.$gz.type.WorkOrderItemScheduledUser:
case window.$gz.type.QuoteItemScheduledUser:
case window.$gz.type.PMItemScheduledUser:
return "$ayiUserClock";
case window.$gz.type.WorkOrderItemTask:
case window.$gz.type.QuoteItemTask:
case window.$gz.type.PMItemTask:
case window.$gz.type.TaskGroup:
return "$ayiTasks";
case window.$gz.type.WorkOrderItemTravel:
case window.$gz.type.QuoteItemTravel:
case window.$gz.type.PMItemTravel:
return "$ayiTruckMonster";
case window.$gz.type.WorkOrderItemUnit:
case window.$gz.type.QuoteItemUnit:
case window.$gz.type.PMItemUnit:
return "$ayiFan";
case window.$gz.type.WorkOrderItemOutsideService:
case window.$gz.type.QuoteItemOutsideService:
case window.$gz.type.PMItemOutsideService:
return "$ayiLuggageCart";
case window.$gz.type.Backup:
return "$ayiFileArchive";
case window.$gz.type.Notification:
return "$ayiBell";
case window.$gz.type.NotifySubscription:
return "$ayiBullhorn";
case window.$gz.type.Reminder:
return "$ayiStickyNote";
case window.$gz.type.UnitMeterReading:
return "$ayiWeight";
case window.$gz.type.CustomerServiceRequest:
return "$ayiConciergeBell";
// case window.$gz.type.ServiceBank:
// return "$ayiCarBattery";
case window.$gz.type.OpsNotificationSettings:
return "$ayiBullhorn";
case window.$gz.type.Report:
return "$ayiThList";
case window.$gz.type.DashboardView:
return "$ayiTachometer";
case window.$gz.type.CustomerNote:
return "$ayiClipboard";
case window.$gz.type.Memo:
return "$ayiInbox";
case window.$gz.type.Review:
return "$ayiCalendarCheck";
case window.$gz.type.ServiceRate:
return "$ayiCalculator";
case window.$gz.type.TravelRate:
return "$ayiCalculator";
case window.$gz.type.TaxCode:
return "$ayiPercent";
case window.$gz.type.WorkOrderStatus:
return "$ayiFlag";
//scroll icon is good one for something
default:
return null;
}
},
//https://gist.github.com/colemanw/9c9a12aae16a4bfe2678de86b661d922
iconForFile: function(fileName, mimeType) {
// List of official MIME Types: http://www.iana.org/assignments/media-types/media-types.xhtml
let extension = null;
if (fileName && fileName.includes(".")) {
extension = fileName.split(".").pop();
extension = extension.toLowerCase();
}
if (!extension && !mimeType) {
console.log(
"gzutil:iconForFile -> No mime or extension for " +
fileName +
" " +
mimeType
);
return "$ayiFile";
}
if (!mimeType) {
mimeType = "";
}
mimeType = mimeType.toLowerCase();
const iconFromExtension = extensions[extension];
const iconFromMIME = mimeTypes[mimeType];
if (iconFromMIME) {
return iconFromMIME;
}
if (iconFromExtension) {
return iconFromExtension;
}
return "$ayiFile";
},
///////////////////////////////////////////////
// attempt to detect image extension name
//
isImageAttachment: function(fileName, mimeType) {
return this.iconForFile(fileName, mimeType) == "$ayiFileImage";
},
///////////////////////////////////////////////
// Sleep async
//
sleepAsync: function(milliseconds) {
// eslint-disable-next-line
return new Promise((resolve) => setTimeout(resolve, milliseconds));
},
///////////////////////////////////////////////
// sortByKey lodash "sortBy" replacement
// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_sortby-and-_orderby
//usage:
// The native sort modifies the array in place. `_.orderBy` and `_.sortBy` do not, so we use `.concat()` to
// copy the array, then sort.
// fruits.concat().sort(sortBy("name"));
// => [{name:"apple", amount: 4}, {name:"banana", amount: 2}, {name:"mango", amount: 1}, {name:"pineapple", amount: 2}]
sortByKey: key => {
return (a, b) => {
const aaa = a[key].toUpperCase();
const bbb = b[key].toUpperCase();
return aaa > bbb ? 1 : bbb > aaa ? -1 : 0;
//this was the original but it was sorting weird as it was taking case into account with uppercase higher than lowercase
//so PMItem came before Part in the object lists
//return a[key] > b[key] ? 1 : b[key] > a[key] ? -1 : 0;
};
},
///////////////////////////////////////////////
// "has" lodash replacement
// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_has
//
has: function(obj, key) {
var keyParts = key.split(".");
return (
!!obj &&
(keyParts.length > 1
? this.has(obj[key.split(".")[0]], keyParts.slice(1).join("."))
: hasOwnProperty.call(obj, key))
);
},
///////////////////////////////////////////////
// Check if object is empty
//
objectIsEmpty: function(obj) {
//https://stackoverflow.com/a/4994265/8939
return !obj || Object.keys(obj).length === 0;
},
///////////////////////////////////////////////
// Trim specific character from start and end
// https://stackoverflow.com/a/55292366/8939
//
trimSpecific: function trim(str, ch) {
var start = 0;
var end = str.length;
while (start < end && str[start] === ch) ++start;
while (end > start && str[end - 1] === ch) --end;
return start > 0 || end < str.length ? str.substring(start, end) : str;
},
///////////////////////////////////////////////
// is numeric replacement for lodash
// https://stackoverflow.com/a/52986361/8939
//
isNumeric: function(n) {
//lodash isNumber returned false if it's a string and that's what the rest of the code expects even though it's parseable to a number
return !this.isString(n) && !isNaN(parseFloat(n)) && isFinite(n);
},
///////////////////////////////////////////////
// is string replacement for lodash
// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_isString
//
isString: function(str) {
return str != null && typeof str.valueOf() === "string";
},
///////////////////////////////////////////////
//
//
//
stringIsNullOrEmpty: function(str) {
if (str === null || str === undefined) {
return true;
}
if (this.isString(str)) {
if (str.trim() == "") {
return true;
}
}
return false;
},
///////////////////////////////////////////////
// is Boolean replacement for lodash
// https://stackoverflow.com/a/43718478/8939
//
isBoolean: function(obj) {
return obj === true || obj === false || typeof variable === "boolean";
},
///////////////////////////////////////////////
// parse to number or null if not a number
// used because route params can turn into strings
// on their own
//
stringToIntOrNull: function(n) {
const ret = Number.parseInt(n, 10);
if (Number.isNaN(ret)) {
return null;
}
return ret;
},
///////////////////////////////////////////////
// Simple array equality comparison
// (will NOT work on arrays of objects)
// Array order is relevant here as they are not sorted
// change of order will equal change of array
// as this is required for datatable sortby
//
isEqualArraysOfPrimitives: function(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
// Please note that calling sort on an array will modify that array.
// you might want to clone your array first.
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
},
///////////////////////////////////////////////
// Use geolocation api to attempt to get current location
// try high accuracy first and downgrade if unavailable
//https://www.openstreetmap.org/?mlat=48.3911&mlon=-124.7353#map=12/48.3910/-124.7353
//https://www.openstreetmap.org/#map=18/49.68155/-125.00435
//https://www.openstreetmap.org/?mlat=49.71236&mlon=-124.96961#map=17/49.71236/-124.96961
//https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393
getGeoLocation: async function() {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
function successHigh(pos) {
resolve({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
});
},
function error(err) {
//if here due to timeout getting high accuracy then try again with low accuracy
if (error.code == error.TIMEOUT) {
navigator.geolocation.getCurrentPosition(
function successLow(pos) {
resolve({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
});
},
function error(err) {
reject(
new Error(
`ERROR getting location(low_accuracy: ${err.code}): ${err.message}`
)
);
},
{ maximumAge: 600000, timeout: 10000, enableHighAccuracy: false }
);
return;
}
reject(
new Error(
`ERROR GETTING LOCATION(high_accuracy:${err.code}): ${err.message}`
)
);
},
{ maximumAge: 600000, timeout: 5000, enableHighAccuracy: true }
);
});
},
///////////////////////////////////////////////
// Open map url
//
//
viewGeoLocation: function(obj) {
const hasGeo =
obj.latitude != null &&
obj.latitude != 0 &&
obj.longitude != null &&
obj.longitude != 0;
const hasAddress =
!this.stringIsNullOrEmpty(obj.address) &&
!this.stringIsNullOrEmpty(obj.city) &&
!this.stringIsNullOrEmpty(obj.region) &&
!this.stringIsNullOrEmpty(obj.country) &&
!this.stringIsNullOrEmpty(obj.postCode);
if (!hasGeo && !hasAddress) {
return;
}
let mapUrl = window.$gz.store.state.userOptions.mapUrlTemplate;
//No pre-set?
if (!mapUrl || mapUrl == "") {
mapUrl =
"https://www.google.com/maps/search/?api=1&query={ayaddress}<|>https://www.google.com/maps/search/?api=1&query={aylatitude},{aylongitude}";
}
let geoMapUrl = null;
let addressMapUrl = null;
//Parse the map url
let mapUrls = [mapUrl];
if (mapUrl.includes("<|>")) {
mapUrls = mapUrl.split("<|>");
}
mapUrls.forEach(z => {
if (!geoMapUrl && z.includes("{aylatitude}")) {
geoMapUrl = z;
}
if (!addressMapUrl && z.includes("{ayaddress}")) {
addressMapUrl = z;
}
});
//decide which map to use here, favor geocode
if (hasGeo && geoMapUrl) {
//geo view
mapUrl = geoMapUrl;
mapUrl = mapUrl.split("{aylatitude}").join(obj.latitude);
mapUrl = mapUrl.split("{aylongitude}").join(obj.longitude);
} else if (hasAddress && addressMapUrl) {
mapUrl = addressMapUrl;
//compile address fields together
//order street to country seems to be standard
//note, if google need plus symbol delimiter, if bing, need comma delimiter
//but both might accept one big string space delimited and url encoded so test that on all first
const delimiter = " ";
let q = "";
if (obj.address) {
q += obj.address + delimiter;
}
if (obj.city) {
q += obj.city + delimiter;
}
if (obj.region) {
q += obj.region + delimiter;
}
if (obj.country) {
q += obj.country + delimiter;
}
if (obj.postCode) {
q += obj.postCode + delimiter;
}
if (obj.addressPostal) {
q += obj.addressPostal + delimiter;
}
if (q.length > 1) {
q = q.substring(0, q.length - 1);
}
//url encode the query
q = encodeURIComponent(q);
mapUrl = mapUrl.split("{ayaddress}").join(q);
} else {
throw new Error(
"View map: error - no matching mapurl / address / geo coordinates set for display, nothing to view"
);
}
window.open(mapUrl, "map");
//This is not valid to do as some platforms don't open a new web browser window
//but rather a map application in which case this is null and throws up the exception even though it's working
// if (window.open(mapUrl, "map") == null) {
// throw new Error(
// "Problem displaying map in new window. Browser must allow pop-ups to view maps; check your browser setting"
// );
// }
},
///////////////////////////////////////////////
// Online mapping service url formats
//
//
mapProviderUrls: function() {
return [
{
name: "Apple",
value:
"http://maps.apple.com/?q={ayaddress}<|>http://maps.apple.com/?ll={aylatitude},{aylongitude}"
},
{
name: "Bing",
value:
"https://bing.com/maps/default.aspx?where1={ayaddress}<|>https://bing.com/maps/default.aspx?cp={aylatitude}~{aylongitude}&lvl=17&style=r&sp=point.{aylatitude}_{aylongitude}"
},
{
name: "Google",
value:
"https://www.google.com/maps/search/?api=1&query={ayaddress}<|>https://www.google.com/maps/search/?api=1&query={aylatitude},{aylongitude}"
},
{
name: "MapQuest",
value:
"https://mapquest.com/?center={ayaddress}&zoom=17<|>https://mapquest.com/?center={aylatitude},{aylongitude}&zoom=17"
},
{
name: "Open Street Map",
value:
"https://www.openstreetmap.org/search?query={ayaddress}<|>https://www.openstreetmap.org/?mlat={aylatitude}&mlon={aylongitude}#map=17/{aylatitude}/{aylongitude}"
},
{
name: "geo URI",
value: "geo:{aylatitude},{aylongitude}"
},
{
name: "Waze",
value:
"https://waze.com/ul?q={ayaddress}<|>https://www.waze.com/ul?ll={aylatitude},{aylongitude}&navigate=yes&zoom=17"
},
{
name: "Yandex",
value:
"https://yandex.ru/maps/?mode=search&text={ayaddress}&z=17<|>https://yandex.ru/maps/?ll={aylatitude},{aylongitude}&z=12&l=map"
}
];
},
///////////////////////////////////////////////
// v-calendar view to AyaNova scheduleview enum
//
//
calendarViewToAyaNovaEnum: function(view) {
switch (view) {
case "day":
return 1;
case "week":
return 2;
case "month":
return 3;
case "4day":
return 4;
case "category":
return 5;
default:
throw new Error(
`gzutil->calendarViewtoAyaNovaEnum - Unknown view type '${view}'`
);
}
},
///////////////////////////////////////////////
// GZDaysOfWeek to VCalendar weekdays
//
//
DaysOfWeekToWeekdays: function(dow) {
/*
AyaDaysOfWeek
Monday = 1,
Tuesday = 2,
Wednesday = 4,
Thursday = 8,
Friday = 16,
Saturday = 32,
Sunday = 64
vCalendar [
0,//sunday
1,
2,
3,
4,
5,
6//saturday
]
*/
if (dow == null || dow == 0) {
return [0, 1, 2, 3, 4, 5, 6]; //all the days
}
const ret = [];
//turn EXCLUDE selected gzDaysOfWeek into INCLUDE selected days for vCalendar
if (!(dow & 64)) {
ret.push(0);
}
if (!(dow & 1)) {
ret.push(1);
}
if (!(dow & 2)) {
ret.push(2);
}
if (!(dow & 4)) {
ret.push(3);
}
if (!(dow & 8)) {
ret.push(4);
}
if (!(dow & 16)) {
ret.push(5);
}
if (!(dow & 32)) {
ret.push(6);
}
return ret;
},
///////////////////////////////////////////////
// Random integer from 0 to max
//
//
getRandomInt: function(max) {
return Math.floor(Math.random() * max);
}
/**
*
*
*/
//new functions above here
};