diff --git a/README.md b/README.md index be8d5cf..3f76736 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AirlineSim Enhancement Suite -The AirlineSime Enhancement Suite (AES) offers a set of tools to help CEOs build their airlines. +The AirlineSim Enhancement Suite (AES) offers a set of tools to help CEOs build their airlines. ## Features @@ -18,7 +18,6 @@ Marcipanas wrote [a guide](https://docs.google.com/document/d/1hzMHb3hTBXSZNtuDK ## Installation Supported platforms: chromium based browsers (Chrome, Edge, etc) - Please follow [racsofp’s guide](https://forums.airlinesim.aero/t/manual-installation-of-the-ase-airlinesim-enhancement-suite-chrome-extension/24671) ## History @@ -34,3 +33,4 @@ Sources: the [original forum thread](https://forums.airlinesim.aero/t/introducin - Marcipanas for the original development - racsofp for their update and installation documentation - Robert73 for the updated manifest file +- Zoë Bijl for the continued development diff --git a/docs/pricingDataObjectExample.js b/docs/pricingDataObjectExample.js new file mode 100644 index 0000000..85ebc87 --- /dev/null +++ b/docs/pricingDataObjectExample.js @@ -0,0 +1,56 @@ +"20240403": { + "data": { + "C": { + "analysisPrice": 0, + "analysisPricePoint": 0, + "currentPrice": 467, + "currentPricePoint": 100, + "recommendation": 0, + "totalBkd": 0, + "totalCap": 0, + "useCurrentPrice": 0, + "valid": 0 + }, + "Cargo": { + "analysisPrice": 139, + "analysisPricePoint": 100, + "currentPrice": 139, + "currentPricePoint": 100, + "index": 39, + "newPrice": 128, + "newPriceChange": -8, + "newPricePoint": 92, + "recType": "bad", + "recommendation": "Drop High", + "totalBkd": 13, + "totalCap": 72, + "useCurrentPrice": 1, + "valid": 1 + }, + "F": { + "analysisPrice": 0, + "analysisPricePoint": 0, + "currentPrice": 859, + "currentPricePoint": 100, + "recommendation": 0, + "totalBkd": 0, + "totalCap": 0, + "useCurrentPrice": 0, + "valid": 0 + }, + "Y": { + "analysisPrice": 0, + "analysisPricePoint": 0, + "currentPrice": 203, + "currentPricePoint": 100, + "recommendation": 0, + "totalBkd": 0, + "totalCap": 0, + "useCurrentPrice": 0, + "valid": 0 + } + }, + "date": 20240403, + "pricingUpdated": 1, + "updateTime": "14:36 UTC" +} \ No newline at end of file diff --git a/extension/content_aircraftFlights.js b/extension/content_aircraftFlights.js index edc10fa..8ddf920 100644 --- a/extension/content_aircraftFlights.js +++ b/extension/content_aircraftFlights.js @@ -119,7 +119,7 @@ function displayFlightProfit(){ if(value.data){ td.push(formatMoney(value.data.money.CM5.Total)); - td.push($('').text(formatDate(value.data.date)+' '+value.data.time)); + td.push($('').text(AES.formatDateString(value.data.date)+' '+value.data.time)); } else { td.push(''); td.push(''); @@ -137,7 +137,7 @@ function buildTable(){ row.push($('').append('Total flights',''+aircraftFlightData.totalFlights+'')); row.push($('').append('Finished flights',''+aircraftFlightData.finishedFlights+'')); row.push($('').append('Finished flights with profit/loss extract',''+aircraftFlightData.profitFlights+'')); - row.push($('').append('Data save time',''+formatDate(aircraftFlightData.date)+' '+aircraftFlightData.time+'')); + row.push($('').append('Data save time',''+AES.formatDateString(aircraftFlightData.date)+' '+aircraftFlightData.time+'')); let tbody = $('').append(row); return $('
').append(tbody); @@ -146,7 +146,7 @@ function getData(){ //Aircraft ID let aircraftId = getAircraftId(); let aircraftInfo = getAircraftInfo(); - let date = getDate(); + let date = AES.getServerDate() let server = getServerName(); let flights = getFlights(); let flightsStats = getFlightsStats(flights); @@ -235,39 +235,6 @@ function formatMoney(value){ return container } -function formatDate(date){ - return date.substring(0, 4)+'-'+date.substring(4, 6)+'-'+date.substring(6, 8); -} -function getDate(){ - let a = $(".as-footer-line-element:has('.fa-clock-o')").text().trim(); - let b = a.split(" "); - //For date - let dateTemp = b[0].split("-"); - let date; - if(dateTemp.length == 1){ - //German - dateTemp = dateTemp[0].split("."); - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[2] + date[1] + date[0]; - } else { - //English - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[0] + date[1] + date[2]; - } - //For time - let time = b[b.length-2] +' '+b[b.length-1]; - return {date:date,time:time}; -} -function cleanInteger(a){ - a = a.replace(',',''); - a = a.replace('.',''); - a = a.replace(' AS$',''); - return parseInt(a, 10); -} function getServerName(){ let server = window.location.hostname server = server.split('.'); diff --git a/extension/content_dashboard.js b/extension/content_dashboard.js index 04f7c5e..cbfb700 100644 --- a/extension/content_dashboard.js +++ b/extension/content_dashboard.js @@ -1,2903 +1,2855 @@ -"use strict"; -//MAIN -//Global vars -var settings,airline,server,todayDate; -$(function(){ - todayDate = (getDate('today',0)); - airline = getAirlineCode(); - server = getServerName(); - chrome.storage.local.get(['settings'], function(result) { - settings = result.settings; - - displayDashboard(); - dashboardHandle(); - $("#aes-select-dashboard-main").change(function () { - dashboardHandle(); - }); - }); -}); -function displayDashboard(){ - let mainDiv = $("#enterprise-dashboard"); - mainDiv.before( - ` -

AirlineSim Enhancement Suite Dashboard

-
-
- - -
-
-
-
- ` - ); - $("#aes-select-dashboard-main").val(settings.general.defaultDashboard); -} -function dashboardHandle(){ - let value = $("#aes-select-dashboard-main").val(); - settings.general.defaultDashboard = value; - chrome.storage.local.set({settings: settings}, function() {}); - switch(value) { - case 'general': - displayGeneral(); - break; - case 'routeManagement': - displayRouteManagement(); - break; - case 'competitorMonitoring': - displayCompetitorMonitoring(); - break; - case 'hr': - displayHr(); - break; - case 'aircraftProfitability': - displayAircraftProfitability(); - break; - default: - displayDefault(); - } -} -//Route Management Dashbord -function displayRouteManagement(){ - //Check ROute Managemetn seetings - if(!settings.routeManagement){ - setDefaultRouteManagementSettings(); - } - - - let mainDiv = $("#aes-div-dashboard"); - //Build layout - mainDiv.empty(); - let title = $('

').text('Route Management'); - let div = $('
'); - mainDiv.append(title,div); - //Get schedule - let scheduleKey = server+airline.code+'schedule'; - chrome.storage.local.get([scheduleKey], function(result) { - let scheduleData = result[scheduleKey]; - if(scheduleData){ - // Table - generateRouteManagementTable(scheduleData); - - // Option buttons - let fieldsetEl = document.createElement("fieldset") - let legendEl = document.createElement("legend") - let buttonGroupEl = document.createElement("div") - - let buttonElements = { - "selectFirstTen": { - "label": "select first 10" - }, - "hideChecked": { - "label": "hide checked" - }, - "openInventory": { - "label": "open inventory (max 10)" - }, - "reloadTable": { - "label": "reload table" - } - } - - for (let key in buttonElements) { - let buttonObj = buttonElements[key] - let buttonEl = document.createElement("button") - let buttonClassNames = buttonObj?.classNames - let buttonType = buttonObj?.type - let buttonDefaultClassNames = "btn btn-default" - buttonEl.innerText = buttonObj.label - - if (buttonType) { - buttonEl.setAttribute("type", buttonType) - } else { - buttonEl.setAttribute("type", "button") - } - - if (buttonClassNames) { - buttonEl.className = buttonClassNames - } else { - buttonEl.className = buttonDefaultClassNames - } - - buttonObj.element = buttonEl - - buttonGroupEl.append(buttonEl) - } - - legendEl.innerText = "Options" - buttonGroupEl.classList.add("btn-group") - - fieldsetEl.append(legendEl, buttonGroupEl) - - let optionsDiv = $('
').append(fieldsetEl); - - // Button actions - - // Select first ten - buttonElements["selectFirstTen"].element.addEventListener("click", function(){ - let count = 0 - $('#aes-table-routeManagement tbody tr').each(function(){ - $(this).find("input").prop('checked', true); - count++; - if(count > 10){ - return false; - } - }) - }); - - // Remove checked - buttonElements["hideChecked"].element.addEventListener("click", function(){ - $('#aes-table-routeManagement tbody tr').has('input:checked').remove(); - }); - - // Open Inventory - buttonElements["openInventory"].element.addEventListener("click", function(){ - //Get checked collumns - let pages = $('#aes-table-routeManagement tbody tr').has('input:checked').map(function() { - let orgdest = $(this).attr('id'); - orgdest = orgdest.split("-"); - orgdest = orgdest[2]; - //let orgdest = $(this).find("td:eq(1)").text() + $(this).find("td:eq(2)").text(); - let url = 'https://'+server+'.airlinesim.aero/app/com/inventory/'+orgdest; - return url; - }).toArray(); - - //Open new tabs - for (let i = 0; i < pages.length; i++) { - window.open(pages[i], '_blank'); - if(i==10){ - break; - } - } - }); - - // Reload table reloadTable - buttonElements["reloadTable"].element.addEventListener("click", function(){ - generateRouteManagementTable(scheduleData); - }); - let divRow = $('
').append(optionsDiv,displayRouteManagementFilters(),displayRouteManagementCollumns()) - div.prepend(divRow); - //Collumns selector Checkbox listener - $('#aes-table-routeManagement-collumns input').change(function(){ - let show; - if(this.checked) { - show = 1; - } else { - show = 0; - } - let value = $(this).val(); - settings.routeManagement.tableCollumns.forEach(function(col){ - if(col.class == value){ - col.show = show; - } - }); - chrome.storage.local.set({settings: settings}, function(){}); - }); - - } else { - //no schedule - div.append("Need schedule info to show this section. Change Dashboard to General -> Schedule -> Extract Schedule.") - } - }); -} -function setDefaultRouteManagementSettings(){ - let collumns = [ - { - name:'Origin', - class:'aes-origin', - number:0, - show:1, - value:'origin' - }, - { - name:'Destination', - class:'aes-destination', - number:0, - show:1, - value:'destination' - }, - { - name:'Hub', - class:'aes-hub', - number:0, - show:1, - value:'hub' - }, - { - name:'OD', - class:'aes-od', - number:0, - show:1, - value:'odName' - }, - { - name:'Direction', - class:'aes-direction', - number:0, - show:1, - value:'direction' - }, - { - name:'# of flight numbers', - class:'aes-fltNr', - number:1, - show:1, - value:'fltNr' - }, - { - name:'PAX frequency', - class:'aes-paxFreq', - number:1, - show:1, - value:'paxFreq' - }, - { - name:'Cargo frequency', - class:'aes-cargoFreq', - number:1, - show:1, - value:'cargoFreq' - }, - { - name:'Total Frequency', - class:'aes-totalFreq', - number:1, - show:1, - value:'totalFreq' - }, - { - name:'Analysis date', - class:'aes-analysisDate', - number:0, - show:1 - }, - { - name:'Previous Analysis date', - class:'aes-analysisPreDate', - number:0, - show:1 - }, - { - name:'Pricing date', - class:'aes-pricingDate', - number:0, - show:1 - }, - { - name:'PAX load', - class:'aes-paxLoad', - number:1, - show:1 - }, - { - name:'PAX load Δ', - class:'aes-paxLoadDelta', - number:1, - show:1 - }, - { - name:'Cargo load', - class:'aes-cargoLoad', - number:1, - show:1 - }, - { - name:'Cargo load Δ', - class:'aes-cargoLoadDelta', - number:1, - show:1 - }, - { - name:'Total load', - class:'aes-load', - number:1, - show:1 - }, - { - name:'Total load Δ', - class:'aes-loadDelta', - number:1, - show:1 - }, - { - name:'PAX index', - class:'aes-paxIndex', - number:1, - show:1 - }, - { - name:'PAX index Δ', - class:'aes-paxIndexDelta', - number:1, - show:1 - }, - { - name:'Cargo index', - class:'aes-cargoIndex', - number:1, - show:1 - }, - { - name:'Cargo index Δ', - class:'aes-cargoIndexDelta', - number:1, - show:1 - }, - { - name:'Index', - class:'aes-index', - number:1, - show:1 - }, - { - name:'Index Δ', - class:'aes-indexDelta', - number:1, - show:1 - }, - { - name:'Route PAX index', - class:'aes-routeIndexPax', - number:1, - show:1 - }, - { - name:'Route Cargo index', - class:'aes-routeIndexCargo', - number:1, - show:1 - }, - { - name:'Route index', - class:'aes-routeIndex', - number:1, - show:1 - } - ]; - settings.routeManagement = { - tableCollumns:collumns, - filter:[] - }; -} -function routeManagementApplyFilter(){ - $('#aes-table-routeManagement tbody tr').each(function(){ - let row = this; - settings.routeManagement.filter.forEach(function(filter){ - let cell = $(row).find("."+filter.collumnCode).text(); - //if(cell){ - //Get collumn info if number or not - let number; - for(let i=0;i': - if(cell < value){ - $(row).remove(); - } - break; - case '<': - if(cell > value){ - $(row).remove(); - } - } - }); - }); -} -function displayRouteManagementFilters(){ - //Table head - let th = []; - th.push('Column'); - th.push('Operation'); - th.push('Value'); - th.push(''); - let thead= $('').append($('').append(th)); - - //Table body - let tbody = $(''); - settings.routeManagement.filter.forEach(function(fil){ - let td = []; - td.push(''+fil.collumn+''); - td.push(''+fil.operation+''); - td.push(''+fil.value+''); - td.push(''); - tbody.append($('').append(td)); - }); - - //Table foot - //select collumn - let option1 = []; - settings.routeManagement.tableCollumns.forEach(function(col){ - option1.push(''); - }); - let select1 = $('').append(option1); - - - - //Select value - let option = []; - option.push(''); - option.push(''); - option.push(''); - option.push(''); - let select = $('').append(option); - //Add button - let btn = $('').text('Add Row'); - btn.click(function(){ - let td = []; - let collumn = $(this).closest("tr").find('#aes-select-routeManagement-filter-collumn option:selected').text(); - let collumnVal = $(this).closest("tr").find('#aes-select-routeManagement-filter-collumn').val(); - let operation = $(this).closest("tr").find('#aes-select-routeManagement-filter-operation option:selected').text(); - let value = $(this).closest("tr").find('#aes-select-routeManagement-filter-value').val() - td.push(''+collumn+''); - td.push(''+operation+''); - td.push(''+value+''); - td.push(''); - - tbody.append($('').append(td)); - }); - - - //Footer rows - let tf = []; - tf.push($('').html(select1)); - tf.push($('').html(select)); - tf.push(''); - tf.push($('').append(btn)); - let tfoot= $('').append($('').append(tf)); - let table = $('
').append(thead,tbody,tfoot); - let divTable = $('
').append(table); - - - // - let saveBtn = $(''); - let saveSpan = $(''); - - //Closable legend - let link = $('').text('Filters'); - let legend = $('').html(link); - link.click(function(){ - divForAll.toggle(); - }); - - let divForAll = $('
').append(divTable,saveBtn,saveSpan); - let fieldset = $('
').append(legend,divForAll); - let div = $('
').append(fieldset); - - //Delete row for filter row - table.on("click", ".aes-a-routeManagement-filter-delete-row", function(){ - $(this).closest("tr").remove(); - }); - - //Save Button - saveBtn.click(function(){ - - saveSpan.removeClass().addClass('warning').text(' saving...'); - let filter = []; - $('#aes-table-routeManagement-filter tbody tr').each(function(){ - filter.push( - { - collumnCode:$(this).find('input').val(), - collumn:$(this).find('td:eq(0)').text(), - operation:$(this).find('td:eq(1)').text(), - value:$(this).find('td:eq(2)').text(), - } - ); - }); - settings.routeManagement.filter = filter; - chrome.storage.local.set({settings: settings}, function(){ - saveSpan.removeClass().addClass('warning').text(' filtering...'); - routeManagementApplyFilter() - saveSpan.removeClass().addClass('good').text(' done!'); - }); - }); - - - return div; -} -function displayRouteManagementCollumns(){ - //Table Head - let th = []; - th.push('Show'); - th.push('Column'); - let thead= $('').append($('').append(th)); - //Table body - let tbody= $(''); - - settings.routeManagement.tableCollumns.forEach(function(col){ - let td = []; - //Checkbox - if (col.show){ - td.push(''); - } else { - td.push(''); - } - //Name - td.push(''+col.name+''); - tbody.append($('').append(td)); - }); - - let table = $('
').append(thead,tbody); - let divTable = $('').append(table); - - //Closable legend - let link = $('').text('Columns'); - let legend = $('').html(link); - link.click(function(){ - $('#aes-div-routeManagement-collumns').toggle(); - }); - - let fieldset = $('
').append(legend,divTable); - let div = $('
').append(fieldset); - return div; -} -function generateRouteManagementTable(scheduleData){ - //Remove table - $('#aes-div-routeManagement').remove(); - //Dates - let dates = []; - for(let date in scheduleData.date){ - dates.push(date); - } - dates.reverse(); - //LatestSchedule - let schedule = scheduleData.date[dates[0]].schedule; - //Generate top - //Table headers - let collumns = settings.routeManagement.tableCollumns; - - //Generate table head - let thead = $(''); - let th = []; - //Check box - let checkbox = $(''); - checkbox.change(function () { - if(this.checked) { - $('#aes-table-routeManagement tbody tr').each(function(){ - $(this).find("input").prop('checked', true); - }); - } else { - $('#aes-table-routeManagement tbody tr').each(function(){ - $(this).find("input").prop('checked', false); - }); - } - }); - th.push($('').html(checkbox)); - collumns.forEach(function(col){ - if(col.show){ - let sort = $('').html(col.name); - sort.click(function(){ - routeManagementSortTable(col.class,col.number); - }); - th.push($('').html(sort)); - } - }); - //Add open inventory column - th.push($('Action')); - - thead.append($('').append(th)); - //Generate table rows - let tbody = $(''); - let uniqueOD = []; - schedule.forEach(function(od){ - //ODs for analysis - uniqueOD.push(od.od); - //Get values flight numbers and total frequency - let fltNr = 0; - let paxFreq = 0; - let cargoFreq = 0; - for (let flight in od.flightNumber){ - cargoFreq += od.flightNumber[flight].cargoFreq, - paxFreq += od.flightNumber[flight].paxFreq, - fltNr++; - } - let totalFreq = cargoFreq + paxFreq; - //hub - let hub = od.od.slice(0, 3); - let cellValue = { - origin:od.origin, - destination:od.destination, - odName:od.od, - direction:od.direction, - fltNr:fltNr, - paxFreq:paxFreq, - cargoFreq:cargoFreq, - totalFreq:totalFreq, - hub:hub - } - //Table cells - let cell = []; - //Checkbox - cell.push(''); - //Schedule - collumns.forEach(function(col){ - if(col.show){ - if(col.value){ - cell.push($('').addClass(col.class).text(cellValue[col.value])); - } else { - cell.push($('').addClass(col.class)); - } - } - }); - let rowId=od.origin+od.destination; - - //Add inventory button - let invBtn = 'Inventory' - cell.push($('').html(invBtn)); - - let row = $('').append(cell); - tbody.append(row); - }); - let table = $('
').append(thead,tbody); - let divTable = $('
').append(table); - $('#aes-div-dashboard-routeManagement').append(divTable) - //Analysis collumns - //Get unique ODs - uniqueOD = [...new Set(uniqueOD)]; - for(let i=0;i b) return -1; - if (a < b) return 1; - if (a = b) return 0; - }); - } else { - sorted.sort(); - } - let same = 1; - for(let i = 0; i < indexes.length; i++){ - if(indexes[i] !== sorted[i]){ - same = 0; - } - } - if(same){ - if(number){ - sorted.sort(function(a, b){ - if (a < b) return -1; - if (a > b) return 1; - if (a = b) return 0; - }); - } else { - sorted.reverse(); - } - } - for(let i=0; i= 0 ; j--){ - if(number){ - let value = parseInt($(tableRows[j]).find("."+collumn).text(),10); - if(!value){ - value = 0; - } - if(value == sorted[i]){ - tableBody.append($(tableRows[j])); - tableRows.splice(j, 1); - } - } else { - if($(tableRows[j]).find("."+collumn).text() == sorted[i]){ - tableBody.append($(tableRows[j])); - tableRows.splice(j, 1); - } - } - } - } -} -function updateRouteAnalysisCollumns(data,dates,routeIndex){ - - if(data){ - let rowId = '#aes-row-'+data.origin+data.destination; - - if(dates.analysis){ - //Analysis date - - $(rowId+' .aes-analysisDate').text(formatDate(dates.analysis)); - - - //Pricing date - if(dates.pricing){ - $(rowId+' .aes-pricingDate').text(formatDate(dates.pricing)); - } - - //Pax Load - $(rowId+' .aes-paxLoad').html(displayLoad(getRouteAnalysisLoad(data.date[dates.analysis].data,'pax'))); - //Cargo Load - $(rowId+' .aes-cargoLoad').html(displayLoad(getRouteAnalysisLoad(data.date[dates.analysis].data,'cargo'))); - //All Load - $(rowId+' .aes-load').html(displayLoad(getRouteAnalysisLoad(data.date[dates.analysis].data,'all'))); - - //PAX Index - $(rowId+' .aes-paxIndex').html(displayIndex(getRouteAnalysisIndex(data.date[dates.analysis].data,'pax'))); - //Cargo Index - $(rowId+' .aes-cargoIndex').html(displayIndex(getRouteAnalysisIndex(data.date[dates.analysis].data,'cargo'))); - //PAX Index - $(rowId+' .aes-index').html(displayIndex(getRouteAnalysisIndex(data.date[dates.analysis].data,'all'))); - - if(dates.analysisOneBefore){ - //Previous analysis date - $(rowId+' .aes-analysisPreDate').text(formatDate(dates.analysisOneBefore)); - - //Pax Load Delta - $(rowId+' .aes-paxLoadDelta').html(displayRouteAnalysisLoadDelta(data.date[dates.analysis].data,data.date[dates.analysisOneBefore].data,'pax')); - //Cargo Load Delta - $(rowId+' .aes-cargoLoadDelta').html(displayRouteAnalysisLoadDelta(data.date[dates.analysis].data,data.date[dates.analysisOneBefore].data,'cargo')); - //All Load Delta - $(rowId+' .aes-loadDelta').html(displayRouteAnalysisLoadDelta(data.date[dates.analysis].data,data.date[dates.analysisOneBefore].data,'all')); - - //PAX Index Delta - $(rowId+' .aes-paxIndexDelta').html(displayRouteAnalysisIndexDelta(data.date[dates.analysis].data,data.date[dates.analysisOneBefore].data,'pax')); - //Cargo Index Delta - $(rowId+' .aes-cargoIndexDelta').html(displayRouteAnalysisIndexDelta(data.date[dates.analysis].data,data.date[dates.analysisOneBefore].data,'cargo')); - //PAX Index Delta - $(rowId+' .aes-indexDelta').html(displayRouteAnalysisIndexDelta(data.date[dates.analysis].data,data.date[dates.analysisOneBefore].data,'all')); - - - } - - //Route Index - if(routeIndex.pax){ - $(rowId+' .aes-routeIndexPax').html(displayIndex(routeIndex.pax)); - } - if(routeIndex.cargo){ - $(rowId+' .aes-routeIndexCargo').html(displayIndex(routeIndex.cargo)); - } - if(routeIndex.all){ - $(rowId+' .aes-routeIndex').html(displayIndex(routeIndex.all)); - } - - - - - } - } - - - return; - let analysisDate = dates.analysis; - let pricingDate = dates.pricing; - - let paxLoad; - let paxLoadDelta ; - let cargoLoad ; - let cargoLoadDelta ; - let totalLoad ; - let totalLoadDelta; - - - - - - - outDates = getInvPricingAnalaysisPricingDate(dataOut.date); - if(outDates.analysis){ - $('#aes-row-invPricing-'+origin+dest+'-analysis',tbody).text(formatDate(outDates.analysis)); - outIndex = dataOut.date[outDates.analysis].routeIndex; - let td = $('#aes-row-invPricing-'+origin+dest+'-OWindex',tbody); - td.html(displayIndex(outIndex)); - if(outDates.analysisOneBefore){ - outIndexChange = dataOut.date[outDates.analysis].routeIndex - dataOut.date[outDates.analysisOneBefore].routeIndex - td.append(displayIndexChange(outIndexChange)); - } - } - if(outDates.pricing){ - $('#aes-row-invPricing-'+origin+dest+'-pricing',tbody).text(formatDate(outDates.pricing)); - } - -} -function displayRouteAnalysisLoadDelta(dataCurrent,dataPrevious,type){ - let load = getRouteAnalysisLoad(dataCurrent,type); - let preLoad = getRouteAnalysisLoad(dataPrevious,type); - if(load && preLoad){ - let diff = load - preLoad; - let span = $(''); - if(diff > 0){ - span.addClass('good').text('+'+diff+"%"); - return span; - } - if(diff < 0){ - span.addClass('bad').text(diff+"%"); - return span; - } - span.addClass('warning').text(diff+"%"); - return span; - } -} -function displayRouteAnalysisIndexDelta(dataCurrent,dataPrevious,type){ - let index = getRouteAnalysisIndex(dataCurrent,type); - let preIndex = getRouteAnalysisIndex(dataPrevious,type); - if(index && preIndex){ - let diff = index - preIndex; - let span = $(''); - if(diff > 0){ - span.addClass('good').text('+'+diff); - return span; - } - if(diff < 0){ - span.addClass('bad').text(diff); - return span; - } - span.addClass('warning').text(diff); - return span; - } -} -function getRouteAnalysisLoad(data,type){ - let cmp = []; - switch(type) { - case 'all': - cmp = ['Y','C','F','Cargo']; - break; - case 'pax': - cmp = ['Y','C','F']; - break; - case 'cargo': - cmp = ['Cargo']; - break; - default: - // code block - } - let cap,bkd; - cap = bkd = 0; - cmp.forEach(function(comp){ - if(data[comp].valid){ - cap += data[comp].totalCap; - bkd += data[comp].totalBkd; - } - }); - if(cap){ - return Math.round(bkd/cap*100); - } else { - return 0; - } -} -function displayLoad(load){ - if(load){ - let span = $(''); - if(load >= 70){ - span.addClass('good').text(load+"%"); - return span; - } - if(load < 40){ - span.addClass('bad').text(load+"%"); - return span; - } - span.addClass('warning').text(load+"%"); - return span; - } -} -function getRouteAnalysisIndex(data,type){ - let cmp = []; - let index=0; - switch(type) { - case 'all': - cmp = ['Y','C','F','Cargo']; - break; - case 'pax': - cmp = ['Y','C','F']; - break; - case 'cargo': - cmp = ['Cargo']; - break; - default: - cmp = 0; - break; - } - if(cmp){ - //Multi index - let count = 0; - cmp.forEach(function(comp){ - if(data[comp].valid){ - index += data[comp].index; - count++; - } - }); - if(index){ - return Math.round(index/count); - } - } -} -function getRouteAnalysisImportantDates(dates){ - //Get latest analysis and pricing date - let latest = { - analysis:0, - pricing:0, - analysisOneBefore:0, - pricingOneBefore:0 - } - let analysisDates = []; - let pricingDates = [] - for(let date in dates) { - if(dates[date].pricingUpdated){ - pricingDates.push(date); - } - analysisDates.push(date); - } - analysisDates.reverse(); - pricingDates.reverse(); - if(analysisDates.length){ - latest.analysis = analysisDates[0]; - if(analysisDates[1]){ - latest.analysisOneBefore = analysisDates[1]; - } - } - if(pricingDates.length){ - latest.pricing = pricingDates[0]; - if(pricingDates[1]){ - latest.pricingOneBefore = pricingDates[1]; - } - } - return latest; -} -function displayIndex(index){ - let span = $(''); - if(index >= 90){ - return span.addClass('good').text(index); - } - if(index <= 50 ){ - return span.addClass('bad').text(index); - } - return span.addClass('warning').text(index); -} -function displayIndexChange(index){ - if(index > 0){ - return ' (+'+index+')'; - } - if(index < 0){ - return ' ('+index+')'; - } - return ' ('+index+')'; -} -//Display General -function displayGeneral(){ - let mainDiv = $("#aes-div-dashboard"); - mainDiv.empty(); - - //Table - //Head cells - let th1 = $('Area'); - let th2 = $('Status'); - let th3 = $('Action'); - let headRow = $('').append(th1,th2,th3); - let thead = $('').append(headRow); - //Body cells - let tbody = $(''); - generalAddScheduleRow(tbody); - generalAddPersonelManagementRow(tbody); - - - let table = $('
').append(thead,tbody); - //Build layout - let divTable = $('
').append(table); - let title = $('

').text('General'); - let div = $('
').append(divTable); - mainDiv.append(title,div); -} -//Display COmpetitor Monitoring -function displayCompetitorMonitoring(){ - //Div - let div = $('
'); - - //Check ROute Managemetn seetings - // - if(!settings.competitorMonitoring){ - setDefaultCompetitorMonitoringSettings(); - } - - //Display airlines table - displayCompetitorMonitoringAirlinesTable(div); - - let mainDiv = $("#aes-div-dashboard"); - //Build layout - mainDiv.empty(); - let title = $('

').text('Competitor Monitoring'); - mainDiv.append(title,div); - -} -function displayCompetitorMonitoringAirlinesTable(div){ - let compAirlines = []; - let compAirlinesSchedule = []; - chrome.storage.local.get(null, function(items) { - //Get data - for(let key in items){ - if(items[key].type){ - if(items[key].type == 'competitorMonitoring'){ - if(items[key].server == server){ - if(items[key].tracking){ - compAirlines.push(items[key]); - } - - } - } - if(items[key].type == 'schedule'){ - if(items[key].server == server){ - let airline = items[key].airline - compAirlinesSchedule[airline] = items[key]; - } - } - } - } - - //Check if any airlines exist - let rows = []; - let hrows = []; - if(compAirlines.length){ - //head - //second head collumns - let firstHead = {}; - let th = []; - settings.competitorMonitoring.tableColumns.forEach(function(col){ - if(col.visible){ - //Sort - let sort = $('').html(col.text); - sort.click(function(){ - CompetitorMonitoringSortTable(col.field,col.number); - }); - th.push($('').html(sort)); - if(firstHead[col.headGroup]){ - firstHead[col.headGroup]++; - } else { - firstHead[col.headGroup] = 1; - } - } - }); - //first head - let th1 = []; - for(let titles in firstHead){ - th1.push($('').text(titles)); - } - hrows.push($('').append(th1)); - hrows.push($('').append(th)); - - //Data collumns - - compAirlines.forEach(function myFunction(value) { - let data = {}; - //Airline - data.airlineId = value.id; - //All Tab0 Collumns - let dates = []; - for(let date in value.tab0) { - dates.push(date); - } - dates.sort(function(a, b){return b-a}); - if(dates.length){ - data.airlineCode = value.tab0[dates[0]].code; - data.airlineName = value.tab0[dates[0]].displayName; - data.overviewDate = formatDate(dates[0]); - data.overviewRating = value.tab0[dates[0]].rating; - data.overviewTotalPax = value.tab0[dates[0]].pax; - data.overviewTotalCargo = value.tab0[dates[0]].cargo; - data.overviewStations = value.tab0[dates[0]].stations; - data.overviewFleet = value.tab0[dates[0]].fleet; - data.overviewStaff = value.tab0[dates[0]].employees; - //If previous date exists - if(dates[1]){ - data.overviewPreDate = formatDate(dates[1]); - data.overviewRatingDelta = getDelta(getRatingNr(data.overviewRating),getRatingNr(value.tab0[dates[1]].rating)); - data.overviewTotalPaxDelta = getDelta(data.overviewTotalPax,value.tab0[dates[1]].pax); - data.overviewTotalCargoDelta = getDelta(data.overviewTotalCargo,value.tab0[dates[1]].cargo); - data.overviewStationsDelta = getDelta(data.overviewStations,value.tab0[dates[1]].stations); - data.overviewFleetDelta = getDelta(data.overviewFleet,value.tab0[dates[1]].fleet); - data.overviewStaffDelta = getDelta(data.overviewStaff,value.tab0[dates[1]].employees); - } - } - //All Tab2 Collumns - dates = []; - for(let date in value.tab2) { - dates.push(date); - } - dates.sort(function(a, b){return b-a}); - if(dates.length){ - data.fafWeek = formatWeekDate(value.tab2[dates[0]].week); - data.fafAirportsServed = value.tab2[dates[0]].airportsServed; - data.fafOperatedFlights = value.tab2[dates[0]].operatedFlights; - data.fafSeatsOffered = value.tab2[dates[0]].seatsOffered; - data.fafsko = value.tab2[dates[0]].sko; - data.fafCargoOffered = value.tab2[dates[0]].cargoOffered; - data.faffko = value.tab2[dates[0]].fko; - //If previous date exists - if(dates[1]){ - data.fafWeekPre = formatWeekDate(value.tab2[dates[1]].week); - data.fafAirportsServedDelta = getDelta(data.fafAirportsServed,value.tab2[dates[1]].airportsServed); - data.fafOperatedFlightsDelta = getDelta(data.fafOperatedFlights,value.tab2[dates[1]].operatedFlights); - data.fafSeatsOfferedDelta = getDelta(data.fafSeatsOffered,value.tab2[dates[1]].seatsOffered); - data.fafskoDelta = getDelta(data.fafsko,value.tab2[dates[1]].sko); - data.fafCargoOfferedDelta = getDelta(data.fafCargoOffered,value.tab2[dates[1]].cargoOffered); - data.faffkoDela = getDelta(data.faffko,value.tab2[dates[1]].fko); - } - } - //Schedule Collumns - if(compAirlinesSchedule[data.airlineCode]){ - dates = []; - for(let date in compAirlinesSchedule[data.airlineCode].date) { - dates.push(date); - } - dates.sort(function(a, b){return b-a}); - if(dates.length){ - let hubs = {}; - //For display - data.scheduleDate = formatDate(dates[0]); - //For table - data.scheduleDateUse = dates[0]; - data.scheduleCargoFreq = 0; - data.schedulePAXFreq = 0; - data.scheduleFltNr = 0; - compAirlinesSchedule[data.airlineCode].date[dates[0]].schedule.forEach(function(schedule){ - //Hubs - let hub = schedule.od.slice(0,3); - if(hubs[hub]){ - hubs[hub]++; - } else { - hubs[hub] = 1; - } - for (let flight in schedule.flightNumber){ - //Cargo Freq - data.scheduleCargoFreq += schedule.flightNumber[flight].cargoFreq; - //Pax Freq - data.schedulePAXFreq += schedule.flightNumber[flight].paxFreq; - //Flight nr - data.scheduleFltNr++; - } - }); - //Total Frequency - data.scheduleTotalFreq = data.schedulePAXFreq + data.scheduleCargoFreq; - //Hubs - let hubArray = []; - for (let hub in hubs) { - hubArray.push([hub, hubs[hub]]); - } - hubArray.sort(function(a, b) { - return b[1] - a[1]; - }); - data.scheduleHubs = ''; - hubArray.forEach(function(hubA,index){ - if(index){ - data.scheduleHubs += ', '; - } - data.scheduleHubs += hubA[0]+' ('+hubA[1]+')'; - }); - - //Previous schedule data - if(dates[1]){ - data.scheduleDatePre = formatDate(dates[1]); - data.scheduleCargoFreqPre = 0; - data.schedulePAXFreqPre = 0; - data.scheduleFltNrPre = 0; - compAirlinesSchedule[data.airlineCode].date[dates[1]].schedule.forEach(function(schedule){ - //Hubs - let hub = schedule.od.slice(0,3); - if(hubs[hub]){ - hubs[hub]++; - } else { - hubs[hub] = 1; - } - for (let flight in schedule.flightNumber){ - //Cargo Freq - data.scheduleCargoFreqPre += schedule.flightNumber[flight].cargoFreq; - //Pax Freq - data.schedulePAXFreqPre += schedule.flightNumber[flight].paxFreq; - //Flight nr - data.scheduleFltNrPre++; - } - }); - //Total Frequency - data.scheduleTotalFreqPre = data.schedulePAXFreq + data.scheduleCargoFreq; - //Delta Collumns - data.scheduleFltNrDelta = getDelta(data.scheduleFltNr,data.scheduleFltNrPre); - data.schedulePAXFreqDelta = getDelta(data.schedulePAXFreq,data.schedulePAXFreqPre); - data.scheduleCargoFreqDelta = getDelta(data.scheduleCargoFreq,data.scheduleCargoFreqPre); - data.scheduleTotalFreqDelta = getDelta(data.scheduleTotalFreq,data.scheduleTotalFreqPre); - } - } - } - //Action collumns - //Open airline - data.actionOpenAirline = 'Airline'; - //Open schedule - if(compAirlinesSchedule[data.airlineCode]){ - data.actionOpenSchedule = $(''); - //Create schedule table - $('#aes-div-dashboard').on('click', 'button#aes-compMon-btn-schedule-'+data.airlineCode, function() { - displayCompetitorMonitoringAirlineScheduleTable(div,compAirlinesSchedule[data.airlineCode],data); - }); - } - //Remove airline ' - data.actionRemoveAirline = $(''); - //Remove airline action - $('#aes-div-dashboard').on('click', 'button#aes-compMon-btn-remove-'+data.airlineCode, function() { - let key = server+data.airlineId+'competitorMonitoring'; - let remove = $(this); - chrome.storage.local.get([key], function(compMonitoringData) { - let compData = compMonitoringData[key]; - compData.tracking = 0; - chrome.storage.local.set({[compData.key]: compData}, function() { - $(remove).closest("tr").remove(); - }); - }); - }); - - - - //Populate collumns - let td = []; - settings.competitorMonitoring.tableColumns.forEach(function(col){ - if(col.visible){ - td.push($('').html(data[col.field])); - - } - }); - rows.push($('').append(td)); - - }); - } else { - rows.push('No airlines marked for competitor monitoring. Open airline info page to mark airline for tracking.'); - } - - let thead = $('').append(hrows); - let tbody = $('').append(rows); - - let table = $('
').append(thead,tbody); - let tableWell = $('
').append(table); - - //Options - let divRow = $('
').append(displayCompetitorMonitoringAirlinesTableOptions(),displayCompetitorMonitoringAirlinesTableCollumns()); - div.append(divRow,tableWell); - }); -} -function displayCompetitorMonitoringAirlineScheduleTable(mainDiv,scheduleData,data){ - mainDiv.hide(); - //Build schedule rows - let rows = []; - let hrow = []; - if(data.scheduleDateUse){ - let collumns = [ - { - field:'schedOrigin', - text:'Origin', - headGroup:'Schedule', - visible:1, - number:0, - }, - { - field:'schedDestination', - text:'Destination', - headGroup:'Schedule', - visible:1, - number:0, - }, - { - field:'schedHub', - text:'Hub', - headGroup:'Schedule', - visible:1, - number:0, - }, - { - field:'schedOd', - text:'OD', - headGroup:'Schedule', - visible:1, - number:0, - }, - { - field:'schedDir', - text:'Direction', - headGroup:'Schedule', - visible:1, - number:0, - }, - { - field:'schedFltNr', - text:'# of flight numbers', - headGroup:'Schedule', - visible:1, - number:1, - }, - { - field:'schedPaxFreq', - text:'PAX frequency', - headGroup:'Schedule', - visible:1, - number:1, - }, - { - field:'schedCargoFreq', - text:'Cargo frequency', - headGroup:'Schedule', - visible:1, - number:1, - }, - { - field:'schedTotalFreq', - text:'Total Frequency', - headGroup:'Schedule', - visible:1, - number:1, - } - ]; - //Table Head - let th = []; - collumns.forEach(function(col){ - if(col.visible){ - //Sort - let sort = $('').html(col.text); - sort.click(function(){ - SortTable(col.field,col.number,'aes-table-competitorMonitoring-airline-schedule','aes-comp-sched-'); - }); - th.push($('').html(sort)); - } - }); - hrow.push($('').append(th)); - //Table Body - scheduleData.date[data.scheduleDateUse].schedule.forEach(function(od){ - let td = []; - let fltNr = 0; - let paxFreq = 0; - let cargoFreq = 0; - for (let flight in od.flightNumber){ - cargoFreq += od.flightNumber[flight].cargoFreq, - paxFreq += od.flightNumber[flight].paxFreq, - fltNr++; - } - let totalFreq = cargoFreq + paxFreq; - //hub - let hub = od.od.slice(0, 3); - let cellValue = { - schedOrigin:od.origin, - schedDestination:od.destination, - schedOd:od.od, - schedDir:od.direction, - schedFltNr:fltNr, - schedPaxFreq:paxFreq, - schedCargoFreq:cargoFreq, - schedTotalFreq:totalFreq, - schedHub:hub - } - collumns.forEach(function(cell){ - if(cell.visible){ - td.push($('').html(cellValue[cell.field])); - } - }); - rows.push($('').append(td)); - }); - - - - - } else { - rows.push('No schedule found'); - } - - - - - //Build layout - let thead = $('').append(hrow); - let tbody = $('').append(rows); - let table = $('
').append(thead,tbody); - let tableWell = $('
').append(table); - let button = $(''); - let panelDiv = $('
').append(button,tableWell); - let heading = $('

'+data.airlineName+' '+data.airlineCode+' schedule

'); - let div = $('
').append(heading,panelDiv); - mainDiv.after(div); - //Button clicks - button.click(function(){ - div.remove(); - mainDiv.show(); - }); -} -function displayCompetitorMonitoringAirlinesTableCollumns(){ - //Table Head - let th = []; - th.push('Show'); - th.push('Column'); - let thead= $('').append($('').append(th)); - //Table body - let tbody= $(''); - - settings.competitorMonitoring.tableColumns.forEach(function(col){ - let td = []; - //Checkbox - if (col.visible){ - td.push(''); - } else { - td.push(''); - } - //Name - td.push(''+col.text+''); - tbody.append($('').append(td)); - }); - - let table = $('
').append(thead,tbody); - let divTable = $('').append(table); - //Collumns selector Checkbox listener - $('input',table).change(function(){ - let show; - if(this.checked) { - show = 1; - } else { - show = 0; - } - let value = $(this).val(); - settings.competitorMonitoring.tableColumns.forEach(function(col){ - if(col.field == value){ - col.visible = show; - } - }); - chrome.storage.local.set({settings: settings}, function(){}); - }); - //Closable legend - let link = $('').text('Columns'); - let legend = $('').html(link); - link.click(function(){ - $('#aes-div-competitorMonitoring-collumns').toggle(); - }); - let fieldset = $('
').append(legend,divTable); - let div = $('
').append(fieldset); - return div; -} -function displayCompetitorMonitoringAirlinesTableOptions(){ - let divFieldset = $('
').html('Options'); - let btn = $(''); - divFieldset.append(btn); - let optionsDiv = $('
').append(divFieldset); - //Reload table - btn.click( function(){ - displayCompetitorMonitoring(); - }); - - - - return optionsDiv; -} -function CompetitorMonitoringSortTable(collumn,number){ - let tableRows = $('#aes-table-competitorMonitoring tbody tr'); - let tableBody = $('#aes-table-competitorMonitoring tbody'); - tableBody.empty(); - let indexes = []; - tableRows.each(function(){ - if(number){ - let value = parseInt($(this).find(".aes-"+collumn).text(),10); - if(value){ - indexes.push(value); - } else { - indexes.push(0); - } - } else { - indexes.push($(this).find(".aes-"+collumn).text()); - } - }); - indexes = [...new Set(indexes)]; - let sorted = [...indexes]; - if(number){ - sorted.sort(function(a, b){ - if (a > b) return -1; - if (a < b) return 1; - if (a = b) return 0; - }); - } else { - sorted.sort(); - } - let same = 1; - for(let i = 0; i < indexes.length; i++){ - if(indexes[i] !== sorted[i]){ - same = 0; - } - } - if(same){ - if(number){ - sorted.sort(function(a, b){ - if (a < b) return -1; - if (a > b) return 1; - if (a = b) return 0; - }); - } else { - sorted.reverse(); - } - } - for(let i=0; i= 0 ; j--){ - if(number){ - let value = parseInt($(tableRows[j]).find(".aes-"+collumn).text(),10); - if(!value){ - value = 0; - } - if(value == sorted[i]){ - tableBody.append($(tableRows[j])); - tableRows.splice(j, 1); - } - } else { - if($(tableRows[j]).find(".aes-"+collumn).text() == sorted[i]){ - tableBody.append($(tableRows[j])); - tableRows.splice(j, 1); - } - } - } - } -} -function setDefaultCompetitorMonitoringSettings(){ - let columns = [ - { - field:'airlineId', - text:'ID', - headGroup:'Airline', - visible:1, - number:1 - }, - { - field:'airlineCode', - text:'Code', - headGroup:'Airline', - visible:1, - number:0 - }, - { - field:'airlineName', - text:'Name', - headGroup:'Airline', - visible:1, - number:0 - }, - { - field:'overviewDate', - text:'Overview date', - headGroup:'Overview', - visible:0, - number:0 - }, - { - field:'overviewPreDate', - text:'Overview previous date', - headGroup:'Overview', - visible:0, - number:0 - }, - { - field:'overviewRating', - text:'Rating', - headGroup:'Overview', - visible:1, - number:0 - }, - { - field:'overviewRatingDelta', - text:'Rating Δ', - headGroup:'Overview', - visible:0, - number:0 - }, - { - field:'overviewTotalPax', - text:'Total pax', - headGroup:'Overview', - visible:1, - number:1 - }, - { - field:'overviewTotalPaxDelta', - text:'Total pax Δ', - headGroup:'Overview', - visible:1, - number:1 - }, - { - field:'overviewTotalCargo', - text:'Total cargo', - headGroup:'Overview', - visible:1, - number:1 - }, - { - field:'overviewTotalCargoDelta', - text:'Total cargo Δ', - headGroup:'Overview', - visible:1, - number:1 - }, - { - field:'overviewStations', - text:'Stations', - headGroup:'Overview', - visible:1, - number:1 - }, - { - field:'overviewStationsDelta', - text:'Stations Δ', - headGroup:'Overview', - visible:1, - number:1 - }, - { - field:'overviewFleet', - text:'Fleet', - headGroup:'Overview', - visible:1, - number:1 - }, - { - field:'overviewFleetDelta', - text:'Fleet Δ', - headGroup:'Overview', - visible:1, - number:1 - }, - { - field:'overviewStaff', - text:'Staff', - headGroup:'Overview', - visible:0, - number:1 - }, - { - field:'overviewStaffDelta', - text:'Staff Δ', - headGroup:'Overview', - visible:0, - number:1 - }, - { - field:'fafWeek', - text:'Week', - headGroup:'Figures', - visible:0, - number:0 - }, - { - field:'fafWeekPre', - text:'Previous week', - headGroup:'Figures', - visible:0, - number:0 - }, - { - field:'fafAirportsServed', - text:'Airports served', - headGroup:'Figures', - visible:1, - number:1 - }, - { - field:'fafAirportsServedDelta', - text:'Airports served Δ', - headGroup:'Figures', - visible:0, - number:1 - }, - { - field:'fafOperatedFlights', - text:'Operated flights', - headGroup:'Figures', - visible:1, - number:1 - }, - { - field:'fafOperatedFlightsDelta', - text:'Operated flights Δ', - headGroup:'Figures', - visible:1, - number:1 - }, - { - field:'fafSeatsOffered', - text:'Seats offered', - headGroup:'Figures', - visible:1, - number:1 - }, - { - field:'fafSeatsOfferedDelta', - text:'Seats offered Δ', - headGroup:'Figures', - visible:1, - number:1 - }, - { - field:'fafsko', - text:'SKO', - headGroup:'Figures', - visible:0, - number:1 - }, - { - field:'fafskoDelta', - text:'SKO Δ', - headGroup:'Figures', - visible:0, - number:1 - }, - { - field:'fafCargoOffered', - text:'Cargo offered', - headGroup:'Figures', - visible:1, - number:1 - }, - { - field:'fafCargoOfferedDelta', - text:'Cargo offered Δ', - headGroup:'Figures', - visible:1, - number:1 - }, - { - field:'faffko', - text:'FKO', - headGroup:'Figures', - visible:0, - number:1 - }, - { - field:'faffkoDela', - text:'FKO Δ', - headGroup:'Figures', - visible:0, - number:1 - }, - { - field:'scheduleDate', - text:'Schedule Date', - headGroup:'Schedule', - visible:0, - number:0 - }, - { - field:'scheduleDatePre', - text:'Previous Schedule Date', - headGroup:'Schedule', - visible:0, - number:0 - }, - { - field:'scheduleHubs', - text:'Hubs (routes)', - headGroup:'Schedule', - visible:1, - number:0 - }, - { - field:'scheduleFltNr', - text:'# of flight numbers', - headGroup:'Schedule', - visible:0, - number:1 - }, - { - field:'scheduleFltNrDelta', - text:'# of flight numbers Δ', - headGroup:'Schedule', - visible:0, - number:1 - }, - { - field:'schedulePAXFreq', - text:'PAX frequency', - headGroup:'Schedule', - visible:0, - number:1 - }, - { - field:'schedulePAXFreqDelta', - text:'PAX frequency Δ', - headGroup:'Schedule', - visible:0, - number:1 - }, - { - field:'scheduleCargoFreq', - text:'Cargo frequency', - headGroup:'Schedule', - visible:0, - number:1 - }, - { - field:'scheduleCargoFreqDelta', - text:'Cargo frequency Δ', - headGroup:'Schedule', - visible:0, - number:1 - }, - { - field:'scheduleTotalFreq', - text:'Total frequency', - headGroup:'Schedule', - visible:1, - number:1 - }, - { - field:'scheduleTotalFreqDelta', - text:'Total frequency Δ', - headGroup:'Schedule', - visible:1, - number:1 - }, - { - field:'actionOpenAirline', - text:'Open airline page', - headGroup:'Actions', - visible:1, - number:0 - }, - { - field:'actionOpenSchedule', - text:'Show airline schedule', - headGroup:'Actions', - visible:1, - number:0 - }, - { - field:'actionRemoveAirline', - text:'Remove airline', - headGroup:'Actions', - visible:0, - number:0 - } - ]; - settings.competitorMonitoring = { - tableColumns:columns - }; -} -function getRatingNr(rating){ - switch(rating) { - case 'AAA': - return 10; - break; - case 'AA': - return 9; - break; - case 'A': - return 8; - break; - case 'BBB': - return 7; - break; - case 'BB': - return 6; - break; - case 'B': - return 5; - break; - case 'CCC': - return 4; - break; - case 'CC': - return 3; - break; - case 'C': - return 2; - break; - case 'D': - return 1; - break; - default: - return 0; - } -} -function getDelta(newNr,oldNr){ - return newNr - oldNr; -}; -//Display Aircraft aircraftProfitability -function displayAircraftProfitability(){ - if(!settings.aircraftProfitability){ - settings.aircraftProfitability = {}; - } - if(!settings.aircraftProfitability.hideColumn){ - settings.aircraftProfitability.hideColumn = []; - } - //columns - let columns = [ - { - category:'Aircraft', - title:'Aircraft ID', - data:'aircraftId', - sortable:1, - visible:1, - number:1, - id:1 - }, - { - category:'Aircraft', - title:'Registration', - data:'registration', - sortable:1, - visible:1 - }, - { - category:'Aircraft', - title:'Equipment', - data:'equipment', - sortable:1, - visible:1 - }, - { - category:'Aircraft', - title:'Fleet', - data:'fleet', - sortable:1, - visible:1 - }, - { - category:'Aircraft', - title:'Nickname', - data:'nickname', - sortable:1, - visible:1 - }, - { - category:'Aircraft', - title:'Note', - data:'note', - sortable:1, - visible:1 - }, - { - category:'Aircraft', - title:'Age', - data:'age', - sortable:1, - visible:1, - number:1 - }, - { - category:'Aircraft', - title:'Maintenance', - data:'maintenance', - sortable:1, - visible:1, - number:1 - }, - { - category:'Aircraft', - title:'Date', - data:'dateAircraft', - sortable:1, - visible:1 - }, - { - category:'Profit', - title:'Total flights', - data:'totalFlights', - sortable:1, - visible:1, - number:1 - }, - { - category:'Profit', - title:'Finished flights', - data:'finishedFlights', - sortable:1, - visible:1, - number:1 - }, - { - category:'Profit', - title:'Profit/loss flights', - data:'profitFlights', - sortable:1, - visible:1, - number:1 - }, - { - category:'Profit', - title:'Profit', - data:'profit', - sortable:1, - visible:1, - number:1, - format:'money' - }, - { - category:'Profit', - title:'Profit extract date', - data:'dateProfit', - sortable:1, - visible:1 - } - ]; - if(settings.aircraftProfitability.hideColumn.length){ - columns.forEach(function(column){ - settings.aircraftProfitability.hideColumn.forEach(function(hideColumn){ - if(column.data == hideColumn){ - column.visible = 0; - } - }); - }); - } - - let key = server + airline.name + 'aircraftFleet'; - //Get storage fleet data - chrome.storage.local.get(key, function(result) { - //get aircraft flight data - let aircraftFleetData = result[key]; - if(aircraftFleetData){ - let keys = []; - aircraftFleetData.fleet.forEach(function(value){ - keys.push(server + 'aircraftFlights' + value.aircraftId); - }); - chrome.storage.local.get(keys, function(result) { - for(let aircraftFlightData in result) { - for (let i=0; i < aircraftFleetData.fleet.length; i++) { - if(aircraftFleetData.fleet[i].aircraftId == result[aircraftFlightData].aircraftId){ - aircraftFleetData.fleet[i].profit = { - date:result[aircraftFlightData].date, - finishedFlights:result[aircraftFlightData].finishedFlights, - profit:result[aircraftFlightData].profit, - profitFlights:result[aircraftFlightData].profitFlights, - time:result[aircraftFlightData].time, - totalFlights:result[aircraftFlightData].totalFlights, - }; - } - } - } - let data = prepareAircraftProfitabilityData(aircraftFleetData); - let tableDiv; - if(data.length){ - tableDiv = generateTable( - { - column:columns, - data:data, - columnPrefix:'aes-aircraftProfit-', - tableSettings:1, - options:['openAircraft','removeAircraft','reloadTableAircraftProfit','applyFilter','removeSelected'], - filter:settings.aircraftProfitability.filter, - hideColumn:settings.aircraftProfitability.hideColumn, - tableSettingStorage:'aircraftProfitability' - } - ); - } else { - //Never happens or only when fleet = 0 because of updated script this output is copied bellow - tableDiv = $('

').text('No aircraft data in memory. Open fleet management to extract aircraft data.') - } - //Div - let div = $('
').append(tableDiv); - let mainDiv = $("#aes-div-dashboard"); - //Build layout - mainDiv.empty(); - let title = $('

').text('Aircraft Profitability'); - mainDiv.append(title,div); - - }); - } else { - //No data - //Div - let tableDiv = $('

').text('No aircraft data in memory. Open fleet management to extract aircraft data.') - let div = $('
').append(tableDiv); - let mainDiv = $("#aes-div-dashboard"); - //Build layout - mainDiv.empty(); - let title = $('

').text('Aircraft Profitability'); - mainDiv.append(title,div); - } - }); - function prepareAircraftProfitabilityData(storage){ - let data = []; - storage.fleet.forEach(function(value){ - let profit = {}; - if(value.profit){ - profit.totalFlights=value.profit.totalFlights; - profit.finishedFlights=value.profit.finishedFlights; - profit.profitFlights=value.profit.profitFlights; - profit.profit=value.profit.profit; - profit.dateProfit=formatDate(value.profit.date)+' '+value.profit.time; - } - data.push({ - aircraftId:value.aircraftId, - registration:value.registration, - equipment:value.equipment, - fleet:value.fleet, - nickname:value.nickname, - note:value.note, - age:value.age, - maintenance:value.maintanance, - dateAircraft:formatDate(value.date)+' '+value.time, - totalFlights:profit.totalFlights, - finishedFlights:profit.finishedFlights, - profitFlights:profit.profitFlights, - profit:profit.profit, - dateProfit:profit.dateProfit - }); - }); - return data; - } -} - -//Auto table generator -function generateTable(tableOptionsRule){ - let tableHtml = $('
'); - let table = {cell:{},row:{},tableHtml:tableHtml}; - //Table Categories - let tableCategory = {}; - tableOptionsRule.column.forEach(function(value){ - if(value.visible){ - if(!tableCategory[value.category]){ - tableCategory[value.category] = 1; - } else { - tableCategory[value.category]++; - } - } - }); - table.cell.category = []; - //Add checkbox - if(tableOptionsRule.tableSettings){ - table.cell.category.push('') - } - for(let category in tableCategory) { - table.cell.category.push(''+category+''); - } - - //Table Headers - table.cell.header = []; - tableOptionsRule.column.forEach(function(value){ - if(value.visible){ - if(value.sortable){ - //Sort - let sort = $('').text(value.title); - sort.click(function(){ - masterSortTable(value.data,value.number,table.tableHtml,tableOptionsRule.columnPrefix); - }); - table.cell.header.push($('').html(sort)); - } else { - table.cell.header.push(''+value.title+''); - } - } - }); - //Head - table.row.head = []; - table.row.head.push($('').append(table.cell.category)); - table.row.head.push($('').append(table.cell.header)); - //Table Body - table.row.body = []; - tableOptionsRule.data.forEach(function(dataValue){ - let cell = []; - //Add checkbox - if(tableOptionsRule.tableSettings){ - cell.push(''); - } - let id; - tableOptionsRule.column.forEach(function(colValue){ - if(colValue.id){ - id = dataValue[colValue.data] - } - if(colValue.visible){ - let td = $('').addClass(tableOptionsRule.columnPrefix+colValue.data); - if(colValue.format){ - td.html(masterCellFormat(colValue.format,dataValue[colValue.data])) - } else { - td.html(dataValue[colValue.data]) - } - cell.push(td); - } - }); - table.row.body.push($('').attr('id', id).append(cell)); - }); - let thead = $('').append(table.row.head); - let tbody = $('').append(table.row.body); - table.tableHtml.append(thead,tbody); - let tableWell = $('
').append(table.tableHtml); - - //Table Settings - let settingsDiv = ''; - if(tableOptionsRule.tableSettings){ - let divCol = []; - //Options - divCol.push($('
').html(masterTableOptions(table.tableHtml,tableOptionsRule.options))); - divCol.push($('
').html(masterTableFilter(tableOptionsRule.filter,tableOptionsRule.column))); - divCol.push($('
').html(masterTableColumns())); - settingsDiv = $('
').append(divCol) - } - - let div = $('
').append(settingsDiv,tableWell); - return div; - //Table functions - function masterSortTable(collumn,number,table,collumnPrefix){ - let tableRows = $('tbody tr',table); - let tableBody = $('tbody',table); - tableBody.empty(); - let indexes = []; - tableRows.each(function(){ - if(number){ - let value = parseInt($(this).find("."+collumnPrefix+collumn).text(),10); - if(value){ - indexes.push(value); - } else { - indexes.push(0); - } - } else { - indexes.push($(this).find("."+collumnPrefix+collumn).text()); - } - }); - indexes = [...new Set(indexes)]; - let sorted = [...indexes]; - if(number){ - sorted.sort(function(a, b){ - if (a > b) return -1; - if (a < b) return 1; - if (a = b) return 0; - }); - } else { - sorted.sort(); - } - let same = 1; - for(let i = 0; i < indexes.length; i++){ - if(indexes[i] !== sorted[i]){ - same = 0; - } - } - if(same){ - if(number){ - sorted.sort(function(a, b){ - if (a < b) return -1; - if (a > b) return 1; - if (a = b) return 0; - }); - } else { - sorted.reverse(); - } - } - for(let i=0; i= 0 ; j--){ - if(number){ - let value = parseInt($(tableRows[j]).find("."+collumnPrefix+collumn).text(),10); - if(!value){ - value = 0; - } - if(value == sorted[i]){ - tableBody.append($(tableRows[j])); - tableRows.splice(j, 1); - } - } else { - if($(tableRows[j]).find("."+collumnPrefix+collumn).text() == sorted[i]){ - tableBody.append($(tableRows[j])); - tableRows.splice(j, 1); - } - } - } - } - } - function masterCellFormat(type,value){ - if(!value){ - return ''; - } - switch(type) { - case 'money': - let span = $(''); - let text = ''; - if(value > 0){ - span.addClass('good'); - text = '+' - } - if(value < 0){ - span.addClass('bad'); - } - text = text + value + ' AS$'; - span.text(text); - return span; - break; - default: - return value; - } - } - function masterTableOptions(table,options){ - let div = $('
'); - options.forEach(function(value,index){ - if(index){ - let span = $(' '); - div.append(span); - } - div.append(masterTableOptionsHandle(value)); - }); - //Closable legend - let link = $('').text('Options'); - let legend = $('').html(link); - link.click(function(){ - div.toggle(); - }); - let fieldset = $('
').append(legend,div); - return fieldset; - - //Functions - function masterTableOptionsHandle(value){ - switch(value) { - case 'openAircraft': - return masterTableOptionsOpenAircraft(); - break; - case 'reloadTableAircraftProfit': - return masterTableOptionsReloadTableAP(); - break; - case 'removeAircraft': - return masterTableOptionsRemoveAircraft(); - break; - case 'applyFilter': - return masterTableOptionsApplyFilter(); - break; - case 'removeSelected': - return masterTableOptionsRemoveSelected(); - break; - default: - // code block - } - //Option Functions - function masterTableOptionsOpenAircraft(){ - let btn = $(''); - btn.click( function(){ - let urls = $('tbody tr',table).has('input:checked').map(function() { - let id = $(this).attr('id'); - let url = 'https://'+server+'.airlinesim.aero/app/fleets/aircraft/'+id+'/1'; - return url; - }).toArray(); - //Open new tabs - for (let i = 0; i < urls.length; i++) { - window.open(urls[i], '_blank'); - if(i==10){ - break; - } - } - }); - return btn; - } - function masterTableOptionsReloadTableAP(){ - let btn = $(''); - btn.click( function(){ - displayAircraftProfitability(); - }); - return btn; - } - function masterTableOptionsRemoveAircraft(){ - let btn = $(''); - btn.click( function(){ - let id = []; - let aircraftKey =[]; - $('tbody tr',table).has('input:checked').each(function() { - let localId = $(this).attr('id'); - id.push(localId); - aircraftKey.push(server + 'aircraftFlights' + localId); - $(this).remove(); - }); - if(id.length){ - let fleetKey = server + airline.name + 'aircraftFleet'; - chrome.storage.local.get(fleetKey, function(result) { - let storedFleetData = result[fleetKey]; - let newFleet = storedFleetData.fleet.filter(function(value) { - let keep = 1; - id.forEach(function(idVal){ - if(idVal == value.aircraftId){ - keep = 0; - } - }); - return keep; - }); - storedFleetData.fleet = newFleet; - chrome.storage.local.set({[fleetKey]: storedFleetData}, function() { - chrome.storage.local.remove(aircraftKey, function() {}); - }); - }); - } - }); - return btn; - } - function masterTableOptionsApplyFilter(){ - let btn = $(''); - btn.click( function(){ - let filter = []; - table.closest(".as-panel").find('fieldset:eq(1) table tbody tr').each(function(){ - filter.push( - { - titlecode:$(this).find('input').val(), - title:$(this).find('td:eq(0)').text(), - operation:$(this).find('td:eq(1)').text(), - value:$(this).find('td:eq(2)').text() - } - ) - }); - settings[tableOptionsRule.tableSettingStorage].filter = filter; - chrome.storage.local.set({settings: settings}, function(){ - $('tbody tr',table).each(function(){ - let row = this; - filter.forEach(function(filter){ - let cell = $(row).find("."+tableOptionsRule.columnPrefix+filter.titlecode).text(); - //if(cell){ - //Get collumn info if number or not - let number; - for(let i=0;i': - if(cell < value){ - $(row).remove(); - } - break; - case '<': - if(cell > value){ - $(row).remove(); - } - } - }); - }); - }); - }); - return btn; - } - function masterTableOptionsRemoveSelected(){ - let btn = $(''); - btn.click( function(){ - $('tbody tr',table).has('input:checked').remove(); - }); - return btn; - } - } - } - function masterTableFilter(filter,column){ - //Table head - let th = []; - th.push('Column'); - th.push('Operation'); - th.push('Value'); - th.push(''); - let thead= $('').append($('').append(th)); - //Table body - let row = []; - if(filter){ - filter.forEach(function(fil){ - row.push($('').append(masterTableFilterAddBodyRow(fil.titlecode,fil.title,fil.operation,fil.value))); - }); - } - - let tbody = $('').append(row); - //Table foot - //select collumn - let option1 = []; - column.forEach(function(col){ - option1.push(''); - }); - let select1 = $('').append(option1); - //Select value - let option = []; - option.push(''); - option.push(''); - option.push(''); - option.push(''); - let select = $('').append(option); - //Value - let input = $(''); - //Add button - let btn = $('').text('Add Row'); - btn.click(function(){ - let column = $('option:selected',select1).text(); - let columnVal = $('option:selected',select1).val(); - let operation = $('option:selected',select).text(); - let value = input.val(); - tbody.append($('').append(masterTableFilterAddBodyRow(columnVal,column,operation,value))); - }); - //Footer rows - let tf = []; - tf.push($('').html(select1)); - tf.push($('').html(select)); - tf.push($('').html(input)); - tf.push($('').append(btn)); - let tfoot= $('').append($('').append(tf)); - let tableFilter = $('
').append(thead,tbody,tfoot); - let divTable = $('
').append(tableFilter); - //Closable legend - let link = $('').text('Filter'); - let legend = $('').html(link); - link.click(function(){ - divTable.toggle(); - }); - let fieldset = $('
').append(legend,divTable); - return fieldset; - //Functions - function masterTableFilterAddBodyRow(titleCode,title,operation,value){ - let td = []; - td.push(''+title+''); - td.push(''+operation+''); - td.push(''+value+''); - let deleteBtn = $('').html(''); - deleteBtn.click(function(){ - $(this).closest("tr").remove(); - }); - td.push($('').append(deleteBtn)); - return td; - } - } - function masterTableColumns(){ - //Table head - let th = []; - th.push('Show'); - th.push('Column'); - let thead= $('').append($('').append(th)); - //Table body - let row = []; - tableOptionsRule.column.forEach(function(col){ - let td = [] - let input = $(''); - input.change(function(){ - if(!tableOptionsRule.hideColumn){ - tableOptionsRule.hideColumn = []; - } - let newHideColumns = []; - let currentColumn = $(this).val(); - newHideColumns = tableOptionsRule.hideColumn.filter(function(value){ - return value != currentColumn; - }); - if(!this.checked){ - newHideColumns.push(currentColumn); - } - - tableOptionsRule.hideColumn = newHideColumns; - settings[tableOptionsRule.tableSettingStorage].hideColumn = tableOptionsRule.hideColumn; - chrome.storage.local.set({settings: settings}, function(){}); - }) - if(col.visible){ - input.prop('checked', true); - } - td.push($('').append(input)); - td.push($('').text(col.title)); - row.push($('').append(td)); - }); - let tbody = $('').append(row); - let tableColumns = $('
').append(thead,tbody); - let divTable = $('
').append(tableColumns).hide(); - //Closable legend - let link = $('').text('Columns'); - let legend = $('').html(link); - link.click(function(){ - divTable.toggle(); - }); - let fieldset = $('
').append(legend,divTable); - return fieldset; - } -} -//Display general helper functions -function generalAddScheduleRow(tbody){ - let td1 = $('').text("Schedule"); - let td2 = $(''); - let td3 = $(''); - let row = $('').append(td1,td2,td3); - tbody.append(row); - //Get schedule - let scheduleKey = server+airline.code+'schedule'; - chrome.storage.local.get([scheduleKey], function(result) { - let scheduleData = result[scheduleKey]; - if(scheduleData){ - let lastUpdate = getDate('schedule',scheduleData.date); - let diff = getDateDiff(todayDate.date,lastUpdate); - let span = $('').text('Last schedule extract '+formatDate(lastUpdate)+' ('+diff+' days ago). Extract new schedule if there are new routes.'); - if(diff >= 0 && diff < 7){ - span.addClass('good'); - } else { - span.addClass('warning'); - } - td2.append(span); - generalUpdateScheduleAction(td3); - - - - } else { - //no schedule - td2.html('No Schedule data found. Extract schedule or some AES parts will not work'); - generalUpdateScheduleAction(td3); - } - - - - }); -} -function generalUpdateScheduleAction(td3){ - let btn = $(''); - btn.click(function(){ - settings.schedule.autoExtract = 1; - //get schedule link - let link = $('#enterprise-dashboard table:eq(0) tfoot td a:eq(2)'); - chrome.storage.local.set({settings: settings}, function() { - link[0].click(); - }); - }); - td3.append(btn); -} -function generalAddPersonelManagementRow(tbody){ - let td = []; - td.push($('').text("Personnel Management")); - td.push($('')); - td.push($('')); - let row = $('').append(td); - tbody.append(row); - //Get Status - let key = server+airline.name+'personelManagement'; - chrome.storage.local.get([key], function(result) { - let personelManagementData = result[key]; - if(personelManagementData){ - let lastUpdate = personelManagementData.date; - let diff = getDateDiff(todayDate.date,lastUpdate); - let span = $('').text('Last personnel salary update: '+formatDate(lastUpdate)+' ('+diff+' days ago).'); - if(diff >= 0 && diff < 7){ - span.addClass('good'); - } else { - span.addClass('warning'); - } - td[1].append(span); - } else { - //no schedule - td[1].html('No personnel salary update date found.'); - } - }); - - //Action - let btn = $(''); - btn.click(function(){ - //get schedule link - let link = $('#as-navbar-main-collapse > ul > li:eq(4) > ul > li:eq(5) > a'); - link[0].click(); - }); - td[2].append(btn); -} -//Display default -function displayDefault(){ - let mainDiv = $("#aes-div-dashboard"); - mainDiv.empty(); -} - -//Table sort and other functions -function SortTable(collumn,number,tableId,collumnPrefix){ - let tableRows = $('#'+tableId+' tbody tr'); - let tableBody = $('#'+tableId+' tbody'); - tableBody.empty(); - let indexes = []; - tableRows.each(function(){ - if(number){ - let value = parseInt($(this).find("."+collumnPrefix+collumn).text(),10); - if(value){ - indexes.push(value); - } else { - indexes.push(0); - } - } else { - indexes.push($(this).find("."+collumnPrefix+collumn).text()); - } - }); - indexes = [...new Set(indexes)]; - let sorted = [...indexes]; - if(number){ - sorted.sort(function(a, b){ - if (a > b) return -1; - if (a < b) return 1; - if (a = b) return 0; - }); - } else { - sorted.sort(); - } - let same = 1; - for(let i = 0; i < indexes.length; i++){ - if(indexes[i] !== sorted[i]){ - same = 0; - } - } - if(same){ - if(number){ - sorted.sort(function(a, b){ - if (a < b) return -1; - if (a > b) return 1; - if (a = b) return 0; - }); - } else { - sorted.reverse(); - } - } - for(let i=0; i= 0 ; j--){ - if(number){ - let value = parseInt($(tableRows[j]).find("."+collumnPrefix+collumn).text(),10); - if(!value){ - value = 0; - } - if(value == sorted[i]){ - tableBody.append($(tableRows[j])); - tableRows.splice(j, 1); - } - } else { - if($(tableRows[j]).find("."+collumnPrefix+collumn).text() == sorted[i]){ - tableBody.append($(tableRows[j])); - tableRows.splice(j, 1); - } - } - } - } -} - - - - -//Helper -function getDate(type, scheduleData){ - switch(type) { - case 'schedule': - //scheduleData must be schedule object with dates as properties - let dates = []; - for(let date in scheduleData){ - if (Number.isInteger(parseInt(date))) { - dates.push(date); - } - } - dates.reverse(); - return dates[0]; - case 'today': - let a = $(".as-footer-line-element:has('.fa-clock-o')").text().trim(); - let b = a.split(" "); - //For date - let dateTemp = b[0].split("-"); - let date; - if(dateTemp.length == 1){ - //German - dateTemp = dateTemp[0].split("."); - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[2] + date[1] + date[0]; - } else { - //English - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[0] + date[1] + date[2]; - } - //For time - let time = b[b.length-2] +' '+b[b.length-1]; - return {date:date,time:time}; - default: - return 0; - } -} -function getDateDiff(date1,date2){ - //Returns day differnece between date1 - date2 - let d1 = new Date(formatDate(date1)+'T12:00:00Z'); - let d2 = new Date(formatDate(date2)+'T12:00:00Z'); - let diff = Math.round((d1 - d2)/(1000 * 60 * 60 * 24)); - return diff; -} -function getAirlineCode(){ - //code - let airline = $("#enterprise-dashboard table tr:eq(1) td").text(); - //name - let name = $("#as-navbar-main-collapse ul li:eq(0) a:eq(0)").text().trim().replace(/[^A-Za-z0-9]/g, ''); - return {code:airline,name:name}; -} -function getServerName(){ - let server = window.location.hostname - server = server.split('.'); - return server[0]; -} -function formatDate(date){ - return date.substring(0, 4)+'-'+date.substring(4, 6)+'-'+date.substring(6, 8); -} -function formatWeekDate(date){ - let a = date.toString(); - return a.substring(0, 2)+'/'+a.substring(2, 6); -} +"use strict"; +//MAIN +//Global vars +var settings, airline, server, todayDate; +$(function() { + todayDate = AES.getServerDate(); + airline = AES.getAirlineCode(); + server = AES.getServerName(); + chrome.storage.local.get(['settings'], function(result) { + settings = result.settings; + + displayDashboard(); + dashboardHandle(); + $("#aes-select-dashboard-main").change(function() { + dashboardHandle(); + }); + }); +}); + +function displayDashboard() { + let mainDiv = $("#enterprise-dashboard"); + mainDiv.before( + ` +

AirlineSim Enhancement Suite Dashboard

+
+
+ + +
+
+
+
+ ` + ); + $("#aes-select-dashboard-main").val(settings.general.defaultDashboard); +} + +function dashboardHandle() { + let value = $("#aes-select-dashboard-main").val(); + settings.general.defaultDashboard = value; + chrome.storage.local.set({ settings: settings }, function() {}); + switch (value) { + case 'general': + displayGeneral(); + break; + case 'routeManagement': + displayRouteManagement(); + break; + case 'competitorMonitoring': + displayCompetitorMonitoring(); + break; + case 'hr': + displayHr(); + break; + case 'aircraftProfitability': + displayAircraftProfitability(); + break; + default: + displayDefault(); + } +} +//Route Management Dashbord +function displayRouteManagement() { + //Check ROute Managemetn seetings + if (!settings.routeManagement) { + setDefaultRouteManagementSettings(); + } + + let mainDiv = $("#aes-div-dashboard"); + //Build layout + mainDiv.empty(); + let title = $('

').text('Route Management'); + let div = $('
'); + mainDiv.append(title, div); + //Get schedule + let scheduleKey = server + airline.code + 'schedule'; + chrome.storage.local.get([scheduleKey], function(result) { + let scheduleData = result[scheduleKey]; + if (scheduleData) { + // Table + generateRouteManagementTable(scheduleData); + + // Option buttons + let fieldsetEl = document.createElement("fieldset") + let legendEl = document.createElement("legend") + let buttonGroupEl = document.createElement("div") + + let buttonElements = { + "selectFirstTen": { + "label": "select first 10" + }, + "hideChecked": { + "label": "hide checked" + }, + "openInventory": { + "label": "open inventory (max 10)" + }, + "reloadTable": { + "label": "reload table" + } + } + + for (let key in buttonElements) { + let buttonObj = buttonElements[key] + let buttonEl = document.createElement("button") + let buttonClassNames = buttonObj?.classNames + let buttonType = buttonObj?.type + let buttonDefaultClassNames = "btn btn-default" + buttonEl.innerText = buttonObj.label + + if (buttonType) { + buttonEl.setAttribute("type", buttonType) + } else { + buttonEl.setAttribute("type", "button") + } + + if (buttonClassNames) { + buttonEl.className = buttonClassNames + } else { + buttonEl.className = buttonDefaultClassNames + } + + buttonObj.element = buttonEl + buttonGroupEl.append(buttonEl) + } + + legendEl.innerText = "Options" + buttonGroupEl.classList.add("btn-group") + + fieldsetEl.append(legendEl, buttonGroupEl) + + let optionsDiv = $('
').append(fieldsetEl); + + // Button actions + + // Select first ten + buttonElements["selectFirstTen"].element.addEventListener("click", function() { + let count = 0 + $('#aes-table-routeManagement tbody tr').each(function() { + $(this).find("input").prop('checked', true); + count++; + if (count > 10) { + return false; + } + }) + }); + + // Remove checked + buttonElements["hideChecked"].element.addEventListener("click", function() { + $('#aes-table-routeManagement tbody tr').has('input:checked').remove(); + }); + + // Open Inventory + buttonElements["openInventory"].element.addEventListener("click", function() { + //Get checked collumns + let pages = $('#aes-table-routeManagement tbody tr').has('input:checked').map(function() { + let orgdest = $(this).attr('id'); + orgdest = orgdest.split("-"); + orgdest = orgdest[2]; + //let orgdest = $(this).find("td:eq(1)").text() + $(this).find("td:eq(2)").text(); + let url = 'https://' + server + '.airlinesim.aero/app/com/inventory/' + orgdest; + return url; + }).toArray(); + + //Open new tabs + for (let i = 0; i < pages.length; i++) { + window.open(pages[i], '_blank'); + if (i == 10) { + break; + } + } + }); + + // Reload table reloadTable + buttonElements["reloadTable"].element.addEventListener("click", function() { + generateRouteManagementTable(scheduleData); + }); + let divRow = $('
').append(optionsDiv, displayRouteManagementFilters(), displayRouteManagementCollumns()) + div.prepend(divRow); + //Collumns selector Checkbox listener + $('#aes-table-routeManagement-collumns input').change(function() { + let show; + if (this.checked) { + show = 1; + } else { + show = 0; + } + let value = $(this).val(); + settings.routeManagement.tableCollumns.forEach(function(col) { + if (col.class == value) { + col.show = show; + } + }); + chrome.storage.local.set({ settings: settings }, function() {}); + }); + + } else { + //no schedule + div.append("Need schedule info to show this section. Change Dashboard to General -> Schedule -> Extract Schedule.") + } + }); +} + +function setDefaultRouteManagementSettings() { + let collumns = [ + { + name: 'Origin', + class: 'aes-origin', + number: 0, + show: 1, + value: 'origin' + }, + { + name: 'Destination', + class: 'aes-destination', + number: 0, + show: 1, + value: 'destination' + }, + { + name: 'Hub', + class: 'aes-hub', + number: 0, + show: 1, + value: 'hub' + }, + { + name: 'OD', + class: 'aes-od', + number: 0, + show: 1, + value: 'odName' + }, + { + name: 'Direction', + class: 'aes-direction', + number: 0, + show: 1, + value: 'direction' + }, + { + name: '# of flight numbers', + class: 'aes-fltNr', + number: 1, + show: 1, + value: 'fltNr' + }, + { + name: 'PAX frequency', + class: 'aes-paxFreq', + number: 1, + show: 1, + value: 'paxFreq' + }, + { + name: 'Cargo frequency', + class: 'aes-cargoFreq', + number: 1, + show: 1, + value: 'cargoFreq' + }, + { + name: 'Total Frequency', + class: 'aes-totalFreq', + number: 1, + show: 1, + value: 'totalFreq' + }, + { + name: 'Analysis date', + class: 'aes-analysisDate', + number: 0, + show: 1 + }, + { + name: 'Previous Analysis date', + class: 'aes-analysisPreDate', + number: 0, + show: 1 + }, + { + name: 'Pricing date', + class: 'aes-pricingDate', + number: 0, + show: 1 + }, + { + name: 'PAX load', + class: 'aes-paxLoad', + number: 1, + show: 1 + }, + { + name: 'PAX load Δ', + class: 'aes-paxLoadDelta', + number: 1, + show: 1 + }, + { + name: 'Cargo load', + class: 'aes-cargoLoad', + number: 1, + show: 1 + }, + { + name: 'Cargo load Δ', + class: 'aes-cargoLoadDelta', + number: 1, + show: 1 + }, + { + name: 'Total load', + class: 'aes-load', + number: 1, + show: 1 + }, + { + name: 'Total load Δ', + class: 'aes-loadDelta', + number: 1, + show: 1 + }, + { + name: 'PAX index', + class: 'aes-paxIndex', + number: 1, + show: 1 + }, + { + name: 'PAX index Δ', + class: 'aes-paxIndexDelta', + number: 1, + show: 1 + }, + { + name: 'Cargo index', + class: 'aes-cargoIndex', + number: 1, + show: 1 + }, + { + name: 'Cargo index Δ', + class: 'aes-cargoIndexDelta', + number: 1, + show: 1 + }, + { + name: 'Index', + class: 'aes-index', + number: 1, + show: 1 + }, + { + name: 'Index Δ', + class: 'aes-indexDelta', + number: 1, + show: 1 + }, + { + name: 'Route PAX index', + class: 'aes-routeIndexPax', + number: 1, + show: 1 + }, + { + name: 'Route Cargo index', + class: 'aes-routeIndexCargo', + number: 1, + show: 1 + }, + { + name: 'Route index', + class: 'aes-routeIndex', + number: 1, + show: 1 + } + ]; + settings.routeManagement = { + tableCollumns: collumns, + filter: [] + }; +} + +function routeManagementApplyFilter() { + $('#aes-table-routeManagement tbody tr').each(function() { + let row = this; + settings.routeManagement.filter.forEach(function(filter) { + let cell = $(row).find("." + filter.collumnCode).text(); + //if(cell){ + //Get collumn info if number or not + let number; + for (let i = 0; i < settings.routeManagement.tableCollumns.length; i++) { + let collumn = settings.routeManagement.tableCollumns[i]; + if (filter.collumnCode == collumn.class) { + number = collumn.number; + break; + } + } + let value = filter.value; + if (number) { + if (cell) { + cell = parseInt(cell, 10); + } + if (value) { + value = parseInt(value, 10); + } + } + switch (filter.operation) { + case '=': + if (cell != value) { + $(row).remove(); + } + break; + case '!=': + if (cell == value) { + $(row).remove(); + } + break; + case '>': + if (cell < value) { + $(row).remove(); + } + break; + case '<': + if (cell > value) { + $(row).remove(); + } + } + }); + }); +} + +function displayRouteManagementFilters() { + //Table head + let th = []; + th.push('Column'); + th.push('Operation'); + th.push('Value'); + th.push(''); + let thead = $('').append($('').append(th)); + + //Table body + let tbody = $(''); + settings.routeManagement.filter.forEach(function(fil) { + let td = []; + td.push('' + fil.collumn + ''); + td.push('' + fil.operation + ''); + td.push('' + fil.value + ''); + td.push(''); + tbody.append($('').append(td)); + }); + + //Table foot + //select collumn + let option1 = []; + settings.routeManagement.tableCollumns.forEach(function(col) { + option1.push(''); + }); + let select1 = $('').append(option1); + + + + //Select value + let option = []; + option.push(''); + option.push(''); + option.push(''); + option.push(''); + let select = $('').append(option); + //Add button + let btn = $('').text('Add Row'); + btn.click(function() { + let td = []; + let collumn = $(this).closest("tr").find('#aes-select-routeManagement-filter-collumn option:selected').text(); + let collumnVal = $(this).closest("tr").find('#aes-select-routeManagement-filter-collumn').val(); + let operation = $(this).closest("tr").find('#aes-select-routeManagement-filter-operation option:selected').text(); + let value = $(this).closest("tr").find('#aes-select-routeManagement-filter-value').val() + td.push('' + collumn + ''); + td.push('' + operation + ''); + td.push('' + value + ''); + td.push(''); + + tbody.append($('').append(td)); + }); + + //Footer rows + let tf = []; + tf.push($('').html(select1)); + tf.push($('').html(select)); + tf.push(''); + tf.push($('').append(btn)); + let tfoot = $('').append($('').append(tf)); + let table = $('
').append(thead, tbody, tfoot); + let divTable = $('
').append(table); + + + // + let saveBtn = $(''); + let saveSpan = $(''); + + //Closable legend + let link = $('').text('Filters'); + let legend = $('').html(link); + link.click(function() { + divForAll.toggle(); + }); + + let divForAll = $('
').append(divTable, saveBtn, saveSpan); + let fieldset = $('
').append(legend, divForAll); + let div = $('
').append(fieldset); + + //Delete row for filter row + table.on("click", ".aes-a-routeManagement-filter-delete-row", function() { + $(this).closest("tr").remove(); + }); + + //Save Button + saveBtn.click(function() { + saveSpan.removeClass().addClass('warning').text(' saving...'); + let filter = []; + $('#aes-table-routeManagement-filter tbody tr').each(function() { + filter.push({ + collumnCode: $(this).find('input').val(), + collumn: $(this).find('td:eq(0)').text(), + operation: $(this).find('td:eq(1)').text(), + value: $(this).find('td:eq(2)').text(), + }); + }); + settings.routeManagement.filter = filter; + chrome.storage.local.set({ settings: settings }, function() { + saveSpan.removeClass().addClass('warning').text(' filtering...'); + routeManagementApplyFilter() + saveSpan.removeClass().addClass('good').text(' done!'); + }); + }); + + return div; +} + +function displayRouteManagementCollumns() { + //Table Head + let th = []; + th.push('Show'); + th.push('Column'); + let thead = $('').append($('').append(th)); + //Table body + let tbody = $(''); + + settings.routeManagement.tableCollumns.forEach(function(col) { + let td = []; + //Checkbox + if (col.show) { + td.push(''); + } else { + td.push(''); + } + //Name + td.push('' + col.name + ''); + tbody.append($('').append(td)); + }); + + let table = $('
').append(thead, tbody); + let divTable = $('').append(table); + + //Closable legend + let link = $('').text('Columns'); + let legend = $('').html(link); + link.click(function() { + $('#aes-div-routeManagement-collumns').toggle(); + }); + + let fieldset = $('
').append(legend, divTable); + let div = $('
').append(fieldset); + return div; +} + +function generateRouteManagementTable(scheduleData) { + //Remove table + $('#aes-div-routeManagement').remove(); + //Dates + let dates = []; + for (let date in scheduleData.date) { + if (Number.isInteger(parseInt(date))) { + dates.push(date); + } + } + dates.reverse(); + //LatestSchedule + let schedule = scheduleData.date[dates[0]].schedule; + //Generate top + //Table headers + let collumns = settings.routeManagement.tableCollumns; + + //Generate table head + let thead = $(''); + let th = []; + //Check box + let checkbox = $(''); + checkbox.change(function() { + if (this.checked) { + $('#aes-table-routeManagement tbody tr').each(function() { + $(this).find("input").prop('checked', true); + }); + } else { + $('#aes-table-routeManagement tbody tr').each(function() { + $(this).find("input").prop('checked', false); + }); + } + }); + th.push($('').html(checkbox)); + collumns.forEach(function(col) { + if (col.show) { + let sort = $('').html(col.name); + sort.click(function() { + routeManagementSortTable(col.class, col.number); + }); + th.push($('').html(sort)); + } + }); + //Add open inventory column + th.push($('Action')); + + thead.append($('').append(th)); + //Generate table rows + let tbody = $(''); + let uniqueOD = []; + schedule.forEach(function(od) { + //ODs for analysis + uniqueOD.push(od.od); + //Get values flight numbers and total frequency + let fltNr = 0; + let paxFreq = 0; + let cargoFreq = 0; + for (let flight in od.flightNumber) { + cargoFreq += od.flightNumber[flight].cargoFreq, + paxFreq += od.flightNumber[flight].paxFreq, + fltNr++; + } + let totalFreq = cargoFreq + paxFreq; + //hub + let hub = od.od.slice(0, 3); + let cellValue = { + origin: od.origin, + destination: od.destination, + odName: od.od, + direction: od.direction, + fltNr: fltNr, + paxFreq: paxFreq, + cargoFreq: cargoFreq, + totalFreq: totalFreq, + hub: hub + } + //Table cells + let cell = []; + //Checkbox + cell.push(''); + //Schedule + collumns.forEach(function(col) { + if (col.show) { + if (col.value) { + cell.push($('').addClass(col.class).text(cellValue[col.value])); + } else { + cell.push($('').addClass(col.class)); + } + } + }); + let rowId = od.origin + od.destination; + + //Add inventory button + let invBtn = 'Inventory' + cell.push($('').html(invBtn)); + + let row = $('').append(cell); + tbody.append(row); + }); + let table = $('
').append(thead, tbody); + let divTable = $('
').append(table); + $('#aes-div-dashboard-routeManagement').append(divTable) + //Analysis collumns + //Get unique ODs + uniqueOD = [...new Set(uniqueOD)]; + for (let i = 0; i < uniqueOD.length; i++) { + let origin = uniqueOD[i].substring(0, 3); + let dest = uniqueOD[i].substring(3, 6); + let keyOutbound = server + airline.code + origin + dest + 'routeAnalysis'; + let keyInbound = server + airline.code + dest + origin + 'routeAnalysis'; + chrome.storage.local.get([keyOutbound], function(outboundData) { + chrome.storage.local.get([keyInbound], function(inboundData) { + let outAnalysis = outboundData[keyOutbound]; + let inAnalysis = inboundData[keyInbound]; + let outDates, inDates; + if (outAnalysis) { + outDates = getRouteAnalysisImportantDates(outAnalysis.date); + } + if (inAnalysis) { + inDates = getRouteAnalysisImportantDates(inAnalysis.date); + } + //Route index + let routeIndex = {}; + let routeIndexPax, routeIndexCargo; + if (outAnalysis && inAnalysis) { + if (outDates.analysis && inDates.analysis) { + let indexType = ['all', 'pax', 'cargo']; + indexType.forEach(function(type) { + let outIndex = getRouteAnalysisIndex(outAnalysis.date[outDates.analysis].data, type); + let inIndex = getRouteAnalysisIndex(inAnalysis.date[inDates.analysis].data, type); + if (outIndex && inIndex) { + routeIndex[type] = Math.round((outIndex + inIndex) / 2); + } + }); + } + } + //For Outbound + updateRouteAnalysisCollumns(outAnalysis, outDates, routeIndex); + updateRouteAnalysisCollumns(inAnalysis, inDates, routeIndex); + }); + }); + } +} + +function routeManagementSortTable(collumn, number) { + let tableRows = $('#aes-table-routeManagement tbody tr'); + let tableBody = $('#aes-table-routeManagement tbody'); + tableBody.empty(); + let indexes = []; + tableRows.each(function() { + if (number) { + let value = parseInt($(this).find("." + collumn).text(), 10); + if (value) { + indexes.push(value); + } else { + indexes.push(0); + } + } else { + indexes.push($(this).find("." + collumn).text()); + } + }); + indexes = [...new Set(indexes)]; + let sorted = [...indexes]; + if (number) { + sorted.sort(function(a, b) { + if (a > b) return -1; + if (a < b) return 1; + if (a = b) return 0; + }); + } else { + sorted.sort(); + } + let same = 1; + for (let i = 0; i < indexes.length; i++) { + if (indexes[i] !== sorted[i]) { + same = 0; + } + } + if (same) { + if (number) { + sorted.sort(function(a, b) { + if (a < b) return -1; + if (a > b) return 1; + if (a = b) return 0; + }); + } else { + sorted.reverse(); + } + } + for (let i = 0; i < sorted.length; i++) { + for (let j = tableRows.length - 1; j >= 0; j--) { + if (number) { + let value = parseInt($(tableRows[j]).find("." + collumn).text(), 10); + if (!value) { + value = 0; + } + if (value == sorted[i]) { + tableBody.append($(tableRows[j])); + tableRows.splice(j, 1); + } + } else { + if ($(tableRows[j]).find("." + collumn).text() == sorted[i]) { + tableBody.append($(tableRows[j])); + tableRows.splice(j, 1); + } + } + } + } +} + +function updateRouteAnalysisCollumns(data, dates, routeIndex) { + + if (data) { + let rowId = '#aes-row-' + data.origin + data.destination; + + if (dates.analysis) { + //Analysis date + $(rowId + ' .aes-analysisDate').text(AES.formatDateString(dates.analysis)); + + //Pricing date + if (dates.pricing) { + $(rowId + ' .aes-pricingDate').text(AES.formatDateString(dates.pricing)); + } + + //Pax Load + $(rowId + ' .aes-paxLoad').html(displayLoad(getRouteAnalysisLoad(data.date[dates.analysis].data, 'pax'))); + + //Cargo Load + $(rowId + ' .aes-cargoLoad').html(displayLoad(getRouteAnalysisLoad(data.date[dates.analysis].data, 'cargo'))); + + //All Load + $(rowId + ' .aes-load').html(displayLoad(getRouteAnalysisLoad(data.date[dates.analysis].data, 'all'))); + + //PAX Index + $(rowId + ' .aes-paxIndex').html(displayIndex(getRouteAnalysisIndex(data.date[dates.analysis].data, 'pax'))); + + //Cargo Index + $(rowId + ' .aes-cargoIndex').html(displayIndex(getRouteAnalysisIndex(data.date[dates.analysis].data, 'cargo'))); + + //PAX Index + $(rowId + ' .aes-index').html(displayIndex(getRouteAnalysisIndex(data.date[dates.analysis].data, 'all'))); + + if (dates.analysisOneBefore) { + //Previous analysis date + $(rowId + ' .aes-analysisPreDate').text(AES.formatDateString(dates.analysisOneBefore)); + + //Pax Load Delta + $(rowId + ' .aes-paxLoadDelta').html(displayRouteAnalysisLoadDelta(data.date[dates.analysis].data, data.date[dates.analysisOneBefore].data, 'pax')); + //Cargo Load Delta + $(rowId + ' .aes-cargoLoadDelta').html(displayRouteAnalysisLoadDelta(data.date[dates.analysis].data, data.date[dates.analysisOneBefore].data, 'cargo')); + //All Load Delta + $(rowId + ' .aes-loadDelta').html(displayRouteAnalysisLoadDelta(data.date[dates.analysis].data, data.date[dates.analysisOneBefore].data, 'all')); + + //PAX Index Delta + $(rowId + ' .aes-paxIndexDelta').html(displayRouteAnalysisIndexDelta(data.date[dates.analysis].data, data.date[dates.analysisOneBefore].data, 'pax')); + //Cargo Index Delta + $(rowId + ' .aes-cargoIndexDelta').html(displayRouteAnalysisIndexDelta(data.date[dates.analysis].data, data.date[dates.analysisOneBefore].data, 'cargo')); + //PAX Index Delta + $(rowId + ' .aes-indexDelta').html(displayRouteAnalysisIndexDelta(data.date[dates.analysis].data, data.date[dates.analysisOneBefore].data, 'all')); + } + + //Route Index + if (routeIndex.pax) { + $(rowId + ' .aes-routeIndexPax').html(displayIndex(routeIndex.pax)); + } + if (routeIndex.cargo) { + $(rowId + ' .aes-routeIndexCargo').html(displayIndex(routeIndex.cargo)); + } + if (routeIndex.all) { + $(rowId + ' .aes-routeIndex').html(displayIndex(routeIndex.all)); + } + } + } + + return; + let analysisDate = dates.analysis; + let pricingDate = dates.pricing; + let paxLoad; + let paxLoadDelta; + let cargoLoad; + let cargoLoadDelta; + let totalLoad; + let totalLoadDelta; + + outDates = getInvPricingAnalaysisPricingDate(dataOut.date); + if (outDates.analysis) { + $('#aes-row-invPricing-' + origin + dest + '-analysis', tbody).text(AES.formatDateString(outDates.analysis)); + outIndex = dataOut.date[outDates.analysis].routeIndex; + let td = $('#aes-row-invPricing-' + origin + dest + '-OWindex', tbody); + td.html(displayIndex(outIndex)); + if (outDates.analysisOneBefore) { + outIndexChange = dataOut.date[outDates.analysis].routeIndex - dataOut.date[outDates.analysisOneBefore].routeIndex + td.append(displayIndexChange(outIndexChange)); + } + } + if (outDates.pricing) { + $('#aes-row-invPricing-' + origin + dest + '-pricing', tbody).text(AES.formatDateString(outDates.pricing)); + } +} + +function displayRouteAnalysisLoadDelta(dataCurrent, dataPrevious, type) { + let load = getRouteAnalysisLoad(dataCurrent, type); + let preLoad = getRouteAnalysisLoad(dataPrevious, type); + if (load && preLoad) { + let diff = load - preLoad; + let span = $(''); + if (diff > 0) { + span.addClass('good').text('+' + diff + "%"); + return span; + } + if (diff < 0) { + span.addClass('bad').text(diff + "%"); + return span; + } + span.addClass('warning').text(diff + "%"); + return span; + } +} + +function displayRouteAnalysisIndexDelta(dataCurrent, dataPrevious, type) { + let index = getRouteAnalysisIndex(dataCurrent, type); + let preIndex = getRouteAnalysisIndex(dataPrevious, type); + if (index && preIndex) { + let diff = index - preIndex; + let span = $(''); + if (diff > 0) { + span.addClass('good').text('+' + diff); + return span; + } + if (diff < 0) { + span.addClass('bad').text(diff); + return span; + } + span.addClass('warning').text(diff); + return span; + } +} + +function getRouteAnalysisLoad(data, type) { + let cmp = []; + switch (type) { + case 'all': + cmp = ['Y', 'C', 'F', 'Cargo']; + break; + case 'pax': + cmp = ['Y', 'C', 'F']; + break; + case 'cargo': + cmp = ['Cargo']; + break; + default: + // code block + } + let cap, bkd; + cap = bkd = 0; + cmp.forEach(function(comp) { + if (data[comp].valid) { + cap += data[comp].totalCap; + bkd += data[comp].totalBkd; + } + }); + if (cap) { + return Math.round(bkd / cap * 100); + } else { + return 0; + } +} + +function displayLoad(load) { + if (load) { + let span = $(''); + if (load >= 70) { + span.addClass('good').text(load + "%"); + return span; + } + if (load < 40) { + span.addClass('bad').text(load + "%"); + return span; + } + span.addClass('warning').text(load + "%"); + return span; + } +} + +function getRouteAnalysisIndex(data, type) { + let cmp = []; + let index = 0; + switch (type) { + case 'all': + cmp = ['Y', 'C', 'F', 'Cargo']; + break; + case 'pax': + cmp = ['Y', 'C', 'F']; + break; + case 'cargo': + cmp = ['Cargo']; + break; + default: + cmp = 0; + break; + } + if (cmp) { + //Multi index + let count = 0; + cmp.forEach(function(comp) { + if (data[comp].valid) { + index += data[comp].index; + count++; + } + }); + if (index) { + return Math.round(index / count); + } + } +} + +function getRouteAnalysisImportantDates(dates) { + //Get latest analysis and pricing date + let latest = { + analysis: 0, + pricing: 0, + analysisOneBefore: 0, + pricingOneBefore: 0 + } + let analysisDates = []; + let pricingDates = [] + for (let date in dates) { + if (Number.isInteger(parseInt(date))) { + if (dates[date].pricingUpdated) { + pricingDates.push(date); + } + analysisDates.push(date); + } + } + analysisDates.reverse(); + pricingDates.reverse(); + if (analysisDates.length) { + latest.analysis = analysisDates[0]; + if (analysisDates[1]) { + latest.analysisOneBefore = analysisDates[1]; + } + } + if (pricingDates.length) { + latest.pricing = pricingDates[0]; + if (pricingDates[1]) { + latest.pricingOneBefore = pricingDates[1]; + } + } + return latest; +} + +function displayIndex(index) { + let span = $(''); + if (index >= 90) { + return span.addClass('good').text(index); + } + if (index <= 50) { + return span.addClass('bad').text(index); + } + return span.addClass('warning').text(index); +} + +function displayIndexChange(index) { + if (index > 0) { + return ' (+' + index + ')'; + } + if (index < 0) { + return ' (' + index + ')'; + } + return ' (' + index + ')'; +} +//Display General +function displayGeneral() { + let mainDiv = $("#aes-div-dashboard"); + mainDiv.empty(); + + //Table + //Head cells + let th1 = $('Area'); + let th2 = $('Status'); + let th3 = $('Action'); + let headRow = $('').append(th1, th2, th3); + let thead = $('').append(headRow); + //Body cells + let tbody = $(''); + generalAddScheduleRow(tbody); + generalAddPersonelManagementRow(tbody); + + + let table = $('
').append(thead, tbody); + //Build layout + let divTable = $('
').append(table); + let title = $('

').text('General'); + let div = $('
').append(divTable); + mainDiv.append(title, div); +} + +//Display COmpetitor Monitoring +function displayCompetitorMonitoring() { + //Div + let div = $('
'); + + //Check ROute Managemetn seetings + // + if (!settings.competitorMonitoring) { + setDefaultCompetitorMonitoringSettings(); + } + + //Display airlines table + displayCompetitorMonitoringAirlinesTable(div); + + let mainDiv = $("#aes-div-dashboard"); + //Build layout + mainDiv.empty(); + let title = $('

').text('Competitor Monitoring'); + mainDiv.append(title, div); + +} + +function displayCompetitorMonitoringAirlinesTable(div) { + let compAirlines = []; + let compAirlinesSchedule = []; + chrome.storage.local.get(null, function(items) { + //Get data + for (let key in items) { + if (items[key].type) { + if (items[key].type == 'competitorMonitoring') { + if (items[key].server == server) { + if (items[key].tracking) { + compAirlines.push(items[key]); + } + } + } + if (items[key].type == 'schedule') { + if (items[key].server == server) { + let airline = items[key].airline + compAirlinesSchedule[airline] = items[key]; + } + } + } + } + + //Check if any airlines exist + let rows = []; + let hrows = []; + if (compAirlines.length) { + //head + //second head collumns + let firstHead = {}; + let th = []; + settings.competitorMonitoring.tableColumns.forEach(function(col) { + if (col.visible) { + //Sort + let sort = $('').html(col.text); + sort.click(function() { + CompetitorMonitoringSortTable(col.field, col.number); + }); + th.push($('').html(sort)); + if (firstHead[col.headGroup]) { + firstHead[col.headGroup]++; + } else { + firstHead[col.headGroup] = 1; + } + } + }); + //first head + let th1 = []; + for (let titles in firstHead) { + th1.push($('').text(titles)); + } + hrows.push($('').append(th1)); + hrows.push($('').append(th)); + + //Data collumns + + compAirlines.forEach(function myFunction(value) { + let data = {}; + //Airline + data.airlineId = value.id; + //All Tab0 Collumns + let dates = []; + for (let date in value.tab0) { + dates.push(date); + } + dates.sort(function(a, b) { return b - a }); + if (dates.length) { + data.airlineCode = value.tab0[dates[0]].code; + data.airlineName = value.tab0[dates[0]].displayName; + data.overviewDate = AES.formatDateString(dates[0]); + data.overviewRating = value.tab0[dates[0]].rating; + data.overviewTotalPax = value.tab0[dates[0]].pax; + data.overviewTotalCargo = value.tab0[dates[0]].cargo; + data.overviewStations = value.tab0[dates[0]].stations; + data.overviewFleet = value.tab0[dates[0]].fleet; + data.overviewStaff = value.tab0[dates[0]].employees; + //If previous date exists + if (dates[1]) { + data.overviewPreDate = AES.formatDateString(dates[1]); + data.overviewRatingDelta = getDelta(getRatingNr(data.overviewRating), getRatingNr(value.tab0[dates[1]].rating)); + data.overviewTotalPaxDelta = getDelta(data.overviewTotalPax, value.tab0[dates[1]].pax); + data.overviewTotalCargoDelta = getDelta(data.overviewTotalCargo, value.tab0[dates[1]].cargo); + data.overviewStationsDelta = getDelta(data.overviewStations, value.tab0[dates[1]].stations); + data.overviewFleetDelta = getDelta(data.overviewFleet, value.tab0[dates[1]].fleet); + data.overviewStaffDelta = getDelta(data.overviewStaff, value.tab0[dates[1]].employees); + } + } + //All Tab2 Collumns + dates = []; + for (let date in value.tab2) { + dates.push(date); + } + dates.sort(function(a, b) { return b - a }); + if (dates.length) { + data.fafWeek = AES.formatDateStringWeek(value.tab2[dates[0]].week); + data.fafAirportsServed = value.tab2[dates[0]].airportsServed; + data.fafOperatedFlights = value.tab2[dates[0]].operatedFlights; + data.fafSeatsOffered = value.tab2[dates[0]].seatsOffered; + data.fafsko = value.tab2[dates[0]].sko; + data.fafCargoOffered = value.tab2[dates[0]].cargoOffered; + data.faffko = value.tab2[dates[0]].fko; + //If previous date exists + if (dates[1]) { + data.fafWeekPre = AES.formatDateStringWeek(value.tab2[dates[1]].week); + data.fafAirportsServedDelta = getDelta(data.fafAirportsServed, value.tab2[dates[1]].airportsServed); + data.fafOperatedFlightsDelta = getDelta(data.fafOperatedFlights, value.tab2[dates[1]].operatedFlights); + data.fafSeatsOfferedDelta = getDelta(data.fafSeatsOffered, value.tab2[dates[1]].seatsOffered); + data.fafskoDelta = getDelta(data.fafsko, value.tab2[dates[1]].sko); + data.fafCargoOfferedDelta = getDelta(data.fafCargoOffered, value.tab2[dates[1]].cargoOffered); + data.faffkoDela = getDelta(data.faffko, value.tab2[dates[1]].fko); + } + } + //Schedule Collumns + if (compAirlinesSchedule[data.airlineCode]) { + dates = []; + for (let date in compAirlinesSchedule[data.airlineCode].date) { + dates.push(date); + } + dates.sort(function(a, b) { return b - a }); + if (dates.length) { + let hubs = {}; + //For display + data.scheduleDate = AES.formatDateString(dates[0]); + //For table + data.scheduleDateUse = dates[0]; + data.scheduleCargoFreq = 0; + data.schedulePAXFreq = 0; + data.scheduleFltNr = 0; + compAirlinesSchedule[data.airlineCode].date[dates[0]].schedule.forEach(function(schedule) { + //Hubs + let hub = schedule.od.slice(0, 3); + if (hubs[hub]) { + hubs[hub]++; + } else { + hubs[hub] = 1; + } + for (let flight in schedule.flightNumber) { + //Cargo Freq + data.scheduleCargoFreq += schedule.flightNumber[flight].cargoFreq; + //Pax Freq + data.schedulePAXFreq += schedule.flightNumber[flight].paxFreq; + //Flight nr + data.scheduleFltNr++; + } + }); + //Total Frequency + data.scheduleTotalFreq = data.schedulePAXFreq + data.scheduleCargoFreq; + //Hubs + let hubArray = []; + for (let hub in hubs) { + hubArray.push([hub, hubs[hub]]); + } + hubArray.sort(function(a, b) { + return b[1] - a[1]; + }); + data.scheduleHubs = ''; + hubArray.forEach(function(hubA, index) { + if (index) { + data.scheduleHubs += ', '; + } + data.scheduleHubs += hubA[0] + ' (' + hubA[1] + ')'; + }); + + //Previous schedule data + if (dates[1]) { + data.scheduleDatePre = AES.formatDateString(dates[1]); + data.scheduleCargoFreqPre = 0; + data.schedulePAXFreqPre = 0; + data.scheduleFltNrPre = 0; + compAirlinesSchedule[data.airlineCode].date[dates[1]].schedule.forEach(function(schedule) { + //Hubs + let hub = schedule.od.slice(0, 3); + if (hubs[hub]) { + hubs[hub]++; + } else { + hubs[hub] = 1; + } + for (let flight in schedule.flightNumber) { + //Cargo Freq + data.scheduleCargoFreqPre += schedule.flightNumber[flight].cargoFreq; + //Pax Freq + data.schedulePAXFreqPre += schedule.flightNumber[flight].paxFreq; + //Flight nr + data.scheduleFltNrPre++; + } + }); + //Total Frequency + data.scheduleTotalFreqPre = data.schedulePAXFreq + data.scheduleCargoFreq; + //Delta Collumns + data.scheduleFltNrDelta = getDelta(data.scheduleFltNr, data.scheduleFltNrPre); + data.schedulePAXFreqDelta = getDelta(data.schedulePAXFreq, data.schedulePAXFreqPre); + data.scheduleCargoFreqDelta = getDelta(data.scheduleCargoFreq, data.scheduleCargoFreqPre); + data.scheduleTotalFreqDelta = getDelta(data.scheduleTotalFreq, data.scheduleTotalFreqPre); + } + } + } + //Action collumns + //Open airline + data.actionOpenAirline = 'Airline'; + //Open schedule + if (compAirlinesSchedule[data.airlineCode]) { + data.actionOpenSchedule = $(''); + //Create schedule table + $('#aes-div-dashboard').on('click', 'button#aes-compMon-btn-schedule-' + data.airlineCode, function() { + displayCompetitorMonitoringAirlineScheduleTable(div, compAirlinesSchedule[data.airlineCode], data); + }); + } + //Remove airline ' + data.actionRemoveAirline = $(''); + //Remove airline action + $('#aes-div-dashboard').on('click', 'button#aes-compMon-btn-remove-' + data.airlineCode, function() { + let key = server + data.airlineId + 'competitorMonitoring'; + let remove = $(this); + chrome.storage.local.get([key], function(compMonitoringData) { + let compData = compMonitoringData[key]; + compData.tracking = 0; + chrome.storage.local.set({ + [compData.key]: compData }, function() { + $(remove).closest("tr").remove(); + }); + }); + }); + + //Populate collumns + let td = []; + settings.competitorMonitoring.tableColumns.forEach(function(col) { + if (col.visible) { + td.push($('').html(data[col.field])); + } + }); + rows.push($('').append(td)); + + }); + } else { + rows.push('No airlines marked for competitor monitoring. Open airline info page to mark airline for tracking.'); + } + + let thead = $('').append(hrows); + let tbody = $('').append(rows); + + let table = $('
').append(thead, tbody); + let tableWell = $('
').append(table); + + //Options + let divRow = $('
').append(displayCompetitorMonitoringAirlinesTableOptions(), displayCompetitorMonitoringAirlinesTableCollumns()); + div.append(divRow, tableWell); + }); +} + +function displayCompetitorMonitoringAirlineScheduleTable(mainDiv, scheduleData, data) { + mainDiv.hide(); + //Build schedule rows + let rows = []; + let hrow = []; + if (data.scheduleDateUse) { + let collumns = [ + { + field: 'schedOrigin', + text: 'Origin', + headGroup: 'Schedule', + visible: 1, + number: 0, + }, + { + field: 'schedDestination', + text: 'Destination', + headGroup: 'Schedule', + visible: 1, + number: 0, + }, + { + field: 'schedHub', + text: 'Hub', + headGroup: 'Schedule', + visible: 1, + number: 0, + }, + { + field: 'schedOd', + text: 'OD', + headGroup: 'Schedule', + visible: 1, + number: 0, + }, + { + field: 'schedDir', + text: 'Direction', + headGroup: 'Schedule', + visible: 1, + number: 0, + }, + { + field: 'schedFltNr', + text: '# of flight numbers', + headGroup: 'Schedule', + visible: 1, + number: 1, + }, + { + field: 'schedPaxFreq', + text: 'PAX frequency', + headGroup: 'Schedule', + visible: 1, + number: 1, + }, + { + field: 'schedCargoFreq', + text: 'Cargo frequency', + headGroup: 'Schedule', + visible: 1, + number: 1, + }, + { + field: 'schedTotalFreq', + text: 'Total Frequency', + headGroup: 'Schedule', + visible: 1, + number: 1, + } + ]; + //Table Head + let th = []; + collumns.forEach(function(col) { + if (col.visible) { + //Sort + let sort = $('').html(col.text); + sort.click(function() { + SortTable(col.field, col.number, 'aes-table-competitorMonitoring-airline-schedule', 'aes-comp-sched-'); + }); + th.push($('').html(sort)); + } + }); + hrow.push($('').append(th)); + //Table Body + scheduleData.date[data.scheduleDateUse].schedule.forEach(function(od) { + let td = []; + let fltNr = 0; + let paxFreq = 0; + let cargoFreq = 0; + for (let flight in od.flightNumber) { + cargoFreq += od.flightNumber[flight].cargoFreq, + paxFreq += od.flightNumber[flight].paxFreq, + fltNr++; + } + let totalFreq = cargoFreq + paxFreq; + //hub + let hub = od.od.slice(0, 3); + let cellValue = { + schedOrigin: od.origin, + schedDestination: od.destination, + schedOd: od.od, + schedDir: od.direction, + schedFltNr: fltNr, + schedPaxFreq: paxFreq, + schedCargoFreq: cargoFreq, + schedTotalFreq: totalFreq, + schedHub: hub + } + collumns.forEach(function(cell) { + if (cell.visible) { + td.push($('').html(cellValue[cell.field])); + } + }); + rows.push($('').append(td)); + }); + } else { + rows.push('No schedule found'); + } + + //Build layout + let thead = $('').append(hrow); + let tbody = $('').append(rows); + let table = $('
').append(thead, tbody); + let tableWell = $('
').append(table); + let button = $(''); + let panelDiv = $('
').append(button, tableWell); + let heading = $('

' + data.airlineName + ' ' + data.airlineCode + ' schedule

'); + let div = $('
').append(heading, panelDiv); + mainDiv.after(div); + //Button clicks + button.click(function() { + div.remove(); + mainDiv.show(); + }); +} + +function displayCompetitorMonitoringAirlinesTableCollumns() { + //Table Head + let th = []; + th.push('Show'); + th.push('Column'); + let thead = $('').append($('').append(th)); + //Table body + let tbody = $(''); + + settings.competitorMonitoring.tableColumns.forEach(function(col) { + let td = []; + //Checkbox + if (col.visible) { + td.push(''); + } else { + td.push(''); + } + //Name + td.push('' + col.text + ''); + tbody.append($('').append(td)); + }); + + let table = $('
').append(thead, tbody); + let divTable = $('').append(table); + //Collumns selector Checkbox listener + $('input', table).change(function() { + let show; + if (this.checked) { + show = 1; + } else { + show = 0; + } + let value = $(this).val(); + settings.competitorMonitoring.tableColumns.forEach(function(col) { + if (col.field == value) { + col.visible = show; + } + }); + chrome.storage.local.set({ settings: settings }, function() {}); + }); + //Closable legend + let link = $('').text('Columns'); + let legend = $('').html(link); + link.click(function() { + $('#aes-div-competitorMonitoring-collumns').toggle(); + }); + let fieldset = $('
').append(legend, divTable); + let div = $('
').append(fieldset); + return div; +} + +function displayCompetitorMonitoringAirlinesTableOptions() { + let divFieldset = $('
').html('Options'); + let btn = $(''); + divFieldset.append(btn); + let optionsDiv = $('
').append(divFieldset); + //Reload table + btn.click(function() { + displayCompetitorMonitoring(); + }); + + return optionsDiv; +} + +function CompetitorMonitoringSortTable(collumn, number) { + let tableRows = $('#aes-table-competitorMonitoring tbody tr'); + let tableBody = $('#aes-table-competitorMonitoring tbody'); + tableBody.empty(); + let indexes = []; + tableRows.each(function() { + if (number) { + let value = parseInt($(this).find(".aes-" + collumn).text(), 10); + if (value) { + indexes.push(value); + } else { + indexes.push(0); + } + } else { + indexes.push($(this).find(".aes-" + collumn).text()); + } + }); + indexes = [...new Set(indexes)]; + let sorted = [...indexes]; + if (number) { + sorted.sort(function(a, b) { + if (a > b) return -1; + if (a < b) return 1; + if (a = b) return 0; + }); + } else { + sorted.sort(); + } + let same = 1; + for (let i = 0; i < indexes.length; i++) { + if (indexes[i] !== sorted[i]) { + same = 0; + } + } + if (same) { + if (number) { + sorted.sort(function(a, b) { + if (a < b) return -1; + if (a > b) return 1; + if (a = b) return 0; + }); + } else { + sorted.reverse(); + } + } + for (let i = 0; i < sorted.length; i++) { + for (let j = tableRows.length - 1; j >= 0; j--) { + if (number) { + let value = parseInt($(tableRows[j]).find(".aes-" + collumn).text(), 10); + if (!value) { + value = 0; + } + if (value == sorted[i]) { + tableBody.append($(tableRows[j])); + tableRows.splice(j, 1); + } + } else { + if ($(tableRows[j]).find(".aes-" + collumn).text() == sorted[i]) { + tableBody.append($(tableRows[j])); + tableRows.splice(j, 1); + } + } + } + } +} + +function setDefaultCompetitorMonitoringSettings() { + let columns = [ + { + field: 'airlineId', + text: 'ID', + headGroup: 'Airline', + visible: 1, + number: 1 + }, + { + field: 'airlineCode', + text: 'Code', + headGroup: 'Airline', + visible: 1, + number: 0 + }, + { + field: 'airlineName', + text: 'Name', + headGroup: 'Airline', + visible: 1, + number: 0 + }, + { + field: 'overviewDate', + text: 'Overview date', + headGroup: 'Overview', + visible: 0, + number: 0 + }, + { + field: 'overviewPreDate', + text: 'Overview previous date', + headGroup: 'Overview', + visible: 0, + number: 0 + }, + { + field: 'overviewRating', + text: 'Rating', + headGroup: 'Overview', + visible: 1, + number: 0 + }, + { + field: 'overviewRatingDelta', + text: 'Rating Δ', + headGroup: 'Overview', + visible: 0, + number: 0 + }, + { + field: 'overviewTotalPax', + text: 'Total pax', + headGroup: 'Overview', + visible: 1, + number: 1 + }, + { + field: 'overviewTotalPaxDelta', + text: 'Total pax Δ', + headGroup: 'Overview', + visible: 1, + number: 1 + }, + { + field: 'overviewTotalCargo', + text: 'Total cargo', + headGroup: 'Overview', + visible: 1, + number: 1 + }, + { + field: 'overviewTotalCargoDelta', + text: 'Total cargo Δ', + headGroup: 'Overview', + visible: 1, + number: 1 + }, + { + field: 'overviewStations', + text: 'Stations', + headGroup: 'Overview', + visible: 1, + number: 1 + }, + { + field: 'overviewStationsDelta', + text: 'Stations Δ', + headGroup: 'Overview', + visible: 1, + number: 1 + }, + { + field: 'overviewFleet', + text: 'Fleet', + headGroup: 'Overview', + visible: 1, + number: 1 + }, + { + field: 'overviewFleetDelta', + text: 'Fleet Δ', + headGroup: 'Overview', + visible: 1, + number: 1 + }, + { + field: 'overviewStaff', + text: 'Staff', + headGroup: 'Overview', + visible: 0, + number: 1 + }, + { + field: 'overviewStaffDelta', + text: 'Staff Δ', + headGroup: 'Overview', + visible: 0, + number: 1 + }, + { + field: 'fafWeek', + text: 'Week', + headGroup: 'Figures', + visible: 0, + number: 0 + }, + { + field: 'fafWeekPre', + text: 'Previous week', + headGroup: 'Figures', + visible: 0, + number: 0 + }, + { + field: 'fafAirportsServed', + text: 'Airports served', + headGroup: 'Figures', + visible: 1, + number: 1 + }, + { + field: 'fafAirportsServedDelta', + text: 'Airports served Δ', + headGroup: 'Figures', + visible: 0, + number: 1 + }, + { + field: 'fafOperatedFlights', + text: 'Operated flights', + headGroup: 'Figures', + visible: 1, + number: 1 + }, + { + field: 'fafOperatedFlightsDelta', + text: 'Operated flights Δ', + headGroup: 'Figures', + visible: 1, + number: 1 + }, + { + field: 'fafSeatsOffered', + text: 'Seats offered', + headGroup: 'Figures', + visible: 1, + number: 1 + }, + { + field: 'fafSeatsOfferedDelta', + text: 'Seats offered Δ', + headGroup: 'Figures', + visible: 1, + number: 1 + }, + { + field: 'fafsko', + text: 'SKO', + headGroup: 'Figures', + visible: 0, + number: 1 + }, + { + field: 'fafskoDelta', + text: 'SKO Δ', + headGroup: 'Figures', + visible: 0, + number: 1 + }, + { + field: 'fafCargoOffered', + text: 'Cargo offered', + headGroup: 'Figures', + visible: 1, + number: 1 + }, + { + field: 'fafCargoOfferedDelta', + text: 'Cargo offered Δ', + headGroup: 'Figures', + visible: 1, + number: 1 + }, + { + field: 'faffko', + text: 'FKO', + headGroup: 'Figures', + visible: 0, + number: 1 + }, + { + field: 'faffkoDela', + text: 'FKO Δ', + headGroup: 'Figures', + visible: 0, + number: 1 + }, + { + field: 'scheduleDate', + text: 'Schedule Date', + headGroup: 'Schedule', + visible: 0, + number: 0 + }, + { + field: 'scheduleDatePre', + text: 'Previous Schedule Date', + headGroup: 'Schedule', + visible: 0, + number: 0 + }, + { + field: 'scheduleHubs', + text: 'Hubs (routes)', + headGroup: 'Schedule', + visible: 1, + number: 0 + }, + { + field: 'scheduleFltNr', + text: '# of flight numbers', + headGroup: 'Schedule', + visible: 0, + number: 1 + }, + { + field: 'scheduleFltNrDelta', + text: '# of flight numbers Δ', + headGroup: 'Schedule', + visible: 0, + number: 1 + }, + { + field: 'schedulePAXFreq', + text: 'PAX frequency', + headGroup: 'Schedule', + visible: 0, + number: 1 + }, + { + field: 'schedulePAXFreqDelta', + text: 'PAX frequency Δ', + headGroup: 'Schedule', + visible: 0, + number: 1 + }, + { + field: 'scheduleCargoFreq', + text: 'Cargo frequency', + headGroup: 'Schedule', + visible: 0, + number: 1 + }, + { + field: 'scheduleCargoFreqDelta', + text: 'Cargo frequency Δ', + headGroup: 'Schedule', + visible: 0, + number: 1 + }, + { + field: 'scheduleTotalFreq', + text: 'Total frequency', + headGroup: 'Schedule', + visible: 1, + number: 1 + }, + { + field: 'scheduleTotalFreqDelta', + text: 'Total frequency Δ', + headGroup: 'Schedule', + visible: 1, + number: 1 + }, + { + field: 'actionOpenAirline', + text: 'Open airline page', + headGroup: 'Actions', + visible: 1, + number: 0 + }, + { + field: 'actionOpenSchedule', + text: 'Show airline schedule', + headGroup: 'Actions', + visible: 1, + number: 0 + }, + { + field: 'actionRemoveAirline', + text: 'Remove airline', + headGroup: 'Actions', + visible: 0, + number: 0 + } + ]; + settings.competitorMonitoring = { + tableColumns: columns + }; +} + +function getRatingNr(rating) { + switch (rating) { + case 'AAA': + return 10; + break; + case 'AA': + return 9; + break; + case 'A': + return 8; + break; + case 'BBB': + return 7; + break; + case 'BB': + return 6; + break; + case 'B': + return 5; + break; + case 'CCC': + return 4; + break; + case 'CC': + return 3; + break; + case 'C': + return 2; + break; + case 'D': + return 1; + break; + default: + return 0; + } +} + +function getDelta(newNr, oldNr) { + return newNr - oldNr; +}; +//Display Aircraft aircraftProfitability +function displayAircraftProfitability() { + if (!settings.aircraftProfitability) { + settings.aircraftProfitability = {}; + } + if (!settings.aircraftProfitability.hideColumn) { + settings.aircraftProfitability.hideColumn = []; + } + //columns + let columns = [ + { + category: 'Aircraft', + title: 'Aircraft ID', + data: 'aircraftId', + sortable: 1, + visible: 1, + number: 1, + id: 1 + }, + { + category: 'Aircraft', + title: 'Registration', + data: 'registration', + sortable: 1, + visible: 1 + }, + { + category: 'Aircraft', + title: 'Equipment', + data: 'equipment', + sortable: 1, + visible: 1 + }, + { + category: 'Aircraft', + title: 'Fleet', + data: 'fleet', + sortable: 1, + visible: 1 + }, + { + category: 'Aircraft', + title: 'Nickname', + data: 'nickname', + sortable: 1, + visible: 1 + }, + { + category: 'Aircraft', + title: 'Note', + data: 'note', + sortable: 1, + visible: 1 + }, + { + category: 'Aircraft', + title: 'Age', + data: 'age', + sortable: 1, + visible: 1, + number: 1 + }, + { + category: 'Aircraft', + title: 'Maintenance', + data: 'maintenance', + sortable: 1, + visible: 1, + number: 1 + }, + { + category: 'Aircraft', + title: 'Date', + data: 'dateAircraft', + sortable: 1, + visible: 1 + }, + { + category: 'Profit', + title: 'Total flights', + data: 'totalFlights', + sortable: 1, + visible: 1, + number: 1 + }, + { + category: 'Profit', + title: 'Finished flights', + data: 'finishedFlights', + sortable: 1, + visible: 1, + number: 1 + }, + { + category: 'Profit', + title: 'Profit/loss flights', + data: 'profitFlights', + sortable: 1, + visible: 1, + number: 1 + }, + { + category: 'Profit', + title: 'Profit', + data: 'profit', + sortable: 1, + visible: 1, + number: 1, + format: 'money' + }, + { + category: 'Profit', + title: 'Profit extract date', + data: 'dateProfit', + sortable: 1, + visible: 1 + } + ]; + if (settings.aircraftProfitability.hideColumn.length) { + columns.forEach(function(column) { + settings.aircraftProfitability.hideColumn.forEach(function(hideColumn) { + if (column.data == hideColumn) { + column.visible = 0; + } + }); + }); + } + + let key = server + airline.name + 'aircraftFleet'; + //Get storage fleet data + chrome.storage.local.get(key, function(result) { + //get aircraft flight data + let aircraftFleetData = result[key]; + if (aircraftFleetData) { + let keys = []; + aircraftFleetData.fleet.forEach(function(value) { + keys.push(server + 'aircraftFlights' + value.aircraftId); + }); + chrome.storage.local.get(keys, function(result) { + for (let aircraftFlightData in result) { + for (let i = 0; i < aircraftFleetData.fleet.length; i++) { + if (aircraftFleetData.fleet[i].aircraftId == result[aircraftFlightData].aircraftId) { + aircraftFleetData.fleet[i].profit = { + date: result[aircraftFlightData].date, + finishedFlights: result[aircraftFlightData].finishedFlights, + profit: result[aircraftFlightData].profit, + profitFlights: result[aircraftFlightData].profitFlights, + time: result[aircraftFlightData].time, + totalFlights: result[aircraftFlightData].totalFlights, + }; + } + } + } + let data = prepareAircraftProfitabilityData(aircraftFleetData); + let tableDiv; + if (data.length) { + tableDiv = generateTable({ + column: columns, + data: data, + columnPrefix: 'aes-aircraftProfit-', + tableSettings: 1, + options: ['openAircraft', 'removeAircraft', 'reloadTableAircraftProfit', 'applyFilter', 'removeSelected'], + filter: settings.aircraftProfitability.filter, + hideColumn: settings.aircraftProfitability.hideColumn, + tableSettingStorage: 'aircraftProfitability' + }); + } else { + //Never happens or only when fleet = 0 because of updated script this output is copied bellow + tableDiv = $('

').text('No aircraft data in memory. Open fleet management to extract aircraft data.') + } + //Div + let div = $('
').append(tableDiv); + let mainDiv = $("#aes-div-dashboard"); + //Build layout + mainDiv.empty(); + let title = $('

').text('Aircraft Profitability'); + mainDiv.append(title, div); + + }); + } else { + //No data + //Div + let tableDiv = $('

').text('No aircraft data in memory. Open fleet management to extract aircraft data.') + let div = $('
').append(tableDiv); + let mainDiv = $("#aes-div-dashboard"); + //Build layout + mainDiv.empty(); + let title = $('

').text('Aircraft Profitability'); + mainDiv.append(title, div); + } + }); + + function prepareAircraftProfitabilityData(storage) { + let data = []; + storage.fleet.forEach(function(value) { + let profit = {}; + if (value.profit) { + profit.totalFlights = value.profit.totalFlights; + profit.finishedFlights = value.profit.finishedFlights; + profit.profitFlights = value.profit.profitFlights; + profit.profit = value.profit.profit; + profit.dateProfit = AES.formatDateString(value.profit.date) + ' ' + value.profit.time; + } + data.push({ + aircraftId: value.aircraftId, + registration: value.registration, + equipment: value.equipment, + fleet: value.fleet, + nickname: value.nickname, + note: value.note, + age: value.age, + maintenance: value.maintanance, + dateAircraft: AES.formatDateString(value.date) + ' ' + value.time, + totalFlights: profit.totalFlights, + finishedFlights: profit.finishedFlights, + profitFlights: profit.profitFlights, + profit: profit.profit, + dateProfit: profit.dateProfit + }); + }); + return data; + } +} + +//Auto table generator +function generateTable(tableOptionsRule) { + let tableHtml = $('
'); + let table = { cell: {}, row: {}, tableHtml: tableHtml }; + //Table Categories + let tableCategory = {}; + tableOptionsRule.column.forEach(function(value) { + if (value.visible) { + if (!tableCategory[value.category]) { + tableCategory[value.category] = 1; + } else { + tableCategory[value.category]++; + } + } + }); + table.cell.category = []; + //Add checkbox + if (tableOptionsRule.tableSettings) { + table.cell.category.push('') + } + for (let category in tableCategory) { + table.cell.category.push('' + category + ''); + } + + //Table Headers + table.cell.header = []; + tableOptionsRule.column.forEach(function(value) { + if (value.visible) { + if (value.sortable) { + //Sort + let sort = $('').text(value.title); + sort.click(function() { + masterSortTable(value.data, value.number, table.tableHtml, tableOptionsRule.columnPrefix); + }); + table.cell.header.push($('').html(sort)); + } else { + table.cell.header.push('' + value.title + ''); + } + } + }); + //Head + table.row.head = []; + table.row.head.push($('').append(table.cell.category)); + table.row.head.push($('').append(table.cell.header)); + //Table Body + table.row.body = []; + tableOptionsRule.data.forEach(function(dataValue) { + let cell = []; + //Add checkbox + if (tableOptionsRule.tableSettings) { + cell.push(''); + } + let id; + tableOptionsRule.column.forEach(function(colValue) { + if (colValue.id) { + id = dataValue[colValue.data] + } + if (colValue.visible) { + let td = $('').addClass(tableOptionsRule.columnPrefix + colValue.data); + if (colValue.format) { + td.html(masterCellFormat(colValue.format, dataValue[colValue.data])) + } else { + td.html(dataValue[colValue.data]) + } + cell.push(td); + } + }); + table.row.body.push($('').attr('id', id).append(cell)); + }); + let thead = $('').append(table.row.head); + let tbody = $('').append(table.row.body); + table.tableHtml.append(thead, tbody); + let tableWell = $('
').append(table.tableHtml); + + //Table Settings + let settingsDiv = ''; + if (tableOptionsRule.tableSettings) { + let divCol = []; + //Options + divCol.push($('
').html(masterTableOptions(table.tableHtml, tableOptionsRule.options))); + divCol.push($('
').html(masterTableFilter(tableOptionsRule.filter, tableOptionsRule.column))); + divCol.push($('
').html(masterTableColumns())); + settingsDiv = $('
').append(divCol) + } + + let div = $('
').append(settingsDiv, tableWell); + return div; + //Table functions + function masterSortTable(collumn, number, table, collumnPrefix) { + let tableRows = $('tbody tr', table); + let tableBody = $('tbody', table); + tableBody.empty(); + let indexes = []; + tableRows.each(function() { + if (number) { + let value = parseInt($(this).find("." + collumnPrefix + collumn).text(), 10); + if (value) { + indexes.push(value); + } else { + indexes.push(0); + } + } else { + indexes.push($(this).find("." + collumnPrefix + collumn).text()); + } + }); + indexes = [...new Set(indexes)]; + let sorted = [...indexes]; + if (number) { + sorted.sort(function(a, b) { + if (a > b) return -1; + if (a < b) return 1; + if (a = b) return 0; + }); + } else { + sorted.sort(); + } + let same = 1; + for (let i = 0; i < indexes.length; i++) { + if (indexes[i] !== sorted[i]) { + same = 0; + } + } + if (same) { + if (number) { + sorted.sort(function(a, b) { + if (a < b) return -1; + if (a > b) return 1; + if (a = b) return 0; + }); + } else { + sorted.reverse(); + } + } + for (let i = 0; i < sorted.length; i++) { + for (let j = tableRows.length - 1; j >= 0; j--) { + if (number) { + let value = parseInt($(tableRows[j]).find("." + collumnPrefix + collumn).text(), 10); + if (!value) { + value = 0; + } + if (value == sorted[i]) { + tableBody.append($(tableRows[j])); + tableRows.splice(j, 1); + } + } else { + if ($(tableRows[j]).find("." + collumnPrefix + collumn).text() == sorted[i]) { + tableBody.append($(tableRows[j])); + tableRows.splice(j, 1); + } + } + } + } + } + + function masterCellFormat(type, value) { + if (!value) { + return ''; + } + switch (type) { + case 'money': + let span = $(''); + let text = ''; + if (value > 0) { + span.addClass('good'); + text = '+' + } + if (value < 0) { + span.addClass('bad'); + } + text = text + new Intl.NumberFormat().format(value) + ' AS$'; + span.text(text); + return span; + break; + default: + return value; + } + } + + function masterTableOptions(table, options) { + let div = $('
'); + options.forEach(function(value, index) { + if (index) { + let span = $(' '); + div.append(span); + } + div.append(masterTableOptionsHandle(value)); + }); + //Closable legend + let link = $('').text('Options'); + let legend = $('').html(link); + link.click(function() { + div.toggle(); + }); + let fieldset = $('
').append(legend, div); + return fieldset; + + //Functions + function masterTableOptionsHandle(value) { + switch (value) { + case 'openAircraft': + return masterTableOptionsOpenAircraft(); + break; + case 'reloadTableAircraftProfit': + return masterTableOptionsReloadTableAP(); + break; + case 'removeAircraft': + return masterTableOptionsRemoveAircraft(); + break; + case 'applyFilter': + return masterTableOptionsApplyFilter(); + break; + case 'removeSelected': + return masterTableOptionsRemoveSelected(); + break; + default: + // code block + } + //Option Functions + function masterTableOptionsOpenAircraft() { + let btn = $(''); + btn.click(function() { + let urls = $('tbody tr', table).has('input:checked').map(function() { + let id = $(this).attr('id'); + let url = 'https://' + server + '.airlinesim.aero/app/fleets/aircraft/' + id + '/1'; + return url; + }).toArray(); + //Open new tabs + for (let i = 0; i < urls.length; i++) { + window.open(urls[i], '_blank'); + if (i == 10) { + break; + } + } + }); + return btn; + } + + function masterTableOptionsReloadTableAP() { + let btn = $(''); + btn.click(function() { + displayAircraftProfitability(); + }); + return btn; + } + + function masterTableOptionsRemoveAircraft() { + let btn = $(''); + btn.click(function() { + let id = []; + let aircraftKey = []; + $('tbody tr', table).has('input:checked').each(function() { + let localId = $(this).attr('id'); + id.push(localId); + aircraftKey.push(server + 'aircraftFlights' + localId); + $(this).remove(); + }); + if (id.length) { + let fleetKey = server + airline.name + 'aircraftFleet'; + chrome.storage.local.get(fleetKey, function(result) { + let storedFleetData = result[fleetKey]; + let newFleet = storedFleetData.fleet.filter(function(value) { + let keep = 1; + id.forEach(function(idVal) { + if (idVal == value.aircraftId) { + keep = 0; + } + }); + return keep; + }); + storedFleetData.fleet = newFleet; + chrome.storage.local.set({ + [fleetKey]: storedFleetData }, function() { + chrome.storage.local.remove(aircraftKey, function() {}); + }); + }); + } + }); + return btn; + } + + function masterTableOptionsApplyFilter() { + let btn = $(''); + btn.click(function() { + let filter = []; + table.closest(".as-panel").find('fieldset:eq(1) table tbody tr').each(function() { + filter.push({ + titlecode: $(this).find('input').val(), + title: $(this).find('td:eq(0)').text(), + operation: $(this).find('td:eq(1)').text(), + value: $(this).find('td:eq(2)').text() + }) + }); + settings[tableOptionsRule.tableSettingStorage].filter = filter; + chrome.storage.local.set({ settings: settings }, function() { + $('tbody tr', table).each(function() { + let row = this; + filter.forEach(function(filter) { + let cell = $(row).find("." + tableOptionsRule.columnPrefix + filter.titlecode).text(); + //if(cell){ + //Get collumn info if number or not + let number; + for (let i = 0; i < tableOptionsRule.column.length; i++) { + let column = tableOptionsRule.column[i]; + if (filter.titlecode == column.data) { + number = column.number; + break; + } + } + let value = filter.value; + if (number) { + if (cell) { + cell = parseInt(cell, 10); + } + if (value) { + value = parseInt(value, 10); + } + } + switch (filter.operation) { + case '=': + if (cell != value) { + $(row).remove(); + } + break; + case '!=': + if (cell == value) { + $(row).remove(); + } + break; + case '>': + if (cell < value) { + $(row).remove(); + } + break; + case '<': + if (cell > value) { + $(row).remove(); + } + } + }); + }); + }); + }); + return btn; + } + + function masterTableOptionsRemoveSelected() { + let btn = $(''); + btn.click(function() { + $('tbody tr', table).has('input:checked').remove(); + }); + return btn; + } + } + } + + function masterTableFilter(filter, column) { + //Table head + let th = []; + th.push('Column'); + th.push('Operation'); + th.push('Value'); + th.push(''); + let thead = $('').append($('').append(th)); + //Table body + let row = []; + if (filter) { + filter.forEach(function(fil) { + row.push($('').append(masterTableFilterAddBodyRow(fil.titlecode, fil.title, fil.operation, fil.value))); + }); + } + + let tbody = $('').append(row); + //Table foot + //select collumn + let option1 = []; + column.forEach(function(col) { + option1.push(''); + }); + let select1 = $('').append(option1); + //Select value + let option = []; + option.push(''); + option.push(''); + option.push(''); + option.push(''); + let select = $('').append(option); + //Value + let input = $(''); + //Add button + let btn = $('').text('Add Row'); + btn.click(function() { + let column = $('option:selected', select1).text(); + let columnVal = $('option:selected', select1).val(); + let operation = $('option:selected', select).text(); + let value = input.val(); + tbody.append($('').append(masterTableFilterAddBodyRow(columnVal, column, operation, value))); + }); + //Footer rows + let tf = []; + tf.push($('').html(select1)); + tf.push($('').html(select)); + tf.push($('').html(input)); + tf.push($('').append(btn)); + let tfoot = $('').append($('').append(tf)); + let tableFilter = $('
').append(thead, tbody, tfoot); + let divTable = $('
').append(tableFilter); + //Closable legend + let link = $('').text('Filter'); + let legend = $('').html(link); + link.click(function() { + divTable.toggle(); + }); + let fieldset = $('
').append(legend, divTable); + return fieldset; + //Functions + function masterTableFilterAddBodyRow(titleCode, title, operation, value) { + let td = []; + td.push('' + title + ''); + td.push('' + operation + ''); + td.push('' + value + ''); + let deleteBtn = $('').html(''); + deleteBtn.click(function() { + $(this).closest("tr").remove(); + }); + td.push($('').append(deleteBtn)); + return td; + } + } + + function masterTableColumns() { + //Table head + let th = []; + th.push('Show'); + th.push('Column'); + let thead = $('').append($('').append(th)); + //Table body + let row = []; + tableOptionsRule.column.forEach(function(col) { + let td = [] + let input = $(''); + input.change(function() { + if (!tableOptionsRule.hideColumn) { + tableOptionsRule.hideColumn = []; + } + let newHideColumns = []; + let currentColumn = $(this).val(); + newHideColumns = tableOptionsRule.hideColumn.filter(function(value) { + return value != currentColumn; + }); + if (!this.checked) { + newHideColumns.push(currentColumn); + } + + tableOptionsRule.hideColumn = newHideColumns; + settings[tableOptionsRule.tableSettingStorage].hideColumn = tableOptionsRule.hideColumn; + chrome.storage.local.set({ settings: settings }, function() {}); + }) + if (col.visible) { + input.prop('checked', true); + } + td.push($('').append(input)); + td.push($('').text(col.title)); + row.push($('').append(td)); + }); + let tbody = $('').append(row); + let tableColumns = $('
').append(thead, tbody); + let divTable = $('
').append(tableColumns).hide(); + //Closable legend + let link = $('').text('Columns'); + let legend = $('').html(link); + link.click(function() { + divTable.toggle(); + }); + let fieldset = $('
').append(legend, divTable); + return fieldset; + } +} +//Display general helper functions +function generalAddScheduleRow(tbody) { + let td1 = $('').text("Schedule"); + let td2 = $(''); + let td3 = $(''); + let row = $('').append(td1, td2, td3); + tbody.append(row); + //Get schedule + let scheduleKey = server + airline.code + 'schedule'; + chrome.storage.local.get([scheduleKey], function(result) { + let scheduleData = result[scheduleKey]; + if (scheduleData) { + let lastUpdate = getDate('schedule', scheduleData.date); + let diff = AES.getDateDiff([todayDate.date, lastUpdate]); + let span = $('').text('Last schedule extract ' + AES.formatDateString(lastUpdate) + ' (' + diff + ' days ago). Extract new schedule if there are new routes.'); + if (diff >= 0 && diff < 7) { + span.addClass('good'); + } else { + span.addClass('warning'); + } + td2.append(span); + generalUpdateScheduleAction(td3); + + + + } else { + //no schedule + td2.html('No Schedule data found. Extract schedule or some AES parts will not work'); + generalUpdateScheduleAction(td3); + } + }); +} + +function generalUpdateScheduleAction(td3) { + let btn = $(''); + btn.click(function() { + settings.schedule.autoExtract = 1; + //get schedule link + let link = $('#enterprise-dashboard table:eq(0) tfoot td a:eq(2)'); + chrome.storage.local.set({ settings: settings }, function() { + link[0].click(); + }); + }); + td3.append(btn); +} + +function generalAddPersonelManagementRow(tbody) { + let td = []; + td.push($('').text("Personnel Management")); + td.push($('')); + td.push($('')); + let row = $('').append(td); + tbody.append(row); + //Get Status + let key = server + airline.name + 'personelManagement'; + chrome.storage.local.get([key], function(result) { + let personelManagementData = result[key]; + if (personelManagementData) { + let lastUpdate = personelManagementData.date; + let diff = AES.getDateDiff([todayDate.date, lastUpdate]); + let span = $('').text('Last personnel salary update: ' + AES.formatDateString(lastUpdate) + ' (' + diff + ' days ago).'); + if (diff >= 0 && diff < 7) { + span.addClass('good'); + } else { + span.addClass('warning'); + } + td[1].append(span); + } else { + //no schedule + td[1].html('No personnel salary update date found.'); + } + }); + + //Action + let btn = $(''); + btn.click(function() { + //get schedule link + let link = $('#as-navbar-main-collapse > ul > li:eq(4) > ul > li:eq(5) > a'); + link[0].click(); + }); + td[2].append(btn); +} +//Display default +function displayDefault() { + let mainDiv = $("#aes-div-dashboard"); + mainDiv.empty(); +} + +//Table sort and other functions +function SortTable(collumn, number, tableId, collumnPrefix) { + let tableRows = $('#' + tableId + ' tbody tr'); + let tableBody = $('#' + tableId + ' tbody'); + tableBody.empty(); + let indexes = []; + tableRows.each(function() { + if (number) { + let value = parseInt($(this).find("." + collumnPrefix + collumn).text(), 10); + if (value) { + indexes.push(value); + } else { + indexes.push(0); + } + } else { + indexes.push($(this).find("." + collumnPrefix + collumn).text()); + } + }); + indexes = [...new Set(indexes)]; + let sorted = [...indexes]; + if (number) { + sorted.sort(function(a, b) { + if (a > b) return -1; + if (a < b) return 1; + if (a = b) return 0; + }); + } else { + sorted.sort(); + } + let same = 1; + for (let i = 0; i < indexes.length; i++) { + if (indexes[i] !== sorted[i]) { + same = 0; + } + } + if (same) { + if (number) { + sorted.sort(function(a, b) { + if (a < b) return -1; + if (a > b) return 1; + if (a = b) return 0; + }); + } else { + sorted.reverse(); + } + } + for (let i = 0; i < sorted.length; i++) { + for (let j = tableRows.length - 1; j >= 0; j--) { + if (number) { + let value = parseInt($(tableRows[j]).find("." + collumnPrefix + collumn).text(), 10); + if (!value) { + value = 0; + } + if (value == sorted[i]) { + tableBody.append($(tableRows[j])); + tableRows.splice(j, 1); + } + } else { + if ($(tableRows[j]).find("." + collumnPrefix + collumn).text() == sorted[i]) { + tableBody.append($(tableRows[j])); + tableRows.splice(j, 1); + } + } + } + } +} + +//Helper +function getDate(type, scheduleData) { + switch (type) { + case 'schedule': + //scheduleData must be schedule object with dates as properties + let dates = []; + for (let date in scheduleData) { + if (Number.isInteger(parseInt(date))) { + dates.push(date); + } + } + dates.reverse(); + return dates[0]; + default: + return 0; + } +} diff --git a/extension/content_enterpriceOverview.js b/extension/content_enterpriceOverview.js index 61fb8a9..fb64b2f 100644 --- a/extension/content_enterpriceOverview.js +++ b/extension/content_enterpriceOverview.js @@ -1,398 +1,366 @@ -"use strict"; -//MAIN -//Global vars -var server,airlineId,activeTab,compData; -$(function(){ - server = getServerName(); - airlineId = getAirlineId(); - activeTab = $(".nav-tabs .active").attr('class').split(" "); - activeTab = activeTab[0]; - let key = server+airlineId+'competitorMonitoring'; - chrome.storage.local.get([key], function(compMonitoringData) { - compData = compMonitoringData[key]; - if(!compData){ - compData = { - key:key, - server:server, - id:airlineId, - type:"competitorMonitoring", - tab0:{}, - tab2:{}, - tracking:0, - autoExtract:0 - } - } - displayMain(); - - - - }); -}); -function displayMain(){ - //Clean - $('#aes-panel-airline-competitive-monitoring').remove(); - //panel - let panel = $('
'); - - //Checkbox - let checkbox = $(''); - let label = $('').append(checkbox,' follow this airline in Competitor Monitoring'); - let divCheckbox = $('
').append(label); - - //Competitive display comp monitoring - let divComp = $('
'); - $(checkbox).change(function() { - if(this.checked) { - //Update tracker - compData.tracking =1; - chrome.storage.local.set({[compData.key]: compData}, function() {}); - - //Action bar - let actionBar = $('
    '); - divComp.append(actionBar); - //Display - displayCompetitorMonitoring(divComp); - - - switch(activeTab) { - case 'tab0': - - displayTab0(actionBar); - break; - case 'tab1': - // Nothing - break; - case 'tab2': - displayTab2(actionBar) - break; - case 'tab3': - // Nothing Controled via schedule content script - break; - default: - console.log('Error 0925: Enterprice Overview Active Tab not found '+activeTab) - die(); - } - displayAutomation(actionBar); - } else { - //Update tracker - compData.tracking =0; - chrome.storage.local.set({[compData.key]: compData}, function() {}); - //Display - divComp.empty(); - } - }); - //Checkbox default - if(compData.tracking){ - checkbox.prop('checked', true); - checkbox.trigger("change"); - } - //panel - panel.append(divCheckbox); - - - //Add display - let mainDiv = $('
    ').append('

    AirlineSim Enhancement Suite Airline

    ',panel,divComp); - $(".container-fluid:eq(2) h2").after(mainDiv); -} -function displayAutomation(actionBar){ - if(!compData.autoExtract){ // - let span = $(''); - let btn = $(''); - btn.click(function(){ - btn.remove(); - span.removeClass().addClass('warning').text('extracting...'); - compData.autoExtract = 1; - if(activeTab == 'tab0'){ - $('#aes-btn-save-tab0-data').click(); - } else { - window.open('./'+airlineId+'?tab=0','_self'); - } - }); - let li = $('
  • ').append(btn,span); - actionBar.append(li); - } else { - let span = $('').addClass('warning').text('Please wait... extracting all tab info...'); - let li = $('
  • ').append(span); - actionBar.append(li); - } -} -function displayCompetitorMonitoring(div){ - let th = []; - th.push('Overview'); - th.push('Facts and Figures'); - th.push('Schedule'); - let headRow = $('').append(th); - let thead = $('').append(headRow); - //body - let td = []; - td.push($('').html(displayOverviewRow())); - td.push($('').html(displayFactsAndFiguresRow())); - td.push($('').html(displayScheduleRow())); - let row = $('').append(td); - let tbody = $('').append(row); - let table = $('
    ').append(thead,tbody); - let divTable = $('
    ').append(table); - let divPanel = $('
    ').append(divTable); - - div.append(divPanel); - -} -function displayTab0(actionBar){ - //Get data - let data = getTab0Data(); - //Save Data - let span = $(''); - let btnSave = $(''); - - btnSave.click(function(){ - btnSave.remove(); - span.removeClass().addClass('warning').text('saving data...'); - let time = getDate(); - - compData.tab0[time.date] = data; - compData.tab0[time.date].updateTime = time.time; - compData.tab0[time.date].date = time.date; - chrome.storage.local.set({[compData.key]: compData}, function() { - span.removeClass().addClass("good").text("Overview Tab data Saved!"); - if(compData.autoExtract){ - window.open('./'+airlineId+'?tab=2','_self'); - } - - }); - }); - let li = $('
  • ').append(span,btnSave); - actionBar.append(li); - - //Automation - if(compData.autoExtract){ - btnSave.click(); - } -} -function displayTab2(actionBar){ - let data = getTab2Data(); - //Save Data - let span = $(''); - let btnSave = $(''); - - //Check if this week already saved - let update =1; - let dates = []; - for(let date in compData.tab2) { - dates.push(date); - } - dates.sort(function(a, b){return b-a}); - if(dates.length){ - if(compData.tab2[dates[0]].week == data.week){ - update=0; - } - } - - - let li; - if(update){ - btnSave.click(function(){ - btnSave.remove(); - span.removeClass().addClass('warning').text('saving data...'); - let time = getDate(); - - compData.tab2[time.date] = data; - compData.tab2[time.date].updateTime = time.time; - compData.tab2[time.date].date = time.date; - chrome.storage.local.set({[compData.key]: compData}, function() { - span.removeClass().addClass("good").text("Fact and figures Tab data Saved!"); - if(compData.autoExtract){ - window.open('./'+airlineId+'?tab=3','_self'); - } - }); - }); - li = $('
  • ').append(span,btnSave); - } else { - span.addClass('good').text('The current week facts and figures data is already saved'); - li = $('
  • ').append(span); - } - - - - - actionBar.append(li); - - //Automation - if(compData.autoExtract){ - if(update){ - btnSave.click(); - } else { - window.open('./'+airlineId+'?tab=3','_self'); - } - } -} -function displayOverviewRow(){ - let span = $(''); - let dates = []; - for(let propertyName in compData.tab0) { - dates.push(propertyName); - } - dates.sort(function(a, b){return b-a}); - if(dates.length){ - let diff = getDateDiff(getDate().date,dates[0]); - span.text('Last overview extract '+formatDate(dates[0])+' ('+diff+' days ago)'); - if(diff >= 0 && diff < 7){ - span.addClass('good'); - } else { - span.addClass('warning'); - } - } else { - span.addClass('bad').text('No Overview data') - } - return span; -} -function displayFactsAndFiguresRow(){ - let span = $(''); - let dates = []; - for(let propertyName in compData.tab2) { - dates.push(propertyName); - } - dates.sort(function(a, b){return b-a}); - if(dates.length){ - let diff = getDateDiff(getDate().date,dates[0]); - span.text('Last facts and figures extract for week '+formatWeekDate(compData.tab2[dates[0]].week)+' done on '+formatDate(dates[0])+' ('+diff+' days ago)'); - if(diff >= 0 && diff < 7){ - span.addClass('good'); - } else { - span.addClass('warning'); - } - } else { - span.addClass('bad').text('No facts and figures data'); - } - return span; -} -function displayScheduleRow(){ - let span = $(''); - let dates = []; - for(let propertyName in compData.tab0) { - dates.push(propertyName); - } - dates.sort(function(a, b){return b-a}); - if(dates.length){ - let code = compData.tab0[dates[0]].code; - let scheduleKey = server+code+'schedule'; - chrome.storage.local.get([scheduleKey], function(result) { - let scheduleData = result[scheduleKey]; - if(scheduleData){ - let scheduleDates = []; - for(let date in scheduleData.date){ - scheduleDates.push(date); - } - scheduleDates.reverse(); - let diff = getDateDiff(getDate().date,scheduleDates[0]); - span.text('Last schedule extract '+formatDate(dates[0])+' ('+diff+' days ago)'); - if(diff >= 0 && diff < 7){ - span.addClass('good'); - } else { - span.addClass('warning'); - } - } else { - //no schedule - span.addClass('bad').text('No Schedule data found.'); - } - }); - } else { - span.addClass('bad').text('Extract overview to see schedule data'); - } - return span; - -} -function getTab0Data(){ - let data = {}; - //airline generic ; - let airline = getAirline(); - data.code = airline.code; - data.name = airline.name; - data.displayName = airline.displayName; - //First table - let table = $(".layout-col-md-4 > .as-fieldset:eq(0) table tbody"); - data.rating = $('td:eq(1)',$('tr',table).last()).text().trim().replace(/[^A-Za-z0-9]/g, ''); - //console.log(table.find('tr:eq(9) td:eq(1)').text().trim().replace(/[^A-Za-z0-9]/g, '')); - //console.log($('tr td:eq(1)',table).last().text().trim().replace(/[^A-Za-z0-9]/g, '')); - //console.log($('td:eq(1)',$('tr',table).last()).text().trim().replace(/[^A-Za-z0-9]/g, '')); - //Second Table - table = $(".layout-col-md-4 > .as-fieldset:eq(1) table tbody"); - data.pax = parseInt(table.find('tr:eq(0) td:eq(1)').text().trim().replace(/\D/g, ''),10); - data.cargo = parseInt(table.find('tr:eq(1) td:eq(1)').text().trim().replace(/\D/g, ''),10); - data.stations = parseInt(table.find('tr:eq(2) td:eq(1)').text().trim().replace(/\D/g, ''),10); - data.fleet = parseInt(table.find('tr:eq(3) td:eq(1)').text().trim().replace(/\D/g, ''),10); - data.employees = parseInt(table.find('tr:eq(4) td:eq(1)').text().trim().replace(/\D/g, ''),10); - data.tab0data=1; - return data; -} -function getTab2Data(){ - //First table - let data = {}; - let table = $(".tab-content table"); - data.week = parseInt(table.find('tr:eq(0) th:eq(2)').text().trim().replace(/\D/g, ''),10); - data.airportsServed = parseInt(table.find('tbody:eq(0) tr:eq(0) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); - data.operatedFlights = parseInt(table.find('tbody:eq(0) tr:eq(1) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); - data.seatsOffered = parseInt(table.find('tbody:eq(1) tr:eq(2) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); - data.sko = parseInt(table.find('tbody:eq(1) tr:eq(5) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); - data.cargoOffered = parseInt(table.find('tbody:eq(2) tr:eq(2) td:eq(1)').text().split('(')[0].trim().replace(/\D/g, ''),10); - data.fko = parseInt(table.find('tbody:eq(2) tr:eq(5) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); - data.tab2data=2; - return data; -} -function getAirline(){ - let table = $(".container-fluid:eq(2) .layout-row:eq(0) > .layout-col-md-4 > .as-fieldset:eq(0) table tbody"); - let code = table.find('tr:eq(1) td:eq(1)').text().trim().replace(/[^A-Za-z0-9]/g, ''); - let displayName = table.find('tr:eq(0) td:eq(1)').text().trim(); - let name = displayName.replace(/[^A-Za-z0-9]/g, ''); - - return {code:code,name:name,displayName:displayName}; -} -function getAirlineId(){ - //ID - let id = window.location.pathname; - id = id.split("/"); - id = id[id.length-1]; - return id; -} -function getServerName(){ - let server = window.location.hostname - server = server.split('.'); - return server[0]; -} -function getDate(){ - let a = $(".as-footer-line-element:has('.fa-clock-o')").text().trim(); - let b = a.split(" "); - //For date - let dateTemp = b[0].split("-"); - let date; - if(dateTemp.length == 1){ - //German - dateTemp = dateTemp[0].split("."); - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[2] + date[1] + date[0]; - } else { - //English - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[0] + date[1] + date[2]; - } - //For time - let time = b[b.length-2] +' '+b[b.length-1]; - return {date:date,time:time}; -} -function getDateDiff(date1,date2){ - //Returns day differnece between date1 - date2 - let d1 = new Date(formatDate(date1)+'T12:00:00Z'); - let d2 = new Date(formatDate(date2)+'T12:00:00Z'); - let diff = Math.round((d1 - d2)/(1000 * 60 * 60 * 24)); - return diff; -} -function formatDate(date){ - return date.substring(0, 4)+'-'+date.substring(4, 6)+'-'+date.substring(6, 8); -} -function formatWeekDate(date){ - let a = date.toString(); - return a.substring(0, 2)+'/'+a.substring(2, 6); -} +"use strict"; +//MAIN +//Global vars +var server,airlineId,activeTab,compData; +$(function(){ + server = getServerName(); + airlineId = getAirlineId(); + activeTab = $(".nav-tabs .active").attr('class').split(" "); + activeTab = activeTab[0]; + let key = server+airlineId+'competitorMonitoring'; + chrome.storage.local.get([key], function(compMonitoringData) { + compData = compMonitoringData[key]; + if(!compData){ + compData = { + key:key, + server:server, + id:airlineId, + type:"competitorMonitoring", + tab0:{}, + tab2:{}, + tracking:0, + autoExtract:0 + } + } + displayMain(); + + + + }); +}); +function displayMain(){ + //Clean + $('#aes-panel-airline-competitive-monitoring').remove(); + //panel + let panel = $('
    '); + + //Checkbox + let checkbox = $(''); + let label = $('').append(checkbox,' follow this airline in Competitor Monitoring'); + let divCheckbox = $('
    ').append(label); + + //Competitive display comp monitoring + let divComp = $('
    '); + $(checkbox).change(function() { + if(this.checked) { + //Update tracker + compData.tracking =1; + chrome.storage.local.set({[compData.key]: compData}, function() {}); + + //Action bar + let actionBar = $('
      '); + divComp.append(actionBar); + //Display + displayCompetitorMonitoring(divComp); + + + switch(activeTab) { + case 'tab0': + + displayTab0(actionBar); + break; + case 'tab1': + // Nothing + break; + case 'tab2': + displayTab2(actionBar) + break; + case 'tab3': + // Nothing Controled via schedule content script + break; + default: + console.log('Error 0925: Enterprice Overview Active Tab not found '+activeTab) + die(); + } + displayAutomation(actionBar); + } else { + //Update tracker + compData.tracking =0; + chrome.storage.local.set({[compData.key]: compData}, function() {}); + //Display + divComp.empty(); + } + }); + //Checkbox default + if(compData.tracking){ + checkbox.prop('checked', true); + checkbox.trigger("change"); + } + //panel + panel.append(divCheckbox); + + + //Add display + let mainDiv = $('
      ').append('

      AirlineSim Enhancement Suite Airline

      ',panel,divComp); + $(".container-fluid:eq(2) h2").after(mainDiv); +} +function displayAutomation(actionBar){ + if(!compData.autoExtract){ // + let span = $(''); + let btn = $(''); + btn.click(function(){ + btn.remove(); + span.removeClass().addClass('warning').text('extracting...'); + compData.autoExtract = 1; + if(activeTab == 'tab0'){ + $('#aes-btn-save-tab0-data').click(); + } else { + window.open('./'+airlineId+'?tab=0','_self'); + } + }); + let li = $('
    • ').append(btn,span); + actionBar.append(li); + } else { + let span = $('').addClass('warning').text('Please wait... extracting all tab info...'); + let li = $('
    • ').append(span); + actionBar.append(li); + } +} +function displayCompetitorMonitoring(div){ + let th = []; + th.push('Overview'); + th.push('Facts and Figures'); + th.push('Schedule'); + let headRow = $('').append(th); + let thead = $('').append(headRow); + //body + let td = []; + td.push($('').html(displayOverviewRow())); + td.push($('').html(displayFactsAndFiguresRow())); + td.push($('').html(displayScheduleRow())); + let row = $('').append(td); + let tbody = $('').append(row); + let table = $('
      ').append(thead,tbody); + let divTable = $('
      ').append(table); + let divPanel = $('
      ').append(divTable); + + div.append(divPanel); + +} +function displayTab0(actionBar){ + //Get data + let data = getTab0Data(); + //Save Data + let span = $(''); + let btnSave = $(''); + + btnSave.click(function(){ + btnSave.remove(); + span.removeClass().addClass('warning').text('saving data...'); + let time = AES.getServerDate() + + compData.tab0[time.date] = data; + compData.tab0[time.date].updateTime = time.time; + compData.tab0[time.date].date = time.date; + chrome.storage.local.set({[compData.key]: compData}, function() { + span.removeClass().addClass("good").text("Overview Tab data Saved!"); + if(compData.autoExtract){ + window.open('./'+airlineId+'?tab=2','_self'); + } + + }); + }); + let li = $('
    • ').append(span,btnSave); + actionBar.append(li); + + //Automation + if(compData.autoExtract){ + btnSave.click(); + } +} +function displayTab2(actionBar){ + let data = getTab2Data(); + //Save Data + let span = $(''); + let btnSave = $(''); + + //Check if this week already saved + let update =1; + let dates = []; + for(let date in compData.tab2) { + dates.push(date); + } + dates.sort(function(a, b){return b-a}); + if(dates.length){ + if(compData.tab2[dates[0]].week == data.week){ + update=0; + } + } + + + let li; + if(update){ + btnSave.click(function(){ + btnSave.remove(); + span.removeClass().addClass('warning').text('saving data...'); + let time = AES.getServerDate() + + compData.tab2[time.date] = data; + compData.tab2[time.date].updateTime = time.time; + compData.tab2[time.date].date = time.date; + chrome.storage.local.set({[compData.key]: compData}, function() { + span.removeClass().addClass("good").text("Fact and figures Tab data Saved!"); + if(compData.autoExtract){ + window.open('./'+airlineId+'?tab=3','_self'); + } + }); + }); + li = $('
    • ').append(span,btnSave); + } else { + span.addClass('good').text('The current week facts and figures data is already saved'); + li = $('
    • ').append(span); + } + + + + + actionBar.append(li); + + //Automation + if(compData.autoExtract){ + if(update){ + btnSave.click(); + } else { + window.open('./'+airlineId+'?tab=3','_self'); + } + } +} +function displayOverviewRow(){ + let span = $(''); + let dates = []; + for(let propertyName in compData.tab0) { + dates.push(propertyName); + } + dates.sort(function(a, b){return b-a}); + if(dates.length){ + let diff = AES.getDateDiff([AES.getServerDate().date, dates[0]]); + span.text('Last overview extract '+AES.formatDateString(dates[0])+' ('+diff+' days ago)'); + if(diff >= 0 && diff < 7){ + span.addClass('good'); + } else { + span.addClass('warning'); + } + } else { + span.addClass('bad').text('No Overview data') + } + return span; +} +function displayFactsAndFiguresRow(){ + let span = $(''); + let dates = []; + for(let propertyName in compData.tab2) { + dates.push(propertyName); + } + dates.sort(function(a, b){return b-a}); + if(dates.length){ + let diff = AES.getDateDiff([AES.getServerDate().date, dates[0]]); + span.text('Last facts and figures extract for week '+formatWeekDate(compData.tab2[dates[0]].week)+' done on '+AES.formatDateString(dates[0])+' ('+diff+' days ago)'); + if(diff >= 0 && diff < 7){ + span.addClass('good'); + } else { + span.addClass('warning'); + } + } else { + span.addClass('bad').text('No facts and figures data'); + } + return span; +} +function displayScheduleRow(){ + let span = $(''); + let dates = []; + for(let propertyName in compData.tab0) { + dates.push(propertyName); + } + dates.sort(function(a, b){return b-a}); + if(dates.length){ + let code = compData.tab0[dates[0]].code; + let scheduleKey = server+code+'schedule'; + chrome.storage.local.get([scheduleKey], function(result) { + let scheduleData = result[scheduleKey]; + if(scheduleData){ + let scheduleDates = []; + for(let date in scheduleData.date){ + if (Number.isInteger(parseInt(date))) { + scheduleDates.push(date); + } + } + scheduleDates.reverse(); + let diff = AES.getDateDiff([AES.getServerDate().date, scheduleDates[0]]); + span.text('Last schedule extract '+AES.formatDateString(dates[0])+' ('+diff+' days ago)'); + if(diff >= 0 && diff < 7){ + span.addClass('good'); + } else { + span.addClass('warning'); + } + } else { + //no schedule + span.addClass('bad').text('No Schedule data found.'); + } + }); + } else { + span.addClass('bad').text('Extract overview to see schedule data'); + } + return span; + +} +function getTab0Data(){ + let data = {}; + //airline generic ; + let airline = getAirline(); + data.code = airline.code; + data.name = airline.name; + data.displayName = airline.displayName; + //First table + let table = $(".layout-col-md-4 > .as-fieldset:eq(0) table tbody"); + data.rating = $('td:eq(1)',$('tr',table).last()).text().trim().replace(/[^A-Za-z0-9]/g, ''); + //console.log(table.find('tr:eq(9) td:eq(1)').text().trim().replace(/[^A-Za-z0-9]/g, '')); + //console.log($('tr td:eq(1)',table).last().text().trim().replace(/[^A-Za-z0-9]/g, '')); + //console.log($('td:eq(1)',$('tr',table).last()).text().trim().replace(/[^A-Za-z0-9]/g, '')); + //Second Table + table = $(".layout-col-md-4 > .as-fieldset:eq(1) table tbody"); + data.pax = parseInt(table.find('tr:eq(0) td:eq(1)').text().trim().replace(/\D/g, ''),10); + data.cargo = parseInt(table.find('tr:eq(1) td:eq(1)').text().trim().replace(/\D/g, ''),10); + data.stations = parseInt(table.find('tr:eq(2) td:eq(1)').text().trim().replace(/\D/g, ''),10); + data.fleet = parseInt(table.find('tr:eq(3) td:eq(1)').text().trim().replace(/\D/g, ''),10); + data.employees = parseInt(table.find('tr:eq(4) td:eq(1)').text().trim().replace(/\D/g, ''),10); + data.tab0data=1; + return data; +} +function getTab2Data(){ + //First table + let data = {}; + let table = $(".tab-content table"); + data.week = parseInt(table.find('tr:eq(0) th:eq(2)').text().trim().replace(/\D/g, ''),10); + data.airportsServed = parseInt(table.find('tbody:eq(0) tr:eq(0) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); + data.operatedFlights = parseInt(table.find('tbody:eq(0) tr:eq(1) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); + data.seatsOffered = parseInt(table.find('tbody:eq(1) tr:eq(2) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); + data.sko = parseInt(table.find('tbody:eq(1) tr:eq(5) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); + data.cargoOffered = parseInt(table.find('tbody:eq(2) tr:eq(2) td:eq(1)').text().split('(')[0].trim().replace(/\D/g, ''),10); + data.fko = parseInt(table.find('tbody:eq(2) tr:eq(5) td:eq(1)').text().trim().split('(')[0].replace(/\D/g, ''),10); + data.tab2data=2; + return data; +} +function getAirline(){ + let table = $(".container-fluid:eq(2) .layout-row:eq(0) > .layout-col-md-4 > .as-fieldset:eq(0) table tbody"); + let code = table.find('tr:eq(1) td:eq(1)').text().trim().replace(/[^A-Za-z0-9]/g, ''); + let displayName = table.find('tr:eq(0) td:eq(1)').text().trim(); + let name = displayName.replace(/[^A-Za-z0-9]/g, ''); + + return {code:code,name:name,displayName:displayName}; +} +function getAirlineId(){ + //ID + let id = window.location.pathname; + id = id.split("/"); + id = id[id.length-1]; + return id; +} +function getServerName(){ + let server = window.location.hostname + server = server.split('.'); + return server[0]; +} +function formatWeekDate(date){ + let a = date.toString(); + return a.substring(0, 2)+'/'+a.substring(2, 6); +} diff --git a/extension/content_fleetManagement.js b/extension/content_fleetManagement.js index 6b6e2be..2797ecd 100644 --- a/extension/content_fleetManagement.js +++ b/extension/content_fleetManagement.js @@ -1,262 +1,237 @@ -"use strict"; -//MAIN -//Global vars -var aircraftData = []; -var server,aircraftFleetKey,aircraftFleetStorageData,airlineName; -$(function(){ - if(fltmng_fleetManagementPageOpen()){ - fltmng_getData(); - //Async start - fltmng_getStorageData(); - } -}); -function fltmng_fleetManagementPageOpen(){ - let a = $('.as-page-fleet-management'); - if(a.length){ - return true; - } else { - return false; - } -} -function fltmng_getData(){ - //Global - server = fltmng_getServerName(); - let date = fltmng_getDate(); - //Aircraft - let table = $('.as-page-fleet-management > .row > .col-md-9 > .as-panel:eq(0) table'); - let fleet = $('.as-page-fleet-management > .row > .col-md-9 > h2:eq(0)').text(); - $('tbody tr',table).each(function(){ - let data = { - registration: $('td:eq(1) > span:eq(0)',this).text(), - nickname: fltmng_getNickname($('td:eq(1) > div:eq(0)',this).text()), - equipment:$('td:eq(2) > a:eq(0)',this).text(), - age:fltmng_getAge($('td:eq(4) > span:eq(0)',this).text()), - maintanance:fltmng_getMaintanance($('td:eq(4) > div > span:eq(1)',this).text()), - aircraftId:fltmng_getAircraftId($('td:eq(6) > div > div:eq(1) > a:eq(0)',this).attr('href')), - note:fltmng_getNickname($('td:eq(7) > span > span',this).text()), - fleet:fleet, - date:date.date, - time:date.time - } - aircraftData.push(data); - }); -} -function fltmng_getNickname(value){ - if(value == '...'){ - return '' - } else { - return value; - } -} -function fltmng_getAge(value){ - value = value.replace(/[a-z]/gi, ''); - value = value.replace(',','.'); - value = parseFloat(value); - return value; -} -function fltmng_getMaintanance(value){ - value = value.replace('%',''); - value = value.replace(',','.'); - value = parseFloat(value); - return value; -} -function fltmng_getAircraftId(value){ - value = value.split('/'); - return parseInt(value[value.length-2],10); -} -function fltmng_getStorageData(){ - let keys = []; - aircraftData.forEach(function(value){ - let key = server + 'aircraftFlights' + value.aircraftId; - keys.push(key); - }); - chrome.storage.local.get(keys, function(result) { - for(let aircraftFlightData in result) { - for (let i=0; i < aircraftData.length; i++) { - if(aircraftData[i].aircraftId == result[aircraftFlightData].aircraftId){ - aircraftData[i].profit = { - date:result[aircraftFlightData].date, - finishedFlights:result[aircraftFlightData].finishedFlights, - profit:result[aircraftFlightData].profit, - profitFlights:result[aircraftFlightData].profitFlights, - time:result[aircraftFlightData].time, - totalFlights:result[aircraftFlightData].totalFlights, - }; - } - } - } - //Async - fltmng_getAircraftStorageFleetData(); - }); -} -function fltmng_getAircraftStorageFleetData(){ - airlineName = fltmng_getAirlineName(); - aircraftFleetKey = server + airlineName + 'aircraftFleet'; - chrome.storage.local.get(aircraftFleetKey, function(result) { - fltmng_updateAircraftFleetStorageData(result[aircraftFleetKey]); - fltmng_saveData(); - }); -} -function fltmng_getAirlineName(){ - let name = $('#as-navbar-main-collapse > ul:eq(0) > li:eq(0) > a:eq(0) > span').text(); - name = name.trim().replace(/[^A-Za-z0-9]/g, ''); - return name; -} -function fltmng_updateAircraftFleetStorageData(data){ - aircraftFleetStorageData = { - server:server, - type:'aircraftFleet', - airline:airlineName, - fleet:aircraftData - } - if(data){ - let newfleet = []; - //Push all new aircrafts - aircraftData.forEach(function(newvalue){ - newfleet.push({ - age:newvalue.age, - aircraftId:newvalue.aircraftId, - date:newvalue.date, - equipment:newvalue.equipment, - fleet:newvalue.fleet, - maintanance:newvalue.maintanance, - nickname:newvalue.nickname, - note:newvalue.note, - registration:newvalue.registration, - time:newvalue.time - }); - }); - - //push all old aircrafts that dont have new data - data.fleet.forEach(function(value){ - let found = 0; - newfleet.forEach(function(newValue){ - if(value.aircraftId == newValue.aircraftId){ - found = 1; - } - }); - if(!found){ - newfleet.push(value); - } - }); - //Attach new fleet - aircraftFleetStorageData.fleet = newfleet; - } -} -function fltmng_saveData(){ - //Remove profit - - chrome.storage.local.set({[aircraftFleetKey]: aircraftFleetStorageData}, function() { - fltmng_display(); - }); -} - -function fltmng_display(){ - fltmng_displayAircraftProfit(); - - let p = []; - p.push($('

      ').html(fltmng_displaySavedAircrafts())); - p.push($('

      ').html(fltmng_displayNewUpdates())); - - let panel = $('
      ').append(p); - //Header - let h = $('

      ').text('AES Fleet Management'); - let div = $('
      ').append(h,panel); - $('.as-page-fleet-management > h1:eq(0)').after(div); -} -function fltmng_displayAircraftProfit(){ - let table = $('.as-page-fleet-management > .row > .col-md-9 > .as-panel:eq(0) table'); - //Head - let th = ['Profit/Loss','Extract date']; - $('thead tr:eq(0)',table).append(th); - //Body - $('tbody tr',table).each(function(){ - let id = fltmng_getAircraftId($('td:eq(6) > div > div:eq(1) > a:eq(0)',this).attr('href')); - let profit,date,time; - aircraftData.forEach(function(value){ - if(value.aircraftId == id){ - if(value.profit){ - if(value.profit.profitFlights){ - profit = value.profit.profit; - date = value.profit.date; - time = value.profit.time; - } - } - } - }); - let td = []; - if(date){ - td.push(fltmng_formatMoney(profit)); - td.push($('').html(fltmng_formatDate(date)+'
      '+time)); - } else { - td.push('',''); - } - $(this).append(td); - - }); -} -function fltmng_displaySavedAircrafts(){ - return 'Currently '+aircraftFleetStorageData.fleet.length+' aircrafts stored in memory.'; -} -function fltmng_displayNewUpdates(){ - let span = $('').text('Updated aircraft data for '+aircraftData.length+ ' from '+aircraftData[0].fleet); - return span; -} -function fltmng_getDate(){ - let a = $(".as-footer-line-element:has('.fa-clock-o')").text().trim(); - let b = a.split(" "); - //For date - let dateTemp = b[0].split("-"); - let date; - if(dateTemp.length == 1){ - //German - dateTemp = dateTemp[0].split("."); - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[2] + date[1] + date[0]; - } else { - //English - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[0] + date[1] + date[2]; - } - //For time - let time = b[b.length-2] +' '+b[b.length-1]; - return {date:date,time:time}; -} -function fltmng_getServerName(){ - let server = window.location.hostname - server = server.split('.'); - return server[0]; -} -function fltmng_formatMoney(value){ - let container = document.createElement("td") - let formattedValue = Intl.NumberFormat().format(value) - let indicatorEl = document.createElement("span") - let valueEl = document.createElement("span") - let currencyEl = document.createElement("span") - - if (value >= 0) { - valueEl.classList.add("good") - indicatorEl.innerText = "+" - } - - if (value < 0) { - valueEl.classList.add("bad") - indicatorEl.innerText = "-" - formattedValue = formattedValue.replace("-", "") - } - - valueEl.innerText = formattedValue - currencyEl.innerText = " AS$" - - container.classList.add("aes-text-right", "aes-no-text-wrap") - container.append(indicatorEl, valueEl, currencyEl) - - return container -} -function fltmng_formatDate(date){ - return date.substring(0, 4)+'-'+date.substring(4, 6)+'-'+date.substring(6, 8); -} +"use strict"; +//MAIN +//Global vars +var aircraftData = []; +var server,aircraftFleetKey,aircraftFleetStorageData,airlineName; +$(function(){ + if(fltmng_fleetManagementPageOpen()){ + fltmng_getData(); + //Async start + fltmng_getStorageData(); + } +}); +function fltmng_fleetManagementPageOpen(){ + let a = $('.as-page-fleet-management'); + if(a.length){ + return true; + } else { + return false; + } +} +function fltmng_getData(){ + //Global + server = fltmng_getServerName(); + let date = AES.getServerDate() + //Aircraft + let table = $('.as-page-fleet-management > .row > .col-md-9 > .as-panel:eq(0) table'); + let fleet = $('.as-page-fleet-management > .row > .col-md-9 > h2:eq(0)').text(); + $('tbody tr',table).each(function(){ + let data = { + registration: $('td:eq(1) > span:eq(0)',this).text(), + nickname: fltmng_getNickname($('td:eq(1) > div:eq(0)',this).text()), + equipment:$('td:eq(2) > a:eq(0)',this).text(), + age:fltmng_getAge($('td:eq(4) > span:eq(0)',this).text()), + maintanance:fltmng_getMaintanance($('td:eq(4) > div > span:eq(1)',this).text()), + aircraftId:fltmng_getAircraftId($('td:eq(6) > div > div:eq(1) > a:eq(0)',this).attr('href')), + note:fltmng_getNickname($('td:eq(7) > span > span',this).text()), + fleet:fleet, + date:date.date, + time:date.time + } + aircraftData.push(data); + }); +} +function fltmng_getNickname(value){ + if(value == '...'){ + return '' + } else { + return value; + } +} +function fltmng_getAge(value){ + value = value.replace(/[a-z]/gi, ''); + value = value.replace(',','.'); + value = parseFloat(value); + return value; +} +function fltmng_getMaintanance(value){ + value = value.replace('%',''); + value = value.replace(',','.'); + value = parseFloat(value); + return value; +} +function fltmng_getAircraftId(value){ + if (value) { + value = value.split('/'); + return parseInt(value[value.length-2],10); + } +} +function fltmng_getStorageData(){ + let keys = []; + aircraftData.forEach(function(value){ + let key = server + 'aircraftFlights' + value.aircraftId; + keys.push(key); + }); + chrome.storage.local.get(keys, function(result) { + for(let aircraftFlightData in result) { + for (let i=0; i < aircraftData.length; i++) { + if(aircraftData[i].aircraftId == result[aircraftFlightData].aircraftId){ + aircraftData[i].profit = { + date:result[aircraftFlightData].date, + finishedFlights:result[aircraftFlightData].finishedFlights, + profit:result[aircraftFlightData].profit, + profitFlights:result[aircraftFlightData].profitFlights, + time:result[aircraftFlightData].time, + totalFlights:result[aircraftFlightData].totalFlights, + }; + } + } + } + //Async + fltmng_getAircraftStorageFleetData(); + }); +} +function fltmng_getAircraftStorageFleetData(){ + airlineName = fltmng_getAirlineName(); + aircraftFleetKey = server + airlineName + 'aircraftFleet'; + chrome.storage.local.get(aircraftFleetKey, function(result) { + fltmng_updateAircraftFleetStorageData(result[aircraftFleetKey]); + fltmng_saveData(); + }); +} +function fltmng_getAirlineName(){ + let name = $('#as-navbar-main-collapse > ul:eq(0) > li:eq(0) > a:eq(0) > span').text(); + name = name.trim().replace(/[^A-Za-z0-9]/g, ''); + return name; +} +function fltmng_updateAircraftFleetStorageData(data){ + aircraftFleetStorageData = { + server:server, + type:'aircraftFleet', + airline:airlineName, + fleet:aircraftData + } + if(data){ + let newfleet = []; + //Push all new aircrafts + aircraftData.forEach(function(newvalue){ + newfleet.push({ + age:newvalue.age, + aircraftId:newvalue.aircraftId, + date:newvalue.date, + equipment:newvalue.equipment, + fleet:newvalue.fleet, + maintanance:newvalue.maintanance, + nickname:newvalue.nickname, + note:newvalue.note, + registration:newvalue.registration, + time:newvalue.time + }); + }); + + //push all old aircrafts that dont have new data + data.fleet.forEach(function(value){ + let found = 0; + newfleet.forEach(function(newValue){ + if(value.aircraftId == newValue.aircraftId){ + found = 1; + } + }); + if(!found){ + newfleet.push(value); + } + }); + //Attach new fleet + aircraftFleetStorageData.fleet = newfleet; + } +} +function fltmng_saveData(){ + //Remove profit + + chrome.storage.local.set({[aircraftFleetKey]: aircraftFleetStorageData}, function() { + fltmng_display(); + }); +} + +function fltmng_display(){ + fltmng_displayAircraftProfit(); + + let p = []; + p.push($('

      ').html(fltmng_displaySavedAircrafts())); + p.push($('

      ').html(fltmng_displayNewUpdates())); + + let panel = $('
      ').append(p); + //Header + let h = $('

      ').text('AES Fleet Management'); + let div = $('
      ').append(h,panel); + $('.as-page-fleet-management > h1:eq(0)').after(div); +} +function fltmng_displayAircraftProfit(){ + let table = $('.as-page-fleet-management > .row > .col-md-9 > .as-panel:eq(0) table'); + //Head + let th = ['Profit/Loss','Extract date']; + $('thead tr:eq(0)',table).append(th); + //Body + $('tbody tr',table).each(function(){ + let id = fltmng_getAircraftId($('td:eq(6) > div > div:eq(1) > a:eq(0)',this).attr('href')); + let profit,date,time; + aircraftData.forEach(function(value){ + if(value.aircraftId == id){ + if(value.profit){ + if(value.profit.profitFlights){ + profit = value.profit.profit; + date = value.profit.date; + time = value.profit.time; + } + } + } + }); + let td = []; + if(date){ + td.push(fltmng_formatMoney(profit)); + td.push($('').html(AES.formatDateString(date)+'
      '+time)); + } else { + td.push('',''); + } + $(this).append(td); + + }); +} +function fltmng_displaySavedAircrafts(){ + return 'Currently '+aircraftFleetStorageData.fleet.length+' aircrafts stored in memory.'; +} +function fltmng_displayNewUpdates(){ + let span = $('').text('Updated aircraft data for '+aircraftData.length+ ' from '+aircraftData[0].fleet); + return span; +} +function fltmng_getServerName(){ + let server = window.location.hostname + server = server.split('.'); + return server[0]; +} +function fltmng_formatMoney(value){ + let container = document.createElement("td") + let formattedValue = Intl.NumberFormat().format(value) + let indicatorEl = document.createElement("span") + let valueEl = document.createElement("span") + let currencyEl = document.createElement("span") + + if (value >= 0) { + valueEl.classList.add("good") + indicatorEl.innerText = "+" + } + + if (value < 0) { + valueEl.classList.add("bad") + indicatorEl.innerText = "-" + formattedValue = formattedValue.replace("-", "") + } + + valueEl.innerText = formattedValue + currencyEl.innerText = " AS$" + + container.classList.add("aes-text-right", "aes-no-text-wrap") + container.append(indicatorEl, valueEl, currencyEl) + + return container +} diff --git a/extension/content_flightInfo.js b/extension/content_flightInfo.js index 1743989..e8ec437 100644 --- a/extension/content_flightInfo.js +++ b/extension/content_flightInfo.js @@ -1,197 +1,164 @@ -"use strict"; -//MAIN -//Global vars -var flightInfoData,saveDataSpan; -$(function(){ - if(privateFlight()){ - if(correctTabOpen()){ - flightInfoData = getData(); - saveData(); - display(); - } - } -}); -function saveData(){ - saveDataSpan = $(''); - let key = flightInfoData.server+flightInfoData.type+flightInfoData.flightId; - chrome.storage.local.set({[key]: flightInfoData}, function() { - saveDataSpan.addClass('good').text('Flight info data saved!'); - chrome.storage.local.get(['settings'], function(result) { - let settings = result.settings; - if(settings.flightInfo){ - if(settings.flightInfo.autoClose){ - close(); - } - } - }); - }); -} -function privateFlight(){ - let headers = $('#privInf'); - if(headers.length){ - return true; - } else { - return false; - } -} -function correctTabOpen(){ - if($('#flight-page > ul > li:eq(0)').hasClass('active')){ - return true; - } else { - return false; - } -} -function getData(){ - //Flight ID - let flightId = getFlightId(); - let date = getDate(); - let money = getFinancials(); - let server = getServerName(); - return { - server:server, - flightId:flightId, - type:'flightInfo', - money:money, - date:date.date, - time:date.time - } -} -function display(){ - let tableWell = $('
      ').append(buildTable()); - let p = $('

      ').html(saveDataSpan); - let panel = $('
      ').append(tableWell,p); - let h = $('

      ').text('AES Flight Information'); - let div = $('
      ').append(h,panel); - $('body > .container-fluid:eq(0) > h1:eq(0)').after(div); -} -function buildTable(){ - //head - let th = []; - th.push(''); - th.push('Y'); - th.push('C'); - th.push('F'); - th.push('PAX'); - th.push('Cargo'); - th.push('Total'); - let hrow = $('').append(th); - let thead = $('').append(hrow); - //body - let row = []; - for(let cm in flightInfoData.money) { - let td = []; - td.push(''+cm+''); - for(let cmp in flightInfoData.money[cm]) { - td.push($('').html(formatMoney(flightInfoData.money[cm][cmp]))); - } - row.push($('').append(td)); - } - let tbody = $('').append(row); - //foot - let tf = []; - tf.push('Flight Id:'); - tf.push(''+flightInfoData.flightId+''); - tf.push('Date:'); - tf.push(''+formatDate(flightInfoData.date)+' '+flightInfoData.time+''); - tf.push(''); - tf.push(''); - tf.push(''); - let frow = $('').append(tf); - let fblankRow = $('').append(''); - let tfoot = $('').append(fblankRow,frow); - return $('
      ').append(thead,tbody,tfoot); -} -function formatMoney(value){ - let span = $(''); - let text = ''; - if(value > 0){ - span.addClass('good'); - text = '+' - } - if(value < 0){ - span.addClass('bad'); - } - text = text + value + ' AS$'; - span.text(text); - return span; -} -function formatDate(date){ - return date.substring(0, 4)+'-'+date.substring(4, 6)+'-'+date.substring(6, 8); -} -function getFinancials(){ - let data = {}; - let cm = $('.cm'); - cm.each(function(index){ - let contMargin = 'CM'+(index + 1); - $('td',this).each(function(i){ - let cmp; - switch(i) { - case 0: - cmp = 'Y' - break; - case 1: - cmp = 'C' - break; - case 2: - cmp = 'F' - break; - case 3: - cmp = 'PAX' - break; - case 4: - cmp = 'Cargo' - break; - case 5: - cmp = 'Total' - break; - } - let value = cleanInteger($(this).text()); - if(!data[contMargin]){ - data[contMargin] = {} - } - data[contMargin][cmp] = value; - }); - }); - return data; -} -function getFlightId(){ - let url = window.location.href; - let a = url.split('id='); - let b = a[1].split('&'); - return parseInt(b[0],10); -} -function getDate(){ - let a = $(".as-footer-line-element:has('.fa-clock-o')").text().trim(); - let b = a.split(" "); - //For date - let dateTemp = b[0].split("-"); - let date; - if(dateTemp.length == 1){ - //German - dateTemp = dateTemp[0].split("."); - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[2] + date[1] + date[0]; - } else { - //English - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[0] + date[1] + date[2]; - } - //For time - let time = b[b.length-2] +' '+b[b.length-1]; - return {date:date,time:time}; -} -function cleanInteger(a){ - a = a.replace(',',''); - a = a.replace('.',''); - a = a.replace(' AS$',''); - return parseInt(a, 10); -} -function getServerName(){ - let server = window.location.hostname - server = server.split('.'); - return server[0]; -} +"use strict"; +//MAIN +//Global vars +var flightInfoData,saveDataSpan; +$(function(){ + if(privateFlight()){ + if(correctTabOpen()){ + flightInfoData = getData(); + saveData(); + display(); + } + } +}); +function saveData(){ + saveDataSpan = $(''); + let key = flightInfoData.server+flightInfoData.type+flightInfoData.flightId; + chrome.storage.local.set({[key]: flightInfoData}, function() { + saveDataSpan.addClass('good').text('Flight info data saved!'); + chrome.storage.local.get(['settings'], function(result) { + let settings = result.settings; + if(settings.flightInfo){ + if(settings.flightInfo.autoClose){ + close(); + } + } + }); + }); +} +function privateFlight(){ + let headers = $('#privInf'); + if(headers.length){ + return true; + } else { + return false; + } +} +function correctTabOpen(){ + if($('#flight-page > ul > li:eq(0)').hasClass('active')){ + return true; + } else { + return false; + } +} +function getData(){ + //Flight ID + let flightId = getFlightId(); + let date = AES.getServerDate() + let money = getFinancials(); + let server = getServerName(); + return { + server:server, + flightId:flightId, + type:'flightInfo', + money:money, + date:date.date, + time:date.time + } +} +function display(){ + let tableWell = $('
      ').append(buildTable()); + let p = $('

      ').html(saveDataSpan); + let panel = $('
      ').append(tableWell,p); + let h = $('

      ').text('AES Flight Information'); + let div = $('
      ').append(h,panel); + $('body > .container-fluid:eq(0) > h1:eq(0)').after(div); +} +function buildTable(){ + //head + let th = []; + th.push(''); + th.push('Y'); + th.push('C'); + th.push('F'); + th.push('PAX'); + th.push('Cargo'); + th.push('Total'); + let hrow = $('').append(th); + let thead = $('').append(hrow); + //body + let row = []; + for(let cm in flightInfoData.money) { + let td = []; + td.push(''+cm+''); + for(let cmp in flightInfoData.money[cm]) { + td.push($('').html(AES.formatCurrency(flightInfoData.money[cm][cmp]))); + } + row.push($('').append(td)); + } + let tbody = $('').append(row); + //foot + let tf = []; + tf.push('Flight Id:'); + tf.push(''+flightInfoData.flightId+''); + tf.push('Date:'); + tf.push(''+AES.formatDateString(flightInfoData.date)+' '+flightInfoData.time+''); + tf.push(''); + tf.push(''); + tf.push(''); + let frow = $('').append(tf); + let fblankRow = $('').append(''); + let tfoot = $('').append(fblankRow,frow); + return $('
      ').append(thead,tbody,tfoot); +} +function formatMoney(value){ + let span = $(''); + let text = ''; + if(value > 0){ + span.addClass('good'); + text = '+' + } + if(value < 0){ + span.addClass('bad'); + } + text = text + value + ' AS$'; + span.text(text); + return span; +} +function getFinancials(){ + let data = {}; + let cm = $('.cm'); + cm.each(function(index){ + let contMargin = 'CM'+(index + 1); + $('td',this).each(function(i){ + let cmp; + switch(i) { + case 0: + cmp = 'Y' + break; + case 1: + cmp = 'C' + break; + case 2: + cmp = 'F' + break; + case 3: + cmp = 'PAX' + break; + case 4: + cmp = 'Cargo' + break; + case 5: + cmp = 'Total' + break; + } + let value = AES.cleanInteger($(this).text()); + if(!data[contMargin]){ + data[contMargin] = {} + } + data[contMargin][cmp] = value; + }); + }); + return data; +} +function getFlightId(){ + let url = window.location.href; + let a = url.split('id='); + let b = a[1].split('&'); + return parseInt(b[0],10); +} +function getServerName(){ + let server = window.location.hostname + server = server.split('.'); + return server[0]; +} diff --git a/extension/content_fligthSchedule.js b/extension/content_fligthSchedule.js index c40b7c1..8619f83 100644 --- a/extension/content_fligthSchedule.js +++ b/extension/content_fligthSchedule.js @@ -109,7 +109,7 @@ function extractSchedule(){ } }); //Save to storage - let dateTime = getDate(); + let dateTime = AES.getServerDate() let server = getServerName(); let airline = getAirlineCode(); let newScheduleData = { @@ -177,30 +177,6 @@ function extractSchedule(){ } } //Helper Functions -function getDate(){ - let a = $(".as-footer-line-element:has('.fa-clock-o')").text().trim(); - let b = a.split(" "); - //For date - let dateTemp = b[0].split("-"); - let date; - if(dateTemp.length == 1){ - //German - dateTemp = dateTemp[0].split("."); - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[2] + date[1] + date[0]; - } else { - //English - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[0] + date[1] + date[2]; - } - //For time - let time = b[b.length-2] +' '+b[b.length-1]; - return {date:date,time:time}; -} function getServerName(){ let server = window.location.hostname server = server.split('.'); diff --git a/extension/content_inventory.js b/extension/content_inventory.js index 7b9201f..b942f99 100644 --- a/extension/content_inventory.js +++ b/extension/content_inventory.js @@ -1,1177 +1,1100 @@ -"use strict"; -//MAIN -//Global vars -var settings,pricingData, todayDate, analysis; -var aesmodule = {valid:1,error:[]}; -$(function(){ - validateAllOptions(); - if(aesmodule.valid){ - //Options correct - chrome.storage.local.get(['settings'], function(result) { - settings = result.settings; - todayDate = parseInt(getCurrentDateTime().date,10); - //Get flights - let flights = getFlights(); - let prices = getPriceDetails(); - - let storageKey = getPricingInventoryKey(); - - //Check if any snapshots saved - let defaultPricingData = { - server:storageKey.server, - airline:storageKey.airline, - type:storageKey.type, - origin:storageKey.origin, - destination:storageKey.destination, - key:storageKey.key, - date:{} - } - chrome.storage.local.get({[storageKey.key]:defaultPricingData}, function(result) { - pricingData = result[storageKey.key]; - //Do Analysis - analysis = getAnalysis(flights, prices,pricingData.date); - //Display analysis - displayAnalysis(analysis,prices); - //Display history - displayHistory(analysis); - - - //Automation - //Check if valid analysis exists - - if(analysis.hasValue('valid')){ - //CHeck if updated todayDate - if(pricingData.date[todayDate]){ - //Today update exists - //Check if pricing updated today - if(pricingData.date[todayDate].pricingUpdated){ - //Pricing updated today - //Do nothing - } else { - //Pricing not updated today - //Check if new price avaialble - if(analysis.hasValue('newPrice')){ - //Update price - if(settings.invPricing.autoPriceUpdate){ - $('#aes-btn-invPricing-apply-new-prices').click(); - } - } - } - } else { - //Today update does not exists - //Check if new price avaialble - if(analysis.hasValue('newPrice')){ - //Update price - if(settings.invPricing.autoPriceUpdate){ - $('#aes-btn-invPricing-apply-new-prices').click(); - } else if(settings.invPricing.autoAnalysisSave){ - $('#aes-btn-invPricing-save-snapshot').click(); - } - } else { - //Update data - if(settings.invPricing.autoAnalysisSave){ - $('#aes-btn-invPricing-save-snapshot').click(); - } - } - } - } - }); - }); - } else { - //Options not correct - displayValidationError(); - } - - -}); - - - -//Get Flights -function getFlights(){ - let flightRows = $("#inventory-grouped-table tbody tr"); - if (!flightRows.length) { - flightRows = $("#inventory-table tbody tr"); - } - let flights = []; - for(let i = 0; i < flightRows.length; i++ ){ - let flight; - let row = $(flightRows[i]).find('td'); - //Get variables - if(row.length == 12){ - //cargo fix for german - let compCode = $(row[5]).text(); - if( compCode == 'Fracht'){ - compCode = 'Cargo'; - } - flight = { - fltNr:$(row[1]).find("a:first").text(), - date:$(row[2]).text(), - cmp:compCode, - cap:cleanInteger($(row[6]).text()), - bkd:cleanInteger($(row[7]).text()), - price:cleanInteger($(row[9]).text()), - status:$(row[10]).text().replace(/\s+/g,'') - }; - - } - if(row.length == 5){ - //cargo fix for german - let compCode = $(row[0]).text(); - if( compCode == 'Fracht'){ - compCode = 'Cargo'; - } - flight = { - fltNr:flight.fltNr, - date:flight.date, - cmp:compCode, - cap:cleanInteger($(row[1]).text()), - bkd:cleanInteger($(row[2]).text()), - price:cleanInteger($(row[4]).text()), - status:flight.status - }; - } - flights.push(flight); - } - return flights; -} -//Get Prices -function getPriceDetails(){ - let pricingRows = $(".pricing table tbody tr"); - let prices = {}; - for(let i = 0; i < pricingRows.length; i++ ){ - let row = $(pricingRows[i]).find('td'); - let cmp = $(row[0]).text(); - if(cmp == 'Fracht'){ - cmp = 'Cargo'; - } - prices[cmp] = { - currentPrice:cleanInteger($(row[1]).text()), - newPriceInput:$(row[2]).find("input"), - defaultPrice:cleanInteger($(row[4]).text().replace(/\s+/g,'')), - currentPricePoint : function() { - return Math.round((this.currentPrice / this.defaultPrice) * 100); - } - }; - } - return prices; -} -//Get Analysis -function getAnalysis(flights, prices, storedData){ - //Setup object - - let data = { - Y:0, - C:0, - F:0, - Cargo:0 - } - let analysis = { - data:data, - getLoad:function(cmp) { - if (this.data[cmp].valid){ - return this.data[cmp].totalBkd / this.data[cmp].totalCap; - } else { - return 0; - } - }, - note:function(cmp) { - if (this.data[cmp].valid){ - if(this.data[cmp].useCurrentPrice){ - return "Current price analysis"; - } else { - return "No current price flights, using old price" - } - } else { - return "No data for analysis"; - } - }, - displayLoad:function(cmp){ - if(this.data[cmp].valid){ - return this.data[cmp].totalBkd + " / " + this.data[cmp].totalCap + " ("+displayPerc(Math.round(this.getLoad(cmp) * 100),'load') +")"; - } else { - return '-'; - } - }, - displayRec:function(cmp){ - if(this.data[cmp].recommendation){ - switch (this.data[cmp].recType) { - case 'good': - return ''+this.data[cmp].recommendation+''; - case 'bad': - return ''+this.data[cmp].recommendation+''; - case 'neutral': - return ''+this.data[cmp].recommendation+''; - default: - return 'ERROR:2501 Wrong recType set:'+this.data[cmp].recType+''; - } - } else { - return '-' - } - }, - displayPrice:function(cmp,type){ - switch (type) { - case 'current': - return formatCurrency(this.data[cmp].currentPrice)+' AS$ ('+displayPerc(this.data[cmp].currentPricePoint,'price')+')'; - case 'new': - if(this.data[cmp].newPrice){ - return formatCurrency(this.data[cmp].newPrice)+' AS$ ('+displayPerc(this.data[cmp].newPricePoint,'price')+')'; - } else { - return '-'; - } - case 'analysis': - if(this.data[cmp].valid){ - return formatCurrency(this.data[cmp].analysisPrice)+' AS$ ('+displayPerc(this.data[cmp].analysisPricePoint,'price')+')'; - } else { - return '-'; - } - default: - return 'ERROR:2502 Wrong type set:'+type+''; - } - }, - displayIndex:function(cmp){ - if(this.data[cmp].valid){ - let span = $(''); - if(this.data[cmp].index >= 90){ - return span.addClass('good').text(this.data[cmp].index); - } - if(this.data[cmp].index <= 50 ){ - return span.addClass('bad').text(this.data[cmp].index); - } - return span.addClass('warning').text(this.data[cmp].index); - - } else { - return '-'; - } - }, - displayTotalLoad:function(type){ - let cmp = []; - switch(type) { - case 'all': - cmp = ['Y','C','F','Cargo']; - break; - case 'pax': - cmp = ['Y','C','F']; - break; - default: - // code block - } - let load,cap,bkd; - load = cap = bkd = 0; - for(let i=0;i'); - if(totalIndex >= 90){ - return span.addClass('good').text(totalIndex); - } - if(totalIndex <= 50 ){ - return span.addClass('bad').text(totalIndex); - } - return span.addClass('warning').text(totalIndex); - } else { - return '-'; - } - }, - hasValue:function(value){ - for(let cmp in this.data){ - if(this.data[cmp][value]){ - return 1; - } - } - return 0; - } - - }; - - //Filter flights - flights = flights.filter(function (flight) { - return flight.status == 'finished' || flight.status == 'inflight'; - }); - - //Check historical data - if(storedData){ - //Shouldbe function inside storage object - var mostRecentDate; - let dates = []; - for(let date in storedData) { - dates.push(date) - } - dates.reverse(); - var mostRecentDate = dates[0]; - var mostRecentData = storedData[mostRecentDate]; - } else { - var mostRecentData = 0; - } - - //extract each cmp analysis - for(let cmp in analysis.data) { - analysis.data[cmp] = { - totalCap:0, - totalBkd:0, - valid:0, - analysisPrice:0, - analysisPricePoint:0, - useCurrentPrice:0, - currentPrice:prices[cmp].currentPrice, - currentPricePoint:prices[cmp].currentPricePoint() - }; - let price = prices[cmp].currentPrice; - //Only cmp flights - let cmpFlights = flights.filter(function (flight) { - return flight.cmp == cmp; - }); - //if no cmp flights - if(cmpFlights.length){ - //Check if current price flights avaialble - let flightsArray = cmpFlights.filter(function (flight) { - return (flight.price == price); - }); - if(flightsArray.length){ - analysis.data[cmp].useCurrentPrice = 1; - analysis.data[cmp].analysisPrice = price; - analysis.data[cmp].analysisPricePoint = Math.round(price / prices[cmp].defaultPrice* 100); - analysis.data[cmp].valid = 1; - } else { - //Check if historical data available - - - if(mostRecentData){ - flightsArray = cmpFlights.filter(function (flight) { - return flight.price == mostRecentData.data[cmp].analysisPrice; - }); - if(flightsArray.length){ - analysis.data[cmp].useCurrentPrice = 0; - analysis.data[cmp].analysisPrice = mostRecentData.data[cmp].analysisPrice; - analysis.data[cmp].analysisPricePoint = Math.round(mostRecentData.data[cmp].analysisPrice / prices[cmp].defaultPrice * 100); - analysis.data[cmp].valid = 1; - } - } - } - if(analysis.data[cmp].valid){ - flightsArray.forEach(function (flight) { - analysis.data[cmp].totalCap += flight.cap; - analysis.data[cmp].totalBkd += flight.bkd; - }); - } - } - } - - //END extract each cmp analysis - analysis = generateRecommendation(analysis,prices); - - - //Make route index - analysis = generateRouteIndex(analysis); - return analysis; -} -function generateRecommendation(analysis,prices){ - for(let cmp in analysis.data) { - analysis.data[cmp].recommendation = 0; - if(analysis.data[cmp].valid){ - if(analysis.data[cmp].useCurrentPrice){ - //Find recommendation - let load = Math.round(analysis.getLoad(cmp) * 100); - //Find step - let step; - for(let i in settings.invPricing.recommendation[cmp].steps){ - step = settings.invPricing.recommendation[cmp].steps[i]; - if(load >= step.min && load <= step.max){ - break; - } - } - //Find new price point - let newPricePoint = prices[cmp].currentPricePoint() + step.step; - //See if new price in bounds for Drop - if(step.step < 0){ - analysis.data[cmp].recType = 'bad'; - if(newPricePoint < settings.invPricing.recommendation[cmp].minPrice){ - newPricePoint = settings.invPricing.recommendation[cmp].minPrice; - } - } - //See if new price in bounds for Raise - if(step.step > 0){ - analysis.data[cmp].recType = 'good'; - if(newPricePoint > settings.invPricing.recommendation[cmp].maxPrice){ - newPricePoint = settings.invPricing.recommendation[cmp].maxPrice; - } - } - //see if already at highest/lowest price point - if(step.step != 0){ - if(newPricePoint == prices[cmp].currentPricePoint()){ - if(newPricePoint == settings.invPricing.recommendation[cmp].minPrice){ - //Already at lowest point - analysis.data[cmp].recommendation = 'Already at lowest price!'; - } - if(newPricePoint == settings.invPricing.recommendation[cmp].maxPrice){ - //Already at highest point - analysis.data[cmp].recommendation = 'Already at highest price!'; - } - } - } else { - analysis.data[cmp].recType = 'neutral'; - } - //check if not set by exceptions - if(!analysis.data[cmp].recommendation){ - analysis.data[cmp].recommendation = step.name; - analysis.data[cmp].newPriceChange = step.step; - if(step.step){ - analysis.data[cmp].newPricePoint = newPricePoint; - analysis.data[cmp].newPrice = Math.round(newPricePoint/100 * prices[cmp].defaultPrice); - - } - } - } - } - } - return analysis; -} -function generateRouteIndex(analysis){ - //Each CMP index - for(let cmp in analysis.data){ - if(analysis.data[cmp].valid){ - let index = (analysis.data[cmp].analysisPricePoint + (analysis.getLoad(cmp) * 100 * 3))/4; - analysis.data[cmp].index = Math.round(index); - } - } - return analysis; -} -//Display analysis -function displayAnalysis(analysis,prices){ - - //Build table - let mainDiv = $(".container-fluid .row .col-md-10 div .as-panel:eq(0)"); - mainDiv.after( - ` -

      Analysis (today's snapshot)

      -
      -
      -
      - -
      -
      -
      -
      - ` - ); - - - - //Table head - let th = []; - th.push('SC'); - th.push('Note'); - th.push('Analysis Price'); - th.push('Load'); - th.push('Index'); - th.push('Current Price'); - th.push('Recommendation'); - th.push('New Price'); - let headRow = $('').append(th); - let thead = $('').append(headRow); - - //Table body - let tbody = $(''); - for(let cmp in analysis.data) { - let td = []; - td.push(''+cmp+''); - td.push(''+analysis.note(cmp)+''); - td.push(''+analysis.displayPrice(cmp,'analysis')+''); - td.push(''+analysis.displayLoad(cmp)+''); - td.push($('').html(analysis.displayIndex(cmp))); - td.push(''+analysis.displayPrice(cmp,'current')+''); - td.push(''+analysis.displayRec(cmp)+''); - td.push(''+analysis.displayPrice(cmp,'new')+''); - let row = $('').append(td); - tbody.append(row); - } - - //Table footer - let footRow = [] - footRow.push(''); - //Total PAX - let tf = []; - tf.push('Total PAX'); - tf.push(''); - tf.push($('').html(analysis.displayTotalLoad('pax'))); - tf.push($('').html(analysis.displayTotalIndex('pax'))); - tf.push(''); - footRow.push($('').append(tf)); - //Total - tf = []; - tf.push('Total PAX+Cargo'); - tf.push(''); - tf.push($('').html(analysis.displayTotalLoad('all'))); - tf.push($('').html(analysis.displayTotalIndex('all'))); - tf.push(''); - footRow.push($('').append(tf)); - let tfoot = $('').append(footRow); - - - $("#aes-table-analysis").append(thead,tbody,tfoot); - - //Display pricing and data save buttons - if(analysis.hasValue('valid')){ - let invPricingAnalysisBar = $('
        '); - let invPricingAnalysisBarSpan = $(''); - invPricingAnalysisBar.append($('
      • ').html(invPricingAnalysisBarSpan)); - $("#aes-div-analysis").prepend(invPricingAnalysisBar); - //create buttons - //Save Data - let saveInvPricingBtn = $(''); - $(saveInvPricingBtn).click(function(){ - $(this).closest("li").remove(); - invPricingAnalysisBarSpan.text('Saving analysis data...'); - //Get updated time - let updateTime = getCurrentDateTime().time; - pricingData.date[todayDate] = analysis; - pricingData.date[todayDate].updateTime = updateTime; - pricingData.date[todayDate].date = todayDate; - pricingData.date[todayDate].pricingUpdated = 0; - chrome.storage.local.set({[pricingData.key]: pricingData}, function() { - invPricingAnalysisBarSpan.removeClass().addClass("good").text("Data Saved!"); - //Automation - if(settings.invPricing.autoClose){ - close(); - } - }); - }); - //Update prices - - - - let applyNewPriceInvPricingBtn = $(''); - $(applyNewPriceInvPricingBtn).click(function(){ - $(this).closest("ul").find("li button").closest("li").remove(); - invPricingAnalysisBarSpan.text('Updating prices...'); - //Get updated time - let updateTime = getCurrentDateTime().time; - pricingData.date[todayDate] = analysis; - pricingData.date[todayDate].updateTime = updateTime; - pricingData.date[todayDate].date = todayDate; - pricingData.date[todayDate].pricingUpdated = 1; - chrome.storage.local.set({[pricingData.key]: pricingData}, function() { - $('[name="submit-prices"]').click(); - }); - - }); - //Update new pricing input - if(analysis.hasValue('newPrice')){ - //Modify new price input - for(let cmp in analysis.data){ - if(analysis.data[cmp].newPrice){ - prices[cmp].newPriceInput.val(analysis.data[cmp].newPrice); - } - } - } - //For snapshot button - if(pricingData.date[todayDate]){ - //Today data does exist - if(pricingData.date[todayDate].pricingUpdated){ - //Today pricing updated - invPricingAnalysisBarSpan.text("Today prices have been updated at: "+pricingData.date[todayDate].updateTime); - - //Automation - if(settings.invPricing.autoClose){ - close(); - } - } else { - //Today pricing not updated - invPricingAnalysisBarSpan.text("Today's snapshot data saved at: "+pricingData.date[todayDate].updateTime); - $(invPricingAnalysisBar).append($('
      • ').html(saveInvPricingBtn.text("save snapshot data again"))); - if(analysis.hasValue('newPrice')){ - $(invPricingAnalysisBar).append($('
      • ').html(applyNewPriceInvPricingBtn)); - } - } - } else { - //Today data does not exist - $(invPricingAnalysisBar).append($('
      • ').html(saveInvPricingBtn.text("save snapshot data"))); - if(analysis.hasValue('newPrice')){ - $(invPricingAnalysisBar).append($('
      • ').html(applyNewPriceInvPricingBtn)); - } - } - } -} - -//Display History -function displayHistory(analysis){ - //Prepare data - let dates = []; - //Get valid dates can add function here - for(let date in pricingData.date) { - dates.push(date) - } - dates.sort(); - //If historical data exist then build - if(dates.length){ - //Build Div - let mainDiv = $("#aes-div-analysis"); - mainDiv.after('

        Historical Data

        '); - - //History Options - let fieldset = $('
        ').html('History Options'); - //Hide Now - let option1 = $('
        ').html(''); - //Show only Priced - let option2 = $('
        ').html(''); - - //Number of records - let option3 = $('').html('') - let wrapper = $('
        ').append('',option3); - - fieldset.append(option1,option2,wrapper); - $("#aes-div-invPricing-historicalData").append(fieldset); - //Default values - if(settings.invPricing.historyTable.showNow){ - $("#aes-check-inventory-history-showNow").prop("checked",true); - } - if(settings.invPricing.historyTable.showOnlyPricing){ - $("#aes-check-inventory-history-showOnlyPricing").prop("checked",true); - } - $("#aes-select-inventory-history-numberPastDates").val(settings.invPricing.historyTable.numberOfDates); - - //Change events - $("#aes-check-inventory-history-showNow").change(function () { - if(this.checked) { - settings.invPricing.historyTable.showNow = 1; - } else { - settings.invPricing.historyTable.showNow = 0; - } - chrome.storage.local.set({settings: settings}, function() {}); - buildHistoryTable(); - }); - $("#aes-check-inventory-history-showOnlyPricing").change(function () { - buildHistoryTable(); - if(this.checked) { - settings.invPricing.historyTable.showOnlyPricing = 1; - } else { - settings.invPricing.historyTable.showOnlyPricing = 0; - } - chrome.storage.local.set({settings: settings}, function() {}); - }); - $("#aes-select-inventory-history-numberPastDates").change(function () { - settings.invPricing.historyTable.numberOfDates = $('#aes-select-inventory-history-numberPastDates').val(); - chrome.storage.local.set({settings: settings}, function() {}); - buildHistoryTable(); - }); - - buildHistoryTable(); - } -} -function buildHistoryTable(){ - //Clean previous table - $('#aes-table-inventory-history').remove(); - - let showNow = 0; - let showOnlyPricing = 0; - if($('#aes-check-inventory-history-showNow:checked').length > 0){ - showNow = 1; - } - if($('#aes-check-inventory-history-showOnlyPricing:checked').length > 0){ - showOnlyPricing = 1; - } - - let numberOfDates = $('#aes-select-inventory-history-numberPastDates').val(); - switch(numberOfDates) { - case '5': - numberOfDates = 5; - break; - case '10': - numberOfDates = 10; - break; - case 'all': - numberOfDates = 0; - break; - default: - numberOfDates = 10; - } - - - - - let dates = []; - //Get valid dates can add function here - for(let date in pricingData.date) { - if(showOnlyPricing){ - if(pricingData.date[date].pricingUpdated){ - dates.push(date); - } - } else { - dates.push(date); - } - } - dates.reverse(); - if(numberOfDates){ - dates = dates.slice(0, numberOfDates); - } - dates.sort(); - if(dates.length){ - - //Headrows - let th = ['']; - let th1 = ['SC']; - for(let i=0;i').text(formatDate(date))); - th1.push('Price'); - th1.push('Δ %'); - th1.push('Load'); - th1.push('Δ %'); - //Index - th1.push('Index'); - } else { - th.push($('').text(formatDate(date))); - th1.push('Price'); - th1.push('Load'); - //Index - th1.push('Index'); - } - } - if(showNow){ - //Now - th.push($('').text('Now')); - th1.push('Price'); - th1.push('Δ %'); - th1.push('Load'); - th1.push('Δ %'); - //index - th1.push('Index'); - } - - - let headRow = $('').append(th); - let headRow2 = $('').append(th1); - - let thead = $('').append(headRow,headRow2); - - - - //Build table - let compartments = ['Y','C','F','Cargo']; - - //Tbody rows - let tbody = $(''); - compartments.forEach(function(cmp){ - let td = []; - td.push($('').text(cmp)); - //Historical tds - for(let i=0;i').html(displayHistoryPrice(data))); - td.push($('').html(displayDifference(data,prevData).price)); - td.push($('').html(displayHistoryLoad(data))); - td.push($('').html(displayDifference(data,prevData).load)); - //index - td.push($('').html(historyDisplayIndex(data,0))); - } else { - //First data point - td.push($('').html(displayHistoryPrice(data))); - td.push($('').html(displayHistoryLoad(data))); - //index - td.push($('').html(historyDisplayIndex(data,0))); - } - } - if(showNow){ - //Now TDs - let data = analysis.data[cmp]; - let prevData = pricingData.date[dates[dates.length-1]].data[cmp]; - td.push($('').html(displayHistoryPrice(data))); - td.push($('').html(displayDifference(data,prevData).price)); - td.push($('').html(displayHistoryLoad(data))); - td.push($('').html(displayDifference(data,prevData).load)); - //Index - td.push($('').html(historyDisplayIndex(data,0))); - } - - - //Finish row - let row = $('').append(td); - tbody.append(row); - }); - - - //Table footer Total Rows - let totalCollumns = th1.length; - let footRow = []; - let footerRows = ['pax','all'] - footRow.push(''); - //Total PAX - footerRows.forEach(function(type){ - let tf = []; - tf.push($('').text(historyDisplayTotalText(type))); - for(let i=0;i'); - tf.push($('').html(historyDisplayTotal(data,type))); - tf.push(''); - //index - tf.push($('').html(historyDisplayIndex(data,type))); - } else { - tf.push(''); - tf.push($('').html(historyDisplayTotal(data,type))); - //index - tf.push($('').html(historyDisplayIndex(data,type))); - } - } - if(showNow){ - //Now - let data = analysis.data; - tf.push(''); - tf.push($('').html(historyDisplayTotal(data,type))); - tf.push(''); - //index - tf.push($('').html(historyDisplayIndex(data,type))); - } - - - footRow.push($('').append(tf)); - - - }); - - let tfoot = $('').append(footRow); - - - let table = $('
        ').append(thead,tbody,tfoot); - - let tableDiv = $('
        ').append(table); - - $("#aes-div-invPricing-historicalData").append(tableDiv); - - } - -} -//Validate -function validateAllOptions(){ - //Get all options - //Check if all flight numbers selected - let valid = $('.col-md-10 > div > .as-panel:eq(1) > ul:eq(0) li:eq(0)').hasClass("active"); - if(!valid){ - aesmodule.valid = 0; - aesmodule.error.push('Please select All Flight Numbers under Current Inventory'); - return; - } - //Check if Apply settings to correctly set - $('.col-md-10 > div > .as-panel:eq(1) > div > div > div:eq(0) fieldset:eq(2) > div input').each(function(index){ - switch(index) { - case 0: - if(!this.checked){ - aesmodule.valid = 0; - aesmodule.error.push('Please check Airport Pair under Apply settings to in Settings panel'); - } - break; - case 1: - if(!this.checked){ - aesmodule.valid = 0; - aesmodule.error.push('Please check Flight Numbers under Apply settings to in Settings panel'); - } - break; - case 2: - if(this.checked){ - aesmodule.valid = 0; - aesmodule.error.push('Please uncheck Return Airport Pair under Apply settings to in Settings panel'); - } - break; - case 3: - if(this.checked){ - aesmodule.valid = 0; - aesmodule.error.push('Please uncheck Return Flight Numbers under Apply settings to in Settings panel'); - } - break; - } - }); - //Check if Data settings correctly set - let dataDiv = $('.col-md-10 > div > .as-panel:eq(1) > div > div > div:eq(1) .layout-col-md-3'); - //Service classes - $('fieldset:eq(0) label',dataDiv).each(function(){ - if(!$('input',this)[0].checked){ - aesmodule.valid = 0; - aesmodule.error.push('Please check '+$(this).text()+' under Service Classes in Data panel'); - } - }); - //Flight Status - $('fieldset:eq(1) label',dataDiv).each(function(index){ - if(index == 1 || index == 2){ - if(!$('input',this)[0].checked){ - aesmodule.valid = 0; - aesmodule.error.push('Please check '+$(this).text()+' under Flight Status in Data panel'); - } - } - }); - //Load - $('fieldset:eq(2) div',dataDiv).each(function(index){ - if(!index){ - //Minimum - if(parseInt($('select option:selected',this).text(),10) != 0){ - aesmodule.valid = 0; - aesmodule.error.push('Please select 0 for '+$('label',this).text()+' under Load in Data panel'); - } - } else { - //Max - if(parseInt($('select option:selected',this).text(),10) != 100){ - aesmodule.valid = 0; - aesmodule.error.push('Please select 100 for '+$('label',this).text()+' under Load in Data panel'); - } - } - }); - //Settings Group by flight - if($('fieldset:eq(3) input',dataDiv)[0].checked){ - aesmodule.valid = 0; - aesmodule.error.push('Please uncheck '+$('fieldset:eq(3) label',dataDiv).text()+' under Settings in Data panel'); - } - -} -function displayValidationError(){ - let p = []; - p.push($('

        ').text('AES Inventory Pricing Module could not be loaded because of errors:')); - aesmodule.error.forEach(function(error){ - p.push($('

        ').html(''+error+'')); - }); - p.push($('

        ').html('Refresh the page after making adjustments.')); - let panel = $('
        ').append(p); - let h2 = $('

        ').text('AES Inventory Pricing Module'); - let div = $('
        ').append(h2,panel); - $('h1:eq(0)').after(h2,panel) -} - -//History Table functions -function historyDisplayIndex(data,type){ - let cmp = []; - let index=0; - switch(type) { - case 'all': - cmp = ['Y','C','F','Cargo']; - break; - case 'pax': - cmp = ['Y','C','F']; - break; - case 0: - cmp = 0; - break; - } - if(cmp){ - //Multi index - let count = 0; - cmp.forEach(function(comp){ - if(data[comp].valid){ - index += data[comp].index; - count++; - } - }); - index = Math.round(index/count); - } else { - //one cmp index - if(data.valid){ - index = data.index; - } - } - if(index){ - let span = $(''); - if(index >= 90){ - return span.addClass('good').text(index); - } - if(index <= 50 ){ - return span.addClass('bad').text(index); - } - return span.addClass('warning').text(index); - } else { - return '-'; - } -} -function historyDisplayTotalText(type){ - switch(type) { - case 'all': - return "Total PAX+Cargo"; - case 'pax': - return "Total PAX"; - } -} -function historyDisplayTotal(data,type){ - let cmp = []; - switch(type) { - case 'all': - cmp = ['Y','C','F','Cargo']; - break; - case 'pax': - cmp = ['Y','C','F']; - break; - default: - // code block - } - let load,cap,bkd; - load = cap = bkd = 0; - cmp.forEach(function(comp){ - if(data[comp].valid){ - cap += data[comp].totalCap; - bkd += data[comp].totalBkd; - } - }); - if(cap){ - load = Math.round(bkd/cap*100); - return bkd+' / '+cap+' ('+displayPerc(load,'load')+')'; - } else { - return '-'; - } -} -function displayHistoryLoad(data){ - if(data.valid){ - let booked = data.totalBkd; - let capacity = data.totalCap; - let load = Math.round(booked/capacity * 100); - return booked+' / '+capacity+' ('+displayPerc(load,'load')+')'; - } else { - return '-'; - } -} -function displayHistoryPrice(data){ - if(data.valid){ - let price =data.analysisPrice; - let pricePoint = data.analysisPricePoint; - return formatCurrency(price)+' AS$ ('+displayPerc(pricePoint,'price')+')'; - } else { - return '-'; - } -} -function displayDifference(current,old){ - if(current.valid && old.valid){ - let currentLoad = Math.round(current.totalBkd/current.totalCap * 100); - let oldLoad = Math.round(old.totalBkd/old.totalCap * 100); - let load = currentLoad-oldLoad; - let price = current.analysisPricePoint - old.analysisPricePoint; - return {load:load,price:price}; - } else { - return {load:'-',price:'-'}; - } -} - -function displayPerc(perc,type){ - let span = $(''); - switch (type) { - case 'price': - if(perc >= 100){ - span.addClass('good').text(perc+"%"); - return span.prop('outerHTML'); - } - if(perc < 75){ - span.addClass('bad').text(perc+"%"); - return span.prop('outerHTML'); - } - span.addClass('warning').text(perc+"%"); - return span.prop('outerHTML'); - case 'load': - if(perc >= 70){ - span.addClass('good').text(perc+"%"); - return span.prop('outerHTML'); - } - if(perc < 40){ - span.addClass('bad').text(perc+"%"); - return span.prop('outerHTML'); - } - span.addClass('warning').text(perc+"%"); - return span.prop('outerHTML'); - default: - return 'ERROR:2502 Wrong type set:'+type+''; - } -} -//Helper functions -function formatDate(date){ - return date.substring(0, 4)+'-'+date.substring(4, 6)+'-'+date.substring(6, 8); -} -function formatCurrency(value) { - return Intl.NumberFormat().format(value) -} -function cleanInteger(a){ - a = a.replace(',',''); - a = a.replace('.',''); - a = a.replace(' AS$',''); - return parseInt(a, 10); -} -function getPricingInventoryKey(){ - //Get Origin and Destination - let x = $("h2:first a"); - let org = $(x[0]).text(); - let dest = $(x[1]).text(); - //get server - let server = getServerName(); - //get airline code - let airline = getAirlineCode(); - //create key - let key = server + airline + org + dest+'routeAnalysis'; - return {key:key,server:server,airline:airline,type:"routeAnalysis",origin:org,destination:dest} -} -function getServerName(){ - let server = window.location.hostname - server = server.split('.'); - return server[0]; -} -function getAirlineCode(){ - let airline = $("#inventory-grouped-table tbody a:first").text().split(" "); - if(!airline[0]){ - airline = $("#inventory-table tbody a:first").text().split(" "); - } - return airline[0]; -} -function getCurrentDateTime(){ - let a = $(".as-footer-line-element:has('.fa-clock-o')").text().trim(); - let b = a.split(" "); - //For date - let dateTemp = b[0].split("-"); - let date; - if(dateTemp.length == 1){ - //German - dateTemp = dateTemp[0].split("."); - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[2] + date[1] + date[0]; - } else { - //English - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[0] + date[1] + date[2]; - } - //For time - let time = b[b.length-2] +' '+b[b.length-1]; - return {date:date,time:time}; -} +"use strict"; +//MAIN +//Global vars +var settings, pricingData, todayDate, analysis; +var aesmodule = { valid: true, error: [] }; + +window.addEventListener("load", async (event) => { + settings = await getSettings() + aesmodule = new Validation() + + if (!aesmodule.valid) { + displayValidationError() + return + } + displayInventory() +}) + +/** + * Get settings from local storage + * @returns {object} data.settings + */ +async function getSettings() { + const data = await chrome.storage.local.get(['settings']) + return data.settings +} + +async function displayInventory() { + todayDate = parseInt(AES.getServerDate().date, 10); + //Get flights + let flights = getFlights(); + let prices = getPriceDetails(); + let storageKey = getPricingInventoryKey(); + + //Check if any snapshots saved + let defaultPricingData = { + server: storageKey.server, + airline: storageKey.airline, + type: storageKey.type, + origin: storageKey.origin, + destination: storageKey.destination, + key: storageKey.key, + date: {} + } + const storageData = await chrome.storage.local.get({[storageKey.key]: defaultPricingData}) + pricingData = storageData[storageKey.key] + + //Do Analysis + analysis = getAnalysis(flights, prices, pricingData.date); + //Display analysis + displayAnalysis(analysis, prices); + //Display history + displayHistory(analysis); + + + //Automation + //Check if valid analysis exists + if (analysis.hasValue('valid')) { + //CHeck if updated todayDate + if (pricingData.date[todayDate]) { + //Today update exists + //Check if pricing updated today + if (pricingData.date[todayDate].pricingUpdated) { + //Pricing updated today + //Do nothing + } else { + //Pricing not updated today + //Check if new price avaialble + if (analysis.hasValue('newPrice')) { + //Update price + if (settings.invPricing.autoPriceUpdate) { + $('#aes-btn-invPricing-apply-new-prices').click(); + } + } + } + } else { + //Today update does not exists + //Check if new price avaialble + if (analysis.hasValue('newPrice')) { + //Update price + if (settings.invPricing.autoPriceUpdate) { + $('#aes-btn-invPricing-apply-new-prices').click(); + } else if (settings.invPricing.autoAnalysisSave) { + $('#aes-btn-invPricing-save-snapshot').click(); + } + } else { + //Update data + if (settings.invPricing.autoAnalysisSave) { + $('#aes-btn-invPricing-save-snapshot').click(); + } + } + } + } +} + +/** + * Get Flights + * @returns {array} flights - array of flight objects + */ +function getFlights() { + const flights = [] + // TODO: also support grouped mode (#inventory-grouped-table) + const flightRows = document.querySelectorAll("#inventory-table tbody tr") + + if (!flightRows) { + throw new Error("\"Group by flight\" needs to be unchecked") + } + + for (const row of flightRows) { + const flight = getFlight(row) + flights.push(flight) + } + + return flights +} + +/** + * Get flight information and return as an object + * @param {HTMLElement} row - the with flight information + * @returns {object} flight - object with the parsed flight information + */ +function getFlight(row) { + const cells = row.querySelectorAll("td") + const flightNumber = cells[1].querySelector("a[href*=numbers").innerText + const date = cells[2].innerText + const compCode = getCompCode(cells[5].innerText) + const capacity = cells[6].innerText + const booked = cells[7].innerText + const price = cells[9].innerText + const status = cells[10].innerText.replace(/\s+/g, "") + + const flight = { + fltNr: flightNumber, + date: date, + cmp: compCode, + cap: AES.cleanInteger(capacity), + bkd: AES.cleanInteger(booked), + price: AES.cleanInteger(price), + status: status + } + + return flight +} + +/** + * Checks if the string is longer than one character and returns a string of "Cargo" + * @param {string} text - localised word for "Cargo" + * @returns {string} text - either passthrough of the input or "Cargo" + */ +function getCompCode(text) { + if (!text) { + throw new Error("no value provided for getCompCode") + } + + if (text.length > 1) { + return "Cargo" + } + + return text +} + +/** + * Get Prices + * @returns {object} prices + */ +function getPriceDetails() { + const pricingRows = document.querySelectorAll(".pricing table tbody tr") + const prices = {} + + for (const row of pricingRows) { + const cells = row.querySelectorAll("td") + const cmp = getCompCode(cells[0].innerText) + const price = getPrice(cells) + + prices[cmp] = price + } + + return prices +} + +/** + * Get price + * @param {array} cells + * @returns {object} price + */ +function getPrice(cells) { + const currentPrice = AES.cleanInteger(cells[1].innerText) + const defaultPrice = AES.cleanInteger(cells[4].innerText.replace(/\s+/g, '')) + const currentPricePoint = getCurrentPricePoint(currentPrice, defaultPrice) + const newPriceInput = cells[2].querySelector("input") + + const price = { + currentPrice: currentPrice, + defaultPrice: defaultPrice, + currentPricePoint: currentPricePoint, + newPriceInput: newPriceInput + } + + return price +} + +/** + * @param {string} currentPrice + * @param {string} defaultPrice + * @returns {integer} + */ +function getCurrentPricePoint(currentPrice, defaultPrice) { + return Math.round((currentPrice / defaultPrice) * 100) +} + +//Get Analysis +function getAnalysis(flights, prices, storedData) { + //Setup object + let mostRecentDate + let mostRecentData + let data = { + Y: 0, + C: 0, + F: 0, + Cargo: 0 + } + let analysis = { + data: data, + getLoad: function(cmp) { + if (this.data[cmp].valid) { + return this.data[cmp].totalBkd / this.data[cmp].totalCap; + } else { + return 0; + } + }, + note: function(cmp) { + if (this.data[cmp].valid) { + if (this.data[cmp].useCurrentPrice) { + return "Current price analysis"; + } else { + return "No current price flights, using old price" + } + } else { + return "No data for analysis"; + } + }, + displayLoad: function(cmp) { + if (this.data[cmp].valid) { + return this.data[cmp].totalBkd + " / " + this.data[cmp].totalCap + " (" + displayPerc(Math.round(this.getLoad(cmp) * 100), 'load') + ")"; + } else { + return '-'; + } + }, + displayRec: function(cmp) { + if (this.data[cmp].recommendation) { + switch (this.data[cmp].recType) { + case 'good': + return '' + this.data[cmp].recommendation + ''; + case 'bad': + return '' + this.data[cmp].recommendation + ''; + case 'neutral': + return '' + this.data[cmp].recommendation + ''; + default: + return 'ERROR:2501 Wrong recType set:' + this.data[cmp].recType + ''; + } + } else { + return '-' + } + }, + displayPrice: function(cmp, type) { + switch (type) { + case 'current': + return formatCurrency(this.data[cmp].currentPrice) + ' AS$ (' + displayPerc(this.data[cmp].currentPricePoint, 'price') + ')'; + case 'new': + if (this.data[cmp].newPrice) { + return formatCurrency(this.data[cmp].newPrice) + ' AS$ (' + displayPerc(this.data[cmp].newPricePoint, 'price') + ')'; + } else { + return '-'; + } + case 'analysis': + if (this.data[cmp].valid) { + return formatCurrency(this.data[cmp].analysisPrice) + ' AS$ (' + displayPerc(this.data[cmp].analysisPricePoint, 'price') + ')'; + } else { + return '-'; + } + default: + return 'ERROR:2502 Wrong type set:' + type + ''; + } + }, + displayIndex: function(cmp) { + if (this.data[cmp].valid) { + let span = $(''); + if (this.data[cmp].index >= 90) { + return span.addClass('good').text(this.data[cmp].index); + } + if (this.data[cmp].index <= 50) { + return span.addClass('bad').text(this.data[cmp].index); + } + return span.addClass('warning').text(this.data[cmp].index); + + } else { + return '-'; + } + }, + displayTotalLoad: function(type) { + let cmp = []; + switch (type) { + case 'all': + cmp = ['Y', 'C', 'F', 'Cargo']; + break; + case 'pax': + cmp = ['Y', 'C', 'F']; + break; + default: + // code block + } + let load, cap, bkd; + load = cap = bkd = 0; + for (let i = 0; i < cmp.length; i++) { + if (this.data[cmp[i]].valid) { + cap += this.data[cmp[i]].totalCap; + bkd += this.data[cmp[i]].totalBkd; + } + } + + if (cap) { + load = Math.round(bkd / cap * 100); + return bkd + ' / ' + cap + ' (' + displayPerc(load, 'load') + ')'; + } else { + return '-'; + } + }, + displayTotalIndex: function(type) { + let cmp = []; + switch (type) { + case 'all': + cmp = ['Y', 'C', 'F', 'Cargo']; + break; + case 'pax': + cmp = ['Y', 'C', 'F']; + break; + default: + // code block + } + let count, totalIndex; + count = totalIndex = 0; + for (let i = 0; i < cmp.length; i++) { + if (this.data[cmp[i]].valid) { + count++; + totalIndex += this.data[cmp[i]].index; + } + } + if (count) { + totalIndex = Math.round(totalIndex / count); + let span = $(''); + if (totalIndex >= 90) { + return span.addClass('good').text(totalIndex); + } + if (totalIndex <= 50) { + return span.addClass('bad').text(totalIndex); + } + return span.addClass('warning').text(totalIndex); + } else { + return '-'; + } + }, + hasValue: function(value) { + for (let cmp in this.data) { + if (this.data[cmp][value]) { + return 1; + } + } + return 0; + } + }; + + //Filter flights + flights = flights.filter(function(flight) { + return flight.status == 'finished' || flight.status == 'inflight'; + }); + + //Check historical data + if (storedData) { + //Shouldbe function inside storage object + let dates = [] + for (let date in storedData) { + if (Number.isInteger(parseInt(date))) { + dates.push(date) + } + } + dates.reverse(); + mostRecentDate = dates[0] + mostRecentData = storedData[mostRecentDate] + } + + //extract each cmp analysis + for (let cmp in analysis.data) { + analysis.data[cmp] = { + totalCap: 0, + totalBkd: 0, + valid: 0, + analysisPrice: 0, + analysisPricePoint: 0, + useCurrentPrice: 0, + currentPrice: prices[cmp].currentPrice, + currentPricePoint: prices[cmp].currentPricePoint + }; + let price = prices[cmp].currentPrice; + //Only cmp flights + let cmpFlights = flights.filter(function(flight) { + return flight.cmp == cmp; + }); + //if no cmp flights + if (cmpFlights.length) { + //Check if current price flights avaialble + let flightsArray = cmpFlights.filter(function(flight) { + return (flight.price == price); + }); + if (flightsArray.length) { + analysis.data[cmp].useCurrentPrice = 1; + analysis.data[cmp].analysisPrice = price; + analysis.data[cmp].analysisPricePoint = Math.round(price / prices[cmp].defaultPrice * 100); + analysis.data[cmp].valid = true; + } else if (mostRecentData) { + // flightsArray = cmpFlights.filter(function(flight) { + // return flight.price == mostRecentData.data[cmp].analysisPrice; + // }); + flightsArray = cmpFlights + if (flightsArray.length) { + analysis.data[cmp].useCurrentPrice = 0; + analysis.data[cmp].analysisPrice = mostRecentData.data[cmp].analysisPrice; + analysis.data[cmp].analysisPricePoint = Math.round(mostRecentData.data[cmp].analysisPrice / prices[cmp].defaultPrice * 100); + analysis.data[cmp].valid = true; + } + } + if (analysis.data[cmp].valid) { + flightsArray.forEach(function(flight) { + analysis.data[cmp].totalCap += flight.cap; + analysis.data[cmp].totalBkd += flight.bkd; + }); + } + } + } + + //END extract each cmp analysis + analysis = generateRecommendation(analysis, prices); + + //Make route index + analysis = generateRouteIndex(analysis); + return analysis; +} + +function generateRecommendation(analysis, prices) { + for (let cmp in analysis.data) { + analysis.data[cmp].recommendation = 0; + if (analysis.data[cmp].valid) { + if (analysis.data[cmp].useCurrentPrice) { + //Find recommendation + let load = Math.round(analysis.getLoad(cmp) * 100); + //Find step + let step; + for (let i in settings.invPricing.recommendation[cmp].steps) { + step = settings.invPricing.recommendation[cmp].steps[i]; + if (load >= step.min && load <= step.max) { + break; + } + } + //Find new price point + let newPricePoint = prices[cmp].currentPricePoint + step.step; + //See if new price in bounds for Drop + if (step.step < 0) { + analysis.data[cmp].recType = 'bad'; + if (newPricePoint < settings.invPricing.recommendation[cmp].minPrice) { + newPricePoint = settings.invPricing.recommendation[cmp].minPrice; + } + } + //See if new price in bounds for Raise + if (step.step > 0) { + analysis.data[cmp].recType = 'good'; + if (newPricePoint > settings.invPricing.recommendation[cmp].maxPrice) { + newPricePoint = settings.invPricing.recommendation[cmp].maxPrice; + } + } + //see if already at highest/lowest price point + if (step.step != 0) { + if (newPricePoint == prices[cmp].currentPricePoint) { + if (newPricePoint == settings.invPricing.recommendation[cmp].minPrice) { + //Already at lowest point + analysis.data[cmp].recommendation = 'Already at lowest price!'; + } + if (newPricePoint == settings.invPricing.recommendation[cmp].maxPrice) { + //Already at highest point + analysis.data[cmp].recommendation = 'Already at highest price!'; + } + } + } else { + analysis.data[cmp].recType = 'neutral'; + } + //check if not set by exceptions + if (!analysis.data[cmp].recommendation) { + analysis.data[cmp].recommendation = step.name; + analysis.data[cmp].newPriceChange = step.step; + if (step.step) { + analysis.data[cmp].newPricePoint = newPricePoint; + analysis.data[cmp].newPrice = Math.round(newPricePoint / 100 * prices[cmp].defaultPrice); + } + } + } + } + } + return analysis; +} + +function generateRouteIndex(analysis) { + //Each CMP index + for (let cmp in analysis.data) { + if (analysis.data[cmp].valid) { + let index = (analysis.data[cmp].analysisPricePoint + (analysis.getLoad(cmp) * 100 * 3)) / 4; + analysis.data[cmp].index = Math.round(index); + } + } + return analysis; +} + +//Display analysis +function displayAnalysis(analysis, prices) { + + //Build table + let mainDiv = $(".container-fluid .row .col-md-10 div .as-panel:eq(0)"); + mainDiv.after( + ` +

        Analysis (today's snapshot)

        +
        +
        +
        + +
        +
        +
        +
        + ` + ); + + //Table head + let th = []; + th.push('SC'); + th.push('Note'); + th.push('Analysis Price'); + th.push('Load'); + th.push('Index'); + th.push('Current Price'); + th.push('Recommendation'); + th.push('New Price'); + let headRow = $('').append(th); + let thead = $('').append(headRow); + + //Table body + let tbody = $(''); + for (let cmp in analysis.data) { + let td = []; + td.push('' + cmp + ''); + td.push('' + analysis.note(cmp) + ''); + td.push('' + analysis.displayPrice(cmp, 'analysis') + ''); + td.push('' + analysis.displayLoad(cmp) + ''); + td.push($('').html(analysis.displayIndex(cmp))); + td.push('' + analysis.displayPrice(cmp, 'current') + ''); + td.push('' + analysis.displayRec(cmp) + ''); + td.push('' + analysis.displayPrice(cmp, 'new') + ''); + let row = $('').append(td); + tbody.append(row); + } + + //Table footer + let footRow = [] + footRow.push(''); + //Total PAX + let tf = []; + tf.push('Total PAX'); + tf.push(''); + tf.push($('').html(analysis.displayTotalLoad('pax'))); + tf.push($('').html(analysis.displayTotalIndex('pax'))); + tf.push(''); + footRow.push($('').append(tf)); + //Total + tf = []; + tf.push('Total PAX+Cargo'); + tf.push(''); + tf.push($('').html(analysis.displayTotalLoad('all'))); + tf.push($('').html(analysis.displayTotalIndex('all'))); + tf.push(''); + footRow.push($('').append(tf)); + let tfoot = $('').append(footRow); + + $("#aes-table-analysis").append(thead, tbody, tfoot); + + //Display pricing and data save buttons + if (analysis.hasValue('valid')) { + let invPricingAnalysisBar = $('
          '); + let invPricingAnalysisBarSpan = $(''); + invPricingAnalysisBar.append($('
        • ').html(invPricingAnalysisBarSpan)); + $("#aes-div-analysis").prepend(invPricingAnalysisBar); + //create buttons + //Save Data + let saveInvPricingBtn = $(''); + $(saveInvPricingBtn).click(function() { + $(this).closest("li").remove(); + invPricingAnalysisBarSpan.text('Saving analysis data...'); + //Get updated time + let updateTime = AES.getServerDate().time; + pricingData.date[todayDate] = analysis; + pricingData.date[todayDate].updateTime = updateTime; + pricingData.date[todayDate].date = todayDate; + pricingData.date[todayDate].pricingUpdated = 0; + chrome.storage.local.set({ + [pricingData.key]: pricingData }, function() { + invPricingAnalysisBarSpan.removeClass().addClass("good").text("Data Saved!"); + //Automation + if (settings.invPricing.autoClose) { + close(); + } + }); + }); + + //Update prices + let applyNewPriceInvPricingBtn = $(''); + $(applyNewPriceInvPricingBtn).click(function() { + $(this).closest("ul").find("li button").closest("li").remove(); + invPricingAnalysisBarSpan.text('Updating prices...'); + //Get updated time + let updateTime = AES.getServerDate().time; + pricingData.date[todayDate] = analysis; + pricingData.date[todayDate].updateTime = updateTime; + pricingData.date[todayDate].date = todayDate; + pricingData.date[todayDate].pricingUpdated = 1; + chrome.storage.local.set({ + [pricingData.key]: pricingData }, function() { + $('[name="submit-prices"]').click(); + }); + }); + //Update new pricing input + if (analysis.hasValue('newPrice')) { + //Modify new price input + for (let cmp in analysis.data) { + if (analysis.data[cmp].newPrice) { + prices[cmp].newPriceInput.value = analysis.data[cmp].newPrice; + } + } + } + //For snapshot button + if (pricingData.date[todayDate]) { + //Today data does exist + if (pricingData.date[todayDate].pricingUpdated) { + //Today pricing updated + invPricingAnalysisBarSpan.text("Today prices have been updated at: " + pricingData.date[todayDate].updateTime); + + //Automation + if (settings.invPricing.autoClose) { + close(); + } + } else { + //Today pricing not updated + invPricingAnalysisBarSpan.text("Today's snapshot data saved at: " + pricingData.date[todayDate].updateTime); + $(invPricingAnalysisBar).append($('
        • ').html(saveInvPricingBtn.text("save snapshot data again"))); + if (analysis.hasValue('newPrice')) { + $(invPricingAnalysisBar).append($('
        • ').html(applyNewPriceInvPricingBtn)); + } + } + } else { + //Today data does not exist + $(invPricingAnalysisBar).append($('
        • ').html(saveInvPricingBtn.text("save snapshot data"))); + if (analysis.hasValue('newPrice')) { + $(invPricingAnalysisBar).append($('
        • ').html(applyNewPriceInvPricingBtn)); + } + } + } +} + +//Display History +function displayHistory(analysis) { + //Prepare data + let dates = []; + //Get valid dates can add function here + for (let date in pricingData.date) { + if (Number.isInteger(parseInt(date))) { + dates.push(date) + } + } + dates.sort(); + //If historical data exist then build + if (dates.length) { + //Build Div + let mainDiv = $("#aes-div-analysis"); + mainDiv.after('

          Historical Data

          '); + + //History Options + let fieldset = $('
          ').html('History Options'); + //Hide Now + let option1 = $('
          ').html(''); + //Show only Priced + let option2 = $('
          ').html(''); + + //Number of records + let option3 = $('').html('') + let wrapper = $('
          ').append('', option3); + + fieldset.append(option1, option2, wrapper); + $("#aes-div-invPricing-historicalData").append(fieldset); + //Default values + if (settings.invPricing.historyTable.showNow) { + $("#aes-check-inventory-history-showNow").prop("checked", true); + } + if (settings.invPricing.historyTable.showOnlyPricing) { + $("#aes-check-inventory-history-showOnlyPricing").prop("checked", true); + } + $("#aes-select-inventory-history-numberPastDates").val(settings.invPricing.historyTable.numberOfDates); + + //Change events + $("#aes-check-inventory-history-showNow").change(function() { + if (this.checked) { + settings.invPricing.historyTable.showNow = 1; + } else { + settings.invPricing.historyTable.showNow = 0; + } + chrome.storage.local.set({ settings: settings }, function() {}); + buildHistoryTable(); + }); + $("#aes-check-inventory-history-showOnlyPricing").change(function() { + buildHistoryTable(); + if (this.checked) { + settings.invPricing.historyTable.showOnlyPricing = 1; + } else { + settings.invPricing.historyTable.showOnlyPricing = 0; + } + chrome.storage.local.set({ settings: settings }, function() {}); + }); + $("#aes-select-inventory-history-numberPastDates").change(function() { + settings.invPricing.historyTable.numberOfDates = $('#aes-select-inventory-history-numberPastDates').val(); + chrome.storage.local.set({ settings: settings }, function() {}); + buildHistoryTable(); + }); + + buildHistoryTable(); + } +} + +function buildHistoryTable() { + //Clean previous table + $('#aes-table-inventory-history').remove(); + + let showNow = 0; + let showOnlyPricing = 0; + if ($('#aes-check-inventory-history-showNow:checked').length > 0) { + showNow = 1; + } + if ($('#aes-check-inventory-history-showOnlyPricing:checked').length > 0) { + showOnlyPricing = 1; + } + + let numberOfDates = $('#aes-select-inventory-history-numberPastDates').val(); + switch (numberOfDates) { + case '5': + numberOfDates = 5; + break; + case '10': + numberOfDates = 10; + break; + case 'all': + numberOfDates = 0; + break; + default: + numberOfDates = 10; + } + + let dates = []; + //Get valid dates can add function here + for (let date in pricingData.date) { + if (showOnlyPricing) { + if (pricingData.date[date].pricingUpdated) { + if (Number.isInteger(parseInt(date))) { + dates.push(date) + } + } + } else { + if (Number.isInteger(parseInt(date))) { + dates.push(date) + } + } + } + if (numberOfDates) { + dates = dates.slice(0, numberOfDates); + } + dates.sort(); + dates.reverse(); + if (dates.length) { + + //Headrows + let th = ['']; + let th1 = ['SC']; + if (showNow) { + //Now + th.push($('').text('Now')); + th1.push('Price'); + th1.push('Δ %'); + th1.push('Load'); + th1.push('Δ %'); + //index + th1.push('Index'); + } + for (let i = 0; i < dates.length; i++) { + let date = dates[i]; + if (i) { + th.push($('').text(AES.formatDateString(date))); + th1.push('Price'); + th1.push('Δ %'); + th1.push('Load'); + th1.push('Δ %'); + //Index + th1.push('Index'); + } else { + th.push($('').text(AES.formatDateString(date))); + th1.push('Price'); + th1.push('Load'); + //Index + th1.push('Index'); + } + } + + let headRow = $('').append(th); + let headRow2 = $('').append(th1); + let thead = $('').append(headRow, headRow2); + + //Build table + let compartments = ['Y', 'C', 'F', 'Cargo']; + + //Tbody rows + let tbody = $(''); + compartments.forEach(function(cmp) { + let td = []; + td.push($('').text(cmp)); + if (showNow) { + //Now TDs + let data = analysis.data[cmp]; + let prevData = pricingData.date[dates[dates.length - 1]].data[cmp]; + td.push($('').html(displayHistoryPrice(data))); + td.push($('').html(displayDifference(data, prevData).price)); + td.push($('').html(displayHistoryLoad(data))); + td.push($('').html(displayDifference(data, prevData).load)); + //Index + td.push($('').html(historyDisplayIndex(data, 0))); + } + //Historical tds + for (let i = 0; i < dates.length; i++) { + let date = dates[i]; + let data = pricingData.date[date].data[cmp]; + if (i) { + let prevData = pricingData.date[dates[i - 1]].data[cmp]; + //Not first data point + td.push($('').html(displayHistoryPrice(data))); + td.push($('').html(displayDifference(data, prevData).price)); + td.push($('').html(displayHistoryLoad(data))); + td.push($('').html(displayDifference(data, prevData).load)); + //index + td.push($('').html(historyDisplayIndex(data, 0))); + } else { + //First data point + td.push($('').html(displayHistoryPrice(data))); + td.push($('').html(displayHistoryLoad(data))); + //index + td.push($('').html(historyDisplayIndex(data, 0))); + } + } + + //Finish row + let row = $('').append(td); + tbody.append(row); + }); + + //Table footer Total Rows + let totalCollumns = th1.length; + let footRow = []; + let footerRows = ['pax', 'all'] + footRow.push(''); + //Total PAX + footerRows.forEach(function(type) { + let tf = []; + tf.push($('').text(historyDisplayTotalText(type))); + if (showNow) { + //Now + let data = analysis.data; + tf.push(''); + tf.push($('').html(historyDisplayTotal(data, type))); + tf.push(''); + //index + tf.push($('').html(historyDisplayIndex(data, type))); + } + for (let i = 0; i < dates.length; i++) { + let date = dates[i]; + let data = pricingData.date[date].data; + if (i) { + tf.push(''); + tf.push($('').html(historyDisplayTotal(data, type))); + tf.push(''); + //index + tf.push($('').html(historyDisplayIndex(data, type))); + } else { + tf.push(''); + tf.push($('').html(historyDisplayTotal(data, type))); + //index + tf.push($('').html(historyDisplayIndex(data, type))); + } + } + + footRow.push($('').append(tf)); + }); + + let tfoot = $('').append(footRow); + let table = $('
          ').append(thead, tbody, tfoot); + let tableDiv = $('
          ').append(table); + + $("#aes-div-invPricing-historicalData").append(tableDiv); + } +} + +function displayValidationError() { + let p = []; + p.push($('

          ').text('AES Inventory Pricing Module could not be loaded because of errors:')); + aesmodule.errors.forEach(function(error) { + p.push($('

          ').html('' + error + '')); + }); + p.push($('

          ').html('Refresh the page after making adjustments.')); + let panel = $('
          ').append(p); + let h2 = $('

          ').text('AES Inventory Pricing Module'); + let div = $('
          ').append(h2, panel); + $('h1:eq(0)').after(h2, panel) +} + +//History Table functions +function historyDisplayIndex(data, type) { + let cmp = []; + let index = 0; + switch (type) { + case 'all': + cmp = ['Y', 'C', 'F', 'Cargo']; + break; + case 'pax': + cmp = ['Y', 'C', 'F']; + break; + case 0: + cmp = 0; + break; + } + if (cmp) { + //Multi index + let count = 0; + cmp.forEach(function(comp) { + if (data[comp].valid) { + index += data[comp].index; + count++; + } + }); + index = Math.round(index / count); + } else { + //one cmp index + if (data.valid) { + index = data.index; + } + } + if (index) { + let span = $(''); + if (index >= 90) { + return span.addClass('good').text(index); + } + if (index <= 50) { + return span.addClass('bad').text(index); + } + return span.addClass('warning').text(index); + } else { + return '-'; + } +} + +function historyDisplayTotalText(type) { + switch (type) { + case 'all': + return "Total PAX+Cargo"; + case 'pax': + return "Total PAX"; + } +} + +function historyDisplayTotal(data, type) { + let cmp = []; + switch (type) { + case 'all': + cmp = ['Y', 'C', 'F', 'Cargo']; + break; + case 'pax': + cmp = ['Y', 'C', 'F']; + break; + default: + // code block + } + let load, cap, bkd; + load = cap = bkd = 0; + cmp.forEach(function(comp) { + if (data[comp].valid) { + cap += data[comp].totalCap; + bkd += data[comp].totalBkd; + } + }); + if (cap) { + load = Math.round(bkd / cap * 100); + return bkd + ' / ' + cap + ' (' + displayPerc(load, 'load') + ')'; + } else { + return '-'; + } +} + +function displayHistoryLoad(data) { + if (data.valid) { + let booked = data.totalBkd; + let capacity = data.totalCap; + let load = Math.round(booked / capacity * 100); + return booked + ' / ' + capacity + ' (' + displayPerc(load, 'load') + ')'; + } else { + return '-'; + } +} + +function displayHistoryPrice(data) { + if (data.valid) { + let price = data.analysisPrice; + let pricePoint = data.analysisPricePoint; + return formatCurrency(price) + ' AS$ (' + displayPerc(pricePoint, 'price') + ')'; + } else { + return '-'; + } +} + +function displayDifference(current, old) { + if (current.valid && old.valid) { + let currentLoad = Math.round(current.totalBkd / current.totalCap * 100); + let oldLoad = Math.round(old.totalBkd / old.totalCap * 100); + let load = currentLoad - oldLoad; + let price = current.analysisPricePoint - old.analysisPricePoint; + return { load: load, price: price }; + } else { + return { load: '-', price: '-' }; + } +} + +function displayPerc(perc, type) { + let span = $(''); + switch (type) { + case 'price': + if (perc >= 100) { + span.addClass('good').text(perc + "%"); + return span.prop('outerHTML'); + } + if (perc < 75) { + span.addClass('bad').text(perc + "%"); + return span.prop('outerHTML'); + } + span.addClass('warning').text(perc + "%"); + return span.prop('outerHTML'); + case 'load': + if (perc >= 70) { + span.addClass('good').text(perc + "%"); + return span.prop('outerHTML'); + } + if (perc < 40) { + span.addClass('bad').text(perc + "%"); + return span.prop('outerHTML'); + } + span.addClass('warning').text(perc + "%"); + return span.prop('outerHTML'); + default: + return 'ERROR:2502 Wrong type set:' + type + ''; + } +} +//Helper functions +function formatCurrency(value) { + return Intl.NumberFormat().format(value) +} + +function getPricingInventoryKey() { + //Get Origin and Destination + let x = $("h2:first a"); + let org = $(x[0]).text(); + let dest = $(x[1]).text(); + //get server + let server = AES.getServerName(); + //get airline code + let airline = getAirlineCode(); + //create key + let key = server + airline + org + dest + 'routeAnalysis'; + return { key: key, server: server, airline: airline, type: "routeAnalysis", origin: org, destination: dest } +} + +function getAirlineCode() { + let airline = $("#inventory-grouped-table tbody a:first").text().split(" "); + if (!airline[0]) { + airline = $("#inventory-table tbody a:first").text().split(" "); + } + return airline[0]; +} diff --git a/extension/content_personelManagement.js b/extension/content_personelManagement.js index 391a4f4..8c5b57b 100644 --- a/extension/content_personelManagement.js +++ b/extension/content_personelManagement.js @@ -69,7 +69,7 @@ function displayPersonelManagement(){ chrome.storage.local.set({settings: settings}, function(){}); }); input.change(function(){ - settings.personelManagement.value = cleanInteger(input.val()); + settings.personelManagement.value = AES.cleanInteger(input.val()); chrome.storage.local.set({settings: settings}, function(){}); }); @@ -92,7 +92,7 @@ function displayPersonelManagement(){ let key = server+airline+"personelManagement"; chrome.storage.local.get([key], function(result) { if(result[key]){ - p.after($('

          ').text('Last time updated on '+formatDate(result[key].date)+' '+result[key].time)); + p.after($('

          ').text('Last time updated on '+AES.formatDateString(result[key].date)+' '+result[key].time)); } else { p.after($('

          ').text('No previous personel management data found.')); } @@ -111,8 +111,8 @@ function priceUpdate(span){ } let salaryInput = $(this).find('form input:eq(2)'); - let salary = cleanInteger(salaryInput.val()); - let average = cleanInteger($(this).find('td:eq(9)').text()); + let salary = AES.cleanInteger(salaryInput.val()); + let average = AES.cleanInteger($(this).find('td:eq(9)').text()); let salaryBtn = $(this).find('td:eq(8) > form .input-group-btn input'); let newSalary; switch(type) { @@ -142,7 +142,7 @@ function priceUpdate(span){ //Save into memory - let today = getDate(); + let today = AES.getServerDate() let key = server + airline + 'personelManagement'; let personelManagementData = { server:server, @@ -158,36 +158,6 @@ function priceUpdate(span){ } }); } -function cleanInteger(a){ - a = a.replace(',',''); - a = a.replace('.',''); - a = a.replace(' AS$',''); - return parseInt(a, 10); -} -function getDate(){ - let a = $(".as-footer-line-element:has('.fa-clock-o')").text().trim(); - let b = a.split(" "); - //For date - let dateTemp = b[0].split("-"); - let date; - if(dateTemp.length == 1){ - //German - dateTemp = dateTemp[0].split("."); - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[2] + date[1] + date[0]; - } else { - //English - date = dateTemp.map(function(value){ - return value.replace(/[^A-Za-z0-9]/g, ''); - }); - date = date[0] + date[1] + date[2]; - } - //For time - let time = b[b.length-2] +' '+b[b.length-1]; - return {date:date,time:time}; -} function getAirline(){ let airline = $("#as-navbar-main-collapse ul li:eq(0) a:eq(0)").text().trim().replace(/[^A-Za-z0-9]/g, ''); return airline; @@ -196,7 +166,4 @@ function getServerName(){ let server = window.location.hostname server = server.split('.'); return server[0]; -} -function formatDate(date){ - return date.substring(0, 4)+'-'+date.substring(4, 6)+'-'+date.substring(6, 8); -} +} diff --git a/extension/css/content.css b/extension/css/content.css index c227b42..817c134 100644 --- a/extension/css/content.css +++ b/extension/css/content.css @@ -1,7 +1,99 @@ +/* Tables */ +.aes-table { + width: auto; +} + +.as-table-well:has(.aes-table) { + width: fit-content; +} + +/* Helpers */ .aes-text-right { - text-align: right; + text-align: right; } .aes-no-text-wrap { - white-space: nowrap; + white-space: nowrap; +} + +/* Specific */ +.aes-aircraftProfit-profit { + text-align: right; + white-space: nowrap; +} + +/** + * About Modal + */ +#aes-about-dialog .modal-dialog { + width: 40em; + max-width: 90vw; + margin-right: auto; + margin-left: auto; +} + +#aes-about-dialog .modal-body { + text-align: center; +} + +#aes-about-dialog h2 { + color: inherit; +} + +#aes-about-dialog img { + width: 64px; + height: 64px; + border-radius: 5px; +} + +#aes-about-dialog button[data-dismiss] { + position: absolute; + top: 10px; + right: 10px; +} + +/** + * General UI improvements + */ + +/* Align icons and text in menu items */ +.dropdown-menu > li > a:has(.fa) { + display: grid; + grid-template-columns: 1em auto; + gap: .5em; + align-items: center; +} + +.dropdown-menu > li > a > .fa { + justify-self: center; +} + +/* Hide empty aircraft categories */ +.as-fieldset:has(.types):not(:has(li)) { + display: none; +} + +[class^="layout-col-md"]:has(.types):not(:has(li)) { + display: none; +} + +/* Dark Mode fixes */ +head:has(link[href^="/dist/assets/theme-dark"]) + body .bad { + color: #ffb8b6; +} + +head:has(link[href^="/dist/assets/theme-dark"]) + body .good { + color: #7cde7c; +} + +head:has(link[href^="/dist/assets/theme-dark"]) + body .warning { + color: #ffbc5d; +} + +head:has(link[href^="/dist/assets/theme-dark"]) + body .visual-flight-plan .vfp div.day div.blocks div.block.flight.locked { + color: #a26900; +} + +head:has(link[href^="/dist/assets/theme-dark"]) + body .visual-flight-plan .vfp div.day div.blocks div.block.flight.error { + color: #cd0000; } \ No newline at end of file diff --git a/extension/helpers.js b/extension/helpers.js index 5db47d7..50da230 100644 --- a/extension/helpers.js +++ b/extension/helpers.js @@ -1,4 +1,170 @@ /** Shared logic */ class AES { + /** + * Returns the airline name and code from the dashboard + * @returns {object} {name: string, code: string} + */ + static getAirlineCode() { + const factsTable = document.querySelector(".facts table") + const nameElement = factsTable.querySelector("tr:nth-child(1) td:last-child") + const codeElement = factsTable.querySelector("tr:nth-child(2) td:last-child") + + return { + name: nameElement.innerText, + code: codeElement.innerText + } + } + /** + * Returns the server name + * @returns {string} server name + */ + static getServerName() { + const hostname = window.location.hostname + const servername = hostname.split(".")[0] + + return servername + } + + /** + * Formats a currency value local standards + * @param {integer} currency value + * @param {string} alignment: "right" | "left" + * @returns {HTMLElement} span with formatted value + */ + static formatCurrency(value, alignment) { + let container = document.createElement("span") + let formattedValue = Intl.NumberFormat().format(value) + let indicatorEl = document.createElement("span") + let valueEl = document.createElement("span") + let currencyEl = document.createElement("span") + let containerClasses = "aes-no-text-wrap" + + if (alignment === "right") { + containerClasses = "aes-text-right aes-no-text-wrap" + } + + if (value >= 0) { + valueEl.classList.add("good") + indicatorEl.classList.add("good") + indicatorEl.innerText = "+" + } + + if (value < 0) { + valueEl.classList.add("bad") + indicatorEl.classList.add("bad") + indicatorEl.innerText = "-" + formattedValue = formattedValue.replace("-", "") + } + + valueEl.innerText = formattedValue + currencyEl.innerText = " AS$" + + container.className = containerClasses + container.append(indicatorEl, valueEl, currencyEl) + + return container + } + + /** + * Formats a date string to human readable format + * @param {string} "20240524" + * @returns {string} "2024-05-24" | "error: invalid format for AES.formatDateString" + */ + static formatDateString(date) { + if (!date) { + return + } + + const correctLength = date.length === 8 + const isInteger = Number.isInteger(parseInt(date)) + let result = "error: invalid format for AES.formatDateString" + + if (correctLength && isInteger) { + const year = date.substring(0, 4) + const month = date.substring(4, 6) + const day = date.substring(6, 8) + result = `${year}-${month}-${day}` + } + + return result + } + /** + * Returns a formatted date (week) string + * @param {string} "212024" + * @returns {string} "21/2014 | "error: invalid format for AES.formatDateStringWeek" + */ + static formatDateStringWeek(date) { + const correctLength = date.toString().length === 6 + const isInteger = Number.isInteger(parseInt(date)) + let result = "error: invalid format for AES.formatDateStringWeek" + + if (correctLength && isInteger) { + const DateAsString = date.toString() + const week = DateAsString.substring(0, 2) + const year = DateAsString.substring(2, 6) + + result = `${week}/${year}` + } + + return result + } + + /** + * Gets the server’s current date and time + * @returns {object} datetime - { date: "20240607", time: "16:24 UTC" } + */ + static getServerDate() { + const source = document.querySelector(".as-navbar-bottom span:has(.fa-clock-o)").innerText.trim() + const sourceAsNumbers = this.cleanInteger(source).toString() + + // The source always consists of 12 numbers + const expectedLength = 12 + if (sourceAsNumbers.length != expectedLength) { + throw new Error(`Unexpected length for source (${sourceAsNumbers.length}). There might’ve been a UI update. Check AES.getServerDate()`) + } + + // Splits the date component from the data, + // then splits that into an array for the year, month, and day + let dateArray = source.split(" ")[0].split(/\D+/) + if (dateArray[0].length === 2) { + dateArray.reverse() + } + let date = dateArray[0]+dateArray[1]+dateArray[2] + + // Strip the date component from the data + // leaving only the time + let time = source.replace(/.{10}\s/, "") + + const datetime = { + date: date, + time: time + } + + return datetime + } + + /** + * Returns the difference between dates in days + * @param {array} ["20240520", "20240524"] + * @returns {integer} 4 + */ + static getDateDiff(dates) { + let dateA = new Date(`${this.formatDateString(dates[0])}T12:00:00Z`) + let dateB = new Date(`${this.formatDateString(dates[1])}T12:00:00Z`) + let result = Math.round((dateA - dateB)/(1000 * 60 * 60 * 24)) + + return result + } + + /** + * Cleans a string of punctuation to returns an integer + * @param {string} "2,000 AS$" | "2.000 AS$" + * @returns {integer} 2000 + */ + static cleanInteger(value) { + // \D matches any character that's not a digit + let result = value.replaceAll(/\D+/g, "") + return parseInt(result, 10) + } } diff --git a/extension/manifest.json b/extension/manifest.json index ab466f6..4876f95 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "AirlineSim Enhancement Suite", "description": "Great quality of live improvements for AirlineSim", - "version": "0.6.7", + "version": "0.6.8", "action": { "default_icon": { "128": "images/AES-logo-128.png", @@ -23,47 +23,59 @@ "content_scripts": [ { "css": ["css/content.css"], - "js": ["helpers.js"], - "matches": ["https://*.airlinesim.aero/app/*"] + "js": [ + "js/jquery-3.4.1.min.js", + "helpers.js", + "modules/aes-menu.js", + "modules/about-dialog.js" + ], + "matches": ["https://*.airlinesim.aero/app/*", "https://*.airlinesim.aero/action/*"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_inventory.js"], + "js": [ + "content_inventory.js", + "modules/inventory/validation.js" + ], "matches": ["https://*.airlinesim.aero/app/com/inventory/*"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_fligthSchedule.js"], + "js": ["content_fligthSchedule.js"], "matches": ["https://*.airlinesim.aero/app/info/enterprises/*tab=3"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_settings.js"], + "js": ["content_settings.js"], "matches": ["https://*.airlinesim.aero/app/enterprise/settings*"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_dashboard.js"], + "js": ["content_dashboard.js"], "matches": ["https://*.airlinesim.aero/app/enterprise/dashboard*"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_personelManagement.js"], + "js": ["content_personelManagement.js"], "matches": ["https://*.airlinesim.aero/action/enterprise/staffOverview*"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_enterpriceOverview.js"], + "js": ["content_enterpriceOverview.js"], "matches": ["https://*.airlinesim.aero/app/info/enterprises/*"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_flightInfo.js"], + "js": ["content_flightInfo.js"], "matches": ["https://*.airlinesim.aero/action/info/flight*"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_aircraftFlights.js"], + "js": ["content_aircraftFlights.js"], "matches": ["https://*.airlinesim.aero/app/fleets/aircraft/*/1*"] }, { - "js": ["js/jquery-3.4.1.min.js", "content_fleetManagement.js"], + "js": ["content_fleetManagement.js"], "matches": ["https://*.airlinesim.aero/app/fleets*"] } ], "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlgUt+9pr5xsoJIdZtv2s/miPjNaUMf5NdIRH6wQzgWpGzMCwf46cUSL0FbpnIo6E9d6pITalABU8Dx72/isyas/da0EQNCQkYxhvu2YakUyUMyzKg0jPuxR+VyaEzDMVQKyVD1xZb/jjOkHXvGew/EHPw9BzCdhKRptRqo4gqdgifS0/xQFiqneA35Hhqwo9bh5zfhZpdMsssMHH3uGGm1f4Yeq+j2ZUtvf74v8pWR5+sVI/dFhTSpQaT60sWFYkPVwtpQ5jUwqm9vDyAH0obZxVtBqI5UkmjEJsCXmQW6QdC4+gRwTgrKc137SH+mUVRHfcXogMSa72+szX63pMUQIDAQAB", "permissions": ["activeTab", "declarativeContent", "storage", "unlimitedStorage"], - "host_permissions": ["https://*.airlinesim.aero/"] + "host_permissions": ["https://*.airlinesim.aero/"], + "web_accessible_resources": [{ + "resources": ["/images/*"], + "matches": ["https://*.airlinesim.aero/*"] + }] } diff --git a/extension/modules/about-dialog.js b/extension/modules/about-dialog.js new file mode 100644 index 0000000..4924b2b --- /dev/null +++ b/extension/modules/about-dialog.js @@ -0,0 +1,103 @@ +class AboutDialog { + #target + #container + #modalDialog + #modalContent + #closeButton + #body + + constructor() { + this.#closeButton = this.#createCloseButton() + this.#body = this.#createBody() + this.#body.prepend(this.#closeButton) + this.#modalContent = this.#createModalContent() + this.#modalContent.append(this.#body) + this.#modalDialog = this.#createModalDialog() + this.#modalDialog.append(this.#modalContent) + this.#container = this.#createContainer() + this.#container.append(this.#modalDialog) + this.#target = this.#setTarget() + this.#target.append(this.#container) + + const observer = this.#mutationObserver() + observer.observe(this.#container, {attributes: true}) + } + + #createContainer() { + const container = document.createElement("div") + container.class = "modal" + container.id = "aes-about-dialog" + container.setAttribute("role", "dialog") + container.setAttribute("aria-modal", "true") + container.setAttribute("aria-hidden", "true") + container.style = "display: none" + + return container + } + + #createModalDialog() { + const modalDialog = document.createElement("div") + modalDialog.className = "modal-dialog modal-md" + + return modalDialog + } + + #createModalContent() { + const modalContent = document.createElement("div") + modalContent.className = "modal-content" + + return modalContent + } + + #createCloseButton() { + const icon = document.createElement("span") + icon.setAttribute("aria-hidden", "true") + icon.innerText = "×" + const label = document.createElement("span") + label.innerText = "Close" + label.className = "sr-only" + const button = document.createElement("button") + button.setAttribute("type", "button") + button.dataset.dismiss = "modal" + button.className = "btn btn-default" + button.append(icon, label) + + return button + } + + #createBody() { + const body = document.createElement("div") + body.className = "modal-body" + const manifest = chrome.runtime.getManifest() + const description = manifest.description + const version = manifest.version + + body.innerHTML = ` + +

          AirlineSim Enhancement Suite

          +

          Version ${version}

          +

          Copyright © 2020-2024 AES Authors. MIT License.

          + ` + + return body + } + + #setTarget() { + const target = document.querySelector("body") + return target + } + + #mutationObserver() { + const observer = new MutationObserver(this.#correctBootstrapBehaviour.bind(this)) + return observer + } + + #correctBootstrapBehaviour() { + if (this.#container.classList.contains("in") && !this.#container.classList.contains("modal")) { + this.#container.classList.add("modal") + this.#container.style = "display: block" + } + } +} + +new AboutDialog() diff --git a/extension/modules/aes-menu.js b/extension/modules/aes-menu.js new file mode 100644 index 0000000..dfa2e61 --- /dev/null +++ b/extension/modules/aes-menu.js @@ -0,0 +1,162 @@ +class AESMenu { + #target + #container + #button + #menu + + constructor() { + this.#target = document.querySelector("#as-navbar-main-collapse .navbar-nav > li:nth-child(5)") + this.#container = this.#createContainer() + this.#button = this.#createButton() + this.#menu = this.#createMenu() + + this.#container.append(this.#button, this.#menu) + this.#target.after(this.#container) + } + + /** + * Creates the menu container + * @returns {HTMLElement} container + */ + #createContainer() { + const container = document.createElement("li") + container.className = "dropdown" + return container + } + + /** + * Creates the menu toggle button + * @returns {HTMLElement} button + */ + #createButton() { + const caret = document.createElement("span") + caret.className = "caret" + const button = document.createElement("a") + button.setAttribute("role", "button") + button.setAttribute("tabindex", "0") + button.dataset.toggle = "dropdown" + button.className = "dropdown-toggle" + button.innerText = "AES" + button.style = "cursor: pointer" + button.append(caret) + + return button + } + + /** + * Creates the menu + * @returns {HTMLElement} menu + */ + #createMenu() { + const menu = document.createElement("ul") + menu.className = "dropdown-menu" + const menuItems = [] + const content = [{ + label: "Community", + isHeader: true + },{ + label: "Forum Topic", + href: "https://forums.airlinesim.aero/t/introducing-airlinesim-enhancement-suite-beta/", + newWindow: true + },{ + isDivider: true + },{ + label: "Support", + isHeader: true + },{ + label: "Report a Bug", + href: `https://github.com/ZoeBijl/airlinesim-enhancement-suite/issues/new?body=AES:%20v${chrome.runtime.getManifest().version}%0AChrome:%20v${window.navigator.userAgent.match(/Chrom(?:e|ium)\/([0-9]+)/)[1]}%0A%0A`, + newWindow: true, + icon: { className: "fa-bug" } + },{ + label: "Handbook", + href: "https://docs.google.com/document/d/1hzMHb3hTBXSZNtuDKoBuvx1HP9CgB7wVYR59yDYympg/", + newWindow: true, + icon: { className: "fa-book" } + },{ + label: "GitHub", + href: "https://github.com/ZoeBijl/airlinesim-enhancement-suite", + newWindow: true, + icon: { className: "fa-github" } + },{ + isDivider: true + },{ + label: "About AES", + icon: { className: "fa-info" }, + data: { + toggle: "modal", + target: "#aes-about-dialog" + } + }] + for (const item of content) { + let menuItem = this.#createMenuItem(item) + menuItems.push(menuItem) + } + for (const item of menuItems) { + menu.append(item) + } + + return menu + } + + /** + * Creates a menu item + * @param {object} content - object containing information required to build the item + * @returns {HTMLElement} menuItem + */ + #createMenuItem(content) { + const menuItem = document.createElement("li") + let menuItemContent, icon + if (content.label) { + menuItemContent = content.label + } + if (content.isDivider) { + menuItem.className = "divider" + return menuItem + } + if (content.isHeader) { + menuItem.className = "dropdown-header" + } + if (content.icon || content.newWindow) { + icon = document.createElement("span") + icon.setAttribute("aria-hidden", "true") + } + if (content.icon) { + icon.className = `fa ${content.icon.className}` + } + if (content.data?.toggle) { + const button = document.createElement("a") + button.setAttribute("role", "button") + button.setAttribute("tabindex", "0") + button.style = "cursor: pointer" + if (icon) { + button.append(icon) + } + button.append(content.label) + for (const attribute in content.data) { + button.dataset[attribute] = content.data[attribute] + } + menuItemContent = button + } + if (content.href) { + const link = document.createElement("a") + link.setAttribute("href", content.href) + if (content.newWindow && !content.icon) { + icon.className = "fa fa-external-link" + } + if (content.newWindow) { + link.setAttribute("target", "_blank") + link.setAttribute("rel", "noreferrer noopener") + } + if (icon) { + link.append(icon) + } + link.append(content.label) + menuItemContent = link + } + menuItem.append(menuItemContent) + return menuItem + } +} + +new AESMenu() diff --git a/extension/modules/inventory/validation.js b/extension/modules/inventory/validation.js new file mode 100644 index 0000000..426b2d1 --- /dev/null +++ b/extension/modules/inventory/validation.js @@ -0,0 +1,172 @@ +class Validation { + valid = true + errors = [] + + constructor() { + this.checkAllFlightNumbersSelected() + this.checkApplyToSettings() + this.checkServiceClasses() + this.checkFlightStatus() + this.checkLoad() + this.checkGroupByFlight() + } + + /** + * Check if “All Flight Numbers” tab is active + */ + checkAllFlightNumbersSelected() { + let message = "Please select \"All Flight Numbers\" under Current Inventory" + let active = $('.col-md-10 > div > .as-panel:eq(1) > ul:eq(0) li:eq(0)').hasClass("active") + if (!active) { + this.valid = false + this.errors.push(message) + } + } + + /** + * Check “Apply settings to” are set correctly + */ + checkApplyToSettings() { + let checkboxes = $('.col-md-10 > div > .as-panel:eq(1) > div > div > div:eq(0) fieldset:eq(2) > div input') + let valid + let messages = [] + checkboxes.each(function(index) { + switch (index) { + case 0: + if (!this.checked) { + let message = "Please check “Airport Pair” under “Apply settings to” in the “Settings”-panel" + valid = false + messages.push(message) + } + break + case 1: + if (!this.checked) { + let message = "Please check “Flight Numbers” under “Apply settings to” in the “Settings”-panel" + valid = false + messages.push(message) + } + break + case 2: + if (this.checked) { + let message = "Please uncheck “Return Airport Pair” under “Apply settings to” in the “Settings”-panel" + valid = false + messages.push(message) + } + break + case 3: + if (this.checked) { + let message = "Please uncheck “Return Flight Numbers” under “Apply settings to” in the “Settings”-panel" + valid = false + messages.push(message) + } + break + } + }) + + if (valid === false) { + this.valid = valid + this.errors.push(...messages) + } + } + + /** + * Check that all “Service Classes” are selected + */ + checkServiceClasses() { + let valid + let messages = [] + let container = $('.col-md-10 > div > .as-panel:eq(1) > div > div > div:eq(1) .layout-col-md-3') + let labels = $('fieldset:eq(0) label', container) + + labels.each(function() { + if (!$('input', this)[0].checked) { + valid = false + messages.push(`Please check “${$(this).text()}” under “Service Classes” in the “Data”-panel`) + } + }) + + if (valid === false) { + this.valid = valid + this.errors.push(...messages) + } + } + + /** + * Check that the correct “Flight Status” is selected + */ + checkFlightStatus() { + let valid + let messages = [] + let container = $('.col-md-10 > div > .as-panel:eq(1) > div > div > div:eq(1) .layout-col-md-3') + let labels = $('fieldset:eq(1) label', container) + labels.each(function(index) { + if (index === 1 || index === 2) { + if (!$('input', this)[0].checked) { + let message = `Please check “${$(this).text()}” under “Flight Status” in the “Data”-panel` + valid = false + messages.push(message) + } + } + }) + + if (valid === false) { + this.valid = valid + this.errors.push(...messages) + } + } + + /** + * Check “Load” settings + */ + checkLoad() { + let valid + let messages = [] + let container = $('.col-md-10 > div > .as-panel:eq(1) > div > div > div:eq(1) .layout-col-md-3') + + $('fieldset:eq(2) div', container).each(function(index) { + let valueAsInteger = parseInt($('select option:selected', this).text(), 10) + + // Minimum + if (index === 0) { + if (valueAsInteger != 0) { + let message = `Please select 0 for “${$('label', this).text()}” under “Load” in the “Data”-panel` + valid = false + messages.push(message) + } + } + // Max + if (index === 1) { + if (valueAsInteger != 100) { + let message = `Please select 100 for “${$('label', this).text()}” under “Load” in the “Data”-panel` + valid = false + messages.push(message) + } + } + }); + + if (valid === false) { + this.valid = valid + this.errors.push(...messages) + } + } + + /** + * Check “Group by flight” settings + */ + checkGroupByFlight() { + let valid + let messages = [] + let container = $('.col-md-10 > div > .as-panel:eq(1) > div > div > div:eq(1) .layout-col-md-3') + let checkboxes = $('fieldset:eq(3) input', container) + if (checkboxes[0].checked) { + let message = `Please uncheck “${$('fieldset:eq(3) label', container).text()}” under “Settings” in the “Data”-panel` + valid = false + messages.push(message) + } + + if (valid === false) { + this.valid = valid + this.errors.push(...messages) + } + } +}