// Static utility class for the app class NomadNumbers_SharedFunctions { // This load the value for each select groups // This is used to pre-load data from the backend // // key: the key to retrieve from the backend // mapKey: the key to map to the datatable column name // nullArray: an array of id+text to specy an optional value to add (usually a null value to unset the selection) // example: { id: null, text: "Not set" }; loadSelectValuesFromBackend(scope, http, key, mapKey, nullArray) { //console.log("In loadSelectValuesFromBackend function..."); //console.log("scope=" + scope); //console.log("http=" + http); //console.log("key=" + key); //console.log("mapKey=" + mapKey); //console.log("nullArray=" + nullArray); return (scope.groupsXEditables[key] != undefined) ? null : http.get('ngtable_mysql_getSelectData.php?key=' + key).then(function(resp) { var resultArray = resp.data.records["x-editable"]; // for use by x-editable if (nullArray != null) { resultArray.unshift(nullArray); } scope.groupsXEditables[mapKey] = resultArray; }); } // Added 05-10-2023 // Return the date to be the date // // For some reason (maybe timezone differences?) // When loading date in a datepicker the date we pass is being set the day after the actual date // This function reset the date using the user timezeone // // This is only for the angular datepicker component // https://github.com/720kb/angular-datepicker fixTheDateToBeTheActualDate(date) { if (!date) { return null; // or you can set a default value here } console.log("fixTheDateToBeTheActualDate: Date=" + date); var parts = date.split('-'); var year = parseInt(parts[0]); var month = parseInt(parts[1]) - 1; var day = parseInt(parts[2]); var actualDate = new Date(year, month, day); //actualDate.setDate(actualDate.getDate() - 0); return actualDate.toISOString().slice(0, 10); }; // Added 05-10-2023 // Private functionCheck if a date match the YYYY-MM-DD format // // Which is the date format used in this app between UI and DB _isDateFormatFollowYYYYMMDDFormat(dateString) { //console.log("_isDateFormatFollowYYYYMMDDFormat: date=" + dateString); // Regular expression pattern for "YYYY-MM-DD" format var pattern = /^\d{4}-\d{2}-\d{2}$/; // Check if the date string matches the pattern if (!pattern.test(dateString)) { return false; } // Convert the date string to a Date object var date = new Date(dateString); // Check if the date is valid and matches the original string var result = date.toISOString().slice(0, 10) === dateString; //console.log("Return " + result); return result; } // Added 05-10-2023 // This function take the expense date and the trip start/end date // And return the array with available spread for that expense // This can then be appended to the scope.groupsXEditables object // // Algo // If the expense date is before the trip start date, return the diff between trip start and trip end dates // If the expense date is after the trip end date, return 0 // If the expense date is within the trip start/end date, return the number of day left before the end of the trip // // expenseDate: the expense date (must be YYYY-MM-DD format) // tStartDate: the trip start date (must be YYYY-MM-DD format) // tEndDate: the trip end date (must be YYYY-MM-DD format) returnSpreadArrayForGroupXEditablesBetweenExpenseDateAndTripBoundaries(expenseDate, tStartDate, tEndDate) { var result = []; //console.log("returnSpreadArrayForGroupXEditablesBetweenExpenseDateAndTripBoundaries"); // Check parameters format if (!this._isDateFormatFollowYYYYMMDDFormat(expenseDate) || !this._isDateFormatFollowYYYYMMDDFormat(tStartDate) || !this._isDateFormatFollowYYYYMMDDFormat(tEndDate)) { if (!this._isDateFormatFollowYYYYMMDDFormat(expenseDate)) { console.error("returnSpreadArrayForGroupXEditablesBetweenExpenseDateAndTripBoundaries: " + "We need to pass dates using the 'YYYY-MM-DD format' are we are not. " + "expenseDate=" + expenseDate); } if (!this._isDateFormatFollowYYYYMMDDFormat(tStartDate)) { console.error("returnSpreadArrayForGroupXEditablesBetweenExpenseDateAndTripBoundaries: " + "We need to pass dates using the 'YYYY-MM-DD format' are we are not. " + "tripStartDate=" + tStartDate); } if (!this._isDateFormatFollowYYYYMMDDFormat(tEndDate)) { console.error("returnSpreadArrayForGroupXEditablesBetweenExpenseDateAndTripBoundaries: " + "We need to pass dates using the 'YYYY-MM-DD format' are we are not. " + "tripEndDate=" + tEndDate); } } else { // Calculate the number of days between expense date and end date // Source: https://stackoverflow.com/questions/542938/how-do-i-get-the-number-of-days-between-two-dates-in-javascript // console.log("returnSpreadArrayForGroupXEditablesBetweenExpenseDateAndTripBoundaries: expense_date=" + expenseDate); var ymdExpenseDate = expenseDate.split('-'); // Format YYYY-MM-DD var tripExpenseDate = new Date(ymdExpenseDate[0], ymdExpenseDate[1]-1, ymdExpenseDate[2]); var ymdStartDate = tStartDate.split('-'); // Format YYYY-MM-DD var tripStartDate = new Date(ymdStartDate[0], ymdStartDate[1]-1, ymdStartDate[2]); var ymdEndDate = tEndDate.split('-'); // Format YYYY-MM-DD var tripEndDate = new Date(ymdEndDate[0], ymdEndDate[1]-1, ymdEndDate[2]); var nb_days_before_end_trip = 0; var noSpreadText = "No spread"; // If the expense date is in the past, use the start date as boundary if (ymdExpenseDate < ymdStartDate) { nb_days_before_end_trip = Math.round((tripEndDate-tripStartDate)/(1000*60*60*24))+1; } else if (ymdExpenseDate > ymdEndDate) { // If it is in the future we allow the full lenght // FULL LENGHT - nb_days_before_end_trip = Math.round((tripEndDate-tripStartDate)/(1000*60*60*24))+1; nb_days_before_end_trip = 0; noSpreadText = "No spread - Expense date is after end of the trip"; } else { // Otherwise we only provide the diffence in number of days available for the spread nb_days_before_end_trip = Math.round((tripEndDate-tripExpenseDate)/(1000*60*60*24))+1; } //console.log("returnSpreadArrayForGroupXEditablesBetweenExpenseDateAndTripBoundaries: " // + " tripExpenseDate:" + tripExpenseDate // + " tripStartDate:" + tripStartDate // + " tripEndDate:" + tripEndDate // + " nb days left:" + nb_days_before_end_trip); // Generate the dropdown options for (var i=0; i 3 ? j % 3 : 0; return sign + (j ? i.substr(0, j) + thouSep : "") + i.substr(j).replace(/(\decSep{3})(?=\decSep)/g, "$1" + thouSep) + (decPlaces ? decSep + Math.abs(number - i).toFixed(decPlaces).slice(2) : ""); } // Default callback function to customize the legend // TODO: Is this still in use? chartjs_legendCallbackFunctionDefault(chart) { // console.log(chart); // Return the HTML string here. var text = []; text.push(''); return text.join(''); } // Generic callback function to customize the legend // Note if the ChartJS Options config is updated with // a extraMenuItem object then an additional item on the legend will be added // at the end. This object should have the following elements // attribute: "description" // number where to position the line // attribute: "lineColor" // css color value for the line // attribute: "textColor" // css color value for the text of the legend // (the background will match 'lineColor') // attribute: "lineStyle" // css borderStyle value { "solid", "dashes" ... } // attribute: "lineDirection" // line direction { "HORIZONTAL", "VERTICAL" } chartjs_legendCallbackFunctionGeneric(chart) { // Return the HTML string here. var text = []; text.push(''); } else { //console.log("chartjs_legendCallbackFunctionGeneric - NOT IN IF"); } //console.log(text); return text.join(''); }; // Function used to draw an vertical line // The chartsJS Options array need to be updated // with proper extraMenuItem object that should includes: // attribute: "lineAtIndex" // number where to position the line // attribute: "lineColor" // css color value // attribute: "lineStyle" // css borderStyle value { "solid", "dashes" ... } // attribute: "lineDirection" // line direction { "HORIZONTAL", "VERTICAL" } chartjs_drawVerticalOrHorizontalLine () { originalLineDraw.apply(this, arguments); var chart = this.chart; var ctx = chart.chart.ctx; if (chart.config.options.extraMenuItem) { for (var i=0; i< chart.config.options.extraMenuItem.length; i++) { var index = chart.config.options.extraMenuItem[i].lineAtIndex; var lineColor = chart.config.options.extraMenuItem[i].lineColor; var lineStyle = chart.config.options.extraMenuItem[i].lineStyle; var lineDirection = chart.config.options.extraMenuItem[i].lineDirection; if (index) { var xaxis = chart.scales['x-axis-0']; var yaxis = chart.scales['y-axis-0']; if (lineDirection && lineDirection.toUpperCase() == "VERTICAL") { var x1 = xaxis.getPixelForValue(index); var y1 = yaxis.top; var x2 = xaxis.getPixelForValue(index); var y2 = yaxis.bottom; } else { // Assuming "HORIZONTAL" var x1 = xaxis.left; var y1 = yaxis.getPixelForValue(index); var x2 = xaxis.right; var y2 = yaxis.getPixelForValue(index); } ctx.save(); ctx.beginPath(); ctx.moveTo(x1, y1); ctx.strokeStyle = lineColor; if (lineStyle && lineStyle == "dashed") { ctx.setLineDash([10, 4]); } ctx.lineTo(x2, y2); ctx.stroke(); ctx.restore(); } } } } // Populate an amcharts v4 Map // Scope is the page scope (include data) // Data is the data coming from the backend amcharts_populateMapGeneric($scope, displayCountries, showFlightRoute) { am4core.ready(function() { // Themes begin am4core.useTheme(am4themes_dataviz); am4core.useTheme(am4themes_animated); // Themes end // Create map instance var chart = am4core.create("chartdiv", am4maps.MapChart); // Set map definition // chart.geodata = am4geodata_worldLow; chart.geodata = am4geodata_worldHigh; // Set projection chart.projection = new am4maps.projections.Miller(); /* ************ */ /* Entire World */ /* ************ */ var worldSeries = chart.series.push(new am4maps.MapPolygonSeries()); worldSeries.name = "Rest of the world"; worldSeries.exclude = ["AQ"]; worldSeries.useGeodata = true; worldSeries.fill = am4core.color("#d9d9d9"); // Process each dataseries for (var i = 0; i < $scope.worldmapData.length; i++) { // Processing series of data var dataSeries = $scope.worldmapData[i]; /* ***************** */ /* Visited Countries */ /* ***************** */ if (displayCountries) { var series1 = chart.series.push(new am4maps.MapPolygonSeries()); series1.name = "Visited Contries"; series1.useGeodata = true; series1.include = dataSeries.worldmap_countryList; series1.mapPolygons.template.tooltipText = "{name}"; series1.mapPolygons.template.fill = am4core.color("#96BDC6"); series1.fill = am4core.color("#96BDC6"); } /* ****************************************************** */ /* Add a dot for each visited cities (using GPS Location) */ /* ****************************************************** */ // Create image series var imageSeries = chart.series.push(new am4maps.MapImageSeries()); imageSeries.name = dataSeries.worldmap_serie_legend; imageSeries.fill = am4core.color(dataSeries.worldmap_serie_color_hexvalue); // Create a circle image in image series template so it gets replicated to all new images var imageSeriesTemplate = imageSeries.mapImages.template; var circle = imageSeriesTemplate.createChild(am4core.Circle); circle.radius = 6; circle.fill = am4core.color(dataSeries.worldmap_serie_color_hexvalue); circle.stroke = am4core.color("#FFFFFF"); circle.strokeWidth = 3; circle.nonScaling = true; circle.tooltipText = "Cost Of Living in {city} | (Data sourced from {source_title})"; // Fallback tooltip if HTML is not supported circle.tooltipHTML = `
Cost Of Living in {city}
`; for (var j = 0; j < dataSeries.worldmap_number_of_cost_sources; j++) { circle.tooltipHTML += ``; } circle.tooltipHTML += "
{source_` + (j+1) + `_name} {source_` + (j+1) + `_value} {source_` + (j+1) + `_valueSuffix}
(Data sourced from {source_title})
"; imageSeries.tooltip.keepTargetHover = true; // Allow for selection of tooltip content imageSeries.tooltip.label.interactionsEnabled = true; // Allow for click events //imageSeries.tooltip.getFillFromObject = false; // Do not fill up the tooltip with the main color // Set property fields imageSeriesTemplate.propertyFields.latitude = "latitude"; imageSeriesTemplate.propertyFields.longitude = "longitude"; // Add data for the cities imageSeries.data = dataSeries.worldmap_cityGPSCoords_withName; // Add heat map data // FIXME - https://www.amcharts.com/docs/v4/tutorials/mapchart-with-bubbles-and-labels/ //imageSeries.heatRules.push({ // "target": circle, // "property": "radius", // "min": 4, // "max": 30, // "dataField": "source_1_value" //}) /* Track of Visited cities per GPS Location */ /* **************************************** */ if (showFlightRoute == true) { // Create a line series to map the route of each cities visited var lineSeries = chart.series.push(new am4maps.MapLineSeries()); lineSeries.name = "Voyage route"; lineSeries.fill = am4core.color("#3e96e0"); lineSeries.mapLines.template.strokeWidth = 2; lineSeries.mapLines.template.stroke = am4core.color("#3e96e0"); lineSeries.mapLines.template.nonScalingStroke = true; lineSeries.mapLines.template.shortestDistance = true; // False will provide a straight line, true a curved line. var line = lineSeries.mapLines.create(); line.multiGeoLine = [dataSeries.worldmap_cityGPSCoords_withoutName]; // Add a bullet item to connect each city on the route var bullet = line.lineObjects.create(); bullet.nonScaling = true; bullet.position = 0.5; bullet.width = 48; bullet.height = 48; bullet.horizontalCenter = "middle"; bullet.verticalCenter = "middle"; // Add a plante on the route var plane = bullet.createChild(am4core.Sprite); plane.scale = 0.15; plane.path = "m2,106h28l24,30h72l-44,-133h35l80,132h98c21,0 21,34 0,34l-98,0 -80,134h-35l43,-133h-71l-24,30h-28l15,-47"; plane.fill = am4core.color("#ffa500"); plane.strokeOpacity = 0; // Animate the plane function goPlane() { bullet.animate({ from: 0, to: 1, property: "position" }, 30000, am4core.ease.sinInOut); } // Start the plane goPlane(); } } /* Add a legend to the graph */ /*****************************/ chart.legend = new am4maps.Legend(); chart.legend.position = "left"; chart.legend.align = "left"; /* Add zoom controls */ /*********************/ chart.zoomControl = new am4maps.ZoomControl(); // Add home button var button = chart.chartContainer.createChild(am4core.Button); button.padding(5, 5, 5, 5); button.align = "right"; button.marginRight = 15; button.events.on("hit", function() { chart.goHome(); }); button.icon = new am4core.Sprite(); button.icon.path = "M16,8 L14,8 L14,16 L10,16 L10,10 L6,10 L6,16 L2,16 L2,8 L0,8 L8,0 L16,8 Z M16,8"; /* Add watermark */ /*****************/ // TBD - https://www.amcharts.com/docs/v4/tutorials/adding-watermarks-to-charts/ // Indicator for the UI to show that the graph has been rendered $scope.graphHasBeenRendered = true; }); // end am4core.ready() } // Populate an amcharts v5 Stacked Column Chart amchartsv5_generateStackedColumnChart(chartBackendData, chartBackendCategories) { am5.ready(function() { // Create root element // https://www.amcharts.com/docs/v5/getting-started/#Root_element var root = am5.Root.new("chartdiv"); // Custom theme adding +30 colors var myTheme = am5.Theme.new(root); myTheme.rule("ColorSet").set("colors", [ // Additional elegant colors am5.color("#9E9E9E"), // Gray am5.color("#00BCD4"), // Cyan am5.color("#FFEB3B"), // Yellow am5.color("#673AB7"), // Deep Purple am5.color("#4CAF50"), // Green am5.color("#FF5722"), // Orange am5.color("#F44336"), // Red am5.color("#2196F3"), // Blue am5.color("#FFC107"), // Amber am5.color("#E91E63"), // Pink am5.color("#8BC34A"), // Light Green am5.color("#9C27B0"), // Purple am5.color("#3F51B5"), // Indigo am5.color("#FF9800"), // Deep Orange am5.color("#607D8B"), // Blue Gray am5.color("#03A9F4"), // Light Blue am5.color("#9E9E9E"), // Gray am5.color("#607D8B"), // Blue Gray am5.color("#009688"), // Teal am5.color("#FFEB3B"), // Yellow am5.color("#FFC107"), // Amber am5.color("#795548"), // Brown am5.color("#00BCD4"), // Cyan am5.color("#F57C00"), // Dark Orange am5.color("#FF9800"), // Deep Orange am5.color("#8BC34A"), // Light Green am5.color("#E91E63"), // Pink am5.color("#4CAF50"), // Green am5.color("#607D8B"), // Blue Gray am5.color("#673AB7"), // Deep Purple am5.color("#2196F3"), // Blue am5.color("#FF5722"), // Orange am5.color("#9C27B0"), // Purple am5.color("#FFC107"), // Amber am5.color("#FFEB3B"), // Yellow am5.color("#F44336"), // Red am5.color("#9E9E9E"), // Gray ]); // Set themes // https://www.amcharts.com/docs/v5/concepts/themes/ root.setThemes([ am5themes_Animated.new(root), myTheme ]); // Create chart // https://www.amcharts.com/docs/v5/charts/xy-chart/ var chart = root.container.children.push(am5xy.XYChart.new(root, { panX: false, panY: false, wheelX: false, wheelY: false, layout: root.verticalLayout })); // Add scrollbar // https://www.amcharts.com/docs/v5/charts/xy-chart/scrollbars/ //chart.set("scrollbarX", am5.Scrollbar.new(root, { // orientation: "horizontal" //})); // Create axes // https://www.amcharts.com/docs/v5/charts/xy-chart/axes/ var yRenderer = am5xy.AxisRendererY.new(root, {}); var yAxis = chart.yAxes.push(am5xy.CategoryAxis.new(root, { categoryField: "voyageName", renderer: yRenderer, tooltip: am5.Tooltip.new(root, {}) })); yRenderer.grid.template.setAll({ location: 1 }) // Getting data from backend var data = chartBackendData; yAxis.data.setAll(data); var xAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, { min: 0, renderer: am5xy.AxisRendererX.new(root, { strokeOpacity: 0.1 }) })); // Add legend // https://www.amcharts.com/docs/v5/charts/xy-chart/legend-xy-series/ var legend = chart.children.push(am5.Legend.new(root, { centerX: am5.p50, x: am5.p50, layout: am5.GridLayout.new(root, { maxColumns: 4, fixedWidthGrid: false }), height: am5.percent(20), verticalScrollbar: am5.Scrollbar.new(root, { orientation: "vertical" }) })); legend.markers.template.setAll({ width: 8, height: 8 }); // Add series // https://www.amcharts.com/docs/v5/charts/xy-chart/series/ function makeSeries(name, fieldName) { var series = chart.series.push(am5xy.ColumnSeries.new(root, { name: name, stacked: true, xAxis: xAxis, yAxis: yAxis, baseAxis: yAxis, valueXField: fieldName, categoryYField: "voyageName" })); series.columns.template.setAll({ tooltipText: "{name}: {valueX}", tooltipY: am5.percent(10) }); series.data.setAll(data); // Make stuff animate on load // https://www.amcharts.com/docs/v5/concepts/animations/ series.appear(); series.bullets.push(function() { return am5.Bullet.new(root, { sprite: am5.Label.new(root, { text: "{valueY}", fill: root.interfaceColors.get("alternativeText"), centerY: am5.p50, centerX: am5.p50, populateText: true }) }); }); legend.data.push(series); } for (var i = 0; i < chartBackendCategories.length; i++) { var categoryData = chartBackendCategories[i]; var categoryName = categoryData['name']; var categoryKey = categoryData['key']; // Example - makeSeries("Visas", "Visas"); makeSeries(categoryName, categoryKey); } // Make stuff animate on load // https://www.amcharts.com/docs/v5/concepts/animations/ chart.appear(1000, 100); }); // end am5.ready() } // Populate an amcharts v5 Map // Scope is the page scope (include data) // Data is the data coming from the backend amchartsv5_populateMapGeneric($scope, displayCountries, showFlightRoute) { am5.ready(function() { //console.log("In amchartsv5_populateMapGeneric... displayCountries=" + displayCountries // + " | showFlightRoute=" + showFlightRoute); /* ********** */ /* Create Map */ /* ********** */ // Create root element // https://www.amcharts.com/docs/v5/getting-started/#Root_element var root = am5.Root.new("chartdiv"); // Set themes // https://www.amcharts.com/docs/v5/concepts/themes/ root.setThemes([ am5themes_Animated.new(root), am5themes_Dataviz.new(root) ]); // Create the map chart // https://www.amcharts.com/docs/v5/charts/map-chart/ var chart = root.container.children.push(am5map.MapChart.new(root, { panX: "rotateX", panY: "translateY", projection: am5map.geoNaturalEarth1(), homeZoomLevel: 1, homeGeoPoint: { longitude: 2, latitude: 2 } })); // Enable zoom controls chart.set("zoomControl", am5map.ZoomControl.new(root, { })); // Set clicking on "water" to zoom out chart.chartContainer.get("background").events.on("click", function () { chart.goHome(); }) /* ************ */ /* Entire World */ /* ************ */ // Add legend var legend = chart.children.push(am5.Legend.new(root, { layout: root.verticalLayout, useDefaultMarker: true, y: am5.percent(50), centerY: am5.percent(50), background: am5.RoundedRectangle.new(root, { fill: am5.color(0xffffff), fillOpacity: 0.2 }) })); // Create main polygon series to draw ALL countries // https://www.amcharts.com/docs/v5/charts/map-chart/map-polygon-series/ var backgroundMapPolygonSeries = chart.series.push(am5map.MapPolygonSeries.new(root, { geoJSON: am5geodata_worldLow, exclude: ["AQ"], fill: am5.color(0xd9d9d9), stroke: am5.color(0xffffff), name: "Rest of the world", })); // Push data to legend legend.data.push(backgroundMapPolygonSeries); // Override the theme colors with the user data colors var themeColorArray = []; for (let x = 0; x < $scope.worldmapData.length; x++) { // Extract data from backend const dataSeries = $scope.worldmapData[x]; // We transform a string from backend in format "#ababab" to an hexadecimal value 0xababab //var seriesHexColor = parseInt(dataSeries.worldmap_serie_color_hexvalue.replace("#", ""), 16); var seriesHexColor = dataSeries.worldmap_serie_color_hexvalue; themeColorArray.push(am5.color(seriesHexColor)); } chart.set("colors", themeColorArray); // Iterate over each series of maps // 1 in the case of the voyage world map // 1 to many in the case of the discover page for (let x = 0; x < $scope.worldmapData.length; x++) { //console.log("In for loop for x=" + x); // Extract data from backend const dataSeries = $scope.worldmapData[x]; const cityData = dataSeries.worldmap_cityGPSCoords_withName; /* ***************** */ /* Visited Countries */ /* ***************** */ if (displayCountries === true && dataSeries.worldmap_countryList) { //console.log("In Visited Countries..."); var visitedCountriesPolygonSeries = chart.series.push( am5map.MapPolygonSeries.new(root, { geoJSON: am5geodata_worldLow, include: dataSeries.worldmap_countryList, fill: am5.color(0x96bdc6), // hard-codding light blue here - assuming only one iteration here name: "Visited Countries" }) ); // Push data to legend legend.data.push(visitedCountriesPolygonSeries); } if (showFlightRoute === true) { //console.log("In Show Flight Route..."); // Create line series to connect each city visited // Note: we want to add the line before the points so they fit underneath // https://www.amcharts.com/docs/v5/charts/map-chart/map-line-series/ var lineSeries = chart.series.push(am5map.MapLineSeries.new(root, { fill: am5.color(0x3e96e0), // Hardcodded to light blue stroke: am5.color(0x3e96e0), // Hardcodded to light blue strokeWidth: 5, name: "Voyage Route" })); // Push data to legend legend.data.push(lineSeries); } /* ************** */ /* Visited Cities */ /* ************** */ // Create point series for each cities visited // https://www.amcharts.com/docs/v5/charts/map-chart/map-point-series/ var pointSeries = chart.series.push(am5map.MapPointSeries.new(root, { tooltip: am5.Tooltip.new(root, { keepTargetHover: true }), name: dataSeries.worldmap_serie_legend, fill: chart.get("colors")[x], stroke: am5.color(0xffffff), } )); // Push data to legend legend.data.push(pointSeries); // Make the tooltip interactive (render HTML links) pointSeries.get("tooltip").label.set("interactive", true); // Format each cities points pointSeries.bullets.push(function() { var tooltipText = "Cost Of Living in {data.city} | (Data sourced from {data.source_title})"; // Fallback tooltip if HTML is not supported var tooltipHTML = `
Cost Of Living in {data.city}
`; for (let j = 0; j < dataSeries.worldmap_number_of_cost_sources; j++) { tooltipHTML += ""; } tooltipHTML += "
{data.source_" + (j+1) + "_name} {data.source_" + (j+1) + "_value} {data.source_" + (j+1) + "_valueSuffix}
(Data sourced from {data.source_title})
"; //console.log("TooltipHTML=" + tooltipHTML); //console.log("In bullets.push function with seriesHexColor=" + seriesHexColor); var circle = am5.Circle.new(root, { radius: 5, cursorOverStyle: "pointer", tooltipY: 0, fill: chart.get("colors")[x], fillOpacity: 1, stroke: root.interfaceColors.get("background"), strokeWidth: 1, draggable: false, tooltipHTML: tooltipHTML }); return am5.Bullet.new(root, { sprite: circle, }); }); /* ********************* */ /* Adding Cities Bullets */ /* ********************* */ // List of city from first to last var cityList = []; for (let i = 0; i < cityData.length - 1; i++) { const city = cityData[i]; const nextCity = cityData[i + 1]; const cityPoint = addCity({ latitude: city.latitude, longitude: city.longitude }, city); const nextCityPoint = addCity({ latitude: nextCity.latitude, longitude: nextCity.longitude }, nextCity.city); cityList.push(cityPoint, nextCityPoint); //console.log("add lat=" + city.latitude + " long=" + city.longitude + " data=", city); } /* ***************** */ /* Connecting Cities */ /* ***************** */ if (showFlightRoute === true) { var lineDataItem = lineSeries.pushDataItem({ pointsToConnect: cityList, fill: am5.color(0x3e96e0) }); } // Function to add a city point on the map // Based on its longitude/latitude // function addCity(coords, data) { return pointSeries.pushDataItem({ geometry: { type: "Point", coordinates: [coords.longitude, coords.latitude] }, data: data }); } /* ********************** */ /* Adding Plane animation */ /* ********************** */ if (showFlightRoute === true) { var planeSeries = chart.series.push(am5map.MapPointSeries.new(root, {})); var plane = am5.Graphics.new(root, { svgPath: "m2,106h28l24,30h72l-44,-133h35l80,132h98c21,0 21,34 0,34l-98,0 -80,134h-35l43,-133h-71l-24,30h-28l15,-47", scale: 0.10, centerY: am5.p50, centerX: am5.p50, fill: am5.color(0xffa500) }); planeSeries.bullets.push(function() { var container = am5.Container.new(root, {}); container.children.push(plane); return am5.Bullet.new(root, { sprite: container }); }); var planeDataItem = planeSeries.pushDataItem({ lineDataItem: lineDataItem, positionOnLine: 0, autoRotate: true }); // Create a new constant to store the animation duration (in ms) const animationDuration = 10000; // 10 seconds for plane travel const pauseDuration = 2000; // 2 seconds pause at the end planeDataItem.animate({ key: "positionOnLine", to: 1, duration: animationDuration, easing: am5.ease.linear, delay: pauseDuration, // delay added before animation starts, which serves as pause at the end of each animation cycle loops: Infinity }); planeDataItem.on("positionOnLine", function(value) { if (value <= 0.01) { plane.set("rotation", 0); } }); // Make stuff animate on load chart.appear(1000, 100); } } // Indicator for the UI to show that the graph has been rendered $scope.graphHasBeenRendered = true; }); // end am5.ready() } }; //Static Security class for the app class NomadNumbers_Security { constructor() { // Define app permission level this.auth_none = false; this.auth_authenticated = false; this.auth_admin = false; this.auth_editor = false; this.auth_viewer = false; this.auth_none = true; } }; //Static Constants class for the app class NomadNumbers_Constants { constructor() { this.EXPENSE_SPENDING_CATEGORIES = new Array(); this.EXPENSE_SPENDING_CATEGORIES_PERID = new Array(); var category; category = new Array(); category['categoryName'] = 'Accommodation'; category['categoryIconClass'] = 'bed'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything you pay toward keeping a roof over your head (ie. rent, internet, water, utilities...)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Accommodation'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['2'] = 'Accommodation'; category = new Array(); category['categoryName'] = 'Alcohol, Bars, Nightlife'; category['categoryIconClass'] = 'beer'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything related to alcoholic your purchase"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Alcohol, Bars, Nightlife'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['21'] = 'Alcohol, Bars, Nightlife'; category = new Array(); category['categoryName'] = 'Business'; category['categoryIconClass'] = 'briefcase'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "This is any business related expenses."; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Business'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['27'] = 'Business'; category = new Array(); category['categoryName'] = 'Cancelation'; category['categoryIconClass'] = 'ban'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "For any cost that you have been occuring due to a cancellation that was not fully refunded."; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Cancelation'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['25'] = 'Cancelation'; category = new Array(); category['categoryName'] = 'Babies, Childcare'; category['categoryIconClass'] = 'baby'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anyting related to your child"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Babies, Childcare'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['28'] = 'Babies, Childcare'; category = new Array(); category['categoryName'] = 'Clothing'; category['categoryIconClass'] = 'socks'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to Clothing"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Clothing'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['39'] = 'Clothing'; category = new Array(); category['categoryName'] = 'Data'; category['categoryIconClass'] = 'wifi'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything related to access data while on the go (phone, Internet, ...)."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Data'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['29'] = 'Data'; category = new Array(); category['categoryName'] = 'Dining Out'; category['categoryIconClass'] = 'utensils'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything related to what you spend when dining outside of your home."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Dining Out'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['4'] = 'Dining Out'; category = new Array(); category['categoryName'] = 'Self Improvement, Education'; category['categoryIconClass'] = 'graduation-cap'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "This is anything related to persoanal growth (classes, education...)."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Self Improvement, Education'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['26'] = 'Self Improvement, Education'; category = new Array(); category['categoryName'] = 'Personal Equipment, Electronics'; category['categoryIconClass'] = 'mobile'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to electronics)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Personal Equipment, Electronics'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['35'] = 'Personal Equipment, Electronics'; category = new Array(); category['categoryName'] = 'Entertainment (Books, Games, Hobbies)'; category['categoryIconClass'] = 'music'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything you pay related to Entertainment (like book your read, games you play, hobbies you have...)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Entertainment (Books, Games, Hobbies)'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['7'] = 'Entertainment (Books, Games, Hobbies)'; category = new Array(); category['categoryName'] = 'Food Delivery, Take Out, Street Food'; category['categoryIconClass'] = 'truck-loading'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything food you eat either delivered to you to taken while on the go (like while hiking, exploring a street/night market, ...)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Food Delivery, Take Out, Street Food'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['31'] = 'Food Delivery, Take Out, Street Food'; category = new Array(); category['categoryName'] = 'Donation, Charitable Giving'; category['categoryIconClass'] = 'heart'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any Charitable giving or Donation you are making to a third party."; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Donation, Charitable Giving'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['9'] = 'Donation, Charitable Giving'; category = new Array(); category['categoryName'] = 'Gifts'; category['categoryIconClass'] = 'gift'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to Gifting to someone"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Gifts'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['40'] = 'Gifts'; category = new Array(); category['categoryName'] = 'Groceries'; category['categoryIconClass'] = 'shopping-cart'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything related to the groceries you get to cook at home"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Groceries'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['5'] = 'Groceries'; category = new Array(); category['categoryName'] = 'Fitness, Gym, Workout'; category['categoryIconClass'] = 'dumbbell'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to Exercising / Fitness (gym, workout, ...)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Fitness, Gym, Workout'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['34'] = 'Fitness, Gym, Workout'; category = new Array(); category['categoryName'] = 'Health Care'; category['categoryIconClass'] = 'medkit'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any cost related to treatment you are receiving on a given location."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Health Care'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['12'] = 'Health Care'; category = new Array(); category['categoryName'] = 'Home Furnishing, Home Supplies, Home Maintenance'; category['categoryIconClass'] = 'mobile'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to improve and keeping your home in shape)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Home Furnishing, Home Supplies, Home Maintenance'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['36'] = 'Home Furnishing, Home Supplies, Home Maintenance'; category = new Array(); category['categoryName'] = 'Income - Business'; category['categoryIconClass'] = 'hand-holding-usd'; category['categoryIconColorClass'] = 'orangeiconcolor'; category['categoryDescription'] = "Any income related to your business"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = '1'; this.EXPENSE_SPENDING_CATEGORIES['Income - Business'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['100'] = 'Income - Business'; category = new Array(); category['categoryName'] = 'Income - Others'; category['categoryIconClass'] = 'hand-holding-usd'; category['categoryIconColorClass'] = 'orangeiconcolor'; category['categoryDescription'] = "Anything other source of income"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = '1'; this.EXPENSE_SPENDING_CATEGORIES['Income - Others'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['103'] = 'Income - Others'; category = new Array(); category['categoryName'] = 'Income - Real Estate Investments'; category['categoryIconClass'] = 'hand-holding-usd'; category['categoryIconColorClass'] = 'orangeiconcolor'; category['categoryDescription'] = "Any income related to real estate (rent...)"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = '1'; this.EXPENSE_SPENDING_CATEGORIES['Income - Real Estate Investments'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['101'] = 'Income - Real Estate Investments'; category = new Array(); category['categoryName'] = 'Income - Refund'; category['categoryIconClass'] = 'hand-holding-usd'; category['categoryIconColorClass'] = 'orangeiconcolor'; category['categoryDescription'] = "Any refund you may received"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = '1'; this.EXPENSE_SPENDING_CATEGORIES['Income - Refund'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['104'] = 'Income - Refund'; category = new Array(); category['categoryName'] = 'Income - Financial Market'; category['categoryIconClass'] = 'hand-holding-usd'; category['categoryIconColorClass'] = 'orangeiconcolor'; category['categoryDescription'] = "Any income related to the financial market (interest, dividends, capital gain...)"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = '1'; this.EXPENSE_SPENDING_CATEGORIES['Income - Financial Market'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['102'] = 'Income - Financial Market'; category = new Array(); category['categoryName'] = 'International Health Insurance'; category['categoryIconClass'] = 'universal-access'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "This is health insurance that your purchase to get cover through multiple trips."; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['International Health Insurance'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['14'] = 'International Health Insurance'; category = new Array(); category['categoryName'] = 'Intercity Transportation'; category['categoryIconClass'] = 'train'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything realted to transportation to go from one city to another."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Intercity Transportation'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['18'] = 'Intercity Transportation'; category = new Array(); category['categoryName'] = 'International Transportation'; category['categoryIconClass'] = 'plane'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything realted to transportation to go from one country to another."; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['International Transportation'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['1'] = 'International Transportation'; category = new Array(); category['categoryName'] = 'Living Expenses'; category['categoryIconClass'] = 'receipt'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "This is a catch-all category for anything else that you are spending money on that is specific to your current location and that can't fit anywhere else"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Living Expenses'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['8'] = 'Living Expenses'; category = new Array(); category['categoryName'] = 'Local Health Insurance'; category['categoryIconClass'] = 'universal-access'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "This is health insurance that your purchase to get cover on a given location."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Local Health Insurance'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['24'] = 'Local Health Insurance'; category = new Array(); category['categoryName'] = 'Local Transportation'; category['categoryIconClass'] = 'bus'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything realted to transportation within the boundary of the location you are staying at."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Local Transportation'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['3'] = 'Local Transportation'; category = new Array(); category['categoryName'] = 'Mortgage'; category['categoryIconClass'] = 'home'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "This is to capture any mortgage payments you might still be making while traveling."; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Mortgage'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['15'] = 'Mortgage'; category = new Array(); category['categoryName'] = 'Non-Living Expenses'; category['categoryIconClass'] = 'receipt'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "This is a catch-all category for anything else that you are spending money on that is not specific to a location that can't fit anywhere else"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Non-Living Expenses'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['22'] = 'Non-Living Expenses'; category = new Array(); category['categoryName'] = 'Personal Care'; category['categoryIconClass'] = 'spa'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything related to your own personal care (beauty products, toileteries, haircut, ...)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Personal Care'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['32'] = 'Personal Care'; category = new Array(); category['categoryName'] = 'Pets'; category['categoryIconClass'] = 'paw'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything related to your pet(s)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Pets'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['16'] = 'Pets'; category = new Array(); category['categoryName'] = 'Professional Services'; category['categoryIconClass'] = 'user-tie'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any people you hire in exchange for a service performed locally (cleaner, local tax accountant, local lawyer, consulting services, payroll services...)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Professional Services'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['30'] = 'Professional Services'; category = new Array(); category['categoryName'] = 'Professional Services (Non-Living cost)'; category['categoryIconClass'] = 'tie'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any people you hire in exchange for a service that should not be accounted as your cost of living (immigration services, international tax account, international lawyer, ...)"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Professional Services (Non-Living cost)'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['41'] = 'Professional Services (Non-Living cost)'; category = new Array(); category['categoryName'] = 'Real Estate (Insurance, Property Management Fees...)'; category['categoryIconClass'] = 'home'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to real estate"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Real Estate (Insurance, Property Management Fees...)'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['33'] = 'Real Estate (Insurance, Property Management Fees...)'; category = new Array(); category['categoryName'] = 'Recreation, Museums, Sightseeing, Tours'; category['categoryIconClass'] = 'torii-gate'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to recreation related activities (museum, sightseeing, tours...)"; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Recreation, Museums, Sightseeing, Tours'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['10'] = 'Recreation, Museums, Sightseeing, Tours'; category = new Array(); category['categoryName'] = 'Scam'; category['categoryIconClass'] = 'frown-open'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "In case you got scam, track this using this category."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Scam'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['20'] = 'Scam'; category = new Array(); category['categoryName'] = 'Cafe, Coffee Shops, Sweets, Snacks'; category['categoryIconClass'] = 'carrot'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything related to treats / food your purchase at cafes (coffee, tea, ice cream, croissants, ...)."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Cafe, Coffee Shops, Sweets, Snacks'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['6'] = 'Cafe, Coffee Shops, Sweets, Snacks'; category = new Array(); category['categoryName'] = 'Subscriptions (recurring)'; category['categoryIconClass'] = 'newspaper'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to recurring subsctiptions (newspaper, online services, ...)"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Subscriptions (recurring)'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['38'] = 'Subscriptions (recurring)'; category = new Array(); category['categoryName'] = 'Taxes'; category['categoryIconClass'] = 'gavel'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any taxes you are paying. (ie. federal, state, property taxes)"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Taxes'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['17'] = 'Taxes'; category = new Array(); category['categoryName'] = 'Travel Gear'; category['categoryIconClass'] = 'suitcase'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything you purchases to improve your travel that is not related to a given location."; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Travel Gear'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['13'] = 'Travel Gear'; category = new Array(); category['categoryName'] = 'Travel Insurance'; category['categoryIconClass'] = 'ambulance'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Anything related to travel insurrance."; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Travel Insurance'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['23'] = 'Travel Insurance'; category = new Array(); category['categoryName'] = 'Travel Rewards, Travel Miles'; category['categoryIconClass'] = 'credit-card'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any expense related to travel rewards or miles)"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Travel Rewards, Travel Miles'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['37'] = 'Travel Rewards, Travel Miles'; category = new Array(); category['categoryName'] = 'Uncategorized'; category['categoryIconClass'] = 'question'; category['categoryIconColorClass'] = 'blackiconcolor'; category['categoryDescription'] = "This category is a catch all for anything that doesn't have a category associated with"; category['isLivingExpense'] = ''; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Uncategorized'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['11'] = 'Uncategorized'; category = new Array(); category['categoryName'] = 'Visas'; category['categoryIconClass'] = 'passport'; category['categoryIconColorClass'] = 'greeniconcolor'; category['categoryDescription'] = "Any cost related to visa to stay within a given country."; category['isLivingExpense'] = '1'; category['isIncomeCategory'] = ''; this.EXPENSE_SPENDING_CATEGORIES['Visas'] = category; this.EXPENSE_SPENDING_CATEGORIES_PERID['19'] = 'Visas'; this.APP_VERSION = "1.14.0"; this.APP_VERSION_MAJOR = Number(1); this.APP_VERSION_MINOR = Number(14); this.APP_VERSION_BF = Number(0); } }; // Instantiate the classs to the global scope // This is automatically done upon importing the script NNUtils = new NomadNumbers_Utils(); NNSharedFunctions = new NomadNumbers_SharedFunctions(); NNConstants = new NomadNumbers_Constants(); NNSecurity = new NomadNumbers_Security();