diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile index c0dce55..8ef7ef2 100644 --- a/docker/api/Dockerfile +++ b/docker/api/Dockerfile @@ -1,4 +1,4 @@ FROM mcr.microsoft.com/dotnet/core/aspnet:latest -COPY flightrecorder.api-1.13.0.0 /opt/flightrecorder.api +COPY flightrecorder.api-1.14.0.0 /opt/flightrecorder.api WORKDIR /opt/flightrecorder.api/bin ENTRYPOINT [ "./FlightRecorder.Api" ] diff --git a/docker/ui/Dockerfile b/docker/ui/Dockerfile index 0037870..75e0b9c 100644 --- a/docker/ui/Dockerfile +++ b/docker/ui/Dockerfile @@ -1,4 +1,4 @@ FROM mcr.microsoft.com/dotnet/aspnet:latest AS runtime -COPY flightrecorder.mvc-1.13.0.0 /opt/flightrecorder.mvc +COPY flightrecorder.mvc-1.14.0.0 /opt/flightrecorder.mvc WORKDIR /opt/flightrecorder.mvc/bin ENTRYPOINT [ "./FlightRecorder.Mvc" ] diff --git a/src/FlightRecorder.Api/Controllers/SightingsController.cs b/src/FlightRecorder.Api/Controllers/SightingsController.cs index 7055566..27fbff4 100644 --- a/src/FlightRecorder.Api/Controllers/SightingsController.cs +++ b/src/FlightRecorder.Api/Controllers/SightingsController.cs @@ -177,6 +177,7 @@ public async Task> UpdateSightingAsync([FromBody] Sightin sighting.Altitude = template.Altitude; sighting.FlightId = template.FlightId; sighting.LocationId = template.LocationId; + sighting.IsMyFlight = template.IsMyFlight; await _factory.Context.SaveChangesAsync(); await _factory.Context.Entry(sighting).Reference(s => s.Aircraft).LoadAsync(); await _factory.Context.Entry(sighting.Aircraft).Reference(m => m.Model).LoadAsync(); @@ -197,7 +198,8 @@ public async Task> CreateSightingAsync([FromBody] Sightin template.Date, template.LocationId, template.FlightId, - template.AircraftId); + template.AircraftId, + template.IsMyFlight); return location; } } diff --git a/src/FlightRecorder.Api/FlightRecorder.Api.csproj b/src/FlightRecorder.Api/FlightRecorder.Api.csproj index 169bf82..995e88d 100644 --- a/src/FlightRecorder.Api/FlightRecorder.Api.csproj +++ b/src/FlightRecorder.Api/FlightRecorder.Api.csproj @@ -2,9 +2,9 @@ net9.0 - 1.13.0.0 - 1.13.0.0 - 1.13.0 + 1.14.0.0 + 1.14.0.0 + 1.14.0 enable false diff --git a/src/FlightRecorder.Api/appsettings.json b/src/FlightRecorder.Api/appsettings.json index 17044bf..e0d1a40 100644 --- a/src/FlightRecorder.Api/appsettings.json +++ b/src/FlightRecorder.Api/appsettings.json @@ -2,10 +2,10 @@ "ApplicationSettings": { "Secret": "e2b6e7fe16ef469d9862d43eb76d00e2802ab769b85848048cc9387743ca2cc38c0f4fd8a0de46798f347bedf676bc31", "TokenLifespanMinutes": 1440, - "SightingsExportPath": "C:\\MyApps\\FlightRecorder\\Export", - "AirportsExportPath": "C:\\MyApps\\FlightRecorder\\Export\\Airports", - "ReportsExportPath": "C:\\MyApps\\FlightRecorder\\Export\\Reports", - "LogFile": "C:\\MyApps\\FlightRecorder\\FlightRecorder.Manager.log", + "SightingsExportPath": "/Users/dave/MyApps/FlightRecorder/Export", + "AirportsExportPath": "/Users/dave/MyApps/FlightRecorder/Export/Airports", + "ReportsExportPath": "/Users/dave/MyApps/FlightRecorder/Export/Reports", + "LogFile": "/Users/dave/MyApps/FlightRecorder/Export/FlightRecorder.Manager.log", "MinimumLogLevel": "Info", "ApiEndpoints": [ { @@ -27,7 +27,7 @@ "ApiServiceKeys": [ { "Service": "AeroDataBox", - "Key": "C:\\MyApps\\FlightRecorder\\rapidapi.key" + "Key": "/Users/dave/MyApps/FlightRecorder/rapidapi.key" } ] }, diff --git a/src/FlightRecorder.BusinessLogic/Database/SightingManager.cs b/src/FlightRecorder.BusinessLogic/Database/SightingManager.cs index 91e8814..b4483ff 100644 --- a/src/FlightRecorder.BusinessLogic/Database/SightingManager.cs +++ b/src/FlightRecorder.BusinessLogic/Database/SightingManager.cs @@ -113,8 +113,9 @@ public async Task GetMostRecent(Expression> predi /// /// /// + /// /// - public async Task AddAsync(long altitude, DateTime date, long locationId, long flightId, long aircraftId) + public async Task AddAsync(long altitude, DateTime date, long locationId, long flightId, long aircraftId, bool isMyFlight) { Sighting sighting = new Sighting { @@ -122,7 +123,8 @@ public async Task AddAsync(long altitude, DateTime date, long location Date = date, LocationId = locationId, FlightId = flightId, - AircraftId = aircraftId + AircraftId = aircraftId, + IsMyFlight = isMyFlight }; await _factory.Context.Sightings.AddAsync(sighting); @@ -150,7 +152,7 @@ public async Task AddAsync(FlattenedSighting flattened) long aircraftId = (await _factory.Aircraft.AddAsync(flattened.Registration, flattened.SerialNumber, yearOfManufacture, flattened.Model, flattened.Manufacturer)).Id; long flightId = (await _factory.Flights.AddAsync(flattened.FlightNumber, flattened.Embarkation, flattened.Destination, flattened.Airline)).Id; long locationId = (await _factory.Locations.AddAsync(flattened.Location)).Id; - return await AddAsync(flattened.Altitude, flattened.Date, locationId, flightId, aircraftId); + return await AddAsync(flattened.Altitude, flattened.Date, locationId, flightId, aircraftId, flattened.IsMyFlight); } /// diff --git a/src/FlightRecorder.BusinessLogic/FlightRecorder.BusinessLogic.csproj b/src/FlightRecorder.BusinessLogic/FlightRecorder.BusinessLogic.csproj index 01ad9e5..e74f600 100644 --- a/src/FlightRecorder.BusinessLogic/FlightRecorder.BusinessLogic.csproj +++ b/src/FlightRecorder.BusinessLogic/FlightRecorder.BusinessLogic.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.BusinessLogic - 1.9.0.0 + 1.10.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024 Dave Walker @@ -16,7 +16,7 @@ https://github.com/davewalker5/FlightRecorderDb MIT false - 1.9.0.0 + 1.10.0.0 diff --git a/src/FlightRecorder.Data/FlightRecorder.Data.csproj b/src/FlightRecorder.Data/FlightRecorder.Data.csproj index d24a896..0c60c22 100644 --- a/src/FlightRecorder.Data/FlightRecorder.Data.csproj +++ b/src/FlightRecorder.Data/FlightRecorder.Data.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.Data - 1.9.0.0 + 1.10.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024 Dave Walker @@ -16,7 +16,7 @@ https://github.com/davewalker5/FlightRecorderDb MIT false - 1.9.0.0 + 1.10.0.0 diff --git a/src/FlightRecorder.Data/FlightRecorderDbContext.cs b/src/FlightRecorder.Data/FlightRecorderDbContext.cs index 5a934e9..72b1b1e 100644 --- a/src/FlightRecorder.Data/FlightRecorderDbContext.cs +++ b/src/FlightRecorder.Data/FlightRecorderDbContext.cs @@ -205,6 +205,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.FlightId).HasColumnName("flight_id"); entity.Property(e => e.LocationId).HasColumnName("location_id"); + + entity.Property(e => e.IsMyFlight).HasColumnName("is_my_flight"); }); modelBuilder.Entity(entity => diff --git a/src/FlightRecorder.Data/Migrations/20240710034411_DefaultLocation.cs b/src/FlightRecorder.Data/Migrations/20240710034411_DefaultLocation.cs index 9604636..5e21bc3 100644 --- a/src/FlightRecorder.Data/Migrations/20240710034411_DefaultLocation.cs +++ b/src/FlightRecorder.Data/Migrations/20240710034411_DefaultLocation.cs @@ -5,6 +5,7 @@ namespace FlightRecorder.Data.Migrations { + [ExcludeFromCodeCoverage] /// public partial class DefaultLocation : Migration { diff --git a/src/FlightRecorder.Data/Migrations/20241130185603_MyFlight.Designer.cs b/src/FlightRecorder.Data/Migrations/20241130185603_MyFlight.Designer.cs new file mode 100644 index 0000000..09de2ea --- /dev/null +++ b/src/FlightRecorder.Data/Migrations/20241130185603_MyFlight.Designer.cs @@ -0,0 +1,553 @@ +// +using System; +using FlightRecorder.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FlightRecorder.Data.Migrations +{ + [DbContext(typeof(FlightRecorderDbContext))] + [Migration("20241130185603_MyFlight")] + partial class MyFlight + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Aircraft", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Manufactured") + .HasColumnType("INTEGER") + .HasColumnName("manufactured"); + + b.Property("ModelId") + .HasColumnType("INTEGER") + .HasColumnName("model_id"); + + b.Property("Registration") + .IsRequired() + .HasColumnType("VARCHAR(50)") + .HasColumnName("registration"); + + b.Property("SerialNumber") + .HasColumnType("VARCHAR(50)") + .HasColumnName("serial_number"); + + b.HasKey("Id"); + + b.HasIndex("ModelId"); + + b.ToTable("AIRCRAFT", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Airline", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("VARCHAR(100)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("AIRLINE", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Airport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Code") + .IsRequired() + .HasColumnType("VARCHAR(5)") + .HasColumnName("code"); + + b.Property("CountryId") + .HasColumnType("INTEGER") + .HasColumnName("country_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("VARCHAR(100)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.ToTable("AIRPORT", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("VARCHAR(50)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("COUNTRY", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Flight", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("AirlineId") + .HasColumnType("INTEGER") + .HasColumnName("airline_id"); + + b.Property("Destination") + .IsRequired() + .HasColumnType("VARCHAR(3)") + .HasColumnName("destination"); + + b.Property("Embarkation") + .IsRequired() + .HasColumnType("VARCHAR(3)") + .HasColumnName("embarkation"); + + b.Property("Number") + .IsRequired() + .HasColumnType("VARCHAR(50)") + .HasColumnName("number"); + + b.HasKey("Id"); + + b.HasIndex("AirlineId"); + + b.ToTable("FLIGHT", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.JobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("End") + .HasColumnType("DATETIME") + .HasColumnName("end"); + + b.Property("Error") + .HasColumnType("TEXT") + .HasColumnName("error"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.Property("Parameters") + .HasColumnType("TEXT") + .HasColumnName("parameters"); + + b.Property("Start") + .HasColumnType("DATETIME") + .HasColumnName("start"); + + b.HasKey("Id"); + + b.ToTable("JOB_STATUS", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("VARCHAR(100)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("LOCATION", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Manufacturer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("VARCHAR(100)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("MANUFACTURER", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Model", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("ManufacturerId") + .HasColumnType("INTEGER") + .HasColumnName("manufacturer_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("VARCHAR(100)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("ManufacturerId"); + + b.ToTable("MODEL", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Sighting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("AircraftId") + .HasColumnType("INTEGER") + .HasColumnName("aircraft_id"); + + b.Property("Altitude") + .HasColumnType("INTEGER") + .HasColumnName("altitude"); + + b.Property("Date") + .HasColumnType("DATETIME") + .HasColumnName("date"); + + b.Property("FlightId") + .HasColumnType("INTEGER") + .HasColumnName("flight_id"); + + b.Property("IsMyFlight") + .HasColumnType("INTEGER") + .HasColumnName("is_my_flight"); + + b.Property("LocationId") + .HasColumnType("INTEGER") + .HasColumnName("location_id"); + + b.HasKey("Id"); + + b.HasIndex("AircraftId"); + + b.HasIndex("FlightId"); + + b.HasIndex("LocationId"); + + b.ToTable("SIGHTING", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Password") + .IsRequired() + .HasColumnType("VARCHAR(1000)") + .HasColumnName("Password"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("VARCHAR(50)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.ToTable("USER", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.UserAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("VARCHAR(100)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("USER_ATTRIBUTE", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.UserAttributeValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("UserAttributeId") + .HasColumnType("INTEGER") + .HasColumnName("user_attribute_id"); + + b.Property("UserId") + .HasColumnType("INTEGER") + .HasColumnName("user_id"); + + b.Property("Value") + .HasColumnType("VARCHAR(1000)") + .HasColumnName("value"); + + b.HasKey("Id"); + + b.HasIndex("UserAttributeId"); + + b.HasIndex("UserId"); + + b.ToTable("USER_ATTRIBUTE_VALUE", (string)null); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Reporting.AirlineStatistics", b => + { + b.Property("Aircraft") + .HasColumnType("INTEGER"); + + b.Property("Flights") + .HasColumnType("INTEGER"); + + b.Property("Locations") + .HasColumnType("INTEGER"); + + b.Property("Manufacturers") + .HasColumnType("INTEGER"); + + b.Property("Models") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Sightings") + .HasColumnType("INTEGER"); + + b.ToTable("AirlineStatistics"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Reporting.FlightsByMonth", b => + { + b.Property("Flights") + .HasColumnType("INTEGER"); + + b.Property("Month") + .HasColumnType("INTEGER"); + + b.Property("Sightings") + .HasColumnType("INTEGER"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.ToTable("FlightsByMonth"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Reporting.LocationStatistics", b => + { + b.Property("Aircraft") + .HasColumnType("INTEGER"); + + b.Property("Flights") + .HasColumnType("INTEGER"); + + b.Property("Manufacturers") + .HasColumnType("INTEGER"); + + b.Property("Models") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Sightings") + .HasColumnType("INTEGER"); + + b.ToTable("LocationStatistics"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Reporting.ManufacturerStatistics", b => + { + b.Property("Aircraft") + .HasColumnType("INTEGER"); + + b.Property("Flights") + .HasColumnType("INTEGER"); + + b.Property("Locations") + .HasColumnType("INTEGER"); + + b.Property("Models") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Sightings") + .HasColumnType("INTEGER"); + + b.ToTable("ManufacturerStatistics"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Reporting.ModelStatistics", b => + { + b.Property("Aircraft") + .HasColumnType("INTEGER"); + + b.Property("Flights") + .HasColumnType("INTEGER"); + + b.Property("Locations") + .HasColumnType("INTEGER"); + + b.Property("Manufacturer") + .HasColumnType("TEXT"); + + b.Property("Model") + .HasColumnType("TEXT"); + + b.Property("Sightings") + .HasColumnType("INTEGER"); + + b.ToTable("ModelStatistics"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Aircraft", b => + { + b.HasOne("FlightRecorder.Entities.Db.Model", "Model") + .WithMany() + .HasForeignKey("ModelId"); + + b.Navigation("Model"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Airport", b => + { + b.HasOne("FlightRecorder.Entities.Db.Country", "Country") + .WithMany() + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Flight", b => + { + b.HasOne("FlightRecorder.Entities.Db.Airline", "Airline") + .WithMany() + .HasForeignKey("AirlineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Airline"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Model", b => + { + b.HasOne("FlightRecorder.Entities.Db.Manufacturer", "Manufacturer") + .WithMany() + .HasForeignKey("ManufacturerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Manufacturer"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.Sighting", b => + { + b.HasOne("FlightRecorder.Entities.Db.Aircraft", "Aircraft") + .WithMany() + .HasForeignKey("AircraftId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FlightRecorder.Entities.Db.Flight", "Flight") + .WithMany() + .HasForeignKey("FlightId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FlightRecorder.Entities.Db.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Aircraft"); + + b.Navigation("Flight"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.UserAttributeValue", b => + { + b.HasOne("FlightRecorder.Entities.Db.UserAttribute", "UserAttribute") + .WithMany() + .HasForeignKey("UserAttributeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("FlightRecorder.Entities.Db.User", null) + .WithMany("Attributes") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("UserAttribute"); + }); + + modelBuilder.Entity("FlightRecorder.Entities.Db.User", b => + { + b.Navigation("Attributes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/FlightRecorder.Data/Migrations/20241130185603_MyFlight.cs b/src/FlightRecorder.Data/Migrations/20241130185603_MyFlight.cs new file mode 100644 index 0000000..9bfca16 --- /dev/null +++ b/src/FlightRecorder.Data/Migrations/20241130185603_MyFlight.cs @@ -0,0 +1,31 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FlightRecorder.Data.Migrations +{ + [ExcludeFromCodeCoverage] + /// + public partial class MyFlight : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "is_my_flight", + table: "SIGHTING", + type: "INTEGER", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "is_my_flight", + table: "SIGHTING"); + } + } +} diff --git a/src/FlightRecorder.Data/Migrations/FlightRecorderDbContextModelSnapshot.cs b/src/FlightRecorder.Data/Migrations/FlightRecorderDbContextModelSnapshot.cs index db057a5..29f24f0 100644 --- a/src/FlightRecorder.Data/Migrations/FlightRecorderDbContextModelSnapshot.cs +++ b/src/FlightRecorder.Data/Migrations/FlightRecorderDbContextModelSnapshot.cs @@ -1,6 +1,5 @@ // using System; -using System.Diagnostics.CodeAnalysis; using FlightRecorder.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -10,14 +9,13 @@ namespace FlightRecorder.Data.Migrations { - [ExcludeFromCodeCoverage] [DbContext(typeof(FlightRecorderDbContext))] partial class FlightRecorderDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.1"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); modelBuilder.Entity("FlightRecorder.Entities.Db.Aircraft", b => { @@ -258,6 +256,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER") .HasColumnName("flight_id"); + b.Property("IsMyFlight") + .HasColumnType("INTEGER") + .HasColumnName("is_my_flight"); + b.Property("LocationId") .HasColumnType("INTEGER") .HasColumnName("location_id"); diff --git a/src/FlightRecorder.DataExchange/FlightRecorder.DataExchange.csproj b/src/FlightRecorder.DataExchange/FlightRecorder.DataExchange.csproj index cd60c30..1c967eb 100644 --- a/src/FlightRecorder.DataExchange/FlightRecorder.DataExchange.csproj +++ b/src/FlightRecorder.DataExchange/FlightRecorder.DataExchange.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.DataExchange - 1.9.0.0 + 1.10.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024 Dave Walker @@ -16,7 +16,7 @@ https://github.com/davewalker5/FlightRecorderDb MIT false - 1.9.0.0 + 1.10.0.0 diff --git a/src/FlightRecorder.Entities/DataExchange/FlattenedSighting.cs b/src/FlightRecorder.Entities/DataExchange/FlattenedSighting.cs index 17bf9e8..f032705 100644 --- a/src/FlightRecorder.Entities/DataExchange/FlattenedSighting.cs +++ b/src/FlightRecorder.Entities/DataExchange/FlattenedSighting.cs @@ -6,7 +6,8 @@ namespace FlightRecorder.Entities.DataExchange { public class FlattenedSighting { - public const string CsvRecordPattern = @"^(""[a-zA-Z0-9-() \/']+"",){6}""[0-9]+"",(""[a-zA-Z0-9-() \/']+"",){3}""[0-9]+\/[0-9]+\/[0-9]+"",""[a-zA-Z0-9-() \/']+""$"; + // TODO : Add IsMyFlight to the Regex pattern + public const string CsvRecordPattern = @"^(""[a-zA-Z0-9-() \/']+"",){6}""[0-9]+"",(""[a-zA-Z0-9-() \/']+"",){3}""[0-9]+\/[0-9]+\/[0-9]+"",""[a-zA-Z0-9-() \/']+"",""True|False""$"; private const string DateTimeFormat = "dd/MM/yyyy"; [Export("Flight", 1)] @@ -45,9 +46,12 @@ public class FlattenedSighting [Export("Location", 12)] public string Location { get; set; } + [Export("My Flight", 13)] + public bool IsMyFlight { get; set; } + public static FlattenedSighting FromCsv(string record) { - string[] words = record.Split(new string[] { "\",\"" }, StringSplitOptions.None); + string[] words = record.Split(["\",\""], StringSplitOptions.None); return new FlattenedSighting { FlightNumber = words[0].Substring(1), @@ -61,7 +65,8 @@ public static FlattenedSighting FromCsv(string record) Destination = words[8], Altitude = long.Parse(words[9]), Date = DateTime.ParseExact(words[10], DateTimeFormat, CultureInfo.CurrentCulture), - Location = words[11].Substring(0, words[11].Length - 1) + Location = words[11][..^1], + IsMyFlight = words[12].Equals("True", StringComparison.OrdinalIgnoreCase) }; } } diff --git a/src/FlightRecorder.Entities/Db/Sighting.cs b/src/FlightRecorder.Entities/Db/Sighting.cs index 5bb7655..1a872dd 100644 --- a/src/FlightRecorder.Entities/Db/Sighting.cs +++ b/src/FlightRecorder.Entities/Db/Sighting.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using FlightRecorder.Entities.DataExchange; @@ -15,6 +15,7 @@ public partial class Sighting public long AircraftId { get; set; } public long Altitude { get; set; } public DateTime Date { get; set; } + public bool IsMyFlight { get; set; } public virtual Aircraft Aircraft { get; set; } public virtual Flight Flight { get; set; } @@ -35,7 +36,8 @@ public FlattenedSighting Flatten() Destination = Flight.Destination, Altitude = Altitude, Date = Date, - Location = Location.Name + Location = Location.Name, + IsMyFlight = IsMyFlight }; } } diff --git a/src/FlightRecorder.Entities/FlightRecorder.Entities.csproj b/src/FlightRecorder.Entities/FlightRecorder.Entities.csproj index 5b49a55..b23335f 100644 --- a/src/FlightRecorder.Entities/FlightRecorder.Entities.csproj +++ b/src/FlightRecorder.Entities/FlightRecorder.Entities.csproj @@ -3,7 +3,7 @@ net9.0 FlightRecorder.Entities - 1.9.0.0 + 1.10.0.0 Dave Walker Copyright (c) Dave Walker 2020, 2021, 2022, 2023, 2024 Dave Walker @@ -16,7 +16,7 @@ https://github.com/davewalker5/FlightRecorderDb MIT false - 1.9.0.0 + 1.10.0.0 diff --git a/src/FlightRecorder.Entities/Interfaces/ISightingManager.cs b/src/FlightRecorder.Entities/Interfaces/ISightingManager.cs index 31c8f69..83b2987 100644 --- a/src/FlightRecorder.Entities/Interfaces/ISightingManager.cs +++ b/src/FlightRecorder.Entities/Interfaces/ISightingManager.cs @@ -9,7 +9,7 @@ namespace FlightRecorder.Entities.Interfaces { public interface ISightingManager { - Task AddAsync(long altitude, DateTime date, long locationId, long flightId, long aircraftId); + Task AddAsync(long altitude, DateTime date, long locationId, long flightId, long aircraftId, bool isMyFlight); Task AddAsync(FlattenedSighting flattened); Task GetAsync(Expression> predicate); Task GetMostRecent(Expression> predicate); diff --git a/src/FlightRecorder.Manager/FlightRecorder.Manager.csproj b/src/FlightRecorder.Manager/FlightRecorder.Manager.csproj index 1d18226..801951c 100644 --- a/src/FlightRecorder.Manager/FlightRecorder.Manager.csproj +++ b/src/FlightRecorder.Manager/FlightRecorder.Manager.csproj @@ -3,9 +3,9 @@ Exe net9.0 - 1.9.0.0 - 1.9.0.0 - 1.9.0.0 + 1.10.0.0 + 1.10.0.0 + 1.10.0.0 Release;Debug diff --git a/src/FlightRecorder.Manager/appsettings.json b/src/FlightRecorder.Manager/appsettings.json index 544c4d4..328a263 100644 --- a/src/FlightRecorder.Manager/appsettings.json +++ b/src/FlightRecorder.Manager/appsettings.json @@ -1,6 +1,6 @@ { "ApplicationSettings": { - "LogFile": "C:\\MyApps\\FlightRecorder\\FlightRecorder.Manager.log", + "LogFile": "/Users/dave/MyApps/FlightRecorder/FlightRecorder.Manager.log", "MinimumLogLevel": "Info", "ApiEndpoints": [ { @@ -22,11 +22,11 @@ "ApiServiceKeys": [ { "Service": "AeroDataBox", - "Key": "C:\\MyApps\\FlightRecorder\\rapidapi.key" + "Key": "/Users/dave/MyApps/FlightRecorder/rapidapi.key" } ] }, "ConnectionStrings": { - "FlightRecorderDB": "Data Source=C:\\MyApps\\FlightRecorder\\flightrecorder_dev.db" + "FlightRecorderDB": "Data Source=/Users/dave/MyApps/FlightRecorder/flightrecorder_dev.db" } } diff --git a/src/FlightRecorder.Mvc/Api/SightingClient.cs b/src/FlightRecorder.Mvc/Api/SightingClient.cs index 2b230a9..141fece 100644 --- a/src/FlightRecorder.Mvc/Api/SightingClient.cs +++ b/src/FlightRecorder.Mvc/Api/SightingClient.cs @@ -41,8 +41,9 @@ public async Task GetSightingAsync(int id) /// /// /// + /// /// - public async Task AddSightingAsync(DateTime date, int altitude, int aircraftId, int flightId, int locationId) + public async Task AddSightingAsync(DateTime date, int altitude, int aircraftId, int flightId, int locationId, bool isMyFlight) { dynamic template = new { @@ -50,7 +51,8 @@ public async Task AddSightingAsync(DateTime date, int altitude, int ai AircraftId = aircraftId, Altitude = altitude, FlightId = flightId, - LocationId = locationId + LocationId = locationId, + IsMyFlight = isMyFlight }; string data = JsonConvert.SerializeObject(template); @@ -68,8 +70,9 @@ public async Task AddSightingAsync(DateTime date, int altitude, int ai /// /// /// + /// /// - public async Task UpdateSightingAsync(int id, DateTime date, int altitude, int aircraftId, int flightId, int locationId) + public async Task UpdateSightingAsync(int id, DateTime date, int altitude, int aircraftId, int flightId, int locationId, bool isMyFlight) { dynamic template = new { @@ -78,7 +81,8 @@ public async Task UpdateSightingAsync(int id, DateTime date, int altit AircraftId = aircraftId, Altitude = altitude, FlightId = flightId, - LocationId = locationId + LocationId = locationId, + IsMyFlight = isMyFlight }; string data = JsonConvert.SerializeObject(template); diff --git a/src/FlightRecorder.Mvc/Configuration/MappingProfile.cs b/src/FlightRecorder.Mvc/Configuration/MappingProfile.cs index 85a18b8..29a7daa 100644 --- a/src/FlightRecorder.Mvc/Configuration/MappingProfile.cs +++ b/src/FlightRecorder.Mvc/Configuration/MappingProfile.cs @@ -44,11 +44,8 @@ public MappingProfile() CreateMap() .ForMember(m => m.Altitude, x => x.MapFrom(m => m.Altitude)) - .ForMember(m => m.Date, x => x.MapFrom(m => m.Date)); - - CreateMap() - .ForMember(m => m.Altitude, x => x.MapFrom(m => m.Altitude)) - .ForMember(m => m.Date, x => x.MapFrom(m => m.Date)); + .ForMember(m => m.Date, x => x.MapFrom(m => m.Date)) + .ForMember(m => m.IsMyFlight, x => x.MapFrom(m => m.IsMyFlight)); CreateMap() .ForMember(m => m.FlightNumber, x => x.MapFrom(m => m.FlightNumber)) diff --git a/src/FlightRecorder.Mvc/FlightRecorder.Mvc.csproj b/src/FlightRecorder.Mvc/FlightRecorder.Mvc.csproj index 27e012e..a302ad3 100644 --- a/src/FlightRecorder.Mvc/FlightRecorder.Mvc.csproj +++ b/src/FlightRecorder.Mvc/FlightRecorder.Mvc.csproj @@ -2,9 +2,9 @@ net9.0 - 1.13.0.0 - 1.13.0.0 - 1.13.0 + 1.14.0.0 + 1.14.0.0 + 1.14.0 enable false diff --git a/src/FlightRecorder.Mvc/Models/ConfirmDetailsViewModel.cs b/src/FlightRecorder.Mvc/Models/ConfirmDetailsViewModel.cs index 2d23e86..57398d6 100644 --- a/src/FlightRecorder.Mvc/Models/ConfirmDetailsViewModel.cs +++ b/src/FlightRecorder.Mvc/Models/ConfirmDetailsViewModel.cs @@ -14,6 +14,12 @@ public class ConfirmDetailsViewModel [DisplayName("Location")] public string Location { get; set; } + [DisplayName("My Flight")] + public bool IsMyFlight { get; set; } + + [DisplayName("My Flight")] + public string MyFlightText { get { return IsMyFlight ? "Yes" : "No"; }} + [DisplayName("Flight Number")] public string FlightNumber { get; set; } diff --git a/src/FlightRecorder.Mvc/Models/SightingDetailsViewModel.cs b/src/FlightRecorder.Mvc/Models/SightingDetailsViewModel.cs index 496068f..279d551 100644 --- a/src/FlightRecorder.Mvc/Models/SightingDetailsViewModel.cs +++ b/src/FlightRecorder.Mvc/Models/SightingDetailsViewModel.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Linq; using FlightRecorder.Mvc.Entities; using Microsoft.AspNetCore.Mvc.Rendering; @@ -34,6 +31,9 @@ public class SightingDetailsViewModel [Required(ErrorMessage = "You must provide an aircraft registration")] public string Registration { get; set; } + [DisplayName("My Flight")] + public bool IsMyFlight { get; set; } + public string LastSightingAddedMessage { get; set; } public string LocationErrorMessage { get; set; } public string Action { get; set; } diff --git a/src/FlightRecorder.Mvc/Views/ConfirmDetails/Index.cshtml b/src/FlightRecorder.Mvc/Views/ConfirmDetails/Index.cshtml index 76c8e9e..29ee0eb 100644 --- a/src/FlightRecorder.Mvc/Views/ConfirmDetails/Index.cshtml +++ b/src/FlightRecorder.Mvc/Views/ConfirmDetails/Index.cshtml @@ -58,6 +58,18 @@ +
+ +
+
+ @Html.LabelFor(m => m.IsMyFlight) +
+
+
+ @Html.TextBoxFor(m => m.MyFlightText, new { @class = "form-control", @disabled = "" }) +
+
+

diff --git a/src/FlightRecorder.Mvc/Views/SightingDetails/Index.cshtml b/src/FlightRecorder.Mvc/Views/SightingDetails/Index.cshtml index d6fd304..00c16fb 100644 --- a/src/FlightRecorder.Mvc/Views/SightingDetails/Index.cshtml +++ b/src/FlightRecorder.Mvc/Views/SightingDetails/Index.cshtml @@ -116,6 +116,19 @@ @Html.ValidationMessageFor(m => m.Registration, "", new { @class = "text-danger" })
+
+ +
+
+ @Html.LabelFor(m => m.IsMyFlight) +
+
+
+ @Html.CheckBoxFor(m => m.IsMyFlight, new { @class = "form-control" }) +
+ @Html.ValidationMessageFor(m => m.IsMyFlight, "", new { @class = "text-danger" }) +
+

diff --git a/src/FlightRecorder.Mvc/Wizard/AddSightingWizard.cs b/src/FlightRecorder.Mvc/Wizard/AddSightingWizard.cs index a7a8816..2a9d671 100644 --- a/src/FlightRecorder.Mvc/Wizard/AddSightingWizard.cs +++ b/src/FlightRecorder.Mvc/Wizard/AddSightingWizard.cs @@ -457,12 +457,24 @@ public async Task CreateSighting(string userName) string message; if (details.SightingId != null) { - sighting = await _sightings.UpdateSightingAsync(details.SightingId ?? 0, details.Date ?? DateTime.Now, details.Altitude ?? 0, aircraft.Id, flight.Id, details.LocationId); + sighting = await _sightings.UpdateSightingAsync( + details.SightingId ?? 0, + details.Date ?? DateTime.Now, + details.Altitude ?? 0, + aircraft.Id, flight.Id, + details.LocationId, + details.IsMyFlight); message = BuildSightingMessage(sighting, true); } else { - sighting = await _sightings.AddSightingAsync(details.Date ?? DateTime.Now, details.Altitude ?? 0, aircraft.Id, flight.Id, details.LocationId); + sighting = await _sightings.AddSightingAsync( + details.Date ?? DateTime.Now, + details.Altitude ?? 0, + aircraft.Id, + flight.Id, + details.LocationId, + details.IsMyFlight); message = BuildSightingMessage(sighting, false); } diff --git a/src/FlightRecorder.Mvc/wwwroot/lib/datatables/datatables.js b/src/FlightRecorder.Mvc/wwwroot/lib/datatables/datatables.js index b00a8f7..a8e1511 100644 --- a/src/FlightRecorder.Mvc/wwwroot/lib/datatables/datatables.js +++ b/src/FlightRecorder.Mvc/wwwroot/lib/datatables/datatables.js @@ -36,15315 +36,14911 @@ /*jslint evil: true, undef: true, browser: true */ /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/ -(function( factory ) { - "use strict"; - - if ( typeof define === 'function' && define.amd ) { - // AMD - define( ['jquery'], function ( $ ) { - return factory( $, window, document ); - } ); - } - else if ( typeof exports === 'object' ) { - // CommonJS - module.exports = function (root, $) { - if ( ! root ) { - // CommonJS environments without a window global must pass a - // root. This will give an error otherwise - root = window; - } - - if ( ! $ ) { - $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window - require('jquery') : - require('jquery')( root ); - } - - return factory( $, root, root.document ); - }; - } - else { - // Browser - factory( jQuery, window, document ); - } -} -(function( $, window, document, undefined ) { - "use strict"; - - /** - * DataTables is a plug-in for the jQuery Javascript library. It is a highly - * flexible tool, based upon the foundations of progressive enhancement, - * which will add advanced interaction controls to any HTML table. For a - * full list of features please refer to - * [DataTables.net](href="http://datatables.net). - * - * Note that the `DataTable` object is not a global variable but is aliased - * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may - * be accessed. - * - * @class - * @param {object} [init={}] Configuration object for DataTables. Options - * are defined by {@link DataTable.defaults} - * @requires jQuery 1.7+ - * - * @example - * // Basic initialisation - * $(document).ready( function { - * $('#example').dataTable(); - * } ); - * - * @example - * // Initialisation with configuration options - in this case, disable - * // pagination and sorting. - * $(document).ready( function { - * $('#example').dataTable( { - * "paginate": false, - * "sort": false - * } ); - * } ); - */ - var DataTable = function ( options ) - { - /** - * Perform a jQuery selector action on the table's TR elements (from the tbody) and - * return the resulting jQuery object. - * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on - * @param {object} [oOpts] Optional parameters for modifying the rows to be included - * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter - * criterion ("applied") or all TR elements (i.e. no filter). - * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. - * Can be either 'current', whereby the current sorting of the table is used, or - * 'original' whereby the original order the data was read into the table is used. - * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page - * ("current") or not ("all"). If 'current' is given, then order is assumed to be - * 'current' and filter is 'applied', regardless of what they might be given as. - * @returns {object} jQuery object, filtered by the given selector. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Highlight every second row - * oTable.$('tr:odd').css('backgroundColor', 'blue'); - * } ); - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Filter to rows with 'Webkit' in them, add a background colour and then - * // remove the filter, thus highlighting the 'Webkit' rows only. - * oTable.fnFilter('Webkit'); - * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue'); - * oTable.fnFilter(''); - * } ); - */ - this.$ = function ( sSelector, oOpts ) - { - return this.api(true).$( sSelector, oOpts ); - }; - - - /** - * Almost identical to $ in operation, but in this case returns the data for the matched - * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes - * rather than any descendants, so the data can be obtained for the row/cell. If matching - * rows are found, the data returned is the original data array/object that was used to - * create the row (or a generated array if from a DOM source). - * - * This method is often useful in-combination with $ where both functions are given the - * same parameters and the array indexes will match identically. - * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on - * @param {object} [oOpts] Optional parameters for modifying the rows to be included - * @param {string} [oOpts.filter=none] Select elements that meet the current filter - * criterion ("applied") or all elements (i.e. no filter). - * @param {string} [oOpts.order=current] Order of the data in the processed array. - * Can be either 'current', whereby the current sorting of the table is used, or - * 'original' whereby the original order the data was read into the table is used. - * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page - * ("current") or not ("all"). If 'current' is given, then order is assumed to be - * 'current' and filter is 'applied', regardless of what they might be given as. - * @returns {array} Data for the matched elements. If any elements, as a result of the - * selector, were not TR, TD or TH elements in the DataTable, they will have a null - * entry in the array. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Get the data from the first row in the table - * var data = oTable._('tr:first'); - * - * // Do something useful with the data - * alert( "First cell is: "+data[0] ); - * } ); - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Filter to 'Webkit' and get all data for - * oTable.fnFilter('Webkit'); - * var data = oTable._('tr', {"search": "applied"}); - * - * // Do something with the data - * alert( data.length+" rows matched the search" ); - * } ); - */ - this._ = function ( sSelector, oOpts ) - { - return this.api(true).rows( sSelector, oOpts ).data(); - }; - - - /** - * Create a DataTables Api instance, with the currently selected tables for - * the Api's context. - * @param {boolean} [traditional=false] Set the API instance's context to be - * only the table referred to by the `DataTable.ext.iApiIndex` option, as was - * used in the API presented by DataTables 1.9- (i.e. the traditional mode), - * or if all tables captured in the jQuery object should be used. - * @return {DataTables.Api} - */ - this.api = function ( traditional ) - { - return traditional ? - new _Api( - _fnSettingsFromNode( this[ _ext.iApiIndex ] ) - ) : - new _Api( this ); - }; - - - /** - * Add a single new row or multiple rows of data to the table. Please note - * that this is suitable for client-side processing only - if you are using - * server-side processing (i.e. "bServerSide": true), then to add data, you - * must add it to the data source, i.e. the server-side, through an Ajax call. - * @param {array|object} data The data to be added to the table. This can be: - *

    - *
  • 1D array of data - add a single row with the data provided
  • - *
  • 2D array of arrays - add multiple rows in a single call
  • - *
  • object - data object when using mData
  • - *
  • array of objects - multiple data objects when using mData
  • - *
- * @param {bool} [redraw=true] redraw the table or not - * @returns {array} An array of integers, representing the list of indexes in - * aoData ({@link DataTable.models.oSettings}) that have been added to - * the table. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * // Global var for counter - * var giCount = 2; - * - * $(document).ready(function() { - * $('#example').dataTable(); - * } ); - * - * function fnClickAddRow() { - * $('#example').dataTable().fnAddData( [ - * giCount+".1", - * giCount+".2", - * giCount+".3", - * giCount+".4" ] - * ); - * - * giCount++; - * } - */ - this.fnAddData = function( data, redraw ) - { - var api = this.api( true ); - - /* Check if we want to add multiple rows or not */ - var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ? - api.rows.add( data ) : - api.row.add( data ); - - if ( redraw === undefined || redraw ) { - api.draw(); - } - - return rows.flatten().toArray(); - }; - - - /** - * This function will make DataTables recalculate the column sizes, based on the data - * contained in the table and the sizes applied to the columns (in the DOM, CSS or - * through the sWidth parameter). This can be useful when the width of the table's - * parent element changes (for example a window resize). - * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable( { - * "sScrollY": "200px", - * "bPaginate": false - * } ); - * - * $(window).on('resize', function () { - * oTable.fnAdjustColumnSizing(); - * } ); - * } ); - */ - this.fnAdjustColumnSizing = function ( bRedraw ) - { - var api = this.api( true ).columns.adjust(); - var settings = api.settings()[0]; - var scroll = settings.oScroll; - - if ( bRedraw === undefined || bRedraw ) { - api.draw( false ); - } - else if ( scroll.sX !== "" || scroll.sY !== "" ) { - /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ - _fnScrollDraw( settings ); - } - }; - - - /** - * Quickly and simply clear a table - * @param {bool} [bRedraw=true] redraw the table or not - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) - * oTable.fnClearTable(); - * } ); - */ - this.fnClearTable = function( bRedraw ) - { - var api = this.api( true ).clear(); - - if ( bRedraw === undefined || bRedraw ) { - api.draw(); - } - }; - - - /** - * The exact opposite of 'opening' a row, this function will close any rows which - * are currently 'open'. - * @param {node} nTr the table row to 'close' - * @returns {int} 0 on success, or 1 if failed (can't find the row) - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable; - * - * // 'open' an information row when a row is clicked on - * $('#example tbody tr').click( function () { - * if ( oTable.fnIsOpen(this) ) { - * oTable.fnClose( this ); - * } else { - * oTable.fnOpen( this, "Temporary row opened", "info_row" ); - * } - * } ); - * - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnClose = function( nTr ) - { - this.api( true ).row( nTr ).child.hide(); - }; - - - /** - * Remove a row for the table - * @param {mixed} target The index of the row from aoData to be deleted, or - * the TR element you want to delete - * @param {function|null} [callBack] Callback function - * @param {bool} [redraw=true] Redraw the table or not - * @returns {array} The row that was deleted - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Immediately remove the first row - * oTable.fnDeleteRow( 0 ); - * } ); - */ - this.fnDeleteRow = function( target, callback, redraw ) - { - var api = this.api( true ); - var rows = api.rows( target ); - var settings = rows.settings()[0]; - var data = settings.aoData[ rows[0][0] ]; - - rows.remove(); - - if ( callback ) { - callback.call( this, settings, data ); - } - - if ( redraw === undefined || redraw ) { - api.draw(); - } - - return data; - }; - - - /** - * Restore the table to it's original state in the DOM by removing all of DataTables - * enhancements, alterations to the DOM structure of the table and event listeners. - * @param {boolean} [remove=false] Completely remove the table from the DOM - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * // This example is fairly pointless in reality, but shows how fnDestroy can be used - * var oTable = $('#example').dataTable(); - * oTable.fnDestroy(); - * } ); - */ - this.fnDestroy = function ( remove ) - { - this.api( true ).destroy( remove ); - }; - - - /** - * Redraw the table - * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) - * oTable.fnDraw(); - * } ); - */ - this.fnDraw = function( complete ) - { - // Note that this isn't an exact match to the old call to _fnDraw - it takes - // into account the new data, but can hold position. - this.api( true ).draw( complete ); - }; - - - /** - * Filter the input based on data - * @param {string} sInput String to filter the table on - * @param {int|null} [iColumn] Column to limit filtering to - * @param {bool} [bRegex=false] Treat as regular expression or not - * @param {bool} [bSmart=true] Perform smart filtering or not - * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) - * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Sometime later - filter... - * oTable.fnFilter( 'test string' ); - * } ); - */ - this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) - { - var api = this.api( true ); - - if ( iColumn === null || iColumn === undefined ) { - api.search( sInput, bRegex, bSmart, bCaseInsensitive ); - } - else { - api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); - } - - api.draw(); - }; - - - /** - * Get the data for the whole table, an individual row or an individual cell based on the - * provided parameters. - * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as - * a TR node then the data source for the whole row will be returned. If given as a - * TD/TH cell node then iCol will be automatically calculated and the data for the - * cell returned. If given as an integer, then this is treated as the aoData internal - * data index for the row (see fnGetPosition) and the data for that row used. - * @param {int} [col] Optional column index that you want the data of. - * @returns {array|object|string} If mRow is undefined, then the data for all rows is - * returned. If mRow is defined, just data for that row, and is iCol is - * defined, only data for the designated cell is returned. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * // Row data - * $(document).ready(function() { - * oTable = $('#example').dataTable(); - * - * oTable.$('tr').click( function () { - * var data = oTable.fnGetData( this ); - * // ... do something with the array / object of data for the row - * } ); - * } ); - * - * @example - * // Individual cell data - * $(document).ready(function() { - * oTable = $('#example').dataTable(); - * - * oTable.$('td').click( function () { - * var sData = oTable.fnGetData( this ); - * alert( 'The cell clicked on had the value of '+sData ); - * } ); - * } ); - */ - this.fnGetData = function( src, col ) - { - var api = this.api( true ); - - if ( src !== undefined ) { - var type = src.nodeName ? src.nodeName.toLowerCase() : ''; - - return col !== undefined || type == 'td' || type == 'th' ? - api.cell( src, col ).data() : - api.row( src ).data() || null; - } - - return api.data().toArray(); - }; - - - /** - * Get an array of the TR nodes that are used in the table's body. Note that you will - * typically want to use the '$' API method in preference to this as it is more - * flexible. - * @param {int} [iRow] Optional row index for the TR element you want - * @returns {array|node} If iRow is undefined, returns an array of all TR elements - * in the table's body, or iRow is defined, just the TR element requested. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Get the nodes from the table - * var nNodes = oTable.fnGetNodes( ); - * } ); - */ - this.fnGetNodes = function( iRow ) - { - var api = this.api( true ); - - return iRow !== undefined ? - api.row( iRow ).node() : - api.rows().nodes().flatten().toArray(); - }; - - - /** - * Get the array indexes of a particular cell from it's DOM element - * and column index including hidden columns - * @param {node} node this can either be a TR, TD or TH in the table's body - * @returns {int} If nNode is given as a TR, then a single index is returned, or - * if given as a cell, an array of [row index, column index (visible), - * column index (all)] is given. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * $('#example tbody td').click( function () { - * // Get the position of the current data from the node - * var aPos = oTable.fnGetPosition( this ); - * - * // Get the data array for this row - * var aData = oTable.fnGetData( aPos[0] ); - * - * // Update the data array and return the value - * aData[ aPos[1] ] = 'clicked'; - * this.innerHTML = 'clicked'; - * } ); - * - * // Init DataTables - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnGetPosition = function( node ) - { - var api = this.api( true ); - var nodeName = node.nodeName.toUpperCase(); - - if ( nodeName == 'TR' ) { - return api.row( node ).index(); - } - else if ( nodeName == 'TD' || nodeName == 'TH' ) { - var cell = api.cell( node ).index(); - - return [ - cell.row, - cell.columnVisible, - cell.column - ]; - } - return null; - }; - - - /** - * Check to see if a row is 'open' or not. - * @param {node} nTr the table row to check - * @returns {boolean} true if the row is currently open, false otherwise - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable; - * - * // 'open' an information row when a row is clicked on - * $('#example tbody tr').click( function () { - * if ( oTable.fnIsOpen(this) ) { - * oTable.fnClose( this ); - * } else { - * oTable.fnOpen( this, "Temporary row opened", "info_row" ); - * } - * } ); - * - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnIsOpen = function( nTr ) - { - return this.api( true ).row( nTr ).child.isShown(); - }; - - - /** - * This function will place a new row directly after a row which is currently - * on display on the page, with the HTML contents that is passed into the - * function. This can be used, for example, to ask for confirmation that a - * particular record should be deleted. - * @param {node} nTr The table row to 'open' - * @param {string|node|jQuery} mHtml The HTML to put into the row - * @param {string} sClass Class to give the new TD cell - * @returns {node} The row opened. Note that if the table row passed in as the - * first parameter, is not found in the table, this method will silently - * return. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable; - * - * // 'open' an information row when a row is clicked on - * $('#example tbody tr').click( function () { - * if ( oTable.fnIsOpen(this) ) { - * oTable.fnClose( this ); - * } else { - * oTable.fnOpen( this, "Temporary row opened", "info_row" ); - * } - * } ); - * - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnOpen = function( nTr, mHtml, sClass ) - { - return this.api( true ) - .row( nTr ) - .child( mHtml, sClass ) - .show() - .child()[0]; - }; - - - /** - * Change the pagination - provides the internal logic for pagination in a simple API - * function. With this function you can have a DataTables table go to the next, - * previous, first or last pages. - * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" - * or page number to jump to (integer), note that page 0 is the first page. - * @param {bool} [bRedraw=true] Redraw the table or not - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * oTable.fnPageChange( 'next' ); - * } ); - */ - this.fnPageChange = function ( mAction, bRedraw ) - { - var api = this.api( true ).page( mAction ); - - if ( bRedraw === undefined || bRedraw ) { - api.draw(false); - } - }; - - - /** - * Show a particular column - * @param {int} iCol The column whose display should be changed - * @param {bool} bShow Show (true) or hide (false) the column - * @param {bool} [bRedraw=true] Redraw the table or not - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Hide the second column after initialisation - * oTable.fnSetColumnVis( 1, false ); - * } ); - */ - this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) - { - var api = this.api( true ).column( iCol ).visible( bShow ); - - if ( bRedraw === undefined || bRedraw ) { - api.columns.adjust().draw(); - } - }; - - - /** - * Get the settings for a particular table for external manipulation - * @returns {object} DataTables settings object. See - * {@link DataTable.models.oSettings} - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * var oSettings = oTable.fnSettings(); - * - * // Show an example parameter from the settings - * alert( oSettings._iDisplayStart ); - * } ); - */ - this.fnSettings = function() - { - return _fnSettingsFromNode( this[_ext.iApiIndex] ); - }; - - - /** - * Sort the table by a particular column - * @param {int} iCol the data index to sort on. Note that this will not match the - * 'display index' if you have hidden data entries - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Sort immediately with columns 0 and 1 - * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); - * } ); - */ - this.fnSort = function( aaSort ) - { - this.api( true ).order( aaSort ).draw(); - }; - - - /** - * Attach a sort listener to an element for a given column - * @param {node} nNode the element to attach the sort listener to - * @param {int} iColumn the column that a click on this node will sort on - * @param {function} [fnCallback] callback function when sort is run - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Sort on column 1, when 'sorter' is clicked on - * oTable.fnSortListener( document.getElementById('sorter'), 1 ); - * } ); - */ - this.fnSortListener = function( nNode, iColumn, fnCallback ) - { - this.api( true ).order.listener( nNode, iColumn, fnCallback ); - }; - - - /** - * Update a table cell or row - this method will accept either a single value to - * update the cell with, an array of values with one element for each column or - * an object in the same format as the original data source. The function is - * self-referencing in order to make the multi column updates easier. - * @param {object|array|string} mData Data to update the cell/row with - * @param {node|int} mRow TR element you want to update or the aoData index - * @param {int} [iColumn] The column to update, give as null or undefined to - * update a whole row. - * @param {bool} [bRedraw=true] Redraw the table or not - * @param {bool} [bAction=true] Perform pre-draw actions or not - * @returns {int} 0 on success, 1 on error - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell - * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row - * } ); - */ - this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) - { - var api = this.api( true ); - - if ( iColumn === undefined || iColumn === null ) { - api.row( mRow ).data( mData ); - } - else { - api.cell( mRow, iColumn ).data( mData ); - } - - if ( bAction === undefined || bAction ) { - api.columns.adjust(); - } - - if ( bRedraw === undefined || bRedraw ) { - api.draw(); - } - return 0; - }; - - - /** - * Provide a common method for plug-ins to check the version of DataTables being used, in order - * to ensure compatibility. - * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the - * formats "X" and "X.Y" are also acceptable. - * @returns {boolean} true if this version of DataTables is greater or equal to the required - * version, or false if this version of DataTales is not suitable - * @method - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * alert( oTable.fnVersionCheck( '1.9.0' ) ); - * } ); - */ - this.fnVersionCheck = _ext.fnVersionCheck; - - - var _that = this; - var emptyInit = options === undefined; - var len = this.length; - - if ( emptyInit ) { - options = {}; - } - - this.oApi = this.internal = _ext.internal; - - // Extend with old style plug-in API methods - for ( var fn in DataTable.ext.internal ) { - if ( fn ) { - this[fn] = _fnExternApiFunc(fn); - } - } - - this.each(function() { - // For each initialisation we want to give it a clean initialisation - // object that can be bashed around - var o = {}; - var oInit = len > 1 ? // optimisation for single table case - _fnExtend( o, options, true ) : - options; - - /*global oInit,_that,emptyInit*/ - var i=0, iLen, j, jLen, k, kLen; - var sId = this.getAttribute( 'id' ); - var bInitHandedOff = false; - var defaults = DataTable.defaults; - var $this = $(this); - - - /* Sanity check */ - if ( this.nodeName.toLowerCase() != 'table' ) - { - _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); - return; - } - - /* Backwards compatibility for the defaults */ - _fnCompatOpts( defaults ); - _fnCompatCols( defaults.column ); - - /* Convert the camel-case defaults to Hungarian */ - _fnCamelToHungarian( defaults, defaults, true ); - _fnCamelToHungarian( defaults.column, defaults.column, true ); - - /* Setting up the initialisation object */ - _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); - - - - /* Check to see if we are re-initialising a table */ - var allSettings = DataTable.settings; - for ( i=0, iLen=allSettings.length ; i').appendTo($this); - } - oSettings.nTHead = thead[0]; - - var tbody = $this.children('tbody'); - if ( tbody.length === 0 ) { - tbody = $('').appendTo($this); - } - oSettings.nTBody = tbody[0]; - - var tfoot = $this.children('tfoot'); - if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) { - // If we are a scrolling table, and no footer has been given, then we need to create - // a tfoot element for the caption element to be appended to - tfoot = $('').appendTo($this); - } - - if ( tfoot.length === 0 || tfoot.children().length === 0 ) { - $this.addClass( oClasses.sNoFooter ); - } - else if ( tfoot.length > 0 ) { - oSettings.nTFoot = tfoot[0]; - _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); - } - - /* Check if there is data passing into the constructor */ - if ( oInit.aaData ) { - for ( i=0 ; i/g; - - // This is not strict ISO8601 - Date.parse() is quite lax, although - // implementations differ between browsers. - var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/; - - // Escape regular expression special characters - var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); - - // http://en.wikipedia.org/wiki/Foreign_exchange_market - // - \u20BD - Russian ruble. - // - \u20a9 - South Korean Won - // - \u20BA - Turkish Lira - // - \u20B9 - Indian Rupee - // - R - Brazil (R$) and South Africa - // - fr - Swiss Franc - // - kr - Swedish krona, Norwegian krone and Danish krone - // - \u2009 is thin space and \u202F is narrow no-break space, both used in many - // - Ƀ - Bitcoin - // - Ξ - Ethereum - // standards as thousands separators. - var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi; - - - var _empty = function ( d ) { - return !d || d === true || d === '-' ? true : false; - }; - - - var _intVal = function ( s ) { - var integer = parseInt( s, 10 ); - return !isNaN(integer) && isFinite(s) ? integer : null; - }; - - // Convert from a formatted number with characters other than `.` as the - // decimal place, to a Javascript number - var _numToDecimal = function ( num, decimalPoint ) { - // Cache created regular expressions for speed as this function is called often - if ( ! _re_dic[ decimalPoint ] ) { - _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); - } - return typeof num === 'string' && decimalPoint !== '.' ? - num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : - num; - }; - - - var _isNumber = function ( d, decimalPoint, formatted ) { - var strType = typeof d === 'string'; - - // If empty return immediately so there must be a number if it is a - // formatted string (this stops the string "k", or "kr", etc being detected - // as a formatted number for currency - if ( _empty( d ) ) { - return true; - } - - if ( decimalPoint && strType ) { - d = _numToDecimal( d, decimalPoint ); - } - - if ( formatted && strType ) { - d = d.replace( _re_formatted_numeric, '' ); - } - - return !isNaN( parseFloat(d) ) && isFinite( d ); - }; - - - // A string without HTML in it can be considered to be HTML still - var _isHtml = function ( d ) { - return _empty( d ) || typeof d === 'string'; - }; - - - var _htmlNumeric = function ( d, decimalPoint, formatted ) { - if ( _empty( d ) ) { - return true; - } - - var html = _isHtml( d ); - return ! html ? - null : - _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? - true : - null; - }; - - - var _pluck = function ( a, prop, prop2 ) { - var out = []; - var i=0, ien=a.length; - - // Could have the test in the loop for slightly smaller code, but speed - // is essential here - if ( prop2 !== undefined ) { - for ( ; i') - .css( { - position: 'fixed', - top: 0, - left: $(window).scrollLeft()*-1, // allow for scrolling - height: 1, - width: 1, - overflow: 'hidden' - } ) - .append( - $('
') - .css( { - position: 'absolute', - top: 1, - left: 1, - width: 100, - overflow: 'scroll' - } ) - .append( - $('
') - .css( { - width: '100%', - height: 10 - } ) - ) - ) - .appendTo( 'body' ); - - var outer = n.children(); - var inner = outer.children(); - - // Numbers below, in order, are: - // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth - // - // IE6 XP: 100 100 100 83 - // IE7 Vista: 100 100 100 83 - // IE 8+ Windows: 83 83 100 83 - // Evergreen Windows: 83 83 100 83 - // Evergreen Mac with scrollbars: 85 85 100 85 - // Evergreen Mac without scrollbars: 100 100 100 100 - - // Get scrollbar width - browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; - - // IE6/7 will oversize a width 100% element inside a scrolling element, to - // include the width of the scrollbar, while other browsers ensure the inner - // element is contained without forcing scrolling - browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100; - - // In rtl text layout, some browsers (most, but not all) will place the - // scrollbar on the left, rather than the right. - browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; - - // IE8- don't provide height and width for getBoundingClientRect - browser.bBounding = n[0].getBoundingClientRect().width ? true : false; - - n.remove(); - } - - $.extend( settings.oBrowser, DataTable.__browser ); - settings.oScroll.iBarWidth = DataTable.__browser.barWidth; - } - - - /** - * Array.prototype reduce[Right] method, used for browsers which don't support - * JS 1.6. Done this way to reduce code size, since we iterate either way - * @param {object} settings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnReduce ( that, fn, init, start, end, inc ) - { - var - i = start, - value, - isSet = false; - - if ( init !== undefined ) { - value = init; - isSet = true; - } - - while ( i !== end ) { - if ( ! that.hasOwnProperty(i) ) { - continue; - } - - value = isSet ? - fn( value, that[i], i, that ) : - that[i]; - - isSet = true; - i += inc; - } - - return value; - } - - /** - * Add a column to the list used for the table with default values - * @param {object} oSettings dataTables settings object - * @param {node} nTh The th element for this column - * @memberof DataTable#oApi - */ - function _fnAddColumn( oSettings, nTh ) - { - // Add column to aoColumns array - var oDefaults = DataTable.defaults.column; - var iCol = oSettings.aoColumns.length; - var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { - "nTh": nTh ? nTh : document.createElement('th'), - "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', - "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], - "mData": oDefaults.mData ? oDefaults.mData : iCol, - idx: iCol - } ); - oSettings.aoColumns.push( oCol ); - - // Add search object for column specific search. Note that the `searchCols[ iCol ]` - // passed into extend can be undefined. This allows the user to give a default - // with only some of the parameters defined, and also not give a default - var searchCols = oSettings.aoPreSearchCols; - searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); - - // Use the default column options function to initialise classes etc - _fnColumnOptions( oSettings, iCol, $(nTh).data() ); - } - - - /** - * Apply options for a column - * @param {object} oSettings dataTables settings object - * @param {int} iCol column index to consider - * @param {object} oOptions object with sType, bVisible and bSearchable etc - * @memberof DataTable#oApi - */ - function _fnColumnOptions( oSettings, iCol, oOptions ) - { - var oCol = oSettings.aoColumns[ iCol ]; - var oClasses = oSettings.oClasses; - var th = $(oCol.nTh); - - // Try to get width information from the DOM. We can't get it from CSS - // as we'd need to parse the CSS stylesheet. `width` option can override - if ( ! oCol.sWidthOrig ) { - // Width attribute - oCol.sWidthOrig = th.attr('width') || null; - - // Style attribute - var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); - if ( t ) { - oCol.sWidthOrig = t[1]; - } - } - - /* User specified column options */ - if ( oOptions !== undefined && oOptions !== null ) - { - // Backwards compatibility - _fnCompatCols( oOptions ); - - // Map camel case parameters to their Hungarian counterparts - _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); - - /* Backwards compatibility for mDataProp */ - if ( oOptions.mDataProp !== undefined && !oOptions.mData ) - { - oOptions.mData = oOptions.mDataProp; - } - - if ( oOptions.sType ) - { - oCol._sManualType = oOptions.sType; - } - - // `class` is a reserved word in Javascript, so we need to provide - // the ability to use a valid name for the camel case input - if ( oOptions.className && ! oOptions.sClass ) - { - oOptions.sClass = oOptions.className; - } - if ( oOptions.sClass ) { - th.addClass( oOptions.sClass ); - } - - $.extend( oCol, oOptions ); - _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); - - /* iDataSort to be applied (backwards compatibility), but aDataSort will take - * priority if defined - */ - if ( oOptions.iDataSort !== undefined ) - { - oCol.aDataSort = [ oOptions.iDataSort ]; - } - _fnMap( oCol, oOptions, "aDataSort" ); - } - - /* Cache the data get and set functions for speed */ - var mDataSrc = oCol.mData; - var mData = _fnGetObjectDataFn( mDataSrc ); - var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; - - var attrTest = function( src ) { - return typeof src === 'string' && src.indexOf('@') !== -1; - }; - oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( - attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) - ); - oCol._setter = null; - - oCol.fnGetData = function (rowData, type, meta) { - var innerData = mData( rowData, type, undefined, meta ); - - return mRender && type ? - mRender( innerData, type, rowData, meta ) : - innerData; - }; - oCol.fnSetData = function ( rowData, val, meta ) { - return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); - }; - - // Indicate if DataTables should read DOM data as an object or array - // Used in _fnGetRowElements - if ( typeof mDataSrc !== 'number' ) { - oSettings._rowReadObject = true; - } - - /* Feature sorting overrides column specific when off */ - if ( !oSettings.oFeatures.bSort ) - { - oCol.bSortable = false; - th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called - } - - /* Check that the class assignment is correct for sorting */ - var bAsc = $.inArray('asc', oCol.asSorting) !== -1; - var bDesc = $.inArray('desc', oCol.asSorting) !== -1; - if ( !oCol.bSortable || (!bAsc && !bDesc) ) - { - oCol.sSortingClass = oClasses.sSortableNone; - oCol.sSortingClassJUI = ""; - } - else if ( bAsc && !bDesc ) - { - oCol.sSortingClass = oClasses.sSortableAsc; - oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; - } - else if ( !bAsc && bDesc ) - { - oCol.sSortingClass = oClasses.sSortableDesc; - oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; - } - else - { - oCol.sSortingClass = oClasses.sSortable; - oCol.sSortingClassJUI = oClasses.sSortJUI; - } - } - - - /** - * Adjust the table column widths for new data. Note: you would probably want to - * do a redraw after calling this function! - * @param {object} settings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnAdjustColumnSizing ( settings ) - { - /* Not interested in doing column width calculation if auto-width is disabled */ - if ( settings.oFeatures.bAutoWidth !== false ) - { - var columns = settings.aoColumns; - - _fnCalculateColumnWidths( settings ); - for ( var i=0 , iLen=columns.length ; i