, props: K): { [P in keyof K]: T[P] extends string ? moment.Moment : (moment.Moment | null) } {\r\n type resultType = T[P] extends string ? moment.Moment : moment.Moment | null;\r\n let result = {} as { [P in keyof K]: resultType
};\r\n for (var k in props) {\r\n result[k] = ((object[k] && moment.utc(object[k])) || null) as resultType;\r\n }\r\n return result;\r\n}\r\n\r\n\r\n//type dateFormat = 'MM/dd/yyyy' | 'MM-dd-yyyy' | 'M/d/yyyy' | 'yyyy-MM-dd' | 'yyyy-M-d' | 'yyyy/MM/dd' | 'yyyy/M/d' | \r\n\r\n// Date Map-- maps C# to jquery and moment formats (08/13/18 RKH: backlog #12359)\r\nconst dateMap = {\r\n\r\n 'MM/dd/yyyy': {\r\n momentFormat: 'MM/DD/YYYY',\r\n jqueryFormat: 'mm/dd/yy'\r\n },\r\n 'MM-dd-yyyy': {\r\n momentFormat: 'MM-DD-YYYY',\r\n jqueryFormat: 'mm-dd-yy'\r\n },\r\n\r\n 'M/d/yyyy': {\r\n momentFormat: 'M/D/YYYY',\r\n jqueryFormat: 'm/d/yy'\r\n },\r\n 'yyyy-MM-dd': {\r\n momentFormat: 'YYYY-MM-DD',\r\n jqueryFormat: 'yy-mm-dd'\r\n },\r\n 'yyyy-M-d': {\r\n momentFormat: 'YYYY-M-D',\r\n jqueryFormat: 'yy-m-d'\r\n },\r\n 'yyyy/MM/dd': {\r\n momentFormat: 'YYYY/MM/DD',\r\n jqueryFormat: 'yy/mm/dd'\r\n },\r\n\r\n 'yyyy/M/d': {\r\n momentFormat: 'YYYY/M/D',\r\n jqueryFormat: 'yy/m/d'\r\n },\r\n 'yyyy.MM.dd.': {\r\n momentFormat: 'YYYY.MM.DD.',\r\n jqueryFormat: 'yy.mm.dd.'\r\n },\r\n 'yyyy.M.d': {\r\n momentFormat: 'YYYY.M.D',\r\n jqueryFormat: 'yy.m.d'\r\n },\r\n 'yyyy.d.M': {\r\n momentFormat: 'YYYY.D.M',\r\n jqueryFormat: 'yy.d.m'\r\n },\r\n 'yyyy. M. d': {\r\n momentFormat: 'YYYY. M. D',\r\n jqueryFormat: 'mm-dd-yy'\r\n },\r\n 'yyyy?M?d?': {\r\n momentFormat: 'YYYY?M?D?',\r\n jqueryFormat: 'yy?m?d?'\r\n },\r\n 'd.M.yyyy': {\r\n momentFormat: 'D.M.YYYY',\r\n jqueryFormat: 'd.m.yy'\r\n },\r\n 'd.MM.yyyy': {\r\n momentFormat: 'D.MM.YYYY',\r\n jqueryFormat: 'd.mm.yy'\r\n },\r\n 'd/M/yyyy': {\r\n momentFormat: 'D/M/YYYY',\r\n jqueryFormat: 'd/m/yy'\r\n },\r\n 'd/MM/yyyy': {\r\n momentFormat: 'D/MM/YYYY',\r\n jqueryFormat: 'd/mm/yy'\r\n },\r\n 'dd.MM.yyyy': {\r\n momentFormat: 'DD.MM.YYYY',\r\n jqueryFormat: 'dd.mm.yy'\r\n },\r\n 'dd/MM/yyyy': {\r\n momentFormat: 'DD/MM/YYYY',\r\n jqueryFormat: 'dd/mm/yy'\r\n },\r\n 'dd-MM-yyyy': {\r\n momentFormat: 'DD-MM-YYYY',\r\n jqueryFormat: 'dd-mm-yy'\r\n },\r\n 'd-M-yyyy': {\r\n momentFormat: 'D-M-YYYY',\r\n jqueryFormat: 'd-m-yy'\r\n },\r\n}\r\ntype dateFormat = keyof typeof dateMap;\r\n\r\n//array of valid moment format's incl. times, used by formatDate function to declare valid formats so moment does not throw js date deprication warning (09/14/18 RKH: backlog #12359-- dynamic date formatting)\r\nvar momentFormatArray = [\r\n 'MM/DD/YYYY',\r\n 'MM/DD/YYYY HH:mm',\r\n 'MM/DD/YYYY hh:mm a',\r\n 'MM/DD/YYYY h:mm a',\r\n 'MM/DD/YYYY hh:mm A',\r\n 'MM/DD/YYYY h:mm A',\r\n\r\n 'MM-DD-YYYY',\r\n 'MM-DD-YYYY HH:mm',\r\n 'MM-DD-YYYY hh:mm a',\r\n 'MM-DD-YYYY h:mm a',\r\n 'MM-DD-YYYY hh:mm A',\r\n 'MM-DD-YYYY h:mm A',\r\n\r\n 'M/D/YYYY',\r\n 'M/D/YYYY HH:mm',\r\n 'M/D/YYYY hh:mm a',\r\n 'M/D/YYYY h:mm a',\r\n 'M/D/YYYY hh:mm A',\r\n 'M/D/YYYY h:mm A',\r\n\r\n 'YYYY-MM-DD',\r\n 'YYYY-MM-DD HH:mm',\r\n 'YYYY-MM-DD hh:mm a',\r\n 'YYYY-MM-DD h:mm a',\r\n 'YYYY-MM-DD hh:mm A',\r\n 'YYYY-MM-DD h:mm A',\r\n\r\n 'YYYY-M-D',\r\n 'YYYY-M-D HH:mm',\r\n 'YYYY-M-D hh:mm a',\r\n 'YYYY-M-D h:mm a',\r\n 'YYYY-M-D hh:mm A',\r\n 'YYYY-M-D h:mm A',\r\n\r\n 'YYYY/MM/DD',\r\n 'YYYY/MM/DD HH:mm',\r\n 'YYYY/MM/DD hh:mm a',\r\n 'YYYY/MM/DD h:mm a',\r\n 'YYYY/MM/DD hh:mm A',\r\n 'YYYY/MM/DD h:mm A',\r\n\r\n 'YYYY/M/D',\r\n 'YYYY/M/D HH:mm',\r\n 'YYYY/M/D hh:mm a',\r\n 'YYYY/M/D h:mm a',\r\n 'YYYY/M/D hh:mm A',\r\n 'YYYY/M/D h:mm A',\r\n\r\n 'YYYY.MM.DD.',\r\n 'YYYY.MM.DD. HH:mm',\r\n 'YYYY.MM.DD. hh:mm a',\r\n 'YYYY.MM.DD. h:mm a',\r\n 'YYYY.MM.DD. hh:mm A',\r\n 'YYYY.MM.DD. h:mm A',\r\n\r\n 'YYYY.M.D',\r\n 'YYYY.M.D HH:mm',\r\n 'YYYY.M.D hh:mm a',\r\n 'YYYY.M.D h:mm a',\r\n 'YYYY.M.D hh:mm A',\r\n 'YYYY.M.D h:mm A',\r\n\r\n 'YYYY.D.M',\r\n 'YYYY.D.M HH:mm',\r\n 'YYYY.D.M hh:mm a',\r\n 'YYYY.D.M h:mm a',\r\n 'YYYY.D.M hh:mm A',\r\n 'YYYY.D.M h:mm A',\r\n\r\n 'YYYY. M. D',\r\n 'YYYY. M. D HH:mm',\r\n 'YYYY. M. D hh:mm a',\r\n 'YYYY. M. D h:mm a',\r\n 'YYYY. M. D hh:mm A',\r\n 'YYYY. M. D h:mm A',\r\n\r\n 'YYYY?M?D?',\r\n 'YYYY?M?D? HH:mm',\r\n 'YYYY?M?D? hh:mm a',\r\n 'YYYY?M?D? h:mm a',\r\n 'YYYY?M?D? hh:mm A',\r\n 'YYYY?M?D? h:mm A',\r\n\r\n 'D.M.YYYY',\r\n 'D.M.YYYY HH:mm',\r\n 'D.M.YYYY hh:mm a',\r\n 'D.M.YYYY h:mm a',\r\n 'D.M.YYYY hh:mm A',\r\n 'D.M.YYYY h:mm A',\r\n\r\n 'D.MM.YYYY',\r\n 'D.MM.YYYY HH:mm',\r\n 'D.MM.YYYY hh:mm a',\r\n 'D.MM.YYYY h:mm a',\r\n 'D.MM.YYYY hh:mm A',\r\n 'D.MM.YYYY h:mm A',\r\n\r\n 'D/M/YYYY',\r\n 'D/M/YYYY HH:mm',\r\n 'D/M/YYYY hh:mm a',\r\n 'D/M/YYYY h:mm a',\r\n 'D/M/YYYY hh:mm A',\r\n 'D/M/YYYY h:mm A',\r\n\r\n 'D/MM/YYYY',\r\n 'D/MM/YYYY HH:mm',\r\n 'D/MM/YYYY hh:mm a',\r\n 'D/MM/YYYY h:mm a',\r\n 'D/MM/YYYY hh:mm A',\r\n 'D/MM/YYYY h:mm A',\r\n\r\n 'DD.MM.YYYY',\r\n 'DD.MM.YYYY HH:mm',\r\n 'DD.MM.YYYY hh:mm a',\r\n 'DD.MM.YYYY h:mm a',\r\n 'DD.MM.YYYY hh:mm A',\r\n 'DD.MM.YYYY h:mm A',\r\n\r\n 'DD/MM/YYYY',\r\n 'DD/MM/YYYY HH:mm',\r\n 'DD/MM/YYYY hh:mm a',\r\n 'DD/MM/YYYY h:mm a',\r\n 'DD/MM/YYYY hh:mm A',\r\n 'DD/MM/YYYY h:mm A',\r\n\r\n 'DD-MM-YYYY',\r\n 'DD-MM-YYYY HH:mm',\r\n 'DD-MM-YYYY hh:mm a',\r\n 'DD-MM-YYYY h:mm a',\r\n 'DD-MM-YYYY hh:mm A',\r\n 'DD-MM-YYYY h:mm A',\r\n\r\n 'D-M-YYYY',\r\n 'D-M-YYYY HH:mm',\r\n 'D-M-YYYY hh:mm a',\r\n 'D-M-YYYY h:mm a',\r\n 'D-M-YYYY hh:mm A',\r\n 'D-M-YYYY h:mm A'\r\n]\r\n\r\nexport enum FormatType {\r\n Date = 1,\r\n DateTime24 = 2,\r\n DateTime12 = 3,\r\n DateServer = 4,\r\n DateTime24Server = 5,\r\n DateTime12Server = 6,\r\n}\r\n\r\n//format a date to the user or server formats using moment (08/13/18 RKH: backlog #12359)\r\n// type = 1 Date Only, 2 = Date and Time, 3 = Date Time and AmPm, 4 - Date Server Format 5 = Date Time Server Format, 6 = Date Time am/pm Server format, 7 = custom format\r\nexport function formatDate(value: string, formatType: FormatType, valueFormat: string |string[], dateFormat?: dateFormat): string {\r\n\r\n //trap empty value\r\n if (value === '')\r\n return '';\r\n\r\n //if input format unknown-- passing array of moment formats prevents moment from falling back to js date and throwing a deprication error\r\n valueFormat = valueFormat || momentFormatArray;\r\n dateFormat = dateFormat || 'MM/dd/yyyy';\r\n\r\n //trap value format not being passed in\r\n if (valueFormat === undefined) {\r\n valueFormat = \"\";\r\n }\r\n\r\n //format value based on type\r\n switch (formatType) {\r\n case FormatType.Date:\r\n return moment.default(value, valueFormat).format(dateMap[dateFormat].momentFormat);\r\n case FormatType.DateTime24:\r\n //input format known-- prevents moment deprication warning, moment does not fall back to js.date\r\n return moment.default(value, valueFormat).format(dateMap[dateFormat].momentFormat + \" HH:mm\");\r\n case FormatType.DateTime12:\r\n return moment.default(value, valueFormat).format(dateMap[dateFormat].momentFormat + \" hh:mm a\");\r\n case FormatType.DateServer:\r\n return moment.default(value, valueFormat).format('YYYY-MM-DD');\r\n case FormatType.DateTime24Server:\r\n return moment.default(value, valueFormat).format(\"YYYY-MM-DD HH:mm\");\r\n case FormatType.DateTime12Server:\r\n return moment.default(value, valueFormat).format(\"YYYY-MM-DD hh:mm a\");\r\n default:\r\n return \"\";\r\n }\r\n}\r\n\r\nconst defaultDateStringMap: {\r\n time: 'time',\r\n date: 'date',\r\n datetime: 'datetime'\r\n} = {\r\n time: 'time',\r\n date: 'date',\r\n datetime: 'datetime'\r\n}\r\n\r\nexport function getDateStrings(date: moment.Moment | null, map?: TMap): ObjectFromValues;\r\nexport function getDateStrings(data: TData, map: TMap, key: K): ObjectFromValues;\r\nexport function getDateStrings(data:any, map:any, key?:any) {\r\n\r\n var props;\r\n var date;\r\n\r\n if (key !== undefined) {\r\n props = map[key];\r\n date = data[key];\r\n } else {\r\n props = map || defaultDateStringMap;\r\n date = data;\r\n }\r\n\r\n var result = {} as { [key: string]: string | null };\r\n props.date && (result[props.date] = date ? formatDate(date, 1, '') : null); //(12/13/18 RKH: backlog #12359-- dynamic date formatting)\r\n props.time && (result[props.time] = date ? date.format('HH:mm') : null);\r\n\tprops.datetime && (result[props.datetime] = date ? formatDate(date, 2, '') : null); //(12/13/18 RKH: backlog #12359-- dynamic date formatting)\r\n return result; \r\n} \r\n\r\nexport function getOffsetFromParent(child: HTMLElement, parent: Element) {\r\n var result = { x: 0, y: 0 };\r\n while (child && child !== parent) {\r\n result.x += child.offsetLeft;\r\n result.y += child.offsetTop;\r\n child = child.offsetParent as HTMLElement;\r\n }\r\n\r\n return result;\r\n}","import { Location } from '../components/googlemaps/location';\r\nimport { WithDates, getDates, getDateStrings, fetchJson } from '../util';\r\n\r\ninterface FlightLegModel {\r\n organizationLogo: string | null,\r\n tailNumber: string\r\n aircraftTypeName: string,\r\n aircraftTypeCode: string,\r\n flightAwareFlightIds: string[],\r\n departureLocation?: Location;\r\n arrivalLocation?: Location;\r\n departure?: string,\r\n departureCity?: string,\r\n departureState?: string,\r\n departureCountry?: string,\r\n arrival?: string,\r\n arrivalCity?: string,\r\n arrivalState?: string,\r\n arrivalCountry: string,\r\n flightAwareArrivalUTC?: string;\r\n flightAwareArrivalLocal?: string;\r\n flightAwareDepartureUTC?: string;\r\n flightAwareDepartureLocal?: string;\r\n flightAwareEstimatedArrivalUTC?: string;\r\n flightAwareEstimatedArrivalLocal?: string;\r\n flightAwareEstimatedDepartureUTC?: string;\r\n flightAwareEstimatedDepartureLocal?: string;\r\n scheduledArrivalUTC?: string;\r\n scheduledArrivalLocal?: string;\r\n scheduledDepartureUTC?: string;\r\n scheduledDepartureLocal?: string; \r\n arrivalTimeZoneOffset?: number;\r\n departureTimeZoneOffset?: number;\r\n expired: boolean;\r\n}\r\n\r\ninterface FlightLegModelDates {\r\n flightAwareEstimatedArrivalUTC: {\r\n date: 'flightAwareEstimatedArrivalDateUTC',\r\n time: 'flightAwareEstimatedArrivalTimeUTC'\r\n },\r\n flightAwareEstimatedArrivalLocal: {\r\n date: 'flightAwareEstimatedArrivalDateLocal',\r\n time: 'flightAwareEstimatedArrivalTimeLocal'\r\n },\r\n scheduledArrivalUTC: {\r\n date: 'scheduledArrivalDateUTC',\r\n time: 'scheduledArrivalTimeUTC'\r\n },\r\n scheduledArrivalLocal: {\r\n date: 'scheduledArrivalDateLocal',\r\n time: 'scheduledArrivalTimeLocal'\r\n },\r\n flightAwareArrivalUTC: {\r\n date: 'flightAwareArrivalDateUTC',\r\n time: 'flightAwareArrivalTimeUTC'\r\n },\r\n flightAwareArrivalLocal: {\r\n date: 'flightAwareArrivalDateLocal',\r\n time: 'flightAwareArrivalTimeLocal'\r\n }, \r\n flightAwareEstimatedDepartureUTC: {\r\n date: 'flightAwareEstimatedDepartureDateUTC',\r\n time: 'flightAwareEstimatedDepartureTimeUTC'\r\n },\r\n flightAwareEstimatedDepartureLocal: {\r\n date: 'flightAwareEstimatedDepartureDateLocal',\r\n time: 'flightAwareEstimatedDepartureTimeLocal'\r\n }, \r\n scheduledDepartureUTC: {\r\n date: 'scheduledDepartureDateUTC',\r\n time: 'scheduledDepartureTimeUTC'\r\n },\r\n scheduledDepartureLocal: {\r\n date: 'scheduledDepartureDateLocal',\r\n time: 'scheduledDepartureTimeLocal'\r\n }\r\n flightAwareDepartureUTC: {\r\n date: 'flightAwareDepartureDateUTC',\r\n time: 'flightAwareDepartureTimeUTC'\r\n },\r\n flightAwareDepartureLocal: {\r\n date: 'flightAwareDepartureDateLocal',\r\n time: 'flightAwareDepartureTimeLocal'\r\n },\r\n}\r\n\r\nconst FlightLegModelDates: FlightLegModelDates = {\r\n flightAwareEstimatedArrivalUTC: {\r\n date: 'flightAwareEstimatedArrivalDateUTC',\r\n time: 'flightAwareEstimatedArrivalTimeUTC'\r\n },\r\n flightAwareEstimatedArrivalLocal: {\r\n date: 'flightAwareEstimatedArrivalDateLocal',\r\n time: 'flightAwareEstimatedArrivalTimeLocal'\r\n }, \r\n scheduledArrivalUTC: {\r\n date: 'scheduledArrivalDateUTC',\r\n time: 'scheduledArrivalTimeUTC'\r\n },\r\n scheduledArrivalLocal: {\r\n date: 'scheduledArrivalDateLocal',\r\n time: 'scheduledArrivalTimeLocal'\r\n },\r\n flightAwareArrivalUTC: {\r\n date: 'flightAwareArrivalDateUTC',\r\n time: 'flightAwareArrivalTimeUTC'\r\n },\r\n flightAwareArrivalLocal: {\r\n date: 'flightAwareArrivalDateLocal',\r\n time: 'flightAwareArrivalTimeLocal'\r\n },\r\n flightAwareEstimatedDepartureUTC: {\r\n date: 'flightAwareEstimatedDepartureDateUTC',\r\n time: 'flightAwareEstimatedDepartureTimeUTC'\r\n },\r\n flightAwareEstimatedDepartureLocal: {\r\n date: 'flightAwareEstimatedDepartureDateLocal',\r\n time: 'flightAwareEstimatedDepartureTimeLocal'\r\n }, \r\n scheduledDepartureUTC: {\r\n date: 'scheduledDepartureDateUTC',\r\n time: 'scheduledDepartureTimeUTC'\r\n },\r\n scheduledDepartureLocal: {\r\n date: 'scheduledDepartureDateLocal',\r\n time: 'scheduledDepartureTimeLocal'\r\n },\r\n flightAwareDepartureUTC: {\r\n date: 'flightAwareDepartureDateUTC',\r\n time: 'flightAwareDepartureTimeUTC'\r\n },\r\n flightAwareDepartureLocal: {\r\n date: 'flightAwareDepartureDateLocal',\r\n time: 'flightAwareDepartureTimeLocal'\r\n },\r\n}\r\n\r\nexport interface FlightLegViewModel extends WithDates {\r\n flightAwareEstimatedArrivalDateLocal: string | null;\r\n flightAwareEstimatedArrivalTimeLocal: string | null,\r\n flightAwareEstimatedArrivalDateUTC: string | null;\r\n flightAwareEstimatedArrivalTimeUTC: string | null,\r\n flightAwareArrivalDateLocal: string | null,\r\n flightAwareArrivalTimeLocal: string | null,\r\n flightAwareArrivalDateUTC: string | null,\r\n flightAwareArrivalTimeUTC: string | null,\r\n scheduledArrivalDateLocal: string,\r\n scheduledArrivalTimeLocal: string,\r\n scheduledArrivalDateUTC: string,\r\n scheduledArrivalTimeUTC: string,\r\n\r\n flightAwareEstimatedDepartureDateLocal: string | null;\r\n flightAwareEstimatedDepartureTimeLocal: string | null,\r\n flightAwareEstimatedDepartureDateUTC: string | null;\r\n flightAwareEstimatedDepartureTimeUTC: string | null,\r\n flightAwareDepartureDateLocal: string | null,\r\n flightAwareDepartureTimeLocal: string | null,\r\n flightAwareDepartureDateUTC: string | null;\r\n flightAwareDepartureTimeUTC: string | null,\r\n scheduledDepartureDateLocal: string,\r\n scheduledDepartureTimeLocal: string,\r\n scheduledDepartureDateUTC: string,\r\n scheduledDepartureTimeUTC: string,\r\n}\r\n\r\nfunction map(model: FlightLegModel): FlightLegViewModel {\r\n const dates = getDates(model, FlightLegModelDates);\r\n return {\r\n ...model,\r\n ...dates,\r\n ...getDateStrings(dates, FlightLegModelDates, 'flightAwareEstimatedArrivalUTC'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'flightAwareEstimatedArrivalLocal'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'flightAwareEstimatedDepartureUTC'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'flightAwareEstimatedDepartureLocal'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'scheduledArrivalUTC'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'scheduledArrivalLocal'), \r\n ...getDateStrings(dates, FlightLegModelDates, 'scheduledDepartureUTC'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'scheduledDepartureLocal'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'flightAwareDepartureUTC'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'flightAwareDepartureLocal'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'flightAwareArrivalUTC'),\r\n ...getDateStrings(dates, FlightLegModelDates, 'flightAwareArrivalLocal')\r\n }\r\n}\r\n\r\nexport function fetch(url: string) {\r\n return fetchJson(url)\r\n .then(map);\r\n}\r\n\r\nexport function fetchMany(url: string) {\r\n return fetchJson(url)\r\n .then(m => m.map(map));\r\n}","import * as PropTypes from 'prop-types';\r\n\r\nexport type Location = { latitude: number, longitude: number } | google.maps.LatLng | google.maps.LatLngLiteral;\r\n\r\nexport const locationPropTypeShape = {\r\n latitude: PropTypes.number.isRequired,\r\n longitude: PropTypes.number.isRequired\r\n};\r\n\r\n\r\nexport const locationPropType = PropTypes.shape(locationPropTypeShape);\r\n\r\n// Location mapping between ours and google's\r\nexport function mapLocation(position: Location): google.maps.LatLngLiteral | google.maps.LatLng\r\nexport function mapLocation(position: Location[]): google.maps.LatLngLiteral[] | google.maps.LatLng[]\r\n\r\nexport function mapLocation(input: Location[] | Location): (google.maps.LatLngLiteral | google.maps.LatLng) | ((google.maps.LatLngLiteral | google.maps.LatLng)[]) {\r\n const map = (p: Location) => p && (isLocation(p) ? { lat: p.latitude, lng: p.longitude } : p);\r\n\r\n if (input instanceof Array) {\r\n return input.map(map);\r\n }\r\n return map(input);\r\n}\r\n\r\nfunction isLocation(input: google.maps.LatLngLiteral | google.maps.LatLng | Location): input is { latitude: number, longitude: number } {\r\n return (input as any).latitude !== undefined;\r\n}","export enum MessageType {\r\n Departure = \"departure\",\r\n Arrival = \"arrival\",\r\n Onblock = \"onblock\",\r\n Offblock = \"offblock\",\r\n Position = \"position\",\r\n Flightplan = \"flightplan\",\r\n Cancellation = \"cancellation\"\r\n}\r\n\r\nexport enum FlightStatus {\r\n Scheduled = \"scheduled\",\r\n Filed = \"filed\",\r\n Active = \"active\",\r\n Completed = \"completed\",\r\n Cancelled = \"cancelled\"\r\n}\r\n\r\nexport enum TimeType {\r\n Actual = \"actual\",\r\n Estimated = \"estimated\",\r\n Enroute = \"enroute\"\r\n}\r\n\r\nexport interface MessageBase {\r\n messageType: MessageType;\r\n flightAwareFlightId: string;\r\n flightIdentifier: string;\r\n pitr: string;\r\n}\r\n\r\nexport interface Waypoint {\r\n latitude: number;\r\n longitude: number;\r\n time?: string;\r\n name?: string;\r\n altitude?: number;\r\n groundSpeed?: number;\r\n}\r\n\r\ninterface ReportedByFacility {\r\n facilityHash?: string;\r\n facilityName?: string;\r\n}\r\n\r\ninterface HasIdentifiers {\r\n atcIdentifier?: string;\r\n aircraftRegistration?: string;\r\n}\r\n\r\ninterface HasEstimatedTimes {\r\n estimatedDepartureTime?: string;\r\n estimatedArrivalTime?: string;\r\n enRouteTime?: string;\r\n}\r\n\r\ninterface HasPosition {\r\n latitude?: number;\r\n longitude?: number;\r\n}\r\n\r\ninterface HasOriginDestination {\r\n origin?: string;\r\n destination?: string;\r\n}\r\n\r\nexport interface FlightPlan extends MessageBase, ReportedByFacility, HasEstimatedTimes, HasIdentifiers, HasPosition, HasOriginDestination {\r\n messageType: MessageType.Flightplan;\r\n filedDepartureTime: string;\r\n flightStatus: FlightStatus;\r\n fourDWaypoints?: Waypoint[];\r\n aircraftType?: string;\r\n cruisingAltitude?: number;\r\n predictedETA?: string;\r\n predictedETAMethod?: string;\r\n groundSpeed?: number;\r\n transponderModeSCode?: string;\r\n prefix?: string;\r\n suffix?: string;\r\n route?: string;\r\n filedCruisingSpeed?: number;\r\n trueCancel?: boolean;\r\n waypoints?: Waypoint[];\r\n}\r\n\r\nexport interface Departure extends MessageBase, ReportedByFacility, HasEstimatedTimes, HasIdentifiers, HasOriginDestination {\r\n messageType: MessageType.Departure;\r\n departureTime: string;\r\n origin: string;\r\n arrivalTime?: string;\r\n aircraftType?: string;\r\n isSynthetic?: boolean;\r\n departureTimeType: TimeType;\r\n}\r\n\r\nexport interface Arrival extends MessageBase, ReportedByFacility, HasEstimatedTimes, HasIdentifiers, HasOriginDestination {\r\n messageType: MessageType.Arrival;\r\n arrivalTime: string;\r\n arrivalTimeType: TimeType;\r\n isSynthetic?: boolean;\r\n}\r\n\r\nexport interface Cancellation extends MessageBase, ReportedByFacility, HasEstimatedTimes, HasIdentifiers, HasOriginDestination {\r\n messageType: MessageType.Cancellation;\r\n origin: string;\r\n aircraftType?: string;\r\n filedCruisingAltitude?: number;\r\n filedDepartureTime?: string;\r\n groundSpeed?: number;\r\n transponderModeSCode?: string;\r\n route?: string;\r\n filedCruisingSpeed?: number;\r\n flightStatus?: FlightStatus;\r\n trueCancel?: boolean;\r\n waypoints?: Waypoint[]\r\n}\r\n\r\nexport enum AirGround {\r\n Air = \"air\",\r\n Ground = \"ground\"\r\n}\r\n\r\nexport enum UpdateType {\r\n AdsB = \"adsB\",\r\n Radar = \"radar\",\r\n Transoceanic = \"transoceanic\",\r\n Estimated = \"estimated\",\r\n Datalink = \"datalink\",\r\n Multilateration = \"multilateration\",\r\n AsdeX = \"asdeX\",\r\n SpaceAdsB = \"spaceAdsB\"\r\n}\r\n\r\nexport enum AltitudeChange {\r\n Climbing = \"climbing\",\r\n Descending = \"descending\"\r\n}\r\n\r\nexport interface Position extends MessageBase, HasIdentifiers, HasEstimatedTimes, HasOriginDestination {\r\n messageType: MessageType.Position;\r\n airGround: AirGround;\r\n reportTime: string;\r\n facilityHash: string;\r\n facilityName: string;\r\n latitude: number;\r\n longitude: number;\r\n updateType: UpdateType;\r\n aircraftType?: string;\r\n altitude?: number;\r\n altitudeChange?: AltitudeChange\r\n barometricAltitude?: number;\r\n fuelOnBoard?: number;\r\n gpsAltitude?: number;\r\n groundspeed?: number;\r\n heading?: number;\r\n transponderModeSCode?: string;\r\n outsideAirTemperature?: number;\r\n nextExpectedReportAltitude?: number;\r\n nextExpectedReportTime?: string;\r\n nextExpectedReportLatitude?: number;\r\n nextExpectedReportLongitude?: number;\r\n filedCruisingSpeed?: number;\r\n transponderSquawkCode?: string;\r\n waypoints?: Waypoint[];\r\n winds?: string;\r\n}\r\n\r\nexport interface OffBlock extends MessageBase, HasOriginDestination, ReportedByFacility {\r\n messageType: MessageType.Offblock;\r\n reportTime: string;\r\n}\r\n\r\nexport interface OnBlock extends MessageBase, HasOriginDestination, ReportedByFacility {\r\n messageType: MessageType.Onblock;\r\n reportTime: string;\r\n}\r\n\r\nexport type FlightMessage = Departure | Arrival | OnBlock | OffBlock | Position | FlightPlan | Cancellation;","import * as React from 'react';\r\nimport * as PropTypes from 'prop-types';\r\nimport { locationPropType, locationPropTypeShape } from '../components/googlemaps/location';\r\nimport { FlightAwareHub } from './flightawarehub';\r\nimport * as flightAwareMessages from '../models/flightawaremessages'\r\nimport { Location } from '../components/googlemaps/location'\r\nimport * as moment from 'moment';\r\nimport { getDateStrings } from '../util';\r\n\r\ninterface FlightAwareTimes {\r\n flightAwareArrivalUTC: moment.Moment | null;\r\n flightAwareArrivalLocal: moment.Moment | null;\r\n flightAwareEstimatedArrivalUTC: moment.Moment | null;\r\n flightAwareEstimatedArrivalLocal: moment.Moment | null;\r\n\r\n flightAwareArrivalDateUTC: string | null;\r\n flightAwareArrivalTimeUTC: string | null;\r\n flightAwareArrivalDateLocal: string | null;\r\n flightAwareArrivalTimeLocal: string | null;\r\n\r\n flightAwareEstimatedArrivalDateUTC: string | null;\r\n flightAwareEstimatedArrivalTimeUTC: string | null;\r\n flightAwareEstimatedArrivalDateLocal: string | null;\r\n flightAwareEstimatedArrivalTimeLocal: string | null;\r\n\r\n flightAwareDepartureUTC: moment.Moment | null;\r\n flightAwareDepartureLocal: moment.Moment | null;\r\n flightAwareEstimatedDepartureUTC: moment.Moment | null;\r\n flightAwareEstimatedDepartureLocal: moment.Moment | null;\r\n\r\n flightAwareDepartureDateUTC: string | null;\r\n flightAwareDepartureTimeUTC: string | null;\r\n flightAwareDepartureDateLocal: string | null;\r\n flightAwareDepartureTimeLocal: string | null;\r\n\r\n flightAwareEstimatedDepartureDateUTC: string | null;\r\n flightAwareEstimatedDepartureTimeUTC: string | null;\r\n flightAwareEstimatedDepartureDateLocal: string | null;\r\n flightAwareEstimatedDepartureTimeLocal: string | null;\r\n enRouteTime?: string | null; \r\n expired: boolean;\r\n}\r\n\r\nexport interface FlightAwareFlightLeg extends FlightAwareTimes {\r\n flight?: Location[],\r\n plan?: Location[],\r\n position?: flightAwareMessages.Position,\r\n}\r\n\r\nexport const flightAwareTrackingPropTypeShape = {\r\n flight: PropTypes.arrayOf(locationPropType),\r\n plan: PropTypes.arrayOf(locationPropType),\r\n position: PropTypes.shape({\r\n messageType: PropTypes.oneOf([\"position\"]),\r\n heading: PropTypes.number,\r\n ...locationPropTypeShape\r\n })\r\n}\r\n\r\ninterface FlightAwareTrackingProps extends FlightAwareTimes{\r\n flightAwareHub?: FlightAwareHub,\r\n flightAwareFlightIds: string[],\r\n arrivalTimeZoneOffset?: number;\r\n departureTimeZoneOffset?: number;\r\n}\r\n\r\nexport type TrackedFlightLegProps = FlightAwareTrackingProps & Subtract
>\r\n\r\nexport function withFlightAwareTracking
(WrappedComponent: React.ComponentType
) {\r\n return class WithFlightAwareTracking extends React.Component, FlightAwareFlightLeg> {\r\n\r\n static propTypes = {\r\n flightAwareHub: PropTypes.instanceOf(FlightAwareHub),\r\n flightAwareFlightIds: PropTypes.arrayOf(PropTypes.string).isRequired,\r\n onUpdated: PropTypes.func\r\n }\r\n\r\n static displayName = `WithFlightAwareTracking(${WrappedComponent.displayName})`;\r\n\r\n state: FlightAwareFlightLeg = {\r\n flightAwareArrivalUTC: this.props.flightAwareArrivalUTC,\r\n flightAwareArrivalLocal: this.props.flightAwareArrivalLocal,\r\n flightAwareArrivalDateUTC: this.props.flightAwareArrivalDateUTC,\r\n flightAwareArrivalTimeUTC: this.props.flightAwareArrivalTimeUTC,\r\n flightAwareArrivalDateLocal: this.props.flightAwareArrivalDateLocal, \r\n flightAwareArrivalTimeLocal: this.props.flightAwareArrivalTimeLocal, \r\n flightAwareEstimatedArrivalUTC: this.props.flightAwareEstimatedArrivalUTC,\r\n flightAwareEstimatedArrivalLocal: this.props.flightAwareEstimatedArrivalLocal,\r\n flightAwareEstimatedArrivalDateUTC: this.props.flightAwareEstimatedArrivalDateUTC,\r\n flightAwareEstimatedArrivalTimeUTC: this.props.flightAwareEstimatedArrivalTimeUTC,\r\n flightAwareEstimatedArrivalDateLocal: this.props.flightAwareEstimatedArrivalDateLocal,\r\n flightAwareEstimatedArrivalTimeLocal: this.props.flightAwareEstimatedArrivalTimeLocal,\r\n\r\n flightAwareDepartureUTC: this.props.flightAwareDepartureUTC,\r\n flightAwareDepartureLocal: this.props.flightAwareDepartureLocal,\r\n flightAwareDepartureDateUTC: this.props.flightAwareDepartureDateUTC,\r\n flightAwareDepartureTimeUTC: this.props.flightAwareDepartureTimeUTC,\r\n flightAwareDepartureDateLocal: this.props.flightAwareDepartureDateLocal,\r\n flightAwareDepartureTimeLocal: this.props.flightAwareDepartureTimeLocal,\r\n flightAwareEstimatedDepartureUTC: this.props.flightAwareEstimatedDepartureUTC,\r\n flightAwareEstimatedDepartureLocal: this.props.flightAwareEstimatedDepartureLocal,\r\n flightAwareEstimatedDepartureDateUTC: this.props.flightAwareEstimatedDepartureDateUTC,\r\n flightAwareEstimatedDepartureTimeUTC: this.props.flightAwareEstimatedDepartureTimeUTC,\r\n flightAwareEstimatedDepartureDateLocal: this.props.flightAwareEstimatedDepartureDateLocal,\r\n flightAwareEstimatedDepartureTimeLocal: this.props.flightAwareEstimatedDepartureTimeLocal,\r\n\r\n expired: this.props.expired\r\n }\r\n\r\n componentDidMount() {\r\n this.props.flightAwareHub && this.props.flightAwareHub.listen(this.props.flightAwareFlightIds, this.onMessages);\r\n }\r\n\r\n componentWillUnmount() {\r\n this.setStateTimeout && clearTimeout(this.setStateTimeout);\r\n this.props.flightAwareHub && this.props.flightAwareHub.unlisten(this.props.flightAwareFlightIds, this.onMessages);\r\n }\r\n\r\n private nextState: {\r\n arrivalTime?: string;\r\n departureTime?: string;\r\n estimatedArrivalTime?: string;\r\n estimatedDepartureTime?: string;\r\n enRouteTime?: string;\r\n expired?: boolean;\r\n plan?: Location[];\r\n flight: Location[];\r\n position?: flightAwareMessages.Position;\r\n } = {\r\n flight: []\r\n };\r\n private setStateTimeout?: ReturnType;\r\n\r\n private debouncedSetState = () => {\r\n const {\r\n arrivalTime,\r\n departureTime,\r\n estimatedArrivalTime,\r\n estimatedDepartureTime,\r\n enRouteTime,\r\n expired,\r\n plan,\r\n flight: newFlight,\r\n position\r\n } = this.nextState;\r\n\r\n const {\r\n flightAwareArrivalUTC,\r\n flightAwareDepartureUTC,\r\n flightAwareEstimatedArrivalUTC,\r\n flightAwareEstimatedDepartureUTC, \r\n flight\r\n } = this.state;\r\n\r\n const {\r\n departureTimeZoneOffset,\r\n arrivalTimeZoneOffset\r\n } = this.props;\r\n\r\n if (estimatedArrivalTime) {\r\n let estimatedArrivalTimeUTC = moment.utc(estimatedArrivalTime);\r\n if (!flightAwareEstimatedArrivalUTC || !estimatedArrivalTimeUTC.isSame(flightAwareEstimatedArrivalUTC)) {\r\n var estimatedArrivalTimeLocal: moment.Moment | null = null;\r\n if (arrivalTimeZoneOffset)\r\n estimatedArrivalTimeLocal = moment.utc(estimatedArrivalTime).utcOffset(arrivalTimeZoneOffset!)\r\n var arrivalTimeStringsUTC = getDateStrings(estimatedArrivalTimeUTC);\r\n var arrivalTimeStringsLocal = getDateStrings(estimatedArrivalTimeLocal);\r\n this.setState({\r\n flightAwareEstimatedArrivalUTC: estimatedArrivalTimeUTC,\r\n flightAwareEstimatedArrivalLocal: estimatedArrivalTimeLocal,\r\n flightAwareEstimatedArrivalDateUTC: arrivalTimeStringsUTC.date,\r\n flightAwareEstimatedArrivalTimeUTC: arrivalTimeStringsUTC.time,\r\n flightAwareEstimatedArrivalDateLocal: arrivalTimeStringsLocal.date,\r\n flightAwareEstimatedArrivalTimeLocal: arrivalTimeStringsLocal.time\r\n })\r\n }\r\n }\r\n\r\n if (estimatedDepartureTime) {\r\n let estimatedDepartureTimeUTC = moment.utc(estimatedDepartureTime);\r\n if (!flightAwareEstimatedDepartureUTC || !estimatedDepartureTimeUTC.isSame(flightAwareEstimatedDepartureUTC)) {\r\n var estimatedDepartureTimeLocal: moment.Moment | null = null;\r\n if (departureTimeZoneOffset)\r\n estimatedDepartureTimeLocal = moment.utc(estimatedDepartureTime).utcOffset(departureTimeZoneOffset!);\r\n var departureTimeStringsUTC = getDateStrings(estimatedDepartureTimeUTC);\r\n var departureTimeStringsLocal = getDateStrings(estimatedDepartureTimeLocal);\r\n this.setState({\r\n flightAwareEstimatedDepartureUTC: estimatedDepartureTimeUTC,\r\n flightAwareEstimatedDepartureLocal: estimatedDepartureTimeLocal,\r\n flightAwareEstimatedDepartureDateUTC: departureTimeStringsUTC.date,\r\n flightAwareEstimatedDepartureTimeUTC: departureTimeStringsUTC.time,\r\n flightAwareEstimatedDepartureDateLocal: departureTimeStringsLocal.date,\r\n flightAwareEstimatedDepartureTimeLocal: departureTimeStringsLocal.time\r\n })\r\n }\r\n }\r\n\r\n if (arrivalTime) {\r\n let arrivalTimeUTC = moment.utc(arrivalTime);\r\n if (!flightAwareArrivalUTC || !arrivalTimeUTC.isSame(flightAwareArrivalUTC)) {\r\n var arrivalTimeLocal: moment.Moment | null = null;\r\n if (arrivalTimeZoneOffset)\r\n arrivalTimeLocal = moment.utc(arrivalTime).utcOffset(arrivalTimeZoneOffset!);\r\n var arrivalTimeStringsUTC = getDateStrings(arrivalTimeUTC);\r\n var arrivalTimeStringsLocal = getDateStrings(arrivalTimeLocal);\r\n this.setState({\r\n flightAwareArrivalUTC: arrivalTimeUTC,\r\n flightAwareArrivalLocal: arrivalTimeLocal,\r\n flightAwareArrivalDateUTC: arrivalTimeStringsUTC.date,\r\n flightAwareArrivalTimeUTC: arrivalTimeStringsUTC.time,\r\n flightAwareArrivalDateLocal: arrivalTimeStringsLocal.date,\r\n flightAwareArrivalTimeLocal: arrivalTimeStringsLocal.time\r\n })\r\n }\r\n }\r\n\r\n if (departureTime) {\r\n let departureTimeUTC = moment.utc(departureTime);\r\n if (!flightAwareDepartureUTC || !departureTimeUTC.isSame(flightAwareDepartureUTC)) {\r\n var departureTimeLocal: moment.Moment | null = null;\r\n if (departureTimeZoneOffset)\r\n departureTimeLocal = moment.utc(departureTime).utcOffset(this.props.departureTimeZoneOffset!);\r\n var departureTimeStringsUTC = getDateStrings(departureTimeUTC);\r\n var departureTimeStringsLocal = getDateStrings(departureTimeLocal);\r\n this.setState({\r\n flightAwareDepartureUTC: departureTimeUTC,\r\n flightAwareDepartureLocal: departureTimeLocal,\r\n flightAwareDepartureDateUTC: departureTimeStringsUTC.date,\r\n flightAwareDepartureTimeUTC: departureTimeStringsUTC.time,\r\n flightAwareDepartureDateLocal: departureTimeStringsLocal.date,\r\n flightAwareDepartureTimeLocal: departureTimeStringsLocal.time\r\n })\r\n }\r\n }\r\n\r\n if (expired) {\r\n this.setState({ expired: expired });\r\n }\r\n\r\n if (enRouteTime) {\r\n this.setState({ enRouteTime });\r\n }\r\n\r\n if (plan) {\r\n this.setState({ plan });\r\n }\r\n\r\n if (position) {\r\n this.setState({ position });\r\n }\r\n\r\n if (newFlight.length > 0) {\r\n //// NOT PUSH. The value of the property needs to change, not the contents.\r\n this.setState({ flight: (flight || []).concat(newFlight) })\r\n }\r\n\r\n this.nextState = {\r\n flight:[]\r\n };\r\n this.setStateTimeout = undefined;\r\n }\r\n\r\n private debounceSetState(state: {\r\n arrivalTimeUTC?: string;\r\n departureTimeUTC?: string;\r\n estimatedArrivalTimeUTC?: string;\r\n estimatedDepartureTimeUTC?: string;\r\n enRouteTime?: string;\r\n expired?: boolean;\r\n position?: flightAwareMessages.Position;\r\n plan?: Location[];\r\n flight?: Location;\r\n }) {\r\n this.setStateTimeout && clearTimeout(this.setStateTimeout);\r\n const {\r\n flight,\r\n ...other\r\n } = state;\r\n\r\n if (flight) { \r\n this.nextState.flight.push(flight);\r\n }\r\n\r\n this.nextState = { ...this.nextState, ...other };\r\n this.setStateTimeout = setTimeout(this.debouncedSetState, 200);\r\n }\r\n\r\n componentDidUpdate(prevProps: FlightAwareTrackingProps) {\r\n if (this.props.flightAwareHub !== prevProps.flightAwareHub) {\r\n prevProps.flightAwareHub && prevProps.flightAwareHub.unlisten(this.props.flightAwareFlightIds, this.onMessages);\r\n this.props.flightAwareHub && this.props.flightAwareHub.listen(this.props.flightAwareFlightIds, this.onMessages);\r\n }\r\n }\r\n\r\n private checkExpired() {\r\n if (this.state.flightAwareArrivalUTC) {\r\n var arrivalPlus30 = this.state.flightAwareArrivalUTC.add(30, 'minutes'); \r\n if (moment.utc() > arrivalPlus30) {\r\n this.setExpired();\r\n } else {\r\n var duration = moment.duration(arrivalPlus30.diff(moment.utc()));\r\n setTimeout(\r\n this.setExpired,\r\n duration.asMilliseconds());\r\n }\r\n }\r\n }\r\n\r\n private setExpired() {\r\n this.debounceSetState({ expired: true });\r\n }\r\n\r\n private onMessages = (messages: flightAwareMessages.FlightMessage[]) => {\r\n messages.forEach(m => {\r\n if (m.messageType !== flightAwareMessages.MessageType.Onblock && m.messageType !== flightAwareMessages.MessageType.Offblock) {\r\n if (m.estimatedArrivalTime) {\r\n this.debounceSetState({ estimatedArrivalTimeUTC: m.estimatedArrivalTime })\r\n }\r\n\r\n if (m.estimatedDepartureTime) {\r\n this.debounceSetState({ estimatedDepartureTimeUTC: m.estimatedDepartureTime })\r\n } \r\n }\r\n\r\n\r\n\r\n if (m.messageType === flightAwareMessages.MessageType.Flightplan && m.enRouteTime) {\r\n this.debounceSetState({ enRouteTime : m.enRouteTime })\r\n }\r\n\r\n if (m.messageType === flightAwareMessages.MessageType.Arrival) {\r\n this.debounceSetState({ arrivalTimeUTC: m.arrivalTime });\r\n\r\n this.checkExpired();\r\n }\r\n\r\n if (m.messageType === flightAwareMessages.MessageType.Departure) {\r\n this.debounceSetState({ departureTimeUTC: m.departureTime });\r\n }\r\n\r\n switch (m.messageType) {\r\n case flightAwareMessages.MessageType.Flightplan:\r\n if (m.waypoints) {\r\n this.debounceSetState({\r\n plan: m.waypoints\r\n })\r\n }\r\n break;\r\n case flightAwareMessages.MessageType.Position:\r\n this.debounceSetState({\r\n position: m\r\n });\r\n if (m.updateType !== flightAwareMessages.UpdateType.Estimated) {\r\n this.debounceSetState({ flight: { latitude: m.latitude, longitude: m.longitude } });\r\n }\r\n break;\r\n }\r\n });\r\n }\r\n\r\n render() {\r\n const props = {\r\n ...this.props,\r\n ...this.state\r\n };\r\n\r\n //https://github.com/Microsoft/TypeScript/issues/28938\r\n // Have to cast props as any since it can't re-merge to the generic P.\r\n return \r\n }\r\n }\r\n}","import _extends from \"@babel/runtime/helpers/esm/extends\";\nimport _objectWithoutPropertiesLoose from \"@babel/runtime/helpers/esm/objectWithoutPropertiesLoose\";\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames';\nimport { mapToCssModules, tagPropType } from './utils';\nvar propTypes = {\n tag: tagPropType,\n fluid: PropTypes.bool,\n className: PropTypes.string,\n cssModule: PropTypes.object\n};\nvar defaultProps = {\n tag: 'div'\n};\n\nvar Container = function Container(props) {\n var className = props.className,\n cssModule = props.cssModule,\n fluid = props.fluid,\n Tag = props.tag,\n attributes = _objectWithoutPropertiesLoose(props, [\"className\", \"cssModule\", \"fluid\", \"tag\"]);\n\n var classes = mapToCssModules(classNames(className, fluid ? 'container-fluid' : 'container'), cssModule);\n return React.createElement(Tag, _extends({}, attributes, {\n className: classes\n }));\n};\n\nContainer.propTypes = propTypes;\nContainer.defaultProps = defaultProps;\nexport default Container;","import _extends from \"@babel/runtime/helpers/esm/extends\";\nimport _objectWithoutPropertiesLoose from \"@babel/runtime/helpers/esm/objectWithoutPropertiesLoose\";\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames';\nimport { mapToCssModules, tagPropType } from './utils';\nvar propTypes = {\n tag: tagPropType,\n noGutters: PropTypes.bool,\n className: PropTypes.string,\n cssModule: PropTypes.object,\n form: PropTypes.bool\n};\nvar defaultProps = {\n tag: 'div'\n};\n\nvar Row = function Row(props) {\n var className = props.className,\n cssModule = props.cssModule,\n noGutters = props.noGutters,\n Tag = props.tag,\n form = props.form,\n attributes = _objectWithoutPropertiesLoose(props, [\"className\", \"cssModule\", \"noGutters\", \"tag\", \"form\"]);\n\n var classes = mapToCssModules(classNames(className, noGutters ? 'no-gutters' : null, form ? 'form-row' : 'row'), cssModule);\n return React.createElement(Tag, _extends({}, attributes, {\n className: classes\n }));\n};\n\nRow.propTypes = propTypes;\nRow.defaultProps = defaultProps;\nexport default Row;","import _extends from \"@babel/runtime/helpers/esm/extends\";\nimport _objectWithoutPropertiesLoose from \"@babel/runtime/helpers/esm/objectWithoutPropertiesLoose\";\nimport isobject from 'lodash.isobject';\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames';\nimport { mapToCssModules, tagPropType } from './utils';\nvar colWidths = ['xs', 'sm', 'md', 'lg', 'xl'];\nvar stringOrNumberProp = PropTypes.oneOfType([PropTypes.number, PropTypes.string]);\nvar columnProps = PropTypes.oneOfType([PropTypes.bool, PropTypes.number, PropTypes.string, PropTypes.shape({\n size: PropTypes.oneOfType([PropTypes.bool, PropTypes.number, PropTypes.string]),\n order: stringOrNumberProp,\n offset: stringOrNumberProp\n})]);\nvar propTypes = {\n tag: tagPropType,\n xs: columnProps,\n sm: columnProps,\n md: columnProps,\n lg: columnProps,\n xl: columnProps,\n className: PropTypes.string,\n cssModule: PropTypes.object,\n widths: PropTypes.array\n};\nvar defaultProps = {\n tag: 'div',\n widths: colWidths\n};\n\nvar getColumnSizeClass = function getColumnSizeClass(isXs, colWidth, colSize) {\n if (colSize === true || colSize === '') {\n return isXs ? 'col' : \"col-\" + colWidth;\n } else if (colSize === 'auto') {\n return isXs ? 'col-auto' : \"col-\" + colWidth + \"-auto\";\n }\n\n return isXs ? \"col-\" + colSize : \"col-\" + colWidth + \"-\" + colSize;\n};\n\nvar Col = function Col(props) {\n var className = props.className,\n cssModule = props.cssModule,\n widths = props.widths,\n Tag = props.tag,\n attributes = _objectWithoutPropertiesLoose(props, [\"className\", \"cssModule\", \"widths\", \"tag\"]);\n\n var colClasses = [];\n widths.forEach(function (colWidth, i) {\n var columnProp = props[colWidth];\n delete attributes[colWidth];\n\n if (!columnProp && columnProp !== '') {\n return;\n }\n\n var isXs = !i;\n\n if (isobject(columnProp)) {\n var _classNames;\n\n var colSizeInterfix = isXs ? '-' : \"-\" + colWidth + \"-\";\n var colClass = getColumnSizeClass(isXs, colWidth, columnProp.size);\n colClasses.push(mapToCssModules(classNames((_classNames = {}, _classNames[colClass] = columnProp.size || columnProp.size === '', _classNames[\"order\" + colSizeInterfix + columnProp.order] = columnProp.order || columnProp.order === 0, _classNames[\"offset\" + colSizeInterfix + columnProp.offset] = columnProp.offset || columnProp.offset === 0, _classNames)), cssModule));\n } else {\n var _colClass = getColumnSizeClass(isXs, colWidth, columnProp);\n\n colClasses.push(_colClass);\n }\n });\n\n if (!colClasses.length) {\n colClasses.push('col');\n }\n\n var classes = mapToCssModules(classNames(className, colClasses), cssModule);\n return React.createElement(Tag, _extends({}, attributes, {\n className: classes\n }));\n};\n\nCol.propTypes = propTypes;\nCol.defaultProps = defaultProps;\nexport default Col;","import React from 'react';\r\nimport { Container, Row, Col } from 'reactstrap';\r\nimport { FlightLegViewModel } from '../../models/flightleg';\r\nimport { withFlightAwareTracking, FlightAwareFlightLeg } from '../../signalr/withflightawaretracking';\r\n\r\ninterface FlightInfoProps extends FlightLegViewModel, FlightAwareFlightLeg {}\r\n\r\nexport const FlightInfo: React.FC = (\r\n {\r\n organizationLogo,\r\n departure,\r\n departureCity,\r\n departureState,\r\n departureCountry,\r\n arrival,\r\n arrivalCity,\r\n arrivalState,\r\n arrivalCountry,\r\n scheduledArrivalTimeUTC,\r\n scheduledArrivalTimeLocal,\r\n flightAwareArrivalTimeUTC,\r\n flightAwareArrivalTimeLocal, \r\n flightAwareEstimatedArrivalTimeUTC, \r\n flightAwareEstimatedArrivalTimeLocal, \r\n scheduledDepartureTimeUTC,\r\n scheduledDepartureTimeLocal,\r\n flightAwareDepartureTimeUTC,\r\n flightAwareDepartureTimeLocal,\r\n flightAwareEstimatedDepartureTimeUTC,\r\n flightAwareEstimatedDepartureTimeLocal,\r\n position,\r\n tailNumber,\r\n aircraftTypeCode,\r\n aircraftTypeName,\r\n enRouteTime\r\n }) => {\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n {organizationLogo &&
}`})
}\r\n
\r\n \r\n \r\n \r\n \r\n {tailNumber}\r\n
\r\n \r\n
\r\n {aircraftTypeName} ({aircraftTypeCode})\r\n
\r\n {enRouteTime &&\r\n
\r\n Time Enroute: {enRouteTime}\r\n
\r\n }\r\n
\r\n \r\n \r\n \r\n \r\n {departure || '????' }\r\n
\r\n \r\n
\r\n {departureCity} {departureState} {departureCountry}\r\n
\r\n
\r\n {flightAwareDepartureTimeUTC && <> Departed: {flightAwareDepartureTimeLocal && <>{flightAwareDepartureTimeLocal} Local / >}{flightAwareDepartureTimeUTC}>}\r\n {!flightAwareDepartureTimeUTC && flightAwareEstimatedDepartureTimeUTC && <>Est. Departure: {flightAwareEstimatedDepartureTimeLocal && <>{flightAwareEstimatedDepartureTimeLocal} Local / >}{flightAwareEstimatedDepartureTimeUTC} UTC>}\r\n {!flightAwareDepartureTimeUTC && !flightAwareEstimatedDepartureTimeUTC && scheduledDepartureTimeUTC && <>Sch. Departure: {scheduledDepartureTimeLocal && <>{scheduledDepartureTimeLocal} Local / >} {scheduledDepartureTimeUTC} UTC>}\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n {arrival || '????'}\r\n
\r\n \r\n
\r\n {arrivalCity} {arrivalState} {arrivalCountry}\r\n
\r\n
\r\n {flightAwareArrivalTimeUTC && <>Arrived: {flightAwareArrivalTimeLocal && <>{flightAwareArrivalTimeLocal} Local / >}{flightAwareArrivalTimeUTC} UTC>}\r\n {!flightAwareArrivalTimeUTC && flightAwareEstimatedArrivalTimeUTC && <>Est. Arrival: {flightAwareEstimatedArrivalTimeLocal && <>{flightAwareEstimatedArrivalTimeLocal} Local / >}{flightAwareEstimatedArrivalTimeUTC} UTC>}\r\n {!flightAwareArrivalTimeUTC && !flightAwareEstimatedArrivalTimeUTC && scheduledArrivalTimeUTC && <>Sch. Arrival: {scheduledArrivalTimeLocal && <>{scheduledArrivalTimeLocal} Local / >}{scheduledArrivalTimeUTC} UTC>}\r\n
\r\n
\r\n \r\n
\r\n \r\n );\r\n}\r\n\r\nexport const TrackedFlightInfo = withFlightAwareTracking(FlightInfo);","import React from 'react';\r\nimport { Container, Row, Col } from 'reactstrap';\r\n\r\nexport const FlightExpired: React.FC = () => {\r\n return (\r\n \r\n \r\n \r\n Tracking information is no longer available for this flight.
\r\n \r\n
\r\n \r\n );\r\n}","import React from \"react\";\r\nimport PropTypes from \"prop-types\";\r\n\r\ninterface ApiProps {\r\n apiKey: string\r\n}\r\n\r\nconst CALLBACK_NAME = '__AvianisGoogleMapsApiCallback';\r\n\r\nexport class Api extends React.Component {\r\n\r\n static propTypes = {\r\n apiKey: PropTypes.string.isRequired\r\n }\r\n\r\n static displayName = \"GoogleMaps.Api\";\r\n\r\n private addScript = false;\r\n\r\n constructor(props: ApiProps) {\r\n super(props);\r\n if (Api.loaded) {\r\n console.warn(\"Attempting to load Google Maps API more than once on this page.\");\r\n } else {\r\n this.addScript = true;\r\n Api.loaded = new Promise((resolve) => {\r\n (window as any)[CALLBACK_NAME]= () => {\r\n resolve();\r\n }\r\n });\r\n }\r\n }\r\n\r\n componentDidMount() {\r\n if (this.addScript) {\r\n var script = document.createElement(\"script\");\r\n let keyParam = (this.props.apiKey && `key=${this.props.apiKey}&`) || \"\";\r\n\r\n script.src = `https://maps.googleapis.com/maps/api/js?${keyParam}callback=${CALLBACK_NAME}`;\r\n script.async = script.defer = true;\r\n\r\n document.body.appendChild(script);\r\n }\r\n }\r\n\r\n public static loaded: Promise;\r\n\r\n render(): null { return null; }\r\n}\r\n","/*global google*/\r\n\r\n// Utilities for looking at zoom vs bounds\r\ntype size = { width: number; height: number };\r\n\r\nexport function getZoomLevelForBounds(bounds: google.maps.LatLngBounds, mapDim: size) {\r\n var WORLD_DIM = { height: 256, width: 256 };\r\n var ZOOM_MAX = 21;\r\n\r\n function latRad(lat: number) {\r\n var sin = Math.sin(lat * Math.PI / 180);\r\n var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;\r\n return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;\r\n }\r\n\r\n function zoom(mapPx: number, worldPx: number, fraction: number) {\r\n return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);\r\n }\r\n\r\n var ne = bounds.getNorthEast();\r\n var sw = bounds.getSouthWest();\r\n\r\n var latFraction = (latRad(ne.lat()) - latRad(sw.lat())) / Math.PI;\r\n\r\n var lngDiff = ne.lng() - sw.lng();\r\n var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;\r\n\r\n var latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction);\r\n var lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction);\r\n\r\n return Math.min(latZoom, lngZoom, ZOOM_MAX);\r\n}\r\n\r\nexport function getBoundsForZoomLevel(map: google.maps.Map, zoom: number) {\r\n var projection = map.getProjection();\r\n var center = map.getCenter();\r\n var div = map.getDiv();\r\n var zoomFactor = Math.pow(2, zoom) * 2;\r\n var dw = div.clientWidth / zoomFactor;\r\n var dh = div.clientHeight / zoomFactor;\r\n var cpx = projection.fromLatLngToPoint(center);\r\n\r\n return new google.maps.LatLngBounds(\r\n projection.fromPointToLatLng(new google.maps.Point(cpx.x - dw, cpx.y + dh)),\r\n projection.fromPointToLatLng(new google.maps.Point(cpx.x + dw, cpx.y - dh))\r\n );\r\n}\r\n\r\nexport function getPixelBounds(map: google.maps.Map, bounds: google.maps.LatLngBounds): { x: number, y: number, width: number, height: number }\r\nexport function getPixelBounds(map: google.maps.Map, southWest: google.maps.LatLng, northEast: google.maps.LatLng): { x: number, y: number, width: number, height: number }\r\n\r\nexport function getPixelBounds(map: google.maps.Map, arg1: google.maps.LatLng | google.maps.LatLngBounds, northEast?: google.maps.LatLng) {\r\n var southWest: google.maps.LatLng;\r\n if (arg1 instanceof google.maps.LatLngBounds) {\r\n southWest = arg1.getSouthWest();\r\n northEast = arg1.getNorthEast();\r\n } else {\r\n southWest = arg1;\r\n northEast = northEast!;\r\n }\r\n\r\n var projection = map.getProjection();\r\n var zoomFactor = Math.pow(2, map.getZoom());\r\n\r\n var llpx = projection.fromLatLngToPoint(southWest);\r\n var urpx = projection.fromLatLngToPoint(northEast);\r\n\r\n return { x: llpx.x * zoomFactor, y: urpx.y * zoomFactor, width: (urpx.x - llpx.x) * zoomFactor, height: (llpx.y - urpx.y) * zoomFactor }\r\n}\r\n\r\n// Enums that match google maps enums, but are declare here so we can use them before loading the Maps API script\r\n\r\nexport enum ControlPosition {\r\n /** Elements are positioned in the center of the bottom row. */\r\n BOTTOM_CENTER,\r\n /**\r\n * Elements are positioned in the bottom left and flow towards the middle.\r\n * Elements are positioned to the right of the Google logo.\r\n */\r\n BOTTOM_LEFT,\r\n /**\r\n * Elements are positioned in the bottom right and flow towards the middle.\r\n * Elements are positioned to the left of the copyrights.\r\n */\r\n BOTTOM_RIGHT,\r\n /**\r\n * Elements are positioned on the left, above bottom-left elements, and flow\r\n * upwards.\r\n */\r\n LEFT_BOTTOM,\r\n /** Elements are positioned in the center of the left side. */\r\n LEFT_CENTER,\r\n /**\r\n * Elements are positioned on the left, below top-left elements, and flow\r\n * downwards.\r\n */\r\n LEFT_TOP,\r\n /**\r\n * Elements are positioned on the right, above bottom-right elements, and\r\n * flow upwards.\r\n */\r\n RIGHT_BOTTOM,\r\n /** Elements are positioned in the center of the right side. */\r\n RIGHT_CENTER,\r\n /**\r\n Elements are positioned on the right, below top-right elements, and flow\r\n downwards.\r\n */\r\n RIGHT_TOP,\r\n /** Elements are positioned in the center of the top row. */\r\n TOP_CENTER,\r\n /** Elements are positioned in the top left and flow towards the middle. */\r\n TOP_LEFT,\r\n /** Elements are positioned in the top right and flow towards the middle. */\r\n TOP_RIGHT\r\n}\r\n\r\nexport function getControlPosition(position: ControlPosition): google.maps.ControlPosition;\r\nexport function getControlPosition(position?: ControlPosition): google.maps.ControlPosition | undefined;\r\n\r\nexport function getControlPosition(position?: ControlPosition) {\r\n return position === undefined ? undefined : google.maps.ControlPosition[ControlPosition[position] as keyof typeof google.maps.ControlPosition];\r\n}\r\n\r\nexport enum ZoomControlStyle {\r\n DEFAULT, LARGE, SMALL\r\n}\r\n\r\nexport const getZoomControlStyle = (style?: ZoomControlStyle) => style === undefined ? undefined : google.maps.ZoomControlStyle[ZoomControlStyle[style] as keyof typeof google.maps.ZoomControlStyle];\r\n\r\n\r\nexport enum Animation { BOUNCE, DROP, NONE }\r\nexport const getAnimation = (animation?: Animation) => (animation === undefined || animation === Animation.NONE) ? undefined : google.maps.Animation[Animation[animation] as keyof typeof google.maps.Animation];\r\n\r\n\r\n","/*global google*/\r\n\r\nimport * as React from \"react\";\r\nimport * as PropTypes from \"prop-types\";\r\nimport { Api } from \"./api\";\r\nimport { ControlPosition, ZoomControlStyle, getControlPosition, getZoomControlStyle } from \"./util\";\r\nimport { Location, locationPropType, mapLocation } from \"./location\";\r\n\r\ninterface ZoomControlOptions {\r\n position?: ControlPosition;\r\n style?: ZoomControlStyle;\r\n}\r\n\r\ninterface StreetViewControlOptions {\r\n position?: ControlPosition;\r\n}\r\n\r\nexport interface MapComponentProps {\r\n map: google.maps.Map;\r\n}\r\n\r\ninterface MapProps extends React.HTMLAttributes {\r\n children?: (map: google.maps.Map) => React.ReactNode;\r\n options: Replace\r\n}\r\n\r\ninterface MapState {\r\n map: google.maps.Map;\r\n}\r\n\r\nexport class Map extends React.Component {\r\n\r\n // We'll set this later, once the GMaps script has loaded.\r\n static propTypes: PropTypes.ValidationMap;\r\n\r\n static displayName = \"GoogleMaps.Map\";\r\n\r\n private mapRef = React.createRef();\r\n private unmounted?: boolean;\r\n\r\n componentDidMount() {\r\n if (!Api) {\r\n throw new Error(\"No GoogleMaps.Api script loader instance found!\");\r\n }\r\n Api.loaded.then(() => this.init());\r\n }\r\n\r\n componentWillUnmount() {\r\n this.state && google.maps.event.clearInstanceListeners(this.state.map);\r\n this.unmounted = true;\r\n }\r\n\r\n init() {\r\n\r\n if (this.unmounted) return;\r\n\r\n Map.propTypes = {\r\n options: PropTypes.shape({\r\n center: PropTypes.oneOfType([\r\n PropTypes.instanceOf(google.maps.LatLng),\r\n PropTypes.shape({ lat: PropTypes.number.isRequired, lng: PropTypes.number.isRequired }),\r\n locationPropType\r\n ]),\r\n zoom: PropTypes.number\r\n })\r\n }\r\n\r\n let map = new google.maps.Map(this.mapRef.current, this.getOptions());\r\n\r\n // Bounds changed will fire when we get our bounds the first time\r\n // We'll use that to consider our map ready for our children.\r\n google.maps.event.addListenerOnce(map, 'bounds_changed', () => this.setState({ map }));\r\n }\r\n\r\n componentDidUpdate(prevProps: MapProps) {\r\n if (this.state) {\r\n const {\r\n options: prevOptions,\r\n } = prevProps;\r\n\r\n const {\r\n options\r\n } = this.props;\r\n\r\n const {\r\n map\r\n } = this.state;\r\n\r\n if (options.center !== prevOptions.center) {\r\n if (options.center) {\r\n map.setCenter(mapLocation(options.center));\r\n google.maps.event.trigger(map, \"center_set\");\r\n }\r\n }\r\n\r\n if (options.styles !== prevOptions.styles) {\r\n map.setOptions({ styles: options.styles });\r\n }\r\n }\r\n }\r\n\r\n render() {\r\n const {\r\n children,\r\n options,\r\n ...divProps\r\n } = this.props;\r\n\r\n return (\r\n <>\r\n \r\n
\r\n {this.state && this.props.children && this.props.children(this.state.map)}\r\n >\r\n )\r\n }\r\n\r\n private getOptions(): google.maps.MapOptions {\r\n const {\r\n options: {\r\n center,\r\n zoomControlOptions,\r\n streetViewControlOptions,\r\n ...options\r\n }\r\n } = this.props;\r\n\r\n return {\r\n zoomControlOptions: zoomControlOptions && { position: getControlPosition(zoomControlOptions.position), style: getZoomControlStyle(zoomControlOptions.style) },\r\n streetViewControlOptions: streetViewControlOptions && { position: getControlPosition(streetViewControlOptions.position) },\r\n center: center && mapLocation(center),\r\n ...options\r\n };\r\n }\r\n}\r\n","import * as React from 'react';\r\nimport * as ReactDOM from 'react-dom';\r\nimport * as PropTypes from 'prop-types';\r\nimport { MapComponentProps } from './map';\r\nimport { ControlPosition, getControlPosition } from './util';\r\n\r\ninterface ControlProps extends MapComponentProps {\r\n position: ControlPosition;\r\n content: (() => React.ReactNode) | React.ReactNode;\r\n}\r\n\r\nexport class Control extends React.PureComponent {\r\n\r\n static propTypes = {\r\n //TODO: MapComponentProps\r\n position: PropTypes.oneOf([\r\n ControlPosition.TOP_LEFT, ControlPosition.TOP_CENTER, ControlPosition.TOP_RIGHT,\r\n ControlPosition.LEFT_BOTTOM, ControlPosition.LEFT_CENTER, ControlPosition.LEFT_TOP,\r\n ControlPosition.RIGHT_BOTTOM, ControlPosition.RIGHT_CENTER, ControlPosition.RIGHT_TOP,\r\n ControlPosition.BOTTOM_LEFT, ControlPosition.BOTTOM_CENTER, ControlPosition.BOTTOM_RIGHT\r\n ]).isRequired,\r\n content: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired\r\n }\r\n\r\n static displayName: \"GoogleMaps.Control\";\r\n\r\n private readonly div: HTMLDivElement = document.createElement('div');\r\n\r\n componentDidMount() {\r\n this.props.map.controls[getControlPosition(this.props.position)].push(this.div);\r\n }\r\n\r\n componentDidUpdate(prevProps: ControlProps) {\r\n let prevPosition = getControlPosition(prevProps.position);\r\n let position = getControlPosition(this.props.position);\r\n\r\n if (position !== prevPosition) {\r\n let map = this.props.map;\r\n let oldIndex = map.controls[prevPosition].getArray().indexOf(this.div);\r\n if (oldIndex >= 0)\r\n map.controls[prevPosition].removeAt(oldIndex);\r\n this.props.map.controls[getControlPosition(this.props.position)].push(this.div);\r\n }\r\n }\r\n\r\n render() {\r\n const content = this.props.content instanceof Function ? this.props.content() : this.props.content;\r\n return ReactDOM.createPortal(content, this.div);\r\n }\r\n}\r\n","import * as React from \"react\";\r\nimport { DefaultStyles } from \"./styles\";\r\nimport { Location } from '../googlemaps/location';\r\nimport { Map as GoogleMap } from '../googlemaps/map';\r\nimport { Control as MapControl } from '../googlemaps/control';\r\nimport { ControlPosition as MapControlPosition } from '../googlemaps/util';\r\n\r\ninterface BaseMapProps extends React.HTMLAttributes {\r\n center?: Location\r\n zoom?: number;\r\n styles?: google.maps.MapTypeStyle[];\r\n onClose?: () => void;\r\n children?: (map: google.maps.Map) => React.ReactNode;\r\n}\r\n\r\ninterface BaseMapState {\r\n\r\n}\r\n\r\nconst defaultProps = {\r\n styles: DefaultStyles,\r\n};\r\n\r\nconst UIControl: React.FC> = props => {\r\n\r\n const {\r\n style,\r\n ...other\r\n } = props;\r\n\r\n return ({props.children} div>)\r\n}\r\nUIControl.displayName = \"Map.BaseMap.UIControl\";\r\n\r\nconst TextControl: React.FC
> = props => {\r\n const {\r\n style,\r\n ...other\r\n } = props;\r\n return ({props.children} div>)\r\n}\r\nTextControl.displayName = \"Map.BaseMap.TextControl\";\r\n\r\nexport const BaseMap: React.ComponentType
= class extends React.PureComponent{\r\n\r\n static defaultProps = defaultProps;\r\n\r\n private readonly closeControl =\r\n this.props.onClose && this.props.onClose()} style={{ marginRight: '10px', width: '40px' }}>\r\n \r\n \r\n \r\n \r\n\r\n\r\n render() {\r\n const {\r\n center,\r\n zoom,\r\n styles,\r\n onClose,\r\n ...divProps\r\n } = this.props;\r\n\r\n return \r\n {(map) =>\r\n <>\r\n {this.props.onClose && }\r\n {this.props.children && this.props.children(map)}\r\n >\r\n }\r\n \r\n }\r\n}","export const DefaultStyles: google.maps.MapTypeStyle[] = [\r\n {\r\n \"featureType\": \"all\",\r\n \"elementType\": \"geometry\",\r\n \"stylers\": [\r\n {\r\n \"color\": \"#55554f\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"all\",\r\n \"elementType\": \"labels.text.fill\",\r\n \"stylers\": [\r\n {\r\n \"gamma\": 0.01\r\n },\r\n {\r\n \"lightness\": 20\r\n },\r\n {\r\n \"saturation\": 19\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"all\",\r\n \"elementType\": \"labels.text.stroke\",\r\n \"stylers\": [\r\n {\r\n \"saturation\": -31\r\n },\r\n {\r\n \"lightness\": -33\r\n },\r\n {\r\n \"weight\": 2\r\n },\r\n {\r\n \"gamma\": 0.8\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"all\",\r\n \"elementType\": \"labels.icon\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"on\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative.country\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative.province\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative.locality\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n },\r\n {\r\n \"color\": \"#ffffff\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative.neighborhood\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"on\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative.land_parcel\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"on\"\r\n },\r\n {\r\n \"color\": \"#ff9500\"\r\n },\r\n {\r\n \"lightness\": 48\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape\",\r\n \"elementType\": \"geometry\",\r\n \"stylers\": [\r\n {\r\n \"lightness\": 30\r\n },\r\n {\r\n \"saturation\": 30\r\n },\r\n {\r\n \"color\": \"#55554f\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"hue\": \"#ff0000\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape.man_made\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"on\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape.natural\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"on\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape.natural.landcover\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape.natural.terrain\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi\",\r\n \"elementType\": \"geometry\",\r\n \"stylers\": [\r\n {\r\n \"saturation\": 20\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi.attraction\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi.attraction\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi.business\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi.government\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi.park\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi.park\",\r\n \"elementType\": \"geometry\",\r\n \"stylers\": [\r\n {\r\n \"lightness\": 20\r\n },\r\n {\r\n \"saturation\": -20\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"color\": \"#252834\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road\",\r\n \"elementType\": \"geometry\",\r\n \"stylers\": [\r\n {\r\n \"lightness\": 10\r\n },\r\n {\r\n \"saturation\": -30\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road\",\r\n \"elementType\": \"geometry.stroke\",\r\n \"stylers\": [\r\n {\r\n \"saturation\": 25\r\n },\r\n {\r\n \"lightness\": 25\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"water\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"lightness\": -20\r\n },\r\n {\r\n \"color\": \"#252834\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"water\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n }\r\n];\r\n\r\n","/*global google*/\r\n\r\nimport * as React from \"react\";\r\nimport { MapComponentProps } from \"./map\";\r\nimport { Location, mapLocation } from \"./location\";\r\n\r\ninterface PolyLineProps extends Replace {\r\n}\r\n\r\nexport class PolyLine extends React.PureComponent{\r\n\r\n static propTypes = {\r\n //TODO: MapComponentProps\r\n }\r\n\r\n static displayName = \"GoogleMaps.PolyLine\"\r\n\r\n private polyLine?: google.maps.Polyline;\r\n\r\n componentDidMount() {\r\n this.polyLine = new google.maps.Polyline(this.getOptions());\r\n }\r\n\r\n componentDidUpdate() {\r\n this.polyLine && this.polyLine.setOptions(this.getOptions());\r\n }\r\n\r\n componentWillUnmount() {\r\n this.polyLine && this.polyLine.setMap(null);\r\n }\r\n\r\n render(): null {\r\n return null;\r\n }\r\n\r\n private getOptions(): google.maps.PolylineOptions {\r\n const {\r\n children,\r\n path,\r\n ...props\r\n } = this.props;\r\n\r\n return {\r\n path: path && mapLocation(path),\r\n ...props\r\n }\r\n }\r\n}\r\n","/*global google*/\r\n\r\nimport * as React from \"react\";\r\nimport * as ReactDOM from \"react-dom\";\r\nimport * as PropTypes from \"prop-types\";\r\nimport { MapComponentProps } from \"./map\";\r\nimport { getOffsetFromParent } from \"../../util\";\r\nimport { Location, mapLocation } from \"./location\";\r\n\r\nexport enum RichMarkerPosition {\r\n TOP_LEFT = 1,\r\n TOP,\r\n TOP_RIGHT,\r\n LEFT,\r\n MIDDLE,\r\n RIGHT,\r\n BOTTOM_LEFT,\r\n BOTTOM,\r\n BOTTOM_RIGHT\r\n}\r\n\r\nexport interface PassedThroughMarkerProps {\r\n anchor?: RichMarkerPosition;\r\n position: Location;\r\n}\r\n\r\ninterface RichMarkerProps extends MapComponentProps, PassedThroughMarkerProps {\r\n content: React.RefForwardingComponent\r\n onClick?: () => void;\r\n}\r\n\r\ninterface RichMarkerOptions extends PassedThroughMarkerProps {\r\n position: google.maps.LatLng | google.maps.LatLngLiteral\r\n}\r\n\r\ndeclare class richMarker extends google.maps.OverlayView {\r\n setOptions(options: RichMarkerOptions): void;\r\n}\r\n\r\nexport class RichMarker extends React.Component>{\r\n\r\n static propTypes = {\r\n //TODO: MapComponentProps\r\n anchor: PropTypes.oneOf([\r\n RichMarkerPosition.TOP_LEFT, RichMarkerPosition.TOP, RichMarkerPosition.TOP_RIGHT,\r\n RichMarkerPosition.LEFT, RichMarkerPosition.MIDDLE, RichMarkerPosition.RIGHT,\r\n RichMarkerPosition.BOTTOM_LEFT, RichMarkerPosition.BOTTOM, RichMarkerPosition.BOTTOM_RIGHT\r\n ]),\r\n //TODO:\r\n //position:\r\n content: PropTypes.oneOfType([PropTypes.func, PropTypes.node])\r\n }\r\n\r\n static displayName = \"GoogleMaps.RichMarker\";\r\n\r\n private readonly onClick = () => this.props.onClick && this.props.onClick();\r\n\r\n private readonly content: HTMLDivElement = (() => {\r\n var content = document.createElement('div');\r\n content.addEventListener('click', this.onClick);\r\n return content;\r\n })();\r\n\r\n private marker?: richMarker;\r\n private contentRef = React.createRef();\r\n\r\n\r\n componentDidMount() {\r\n // Need to create class in here, because we need the google map scripts loaded.\r\n class richMarker extends google.maps.OverlayView {\r\n private markerWrapper: HTMLDivElement;\r\n\r\n constructor(options: RichMarkerOptions, private marker: HTMLDivElement, private markerAnchor?: React.RefObject) {\r\n super();\r\n this.setValues(options);\r\n this.markerWrapper = document.createElement(\"div\");\r\n this.markerWrapper.style.position = \"absolute\";\r\n this.markerWrapper.style.zIndex = '-999999';\r\n this.markerWrapper.appendChild(marker);\r\n }\r\n\r\n public setOptions(options: RichMarkerOptions) {\r\n this.setValues(options);\r\n }\r\n\r\n public getMarker() {\r\n return this.marker;\r\n }\r\n\r\n onAdd() {\r\n let panes = this.getPanes();\r\n panes.floatPane.appendChild(this.markerWrapper);\r\n }\r\n\r\n onRemove() {\r\n if (this.markerWrapper && this.markerWrapper.parentNode) {\r\n this.markerWrapper.parentNode.removeChild(this.markerWrapper);\r\n }\r\n }\r\n\r\n anchor_changed() {\r\n this.draw();\r\n }\r\n\r\n position_changed() {\r\n this.draw();\r\n }\r\n\r\n draw() {\r\n if (!this.markerWrapper) return;\r\n\r\n let projection = this.getProjection();\r\n if (!projection) return;\r\n\r\n let latLng = this.get('position');\r\n if (!(latLng instanceof google.maps.LatLng))\r\n latLng = new google.maps.LatLng(latLng.lat, latLng.lng);\r\n var pos = projection.fromLatLngToDivPixel(latLng);\r\n\r\n var offset = this.getOffset();\r\n this.markerWrapper.style.top = (pos.y + offset.height) + 'px';\r\n this.markerWrapper.style.left = (pos.x + offset.width) + 'px';\r\n\r\n var height = this.marker.offsetHeight;\r\n var width = this.marker.offsetWidth;\r\n\r\n if (width !== this.get('width')) {\r\n this.set('width', width);\r\n }\r\n\r\n if (height !== this.get('height')) {\r\n this.set('height', height);\r\n }\r\n }\r\n\r\n private getOffset() {\r\n let offset = new google.maps.Size(0, 0);\r\n if (!this.marker) return offset;\r\n\r\n let offsetAnchor = (this.markerAnchor && this.markerAnchor.current) || this.marker;\r\n\r\n let width = offsetAnchor.offsetWidth;\r\n let height = offsetAnchor.offsetHeight;\r\n let anchor = this.get('anchor') as RichMarkerPosition || RichMarkerPosition.TOP_LEFT;\r\n\r\n switch (anchor) {\r\n case RichMarkerPosition.TOP_LEFT:\r\n break;\r\n case RichMarkerPosition.TOP:\r\n offset.width = -width / 2;\r\n break;\r\n case RichMarkerPosition.TOP_RIGHT:\r\n offset.width = -width;\r\n break;\r\n case RichMarkerPosition.LEFT:\r\n offset.height = -height / 2;\r\n break;\r\n case RichMarkerPosition.MIDDLE:\r\n offset.height = -height / 2;\r\n offset.width = -width / 2;\r\n break;\r\n case RichMarkerPosition.RIGHT:\r\n offset.width = -width;\r\n offset.height = -height / 2;\r\n break;\r\n case RichMarkerPosition.BOTTOM_LEFT:\r\n offset.height = -height;\r\n break;\r\n case RichMarkerPosition.BOTTOM:\r\n offset.width = -width / 2;\r\n offset.height = -height;\r\n break;\r\n case RichMarkerPosition.BOTTOM_RIGHT:\r\n offset.width = -width;\r\n offset.height = -height;\r\n break;\r\n }\r\n\r\n // Maybe a bug, why force the not null?\r\n let anchorOffset = getOffsetFromParent(offsetAnchor, this.marker.offsetParent!);\r\n offset.width -= anchorOffset.x;\r\n offset.height -= anchorOffset.y;\r\n\r\n return offset;\r\n }\r\n }\r\n\r\n this.marker = new richMarker(this.getOptions(), this.content, this.contentRef);\r\n this.marker.setMap(this.props.map);\r\n }\r\n\r\n componentWillUnmount() {\r\n this.marker && this.marker.setMap(null);\r\n this.content.removeEventListener('click', this.onClick);\r\n }\r\n\r\n componentDidUpdate() {\r\n this.marker && this.marker.setOptions(this.getOptions());\r\n }\r\n\r\n\r\n render() {\r\n let Content = React.forwardRef(this.props.content);\r\n let portal = ReactDOM.createPortal(, this.content);\r\n this.marker && this.marker.draw();\r\n return portal;\r\n }\r\n\r\n getComponent() {\r\n return this.marker;\r\n }\r\n\r\n private getOptions(): RichMarkerOptions {\r\n const {\r\n map,\r\n children,\r\n content,\r\n position,\r\n onClick,\r\n ...props\r\n } = this.props;\r\n\r\n return {\r\n position: mapLocation(position),\r\n ...props\r\n }\r\n }\r\n\r\n}\r\n","import * as React from \"react\";\r\nimport { RichMarker, RichMarkerPosition } from \"../googlemaps/richmarker\";\r\nimport { MapComponentProps } from \"../googlemaps/map\";\r\nimport * as flightAwareMessages from '../../models/flightawaremessages';\r\n//Simport { isFlightAwarePosition } from \"../../models/flightaware\";\r\n\r\nimport '../../map.css';\r\n\r\ninterface AircraftMarkerProps extends DOMEvents, MapComponentProps {\r\n aircraftImage?: string;\r\n tailNumber: string;\r\n aircraftType: string;\r\n aircraftTypeCode: string;\r\n departure?: string;\r\n arrival?: string;\r\n scheduledArrivalTimeUTC: string;\r\n scheduledArrivalTimeLocal: string;\r\n flightAwareEstimatedArrivalTimeUTC: string | null;\r\n flightAwareEstimatedArrivalTimeLocal: string | null;\r\n position: flightAwareMessages.Position;\r\n children?: never;\r\n}\r\n\r\nexport const AircraftMarker: React.FC = ({\r\n map,\r\n aircraftImage,\r\n tailNumber,\r\n aircraftType,\r\n aircraftTypeCode,\r\n departure,\r\n arrival,\r\n scheduledArrivalTimeUTC,\r\n scheduledArrivalTimeLocal,\r\n flightAwareEstimatedArrivalTimeUTC,\r\n flightAwareEstimatedArrivalTimeLocal,\r\n position,\r\n ...imgEvents\r\n}) => {\r\n\r\n\r\n\r\n\r\n function renderAircraft(_: {}, ref: React.Ref) {\r\n\r\n function getTime() {\r\n if (flightAwareEstimatedArrivalTimeUTC) {\r\n return <> {flightAwareEstimatedArrivalTimeLocal && <>{flightAwareEstimatedArrivalTimeLocal}L / >}{flightAwareEstimatedArrivalTimeUTC}Z>\r\n } else {\r\n return <>{scheduledArrivalTimeLocal && <>{scheduledArrivalTimeLocal}L / >}{scheduledArrivalTimeUTC}Z>\r\n }\r\n }\r\n\r\n function getDetails() {\r\n return (\r\n <>\r\n {tailNumber} {aircraftTypeCode}
\r\n {departure || ????} > {arrival || ????}
\r\n {getTime()}\r\n >\r\n )\r\n }\r\n\r\n if (position.heading) {\r\n if (position.heading <= 180) {\r\n return (\r\n \r\n \r\n \r\n | \r\n \r\n {getDetails()}\r\n | \r\n
\r\n \r\n
\r\n )\r\n }\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n {getDetails()}\r\n | \r\n  | \r\n
\r\n \r\n
\r\n )\r\n }\r\n\r\n return (\r\n \r\n \r\n \r\n  | \r\n {tailNumber} | \r\n
\r\n \r\n
\r\n )\r\n }\r\n\r\n if (!position) return null;\r\n\r\n return \r\n}\r\n\r\nAircraftMarker.defaultProps = {\r\n aircraftImage: '/img/map/baseline-airplanemode_active-24px.svg',\r\n }\r\n\r\nAircraftMarker.displayName = 'Map.AircraftMarker';\r\n","/*global google*/\r\n \r\nimport * as React from 'react';\r\nimport { PolyLine } from '../googlemaps/polyline';\r\nimport { FlightLegViewModel} from '../../models/flightleg';\r\nimport { MapComponentProps } from '../googlemaps/map';\r\nimport { withFlightAwareTracking, FlightAwareFlightLeg } from '../../signalr/withflightawaretracking';\r\nimport { AircraftMarker } from './aircraftmarker';\r\nimport { mapLocation } from '../googlemaps/location';\r\n\r\nexport interface FlightLegProps extends FlightLegViewModel, FlightAwareFlightLeg, MapComponentProps {\r\n aircraftImage?: string;\r\n planColor?: string;\r\n planWeight?: number;\r\n flightColor?: string;\r\n flightWeight?: number;\r\n legColor?: string;\r\n legWeight?: number;\r\n children?: never;\r\n showPlan?: boolean;\r\n aircraftEvents?: DOMEvents\r\n}\r\n\r\nexport class FlightLeg extends React.PureComponent {\r\n\r\n static defaultProps = {\r\n planColor: \"#3276b1\",\r\n planWeight: 2,\r\n flightColor: \"#ffffff\",\r\n flightWeight: 2,\r\n legColor: \"#ff3b30\",\r\n legWeight: 2,\r\n showPlan: true,\r\n aircraftEvents: {}\r\n };\r\n\r\n static displayName = \"Map.FlightLeg\";\r\n\r\n componentDidMount() {\r\n const {\r\n arrivalLocation,\r\n departureLocation,\r\n map,\r\n position\r\n } = this.props;\r\n\r\n if (arrivalLocation && departureLocation) {\r\n let bounds = new google.maps.LatLngBounds();\r\n bounds.extend(mapLocation(arrivalLocation));\r\n bounds.extend(mapLocation(departureLocation));\r\n map.fitBounds(bounds);\r\n } else if (position) {\r\n map.setCenter(mapLocation(position))\r\n map.setZoom(5);\r\n }\r\n }\r\n\r\n componentDidUpdate(prevProps: FlightLegProps) {\r\n if (!((prevProps.arrivalLocation && prevProps.departureLocation) || prevProps.position)) {\r\n if (this.props.position) {\r\n this.props.map.setCenter(mapLocation(this.props.position));\r\n this.props.map.setZoom(5);\r\n }\r\n }\r\n }\r\n\r\n render() {\r\n const {\r\n map,\r\n aircraftImage,\r\n plan,\r\n flight,\r\n departureLocation,\r\n arrivalLocation,\r\n tailNumber,\r\n position,\r\n aircraftTypeName,\r\n aircraftTypeCode,\r\n departure,\r\n arrival,\r\n flightAwareEstimatedArrivalTimeLocal,\r\n flightAwareEstimatedArrivalTimeUTC,\r\n scheduledArrivalTimeLocal,\r\n scheduledArrivalTimeUTC,\r\n planColor,\r\n planWeight,\r\n flightColor,\r\n flightWeight,\r\n legColor,\r\n legWeight,\r\n showPlan,\r\n aircraftEvents\r\n } = this.props;\r\n\r\n return <>\r\n {showPlan && plan && (plan.length > 0) && }\r\n {flight && }\r\n {showPlan && plan && !plan.length && departureLocation && arrivalLocation && }\r\n {position && }\r\n >\r\n }\r\n}\r\n\r\n\r\nexport const TrackedFlightLeg = withFlightAwareTracking(FlightLeg);","/*global google*/\r\n\r\nimport * as React from \"react\";\r\nimport * as PropTypes from \"prop-types\";\r\nimport { MapComponentProps } from \"./map\";\r\nimport { Location, locationPropType, mapLocation } from \"./location\";\r\nimport { Animation, getAnimation } from \"./util\";\r\n\r\ninterface MarkerProps extends MapComponentProps {\r\n position: Location;\r\n icon: string;\r\n animation?: Animation;\r\n zIndex?: number;\r\n onClick?: () => void;\r\n children?: never;\r\n}\r\n\r\nexport class Marker extends React.PureComponent{\r\n\r\n static propTypes = {\r\n //TODO: MapComponentProps\r\n icon: PropTypes.string.isRequired,\r\n onClick: PropTypes.func,\r\n position: locationPropType.isRequired,\r\n animation: PropTypes.oneOf([Animation.BOUNCE, Animation.DROP, Animation.NONE])\r\n }\r\n\r\n static displayName = \"GoogleMaps.Marker\";\r\n\r\n private listener?: google.maps.MapsEventListener;\r\n private marker?: google.maps.Marker;\r\n\r\n private onClick = () => this.props.onClick && this.props.onClick();\r\n\r\n componentDidMount() {\r\n this.marker = new google.maps.Marker(this.getOptions());\r\n this.listener = google.maps.event.addListener(this.marker, 'click', this.onClick);\r\n }\r\n\r\n componentWillUnmount() {\r\n this.marker && this.marker.setMap(null);\r\n this.listener && this.listener.remove();\r\n }\r\n\r\n componentDidUpdate(prevProps: MarkerProps) {\r\n if (this.marker) {\r\n if (this.props.icon !== prevProps.icon) {\r\n this.marker.setIcon(this.props.icon);\r\n }\r\n if (this.props.position !== prevProps.position) {\r\n this.marker.setPosition(mapLocation(this.props.position));\r\n }\r\n if (this.props.zIndex && this.props.zIndex !== prevProps.zIndex) {\r\n this.marker.setZIndex(this.props.zIndex)\r\n }\r\n }\r\n }\r\n\r\n render(): null {\r\n return null;\r\n }\r\n\r\n getComponent() {\r\n return this.marker;\r\n }\r\n\r\n private getOptions(): google.maps.MarkerOptions {\r\n const {\r\n onClick,\r\n position,\r\n children,\r\n animation,\r\n icon,\r\n ...props\r\n } = this.props;\r\n\r\n return {\r\n position: mapLocation(position),\r\n animation: getAnimation(animation),\r\n optimized: false,\r\n icon: {\r\n scaledSize: new google.maps.Size(20, 20),\r\n url: icon\r\n },\r\n ...props\r\n }\r\n }\r\n}\r\n","import * as React from 'react';\r\nimport { FlightLegViewModel } from '../../models/flightleg';\r\nimport { withFlightAwareTracking, FlightAwareFlightLeg } from '../../signalr/withflightawaretracking';\r\nimport { FlightInfo } from './flightinfo';\r\nimport { FlightExpired } from './flightexpired';\r\nimport { BaseMap } from '../map/basemap';\r\nimport { FlightLeg } from '../map/flightleg';\r\nimport { Marker } from '../googlemaps/marker';\r\nimport { Animation } from '../googlemaps/util';\r\n\r\ninterface TrackingMapProps extends FlightLegViewModel, FlightAwareFlightLeg {\r\n airportImage?: string;\r\n aircraftImage?: string;\r\n animation?: Animation;\r\n children?: never;\r\n}\r\n\r\nconst trackingMap: React.FC = props => {\r\n\r\n function renderMap(map: google.maps.Map) {\r\n return <>\r\n {props.departureLocation && }\r\n {props.arrivalLocation && }\r\n \r\n >\r\n }\r\n\r\n function displayMap() {\r\n if (props.expired) {\r\n return (\r\n \r\n )\r\n } else {\r\n return (\r\n <>\r\n \r\n \r\n \r\n {renderMap}\r\n \r\n
\r\n >\r\n )\r\n }\r\n }\r\n\r\n return <>\r\n {displayMap()}\r\n >\r\n\r\n}\r\n\r\ntrackingMap.defaultProps = {\r\n airportImage: '/img/map/baseline-place-24px.svg',\r\n aircraftImage: '/img/map/baseline-airplanemode_active-24px.svg',\r\n animation: Animation.DROP \r\n}\r\n\r\n\r\nexport const TrackingMap = withFlightAwareTracking(trackingMap);","import React from 'react';\r\nimport { FlightAwareHub } from '../signalr/flightawarehub';\r\nimport { RouteComponentProps } from 'react-router';\r\nimport { fetch, FlightLegViewModel } from '../models/flightleg';\r\nimport { TrackingMap } from '../components/flight/trackingmap';\r\nimport { FlightExpired } from '../components/flight/flightexpired';\r\n\r\nexport interface MapParams {\r\n flightToken: string;\r\n}\r\n\r\ninterface MapProps extends RouteComponentProps {\r\n flightAwareHub: FlightAwareHub;\r\n children?: never;\r\n}\r\n\r\ninterface MapState {\r\n flightLeg?: FlightLegViewModel;\r\n}\r\n\r\nexport class Map extends React.PureComponent{\r\n\r\n state: MapState = {}\r\n\r\n componentDidMount() {\r\n const {\r\n flightToken\r\n } = this.props.match.params;\r\n\r\n fetch(`api/tracking/GetFlightLegData?token=${flightToken}`)\r\n .then(flightLeg => this.setState({ flightLeg }));\r\n }\r\n \r\n render() { \r\n if (this.state.flightLeg && this.state.flightLeg.expired) {\r\n return \r\n } else {\r\n return !!this.state.flightLeg && \r\n } \r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { IRetryPolicy, RetryContext } from \"./IRetryPolicy\";\r\n\r\n// 0, 2, 10, 30 second delays before reconnect attempts.\r\nconst DEFAULT_RETRY_DELAYS_IN_MILLISECONDS = [0, 2000, 10000, 30000, null];\r\n\r\n/** @private */\r\nexport class DefaultReconnectPolicy implements IRetryPolicy {\r\n private readonly _retryDelays: (number | null)[];\r\n\r\n constructor(retryDelays?: number[]) {\r\n this._retryDelays = retryDelays !== undefined ? [...retryDelays, null] : DEFAULT_RETRY_DELAYS_IN_MILLISECONDS;\r\n }\r\n\r\n public nextRetryDelayInMilliseconds(retryContext: RetryContext): number | null {\r\n return this._retryDelays[retryContext.previousRetryCount];\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { HttpTransportType } from \"./ITransport\";\r\n\r\n/** Error thrown when an HTTP request fails. */\r\nexport class HttpError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private __proto__: Error;\r\n\r\n /** The HTTP status code represented by this error. */\r\n public statusCode: number;\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpError}.\r\n *\r\n * @param {string} errorMessage A descriptive error message.\r\n * @param {number} statusCode The HTTP status code represented by this error.\r\n */\r\n constructor(errorMessage: string, statusCode: number) {\r\n const trueProto = new.target.prototype;\r\n super(`${errorMessage}: Status code '${statusCode}'`);\r\n this.statusCode = statusCode;\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when a timeout elapses. */\r\nexport class TimeoutError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private __proto__: Error;\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.TimeoutError}.\r\n *\r\n * @param {string} errorMessage A descriptive error message.\r\n */\r\n constructor(errorMessage: string = \"A timeout occurred.\") {\r\n const trueProto = new.target.prototype;\r\n super(errorMessage);\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when an action is aborted. */\r\nexport class AbortError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private __proto__: Error;\r\n\r\n /** Constructs a new instance of {@link AbortError}.\r\n *\r\n * @param {string} errorMessage A descriptive error message.\r\n */\r\n constructor(errorMessage: string = \"An abort occurred.\") {\r\n const trueProto = new.target.prototype;\r\n super(errorMessage);\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when the selected transport is unsupported by the browser. */\r\n/** @private */\r\nexport class UnsupportedTransportError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private __proto__: Error;\r\n\r\n /** The {@link @microsoft/signalr.HttpTransportType} this error occured on. */\r\n public transport: HttpTransportType;\r\n\r\n /** The type name of this error. */\r\n public errorType: string;\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.UnsupportedTransportError}.\r\n *\r\n * @param {string} message A descriptive error message.\r\n * @param {HttpTransportType} transport The {@link @microsoft/signalr.HttpTransportType} this error occured on.\r\n */\r\n constructor(message: string, transport: HttpTransportType) {\r\n const trueProto = new.target.prototype;\r\n super(message);\r\n this.transport = transport;\r\n this.errorType = 'UnsupportedTransportError';\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when the selected transport is disabled by the browser. */\r\n/** @private */\r\nexport class DisabledTransportError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private __proto__: Error;\r\n\r\n /** The {@link @microsoft/signalr.HttpTransportType} this error occured on. */\r\n public transport: HttpTransportType;\r\n\r\n /** The type name of this error. */\r\n public errorType: string;\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.DisabledTransportError}.\r\n *\r\n * @param {string} message A descriptive error message.\r\n * @param {HttpTransportType} transport The {@link @microsoft/signalr.HttpTransportType} this error occured on.\r\n */\r\n constructor(message: string, transport: HttpTransportType) {\r\n const trueProto = new.target.prototype;\r\n super(message);\r\n this.transport = transport;\r\n this.errorType = 'DisabledTransportError';\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when the selected transport cannot be started. */\r\n/** @private */\r\nexport class FailedToStartTransportError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private __proto__: Error;\r\n\r\n /** The {@link @microsoft/signalr.HttpTransportType} this error occured on. */\r\n public transport: HttpTransportType;\r\n\r\n /** The type name of this error. */\r\n public errorType: string;\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.FailedToStartTransportError}.\r\n *\r\n * @param {string} message A descriptive error message.\r\n * @param {HttpTransportType} transport The {@link @microsoft/signalr.HttpTransportType} this error occured on.\r\n */\r\n constructor(message: string, transport: HttpTransportType) {\r\n const trueProto = new.target.prototype;\r\n super(message);\r\n this.transport = transport;\r\n this.errorType = 'FailedToStartTransportError';\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when the negotiation with the server failed to complete. */\r\n/** @private */\r\nexport class FailedToNegotiateWithServerError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private __proto__: Error;\r\n\r\n /** The type name of this error. */\r\n public errorType: string;\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.FailedToNegotiateWithServerError}.\r\n *\r\n * @param {string} message A descriptive error message.\r\n */\r\n constructor(message: string) {\r\n const trueProto = new.target.prototype;\r\n super(message);\r\n this.errorType = 'FailedToNegotiateWithServerError';\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when multiple errors have occured. */\r\n/** @private */\r\nexport class AggregateErrors extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private __proto__: Error;\r\n\r\n /** The collection of errors this error is aggregating. */\r\n public innerErrors: Error[];\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.AggregateErrors}.\r\n *\r\n * @param {string} message A descriptive error message.\r\n * @param {Error[]} innerErrors The collection of errors this error is aggregating.\r\n */\r\n constructor(message: string, innerErrors: Error[]) {\r\n const trueProto = new.target.prototype;\r\n super(message);\r\n\r\n this.innerErrors = innerErrors;\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { AbortSignal } from \"./AbortController\";\r\nimport { MessageHeaders } from \"./IHubProtocol\";\r\n\r\n/** Represents an HTTP request. */\r\nexport interface HttpRequest {\r\n /** The HTTP method to use for the request. */\r\n method?: string;\r\n\r\n /** The URL for the request. */\r\n url?: string;\r\n\r\n /** The body content for the request. May be a string or an ArrayBuffer (for binary data). */\r\n content?: string | ArrayBuffer;\r\n\r\n /** An object describing headers to apply to the request. */\r\n headers?: MessageHeaders;\r\n\r\n /** The XMLHttpRequestResponseType to apply to the request. */\r\n responseType?: XMLHttpRequestResponseType;\r\n\r\n /** An AbortSignal that can be monitored for cancellation. */\r\n abortSignal?: AbortSignal;\r\n\r\n /** The time to wait for the request to complete before throwing a TimeoutError. Measured in milliseconds. */\r\n timeout?: number;\r\n\r\n /** This controls whether credentials such as cookies are sent in cross-site requests. */\r\n withCredentials?: boolean;\r\n}\r\n\r\n/** Represents an HTTP response. */\r\nexport class HttpResponse {\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n */\r\n constructor(statusCode: number);\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code and message.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n * @param {string} statusText The status message of the response.\r\n */\r\n constructor(statusCode: number, statusText: string);\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code, message and string content.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n * @param {string} statusText The status message of the response.\r\n * @param {string} content The content of the response.\r\n */\r\n constructor(statusCode: number, statusText: string, content: string);\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code, message and binary content.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n * @param {string} statusText The status message of the response.\r\n * @param {ArrayBuffer} content The content of the response.\r\n */\r\n constructor(statusCode: number, statusText: string, content: ArrayBuffer);\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code, message and binary content.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n * @param {string} statusText The status message of the response.\r\n * @param {string | ArrayBuffer} content The content of the response.\r\n */\r\n constructor(statusCode: number, statusText: string, content: string | ArrayBuffer);\r\n constructor(\r\n public readonly statusCode: number,\r\n public readonly statusText?: string,\r\n public readonly content?: string | ArrayBuffer) {\r\n }\r\n}\r\n\r\n/** Abstraction over an HTTP client.\r\n *\r\n * This class provides an abstraction over an HTTP client so that a different implementation can be provided on different platforms.\r\n */\r\nexport abstract class HttpClient {\r\n /** Issues an HTTP GET request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public get(url: string): Promise;\r\n\r\n /** Issues an HTTP GET request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public get(url: string, options: HttpRequest): Promise;\r\n public get(url: string, options?: HttpRequest): Promise {\r\n return this.send({\r\n ...options,\r\n method: \"GET\",\r\n url,\r\n });\r\n }\r\n\r\n /** Issues an HTTP POST request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public post(url: string): Promise;\r\n\r\n /** Issues an HTTP POST request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public post(url: string, options: HttpRequest): Promise;\r\n public post(url: string, options?: HttpRequest): Promise {\r\n return this.send({\r\n ...options,\r\n method: \"POST\",\r\n url,\r\n });\r\n }\r\n\r\n /** Issues an HTTP DELETE request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public delete(url: string): Promise;\r\n\r\n /** Issues an HTTP DELETE request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public delete(url: string, options: HttpRequest): Promise;\r\n public delete(url: string, options?: HttpRequest): Promise {\r\n return this.send({\r\n ...options,\r\n method: \"DELETE\",\r\n url,\r\n });\r\n }\r\n\r\n /** Issues an HTTP request to the specified URL, returning a {@link Promise} that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {HttpRequest} request An {@link @microsoft/signalr.HttpRequest} describing the request to send.\r\n * @returns {Promise} A Promise that resolves with an HttpResponse describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public abstract send(request: HttpRequest): Promise;\r\n\r\n /** Gets all cookies that apply to the specified URL.\r\n *\r\n * @param url The URL that the cookies are valid for.\r\n * @returns {string} A string containing all the key-value cookie pairs for the specified URL.\r\n */\r\n // @ts-ignore\r\n public getCookieString(url: string): string {\r\n return \"\";\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\n// These values are designed to match the ASP.NET Log Levels since that's the pattern we're emulating here.\r\n/** Indicates the severity of a log message.\r\n *\r\n * Log Levels are ordered in increasing severity. So `Debug` is more severe than `Trace`, etc.\r\n */\r\nexport enum LogLevel {\r\n /** Log level for very low severity diagnostic messages. */\r\n Trace = 0,\r\n /** Log level for low severity diagnostic messages. */\r\n Debug = 1,\r\n /** Log level for informational diagnostic messages. */\r\n Information = 2,\r\n /** Log level for diagnostic messages that indicate a non-fatal problem. */\r\n Warning = 3,\r\n /** Log level for diagnostic messages that indicate a failure in the current operation. */\r\n Error = 4,\r\n /** Log level for diagnostic messages that indicate a failure that will terminate the entire application. */\r\n Critical = 5,\r\n /** The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted. */\r\n None = 6,\r\n}\r\n\r\n/** An abstraction that provides a sink for diagnostic messages. */\r\nexport interface ILogger {\r\n /** Called by the framework to emit a diagnostic message.\r\n *\r\n * @param {LogLevel} logLevel The severity level of the message.\r\n * @param {string} message The message.\r\n */\r\n log(logLevel: LogLevel, message: string): void;\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\n\r\n/** A logger that does nothing when log messages are sent to it. */\r\nexport class NullLogger implements ILogger {\r\n /** The singleton instance of the {@link @microsoft/signalr.NullLogger}. */\r\n public static instance: ILogger = new NullLogger();\r\n\r\n private constructor() {}\r\n\r\n /** @inheritDoc */\r\n // eslint-disable-next-line\r\n public log(_logLevel: LogLevel, _message: string): void {\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { HttpClient } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { NullLogger } from \"./Loggers\";\r\nimport { IStreamSubscriber, ISubscription } from \"./Stream\";\r\nimport { Subject } from \"./Subject\";\r\nimport { IHttpConnectionOptions } from \"./IHttpConnectionOptions\";\r\n\r\n// Version token that will be replaced by the prepack command\r\n/** The version of the SignalR client. */\r\n\r\nexport const VERSION: string = \"0.0.0-DEV_BUILD\";\r\n/** @private */\r\nexport class Arg {\r\n public static isRequired(val: any, name: string): void {\r\n if (val === null || val === undefined) {\r\n throw new Error(`The '${name}' argument is required.`);\r\n }\r\n }\r\n public static isNotEmpty(val: string, name: string): void {\r\n if (!val || val.match(/^\\s*$/)) {\r\n throw new Error(`The '${name}' argument should not be empty.`);\r\n }\r\n }\r\n\r\n public static isIn(val: any, values: any, name: string): void {\r\n // TypeScript enums have keys for **both** the name and the value of each enum member on the type itself.\r\n if (!(val in values)) {\r\n throw new Error(`Unknown ${name} value: ${val}.`);\r\n }\r\n }\r\n}\r\n\r\n/** @private */\r\nexport class Platform {\r\n // react-native has a window but no document so we should check both\r\n public static get isBrowser(): boolean {\r\n return typeof window === \"object\" && typeof window.document === \"object\";\r\n }\r\n\r\n // WebWorkers don't have a window object so the isBrowser check would fail\r\n public static get isWebWorker(): boolean {\r\n return typeof self === \"object\" && \"importScripts\" in self;\r\n }\r\n\r\n // react-native has a window but no document\r\n static get isReactNative(): boolean {\r\n return typeof window === \"object\" && typeof window.document === \"undefined\";\r\n }\r\n\r\n // Node apps shouldn't have a window object, but WebWorkers don't either\r\n // so we need to check for both WebWorker and window\r\n public static get isNode(): boolean {\r\n return !this.isBrowser && !this.isWebWorker && !this.isReactNative;\r\n }\r\n}\r\n\r\n/** @private */\r\nexport function getDataDetail(data: any, includeContent: boolean): string {\r\n let detail = \"\";\r\n if (isArrayBuffer(data)) {\r\n detail = `Binary data of length ${data.byteLength}`;\r\n if (includeContent) {\r\n detail += `. Content: '${formatArrayBuffer(data)}'`;\r\n }\r\n } else if (typeof data === \"string\") {\r\n detail = `String data of length ${data.length}`;\r\n if (includeContent) {\r\n detail += `. Content: '${data}'`;\r\n }\r\n }\r\n return detail;\r\n}\r\n\r\n/** @private */\r\nexport function formatArrayBuffer(data: ArrayBuffer): string {\r\n const view = new Uint8Array(data);\r\n\r\n // Uint8Array.map only supports returning another Uint8Array?\r\n let str = \"\";\r\n view.forEach((num) => {\r\n const pad = num < 16 ? \"0\" : \"\";\r\n str += `0x${pad}${num.toString(16)} `;\r\n });\r\n\r\n // Trim of trailing space.\r\n return str.substr(0, str.length - 1);\r\n}\r\n\r\n// Also in signalr-protocol-msgpack/Utils.ts\r\n/** @private */\r\nexport function isArrayBuffer(val: any): val is ArrayBuffer {\r\n return val && typeof ArrayBuffer !== \"undefined\" &&\r\n (val instanceof ArrayBuffer ||\r\n // Sometimes we get an ArrayBuffer that doesn't satisfy instanceof\r\n (val.constructor && val.constructor.name === \"ArrayBuffer\"));\r\n}\r\n\r\n/** @private */\r\nexport async function sendMessage(logger: ILogger, transportName: string, httpClient: HttpClient, url: string, accessTokenFactory: (() => string | Promise) | undefined,\r\n content: string | ArrayBuffer, options: IHttpConnectionOptions): Promise {\r\n let headers: {[k: string]: string} = {};\r\n if (accessTokenFactory) {\r\n const token = await accessTokenFactory();\r\n if (token) {\r\n headers = {\r\n [\"Authorization\"]: `Bearer ${token}`,\r\n };\r\n }\r\n }\r\n\r\n const [name, value] = getUserAgentHeader();\r\n headers[name] = value;\r\n\r\n logger.log(LogLevel.Trace, `(${transportName} transport) sending data. ${getDataDetail(content, options.logMessageContent!)}.`);\r\n\r\n const responseType = isArrayBuffer(content) ? \"arraybuffer\" : \"text\";\r\n const response = await httpClient.post(url, {\r\n content,\r\n headers: { ...headers, ...options.headers},\r\n responseType,\r\n timeout: options.timeout,\r\n withCredentials: options.withCredentials,\r\n });\r\n\r\n logger.log(LogLevel.Trace, `(${transportName} transport) request complete. Response status: ${response.statusCode}.`);\r\n}\r\n\r\n/** @private */\r\nexport function createLogger(logger?: ILogger | LogLevel): ILogger {\r\n if (logger === undefined) {\r\n return new ConsoleLogger(LogLevel.Information);\r\n }\r\n\r\n if (logger === null) {\r\n return NullLogger.instance;\r\n }\r\n\r\n if ((logger as ILogger).log !== undefined) {\r\n return logger as ILogger;\r\n }\r\n\r\n return new ConsoleLogger(logger as LogLevel);\r\n}\r\n\r\n/** @private */\r\nexport class SubjectSubscription implements ISubscription {\r\n private _subject: Subject;\r\n private _observer: IStreamSubscriber;\r\n\r\n constructor(subject: Subject, observer: IStreamSubscriber) {\r\n this._subject = subject;\r\n this._observer = observer;\r\n }\r\n\r\n public dispose(): void {\r\n const index: number = this._subject.observers.indexOf(this._observer);\r\n if (index > -1) {\r\n this._subject.observers.splice(index, 1);\r\n }\r\n\r\n if (this._subject.observers.length === 0 && this._subject.cancelCallback) {\r\n this._subject.cancelCallback().catch((_) => { });\r\n }\r\n }\r\n}\r\n\r\n/** @private */\r\nexport class ConsoleLogger implements ILogger {\r\n private readonly _minLevel: LogLevel;\r\n\r\n // Public for testing purposes.\r\n public out: {\r\n error(message: any): void,\r\n warn(message: any): void,\r\n info(message: any): void,\r\n log(message: any): void,\r\n };\r\n\r\n constructor(minimumLogLevel: LogLevel) {\r\n this._minLevel = minimumLogLevel;\r\n this.out = console;\r\n }\r\n\r\n public log(logLevel: LogLevel, message: string): void {\r\n if (logLevel >= this._minLevel) {\r\n const msg = `[${new Date().toISOString()}] ${LogLevel[logLevel]}: ${message}`;\r\n switch (logLevel) {\r\n case LogLevel.Critical:\r\n case LogLevel.Error:\r\n this.out.error(msg);\r\n break;\r\n case LogLevel.Warning:\r\n this.out.warn(msg);\r\n break;\r\n case LogLevel.Information:\r\n this.out.info(msg);\r\n break;\r\n default:\r\n // console.debug only goes to attached debuggers in Node, so we use console.log for Trace and Debug\r\n this.out.log(msg);\r\n break;\r\n }\r\n }\r\n }\r\n}\r\n\r\n/** @private */\r\nexport function getUserAgentHeader(): [string, string] {\r\n let userAgentHeaderName = \"X-SignalR-User-Agent\";\r\n if (Platform.isNode) {\r\n userAgentHeaderName = \"User-Agent\";\r\n }\r\n return [ userAgentHeaderName, constructUserAgent(VERSION, getOsName(), getRuntime(), getRuntimeVersion()) ];\r\n}\r\n\r\n/** @private */\r\nexport function constructUserAgent(version: string, os: string, runtime: string, runtimeVersion: string | undefined): string {\r\n // Microsoft SignalR/[Version] ([Detailed Version]; [Operating System]; [Runtime]; [Runtime Version])\r\n let userAgent: string = \"Microsoft SignalR/\";\r\n\r\n const majorAndMinor = version.split(\".\");\r\n userAgent += `${majorAndMinor[0]}.${majorAndMinor[1]}`;\r\n userAgent += ` (${version}; `;\r\n\r\n if (os && os !== \"\") {\r\n userAgent += `${os}; `;\r\n } else {\r\n userAgent += \"Unknown OS; \";\r\n }\r\n\r\n userAgent += `${runtime}`;\r\n\r\n if (runtimeVersion) {\r\n userAgent += `; ${runtimeVersion}`;\r\n } else {\r\n userAgent += \"; Unknown Runtime Version\";\r\n }\r\n\r\n userAgent += \")\";\r\n return userAgent;\r\n}\r\n\r\n// eslint-disable-next-line spaced-comment\r\n/*#__PURE__*/ function getOsName(): string {\r\n if (Platform.isNode) {\r\n switch (process.platform) {\r\n case \"win32\":\r\n return \"Windows NT\";\r\n case \"darwin\":\r\n return \"macOS\";\r\n case \"linux\":\r\n return \"Linux\";\r\n default:\r\n return process.platform;\r\n }\r\n } else {\r\n return \"\";\r\n }\r\n}\r\n\r\n// eslint-disable-next-line spaced-comment\r\n/*#__PURE__*/ function getRuntimeVersion(): string | undefined {\r\n if (Platform.isNode) {\r\n return process.versions.node;\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction getRuntime(): string {\r\n if (Platform.isNode) {\r\n return \"NodeJS\";\r\n } else {\r\n return \"Browser\";\r\n }\r\n}\r\n\r\n/** @private */\r\nexport function getErrorString(e: any): string {\r\n if (e.stack) {\r\n return e.stack;\r\n } else if (e.message) {\r\n return e.message;\r\n }\r\n return `${e}`;\r\n}\r\n\r\n/** @private */\r\nexport function getGlobalThis(): unknown {\r\n // globalThis is semi-new and not available in Node until v12\r\n if (typeof globalThis !== \"undefined\") {\r\n return globalThis;\r\n }\r\n if (typeof self !== \"undefined\") {\r\n return self;\r\n }\r\n if (typeof window !== \"undefined\") {\r\n return window;\r\n }\r\n if (typeof global !== \"undefined\") {\r\n return global;\r\n }\r\n throw new Error(\"could not find global\");\r\n}","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\n// @ts-ignore: This will be removed from built files and is here to make the types available during dev work\r\nimport { CookieJar } from \"@types/tough-cookie\";\r\n\r\nimport { AbortError, HttpError, TimeoutError } from \"./Errors\";\r\nimport { HttpClient, HttpRequest, HttpResponse } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { Platform, getGlobalThis } from \"./Utils\";\r\n\r\nexport class FetchHttpClient extends HttpClient {\r\n private readonly _abortControllerType: { prototype: AbortController, new(): AbortController };\r\n private readonly _fetchType: (input: RequestInfo, init?: RequestInit) => Promise;\r\n private readonly _jar?: CookieJar;\r\n\r\n private readonly _logger: ILogger;\r\n\r\n public constructor(logger: ILogger) {\r\n super();\r\n this._logger = logger;\r\n\r\n if (typeof fetch === \"undefined\") {\r\n // In order to ignore the dynamic require in webpack builds we need to do this magic\r\n // @ts-ignore: TS doesn't know about these names\r\n const requireFunc = typeof __webpack_require__ === \"function\" ? __non_webpack_require__ : require;\r\n\r\n // Cookies aren't automatically handled in Node so we need to add a CookieJar to preserve cookies across requests\r\n this._jar = new (requireFunc(\"tough-cookie\")).CookieJar();\r\n this._fetchType = requireFunc(\"node-fetch\");\r\n\r\n // node-fetch doesn't have a nice API for getting and setting cookies\r\n // fetch-cookie will wrap a fetch implementation with a default CookieJar or a provided one\r\n this._fetchType = requireFunc(\"fetch-cookie\")(this._fetchType, this._jar);\r\n } else {\r\n this._fetchType = fetch.bind(getGlobalThis());\r\n }\r\n if (typeof AbortController === \"undefined\") {\r\n // In order to ignore the dynamic require in webpack builds we need to do this magic\r\n // @ts-ignore: TS doesn't know about these names\r\n const requireFunc = typeof __webpack_require__ === \"function\" ? __non_webpack_require__ : require;\r\n\r\n // Node needs EventListener methods on AbortController which our custom polyfill doesn't provide\r\n this._abortControllerType = requireFunc(\"abort-controller\");\r\n } else {\r\n this._abortControllerType = AbortController;\r\n }\r\n }\r\n\r\n /** @inheritDoc */\r\n public async send(request: HttpRequest): Promise {\r\n // Check that abort was not signaled before calling send\r\n if (request.abortSignal && request.abortSignal.aborted) {\r\n throw new AbortError();\r\n }\r\n\r\n if (!request.method) {\r\n throw new Error(\"No method defined.\");\r\n }\r\n if (!request.url) {\r\n throw new Error(\"No url defined.\");\r\n }\r\n\r\n const abortController = new this._abortControllerType();\r\n\r\n let error: any;\r\n // Hook our abortSignal into the abort controller\r\n if (request.abortSignal) {\r\n request.abortSignal.onabort = () => {\r\n abortController.abort();\r\n error = new AbortError();\r\n };\r\n }\r\n\r\n // If a timeout has been passed in, setup a timeout to call abort\r\n // Type needs to be any to fit window.setTimeout and NodeJS.setTimeout\r\n let timeoutId: any = null;\r\n if (request.timeout) {\r\n const msTimeout = request.timeout!;\r\n timeoutId = setTimeout(() => {\r\n abortController.abort();\r\n this._logger.log(LogLevel.Warning, `Timeout from HTTP request.`);\r\n error = new TimeoutError();\r\n }, msTimeout);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetchType(request.url!, {\r\n body: request.content!,\r\n cache: \"no-cache\",\r\n credentials: request.withCredentials === true ? \"include\" : \"same-origin\",\r\n headers: {\r\n \"Content-Type\": \"text/plain;charset=UTF-8\",\r\n \"X-Requested-With\": \"XMLHttpRequest\",\r\n ...request.headers,\r\n },\r\n method: request.method!,\r\n mode: \"cors\",\r\n redirect: \"follow\",\r\n signal: abortController.signal,\r\n });\r\n } catch (e) {\r\n if (error) {\r\n throw error;\r\n }\r\n this._logger.log(\r\n LogLevel.Warning,\r\n `Error from HTTP request. ${e}.`,\r\n );\r\n throw e;\r\n } finally {\r\n if (timeoutId) {\r\n clearTimeout(timeoutId);\r\n }\r\n if (request.abortSignal) {\r\n request.abortSignal.onabort = null;\r\n }\r\n }\r\n\r\n if (!response.ok) {\r\n const errorMessage = await deserializeContent(response, \"text\") as string;\r\n throw new HttpError(errorMessage || response.statusText, response.status);\r\n }\r\n\r\n const content = deserializeContent(response, request.responseType);\r\n const payload = await content;\r\n\r\n return new HttpResponse(\r\n response.status,\r\n response.statusText,\r\n payload,\r\n );\r\n }\r\n\r\n public getCookieString(url: string): string {\r\n let cookies: string = \"\";\r\n if (Platform.isNode && this._jar) {\r\n // @ts-ignore: unused variable\r\n this._jar.getCookies(url, (e, c) => cookies = c.join(\"; \"));\r\n }\r\n return cookies;\r\n }\r\n}\r\n\r\nfunction deserializeContent(response: Response, responseType?: XMLHttpRequestResponseType): Promise {\r\n let content;\r\n switch (responseType) {\r\n case \"arraybuffer\":\r\n content = response.arrayBuffer();\r\n break;\r\n case \"text\":\r\n content = response.text();\r\n break;\r\n case \"blob\":\r\n case \"document\":\r\n case \"json\":\r\n throw new Error(`${responseType} is not supported.`);\r\n default:\r\n content = response.text();\r\n break;\r\n }\r\n\r\n return content;\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { AbortError, HttpError, TimeoutError } from \"./Errors\";\r\nimport { HttpClient, HttpRequest, HttpResponse } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\n\r\nexport class XhrHttpClient extends HttpClient {\r\n private readonly _logger: ILogger;\r\n\r\n public constructor(logger: ILogger) {\r\n super();\r\n this._logger = logger;\r\n }\r\n\r\n /** @inheritDoc */\r\n public send(request: HttpRequest): Promise {\r\n // Check that abort was not signaled before calling send\r\n if (request.abortSignal && request.abortSignal.aborted) {\r\n return Promise.reject(new AbortError());\r\n }\r\n\r\n if (!request.method) {\r\n return Promise.reject(new Error(\"No method defined.\"));\r\n }\r\n if (!request.url) {\r\n return Promise.reject(new Error(\"No url defined.\"));\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const xhr = new XMLHttpRequest();\r\n\r\n xhr.open(request.method!, request.url!, true);\r\n xhr.withCredentials = request.withCredentials === undefined ? true : request.withCredentials;\r\n xhr.setRequestHeader(\"X-Requested-With\", \"XMLHttpRequest\");\r\n // Explicitly setting the Content-Type header for React Native on Android platform.\r\n xhr.setRequestHeader(\"Content-Type\", \"text/plain;charset=UTF-8\");\r\n\r\n const headers = request.headers;\r\n if (headers) {\r\n Object.keys(headers)\r\n .forEach((header) => {\r\n xhr.setRequestHeader(header, headers[header]);\r\n });\r\n }\r\n\r\n if (request.responseType) {\r\n xhr.responseType = request.responseType;\r\n }\r\n\r\n if (request.abortSignal) {\r\n request.abortSignal.onabort = () => {\r\n xhr.abort();\r\n reject(new AbortError());\r\n };\r\n }\r\n\r\n if (request.timeout) {\r\n xhr.timeout = request.timeout;\r\n }\r\n\r\n xhr.onload = () => {\r\n if (request.abortSignal) {\r\n request.abortSignal.onabort = null;\r\n }\r\n\r\n if (xhr.status >= 200 && xhr.status < 300) {\r\n resolve(new HttpResponse(xhr.status, xhr.statusText, xhr.response || xhr.responseText));\r\n } else {\r\n reject(new HttpError(xhr.response || xhr.responseText || xhr.statusText, xhr.status));\r\n }\r\n };\r\n\r\n xhr.onerror = () => {\r\n this._logger.log(LogLevel.Warning, `Error from HTTP request. ${xhr.status}: ${xhr.statusText}.`);\r\n reject(new HttpError(xhr.statusText, xhr.status));\r\n };\r\n\r\n xhr.ontimeout = () => {\r\n this._logger.log(LogLevel.Warning, `Timeout from HTTP request.`);\r\n reject(new TimeoutError());\r\n };\r\n\r\n xhr.send(request.content || \"\");\r\n });\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { AbortError } from \"./Errors\";\r\nimport { FetchHttpClient } from \"./FetchHttpClient\";\r\nimport { HttpClient, HttpRequest, HttpResponse } from \"./HttpClient\";\r\nimport { ILogger } from \"./ILogger\";\r\nimport { Platform } from \"./Utils\";\r\nimport { XhrHttpClient } from \"./XhrHttpClient\";\r\n\r\n/** Default implementation of {@link @microsoft/signalr.HttpClient}. */\r\nexport class DefaultHttpClient extends HttpClient {\r\n private readonly _httpClient: HttpClient;\r\n\r\n /** Creates a new instance of the {@link @microsoft/signalr.DefaultHttpClient}, using the provided {@link @microsoft/signalr.ILogger} to log messages. */\r\n public constructor(logger: ILogger) {\r\n super();\r\n\r\n if (typeof fetch !== \"undefined\" || Platform.isNode) {\r\n this._httpClient = new FetchHttpClient(logger);\r\n } else if (typeof XMLHttpRequest !== \"undefined\") {\r\n this._httpClient = new XhrHttpClient(logger);\r\n } else {\r\n throw new Error(\"No usable HttpClient found.\");\r\n }\r\n }\r\n\r\n /** @inheritDoc */\r\n public send(request: HttpRequest): Promise {\r\n // Check that abort was not signaled before calling send\r\n if (request.abortSignal && request.abortSignal.aborted) {\r\n return Promise.reject(new AbortError());\r\n }\r\n\r\n if (!request.method) {\r\n return Promise.reject(new Error(\"No method defined.\"));\r\n }\r\n if (!request.url) {\r\n return Promise.reject(new Error(\"No url defined.\"));\r\n }\r\n\r\n return this._httpClient.send(request);\r\n }\r\n\r\n public getCookieString(url: string): string {\r\n return this._httpClient.getCookieString(url);\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nexport abstract class HeaderNames {\r\n static readonly Authorization = \"Authorization\";\r\n static readonly Cookie = \"Cookie\";\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\n// This will be treated as a bit flag in the future, so we keep it using power-of-two values.\r\n/** Specifies a specific HTTP transport type. */\r\nexport enum HttpTransportType {\r\n /** Specifies no transport preference. */\r\n None = 0,\r\n /** Specifies the WebSockets transport. */\r\n WebSockets = 1,\r\n /** Specifies the Server-Sent Events transport. */\r\n ServerSentEvents = 2,\r\n /** Specifies the Long Polling transport. */\r\n LongPolling = 4,\r\n}\r\n\r\n/** Specifies the transfer format for a connection. */\r\nexport enum TransferFormat {\r\n /** Specifies that only text data will be transmitted over the connection. */\r\n Text = 1,\r\n /** Specifies that binary data will be transmitted over the connection. */\r\n Binary = 2,\r\n}\r\n\r\n/** An abstraction over the behavior of transports. This is designed to support the framework and not intended for use by applications. */\r\nexport interface ITransport {\r\n connect(url: string, transferFormat: TransferFormat): Promise;\r\n send(data: any): Promise;\r\n stop(): Promise;\r\n onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n onclose: ((error?: Error) => void) | null;\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\n// Rough polyfill of https://developer.mozilla.org/en-US/docs/Web/API/AbortController\r\n// We don't actually ever use the API being polyfilled, we always use the polyfill because\r\n// it's a very new API right now.\r\n\r\n// Not exported from index.\r\n/** @private */\r\nexport class AbortController implements AbortSignal {\r\n private _isAborted: boolean = false;\r\n public onabort: (() => void) | null = null;\r\n\r\n public abort(): void {\r\n if (!this._isAborted) {\r\n this._isAborted = true;\r\n if (this.onabort) {\r\n this.onabort();\r\n }\r\n }\r\n }\r\n\r\n get signal(): AbortSignal {\r\n return this;\r\n }\r\n\r\n get aborted(): boolean {\r\n return this._isAborted;\r\n }\r\n}\r\n\r\n/** Represents a signal that can be monitored to determine if a request has been aborted. */\r\nexport interface AbortSignal {\r\n /** Indicates if the request has been aborted. */\r\n aborted: boolean;\r\n /** Set this to a handler that will be invoked when the request is aborted. */\r\n onabort: (() => void) | null;\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { AbortController } from \"./AbortController\";\r\nimport { HttpError, TimeoutError } from \"./Errors\";\r\nimport { HeaderNames } from \"./HeaderNames\";\r\nimport { HttpClient, HttpRequest } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { ITransport, TransferFormat } from \"./ITransport\";\r\nimport { Arg, getDataDetail, getUserAgentHeader, sendMessage } from \"./Utils\";\r\nimport { IHttpConnectionOptions } from \"./IHttpConnectionOptions\";\r\n\r\n// Not exported from 'index', this type is internal.\r\n/** @private */\r\nexport class LongPollingTransport implements ITransport {\r\n private readonly _httpClient: HttpClient;\r\n private readonly _accessTokenFactory: (() => string | Promise) | undefined;\r\n private readonly _logger: ILogger;\r\n private readonly _options: IHttpConnectionOptions;\r\n private readonly _pollAbort: AbortController;\r\n\r\n private _url?: string;\r\n private _running: boolean;\r\n private _receiving?: Promise;\r\n private _closeError?: Error;\r\n\r\n public onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n public onclose: ((error?: Error) => void) | null;\r\n\r\n // This is an internal type, not exported from 'index' so this is really just internal.\r\n public get pollAborted(): boolean {\r\n return this._pollAbort.aborted;\r\n }\r\n\r\n constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise) | undefined, logger: ILogger, options: IHttpConnectionOptions) {\r\n this._httpClient = httpClient;\r\n this._accessTokenFactory = accessTokenFactory;\r\n this._logger = logger;\r\n this._pollAbort = new AbortController();\r\n this._options = options;\r\n\r\n this._running = false;\r\n\r\n this.onreceive = null;\r\n this.onclose = null;\r\n }\r\n\r\n public async connect(url: string, transferFormat: TransferFormat): Promise {\r\n Arg.isRequired(url, \"url\");\r\n Arg.isRequired(transferFormat, \"transferFormat\");\r\n Arg.isIn(transferFormat, TransferFormat, \"transferFormat\");\r\n\r\n this._url = url;\r\n\r\n this._logger.log(LogLevel.Trace, \"(LongPolling transport) Connecting.\");\r\n\r\n // Allow binary format on Node and Browsers that support binary content (indicated by the presence of responseType property)\r\n if (transferFormat === TransferFormat.Binary &&\r\n (typeof XMLHttpRequest !== \"undefined\" && typeof new XMLHttpRequest().responseType !== \"string\")) {\r\n throw new Error(\"Binary protocols over XmlHttpRequest not implementing advanced features are not supported.\");\r\n }\r\n\r\n const [name, value] = getUserAgentHeader();\r\n const headers = { [name]: value, ...this._options.headers };\r\n\r\n const pollOptions: HttpRequest = {\r\n abortSignal: this._pollAbort.signal,\r\n headers,\r\n timeout: 100000,\r\n withCredentials: this._options.withCredentials,\r\n };\r\n\r\n if (transferFormat === TransferFormat.Binary) {\r\n pollOptions.responseType = \"arraybuffer\";\r\n }\r\n\r\n const token = await this._getAccessToken();\r\n this._updateHeaderToken(pollOptions, token);\r\n\r\n // Make initial long polling request\r\n // Server uses first long polling request to finish initializing connection and it returns without data\r\n const pollUrl = `${url}&_=${Date.now()}`;\r\n this._logger.log(LogLevel.Trace, `(LongPolling transport) polling: ${pollUrl}.`);\r\n const response = await this._httpClient.get(pollUrl, pollOptions);\r\n if (response.statusCode !== 200) {\r\n this._logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}.`);\r\n\r\n // Mark running as false so that the poll immediately ends and runs the close logic\r\n this._closeError = new HttpError(response.statusText || \"\", response.statusCode);\r\n this._running = false;\r\n } else {\r\n this._running = true;\r\n }\r\n\r\n this._receiving = this._poll(this._url, pollOptions);\r\n }\r\n\r\n private async _getAccessToken(): Promise {\r\n if (this._accessTokenFactory) {\r\n return await this._accessTokenFactory();\r\n }\r\n\r\n return null;\r\n }\r\n\r\n private _updateHeaderToken(request: HttpRequest, token: string | null) {\r\n if (!request.headers) {\r\n request.headers = {};\r\n }\r\n if (token) {\r\n request.headers[HeaderNames.Authorization] = `Bearer ${token}`;\r\n return;\r\n }\r\n if (request.headers[HeaderNames.Authorization]) {\r\n delete request.headers[HeaderNames.Authorization];\r\n }\r\n }\r\n\r\n private async _poll(url: string, pollOptions: HttpRequest): Promise {\r\n try {\r\n while (this._running) {\r\n // We have to get the access token on each poll, in case it changes\r\n const token = await this._getAccessToken();\r\n this._updateHeaderToken(pollOptions, token);\r\n\r\n try {\r\n const pollUrl = `${url}&_=${Date.now()}`;\r\n this._logger.log(LogLevel.Trace, `(LongPolling transport) polling: ${pollUrl}.`);\r\n const response = await this._httpClient.get(pollUrl, pollOptions);\r\n\r\n if (response.statusCode === 204) {\r\n this._logger.log(LogLevel.Information, \"(LongPolling transport) Poll terminated by server.\");\r\n\r\n this._running = false;\r\n } else if (response.statusCode !== 200) {\r\n this._logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}.`);\r\n\r\n // Unexpected status code\r\n this._closeError = new HttpError(response.statusText || \"\", response.statusCode);\r\n this._running = false;\r\n } else {\r\n // Process the response\r\n if (response.content) {\r\n this._logger.log(LogLevel.Trace, `(LongPolling transport) data received. ${getDataDetail(response.content, this._options.logMessageContent!)}.`);\r\n if (this.onreceive) {\r\n this.onreceive(response.content);\r\n }\r\n } else {\r\n // This is another way timeout manifest.\r\n this._logger.log(LogLevel.Trace, \"(LongPolling transport) Poll timed out, reissuing.\");\r\n }\r\n }\r\n } catch (e) {\r\n if (!this._running) {\r\n // Log but disregard errors that occur after stopping\r\n this._logger.log(LogLevel.Trace, `(LongPolling transport) Poll errored after shutdown: ${e.message}`);\r\n } else {\r\n if (e instanceof TimeoutError) {\r\n // Ignore timeouts and reissue the poll.\r\n this._logger.log(LogLevel.Trace, \"(LongPolling transport) Poll timed out, reissuing.\");\r\n } else {\r\n // Close the connection with the error as the result.\r\n this._closeError = e;\r\n this._running = false;\r\n }\r\n }\r\n }\r\n }\r\n } finally {\r\n this._logger.log(LogLevel.Trace, \"(LongPolling transport) Polling complete.\");\r\n\r\n // We will reach here with pollAborted==false when the server returned a response causing the transport to stop.\r\n // If pollAborted==true then client initiated the stop and the stop method will raise the close event after DELETE is sent.\r\n if (!this.pollAborted) {\r\n this._raiseOnClose();\r\n }\r\n }\r\n }\r\n\r\n public async send(data: any): Promise {\r\n if (!this._running) {\r\n return Promise.reject(new Error(\"Cannot send until the transport is connected\"));\r\n }\r\n return sendMessage(this._logger, \"LongPolling\", this._httpClient, this._url!, this._accessTokenFactory, data, this._options);\r\n }\r\n\r\n public async stop(): Promise {\r\n this._logger.log(LogLevel.Trace, \"(LongPolling transport) Stopping polling.\");\r\n\r\n // Tell receiving loop to stop, abort any current request, and then wait for it to finish\r\n this._running = false;\r\n this._pollAbort.abort();\r\n\r\n try {\r\n await this._receiving;\r\n\r\n // Send DELETE to clean up long polling on the server\r\n this._logger.log(LogLevel.Trace, `(LongPolling transport) sending DELETE request to ${this._url}.`);\r\n\r\n const headers: {[k: string]: string} = {};\r\n const [name, value] = getUserAgentHeader();\r\n headers[name] = value;\r\n\r\n const deleteOptions: HttpRequest = {\r\n headers: { ...headers, ...this._options.headers },\r\n timeout: this._options.timeout,\r\n withCredentials: this._options.withCredentials,\r\n };\r\n const token = await this._getAccessToken();\r\n this._updateHeaderToken(deleteOptions, token);\r\n await this._httpClient.delete(this._url!, deleteOptions);\r\n\r\n this._logger.log(LogLevel.Trace, \"(LongPolling transport) DELETE request sent.\");\r\n } finally {\r\n this._logger.log(LogLevel.Trace, \"(LongPolling transport) Stop finished.\");\r\n\r\n // Raise close event here instead of in polling\r\n // It needs to happen after the DELETE request is sent\r\n this._raiseOnClose();\r\n }\r\n }\r\n\r\n private _raiseOnClose() {\r\n if (this.onclose) {\r\n let logMessage = \"(LongPolling transport) Firing onclose event.\";\r\n if (this._closeError) {\r\n logMessage += \" Error: \" + this._closeError;\r\n }\r\n this._logger.log(LogLevel.Trace, logMessage);\r\n this.onclose(this._closeError);\r\n }\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { HttpClient } from \"./HttpClient\";\r\nimport { MessageHeaders } from \"./IHubProtocol\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { ITransport, TransferFormat } from \"./ITransport\";\r\nimport { Arg, getDataDetail, getUserAgentHeader, Platform, sendMessage } from \"./Utils\";\r\nimport { IHttpConnectionOptions } from \"./IHttpConnectionOptions\";\r\n\r\n/** @private */\r\nexport class ServerSentEventsTransport implements ITransport {\r\n private readonly _httpClient: HttpClient;\r\n private readonly _accessTokenFactory: (() => string | Promise) | undefined;\r\n private readonly _logger: ILogger;\r\n private readonly _options: IHttpConnectionOptions;\r\n private _eventSource?: EventSource;\r\n private _url?: string;\r\n\r\n public onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n public onclose: ((error?: Error) => void) | null;\r\n\r\n constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise) | undefined, logger: ILogger,\r\n options: IHttpConnectionOptions) {\r\n this._httpClient = httpClient;\r\n this._accessTokenFactory = accessTokenFactory;\r\n this._logger = logger;\r\n this._options = options;\r\n\r\n this.onreceive = null;\r\n this.onclose = null;\r\n }\r\n\r\n public async connect(url: string, transferFormat: TransferFormat): Promise {\r\n Arg.isRequired(url, \"url\");\r\n Arg.isRequired(transferFormat, \"transferFormat\");\r\n Arg.isIn(transferFormat, TransferFormat, \"transferFormat\");\r\n\r\n this._logger.log(LogLevel.Trace, \"(SSE transport) Connecting.\");\r\n\r\n // set url before accessTokenFactory because this.url is only for send and we set the auth header instead of the query string for send\r\n this._url = url;\r\n\r\n if (this._accessTokenFactory) {\r\n const token = await this._accessTokenFactory();\r\n if (token) {\r\n url += (url.indexOf(\"?\") < 0 ? \"?\" : \"&\") + `access_token=${encodeURIComponent(token)}`;\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n let opened = false;\r\n if (transferFormat !== TransferFormat.Text) {\r\n reject(new Error(\"The Server-Sent Events transport only supports the 'Text' transfer format\"));\r\n return;\r\n }\r\n\r\n let eventSource: EventSource;\r\n if (Platform.isBrowser || Platform.isWebWorker) {\r\n eventSource = new this._options.EventSource!(url, { withCredentials: this._options.withCredentials });\r\n } else {\r\n // Non-browser passes cookies via the dictionary\r\n const cookies = this._httpClient.getCookieString(url);\r\n const headers: MessageHeaders = {};\r\n headers.Cookie = cookies;\r\n const [name, value] = getUserAgentHeader();\r\n headers[name] = value;\r\n\r\n eventSource = new this._options.EventSource!(url, { withCredentials: this._options.withCredentials, headers: { ...headers, ...this._options.headers} } as EventSourceInit);\r\n }\r\n\r\n try {\r\n eventSource.onmessage = (e: MessageEvent) => {\r\n if (this.onreceive) {\r\n try {\r\n this._logger.log(LogLevel.Trace, `(SSE transport) data received. ${getDataDetail(e.data, this._options.logMessageContent!)}.`);\r\n this.onreceive(e.data);\r\n } catch (error) {\r\n this._close(error);\r\n return;\r\n }\r\n }\r\n };\r\n\r\n // @ts-ignore: not using event on purpose\r\n eventSource.onerror = (e: Event) => {\r\n // EventSource doesn't give any useful information about server side closes.\r\n if (opened) {\r\n this._close();\r\n } else {\r\n reject(new Error(\"EventSource failed to connect. The connection could not be found on the server,\"\r\n + \" either the connection ID is not present on the server, or a proxy is refusing/buffering the connection.\"\r\n + \" If you have multiple servers check that sticky sessions are enabled.\"));\r\n }\r\n };\r\n\r\n eventSource.onopen = () => {\r\n this._logger.log(LogLevel.Information, `SSE connected to ${this._url}`);\r\n this._eventSource = eventSource;\r\n opened = true;\r\n resolve();\r\n };\r\n } catch (e) {\r\n reject(e);\r\n return;\r\n }\r\n });\r\n }\r\n\r\n public async send(data: any): Promise {\r\n if (!this._eventSource) {\r\n return Promise.reject(new Error(\"Cannot send until the transport is connected\"));\r\n }\r\n return sendMessage(this._logger, \"SSE\", this._httpClient, this._url!, this._accessTokenFactory, data, this._options);\r\n }\r\n\r\n public stop(): Promise {\r\n this._close();\r\n return Promise.resolve();\r\n }\r\n\r\n private _close(e?: Error) {\r\n if (this._eventSource) {\r\n this._eventSource.close();\r\n this._eventSource = undefined;\r\n\r\n if (this.onclose) {\r\n this.onclose(e);\r\n }\r\n }\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { HeaderNames } from \"./HeaderNames\";\r\nimport { HttpClient } from \"./HttpClient\";\r\nimport { MessageHeaders } from \"./IHubProtocol\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { ITransport, TransferFormat } from \"./ITransport\";\r\nimport { WebSocketConstructor } from \"./Polyfills\";\r\nimport { Arg, getDataDetail, getUserAgentHeader, Platform } from \"./Utils\";\r\n\r\n/** @private */\r\nexport class WebSocketTransport implements ITransport {\r\n private readonly _logger: ILogger;\r\n private readonly _accessTokenFactory: (() => string | Promise) | undefined;\r\n private readonly _logMessageContent: boolean;\r\n private readonly _webSocketConstructor: WebSocketConstructor;\r\n private readonly _httpClient: HttpClient;\r\n private _webSocket?: WebSocket;\r\n private _headers: MessageHeaders;\r\n\r\n public onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n public onclose: ((error?: Error) => void) | null;\r\n\r\n constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise) | undefined, logger: ILogger,\r\n logMessageContent: boolean, webSocketConstructor: WebSocketConstructor, headers: MessageHeaders) {\r\n this._logger = logger;\r\n this._accessTokenFactory = accessTokenFactory;\r\n this._logMessageContent = logMessageContent;\r\n this._webSocketConstructor = webSocketConstructor;\r\n this._httpClient = httpClient;\r\n\r\n this.onreceive = null;\r\n this.onclose = null;\r\n this._headers = headers;\r\n }\r\n\r\n public async connect(url: string, transferFormat: TransferFormat): Promise {\r\n Arg.isRequired(url, \"url\");\r\n Arg.isRequired(transferFormat, \"transferFormat\");\r\n Arg.isIn(transferFormat, TransferFormat, \"transferFormat\");\r\n this._logger.log(LogLevel.Trace, \"(WebSockets transport) Connecting.\");\r\n\r\n if (this._accessTokenFactory) {\r\n const token = await this._accessTokenFactory();\r\n if (token) {\r\n url += (url.indexOf(\"?\") < 0 ? \"?\" : \"&\") + `access_token=${encodeURIComponent(token)}`;\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n url = url.replace(/^http/, \"ws\");\r\n let webSocket: WebSocket | undefined;\r\n const cookies = this._httpClient.getCookieString(url);\r\n let opened = false;\r\n\r\n if (Platform.isNode) {\r\n const headers: {[k: string]: string} = {};\r\n const [name, value] = getUserAgentHeader();\r\n headers[name] = value;\r\n\r\n if (cookies) {\r\n headers[HeaderNames.Cookie] = `${cookies}`;\r\n }\r\n\r\n // Only pass headers when in non-browser environments\r\n webSocket = new this._webSocketConstructor(url, undefined, {\r\n headers: { ...headers, ...this._headers },\r\n });\r\n }\r\n\r\n if (!webSocket) {\r\n // Chrome is not happy with passing 'undefined' as protocol\r\n webSocket = new this._webSocketConstructor(url);\r\n }\r\n\r\n if (transferFormat === TransferFormat.Binary) {\r\n webSocket.binaryType = \"arraybuffer\";\r\n }\r\n\r\n webSocket.onopen = (_event: Event) => {\r\n this._logger.log(LogLevel.Information, `WebSocket connected to ${url}.`);\r\n this._webSocket = webSocket;\r\n opened = true;\r\n resolve();\r\n };\r\n\r\n webSocket.onerror = (event: Event) => {\r\n let error: any = null;\r\n // ErrorEvent is a browser only type we need to check if the type exists before using it\r\n if (typeof ErrorEvent !== \"undefined\" && event instanceof ErrorEvent) {\r\n error = event.error;\r\n } else {\r\n error = \"There was an error with the transport\";\r\n }\r\n\r\n this._logger.log(LogLevel.Information, `(WebSockets transport) ${error}.`);\r\n };\r\n\r\n webSocket.onmessage = (message: MessageEvent) => {\r\n this._logger.log(LogLevel.Trace, `(WebSockets transport) data received. ${getDataDetail(message.data, this._logMessageContent)}.`);\r\n if (this.onreceive) {\r\n try {\r\n this.onreceive(message.data);\r\n } catch (error) {\r\n this._close(error);\r\n return;\r\n }\r\n }\r\n };\r\n\r\n webSocket.onclose = (event: CloseEvent) => {\r\n // Don't call close handler if connection was never established\r\n // We'll reject the connect call instead\r\n if (opened) {\r\n this._close(event);\r\n } else {\r\n let error: any = null;\r\n // ErrorEvent is a browser only type we need to check if the type exists before using it\r\n if (typeof ErrorEvent !== \"undefined\" && event instanceof ErrorEvent) {\r\n error = event.error;\r\n } else {\r\n error = \"WebSocket failed to connect. The connection could not be found on the server,\"\r\n + \" either the endpoint may not be a SignalR endpoint,\"\r\n + \" the connection ID is not present on the server, or there is a proxy blocking WebSockets.\"\r\n + \" If you have multiple servers check that sticky sessions are enabled.\";\r\n }\r\n\r\n reject(new Error(error));\r\n }\r\n };\r\n });\r\n }\r\n\r\n public send(data: any): Promise {\r\n if (this._webSocket && this._webSocket.readyState === this._webSocketConstructor.OPEN) {\r\n this._logger.log(LogLevel.Trace, `(WebSockets transport) sending data. ${getDataDetail(data, this._logMessageContent)}.`);\r\n this._webSocket.send(data);\r\n return Promise.resolve();\r\n }\r\n\r\n return Promise.reject(\"WebSocket is not in the OPEN state\");\r\n }\r\n\r\n public stop(): Promise {\r\n if (this._webSocket) {\r\n // Manually invoke onclose callback inline so we know the HttpConnection was closed properly before returning\r\n // This also solves an issue where websocket.onclose could take 18+ seconds to trigger during network disconnects\r\n this._close(undefined);\r\n }\r\n\r\n return Promise.resolve();\r\n }\r\n\r\n private _close(event?: CloseEvent | Error): void {\r\n // webSocket will be null if the transport did not start successfully\r\n if (this._webSocket) {\r\n // Clear websocket handlers because we are considering the socket closed now\r\n this._webSocket.onclose = () => {};\r\n this._webSocket.onmessage = () => {};\r\n this._webSocket.onerror = () => {};\r\n this._webSocket.close();\r\n this._webSocket = undefined;\r\n }\r\n\r\n this._logger.log(LogLevel.Trace, \"(WebSockets transport) socket closed.\");\r\n if (this.onclose) {\r\n if (this._isCloseEvent(event) && (event.wasClean === false || event.code !== 1000)) {\r\n this.onclose(new Error(`WebSocket closed with status code: ${event.code} (${event.reason || \"no reason given\"}).`));\r\n } else if (event instanceof Error) {\r\n this.onclose(event);\r\n } else {\r\n this.onclose();\r\n }\r\n }\r\n }\r\n\r\n private _isCloseEvent(event?: any): event is CloseEvent {\r\n return event && typeof event.wasClean === \"boolean\" && typeof event.code === \"number\";\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { DefaultHttpClient } from \"./DefaultHttpClient\";\r\nimport { AggregateErrors, DisabledTransportError, FailedToNegotiateWithServerError, FailedToStartTransportError, HttpError, UnsupportedTransportError } from \"./Errors\";\r\nimport { HeaderNames } from \"./HeaderNames\";\r\nimport { HttpClient } from \"./HttpClient\";\r\nimport { IConnection } from \"./IConnection\";\r\nimport { IHttpConnectionOptions } from \"./IHttpConnectionOptions\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { HttpTransportType, ITransport, TransferFormat } from \"./ITransport\";\r\nimport { LongPollingTransport } from \"./LongPollingTransport\";\r\nimport { ServerSentEventsTransport } from \"./ServerSentEventsTransport\";\r\nimport { Arg, createLogger, getUserAgentHeader, Platform } from \"./Utils\";\r\nimport { WebSocketTransport } from \"./WebSocketTransport\";\r\n\r\n/** @private */\r\nconst enum ConnectionState {\r\n Connecting = \"Connecting\",\r\n Connected = \"Connected\",\r\n Disconnected = \"Disconnected\",\r\n Disconnecting = \"Disconnecting\",\r\n}\r\n\r\n/** @private */\r\nexport interface INegotiateResponse {\r\n connectionId?: string;\r\n connectionToken?: string;\r\n negotiateVersion?: number;\r\n availableTransports?: IAvailableTransport[];\r\n url?: string;\r\n accessToken?: string;\r\n error?: string;\r\n}\r\n\r\n/** @private */\r\nexport interface IAvailableTransport {\r\n transport: keyof typeof HttpTransportType;\r\n transferFormats: (keyof typeof TransferFormat)[];\r\n}\r\n\r\nconst MAX_REDIRECTS = 100;\r\n\r\n/** @private */\r\nexport class HttpConnection implements IConnection {\r\n private _connectionState: ConnectionState;\r\n // connectionStarted is tracked independently from connectionState, so we can check if the\r\n // connection ever did successfully transition from connecting to connected before disconnecting.\r\n private _connectionStarted: boolean;\r\n private readonly _httpClient: HttpClient;\r\n private readonly _logger: ILogger;\r\n private readonly _options: IHttpConnectionOptions;\r\n // Needs to not start with _ to be available for tests\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private transport?: ITransport;\r\n private _startInternalPromise?: Promise;\r\n private _stopPromise?: Promise;\r\n private _stopPromiseResolver: (value?: PromiseLike) => void = () => {};\r\n private _stopError?: Error;\r\n private _accessTokenFactory?: () => string | Promise;\r\n private _sendQueue?: TransportSendQueue;\r\n\r\n public readonly features: any = {};\r\n public baseUrl: string;\r\n public connectionId?: string;\r\n public onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n public onclose: ((e?: Error) => void) | null;\r\n\r\n private readonly _negotiateVersion: number = 1;\r\n\r\n constructor(url: string, options: IHttpConnectionOptions = {}) {\r\n Arg.isRequired(url, \"url\");\r\n\r\n this._logger = createLogger(options.logger);\r\n this.baseUrl = this._resolveUrl(url);\r\n\r\n options = options || {};\r\n options.logMessageContent = options.logMessageContent === undefined ? false : options.logMessageContent;\r\n if (typeof options.withCredentials === \"boolean\" || options.withCredentials === undefined) {\r\n options.withCredentials = options.withCredentials === undefined ? true : options.withCredentials;\r\n } else {\r\n throw new Error(\"withCredentials option was not a 'boolean' or 'undefined' value\");\r\n }\r\n options.timeout = options.timeout === undefined ? 100 * 1000 : options.timeout;\r\n\r\n let webSocketModule: any = null;\r\n let eventSourceModule: any = null;\r\n\r\n if (Platform.isNode && typeof require !== \"undefined\") {\r\n // In order to ignore the dynamic require in webpack builds we need to do this magic\r\n // @ts-ignore: TS doesn't know about these names\r\n const requireFunc = typeof __webpack_require__ === \"function\" ? __non_webpack_require__ : require;\r\n webSocketModule = requireFunc(\"ws\");\r\n eventSourceModule = requireFunc(\"eventsource\");\r\n }\r\n\r\n if (!Platform.isNode && typeof WebSocket !== \"undefined\" && !options.WebSocket) {\r\n options.WebSocket = WebSocket;\r\n } else if (Platform.isNode && !options.WebSocket) {\r\n if (webSocketModule) {\r\n options.WebSocket = webSocketModule;\r\n }\r\n }\r\n\r\n if (!Platform.isNode && typeof EventSource !== \"undefined\" && !options.EventSource) {\r\n options.EventSource = EventSource;\r\n } else if (Platform.isNode && !options.EventSource) {\r\n if (typeof eventSourceModule !== \"undefined\") {\r\n options.EventSource = eventSourceModule;\r\n }\r\n }\r\n\r\n this._httpClient = options.httpClient || new DefaultHttpClient(this._logger);\r\n this._connectionState = ConnectionState.Disconnected;\r\n this._connectionStarted = false;\r\n this._options = options;\r\n\r\n this.onreceive = null;\r\n this.onclose = null;\r\n }\r\n\r\n public start(): Promise;\r\n public start(transferFormat: TransferFormat): Promise;\r\n public async start(transferFormat?: TransferFormat): Promise {\r\n transferFormat = transferFormat || TransferFormat.Binary;\r\n\r\n Arg.isIn(transferFormat, TransferFormat, \"transferFormat\");\r\n\r\n this._logger.log(LogLevel.Debug, `Starting connection with transfer format '${TransferFormat[transferFormat]}'.`);\r\n\r\n if (this._connectionState !== ConnectionState.Disconnected) {\r\n return Promise.reject(new Error(\"Cannot start an HttpConnection that is not in the 'Disconnected' state.\"));\r\n }\r\n\r\n this._connectionState = ConnectionState.Connecting;\r\n\r\n this._startInternalPromise = this._startInternal(transferFormat);\r\n await this._startInternalPromise;\r\n\r\n // The TypeScript compiler thinks that connectionState must be Connecting here. The TypeScript compiler is wrong.\r\n if (this._connectionState as any === ConnectionState.Disconnecting) {\r\n // stop() was called and transitioned the client into the Disconnecting state.\r\n const message = \"Failed to start the HttpConnection before stop() was called.\";\r\n this._logger.log(LogLevel.Error, message);\r\n\r\n // We cannot await stopPromise inside startInternal since stopInternal awaits the startInternalPromise.\r\n await this._stopPromise;\r\n\r\n return Promise.reject(new Error(message));\r\n } else if (this._connectionState as any !== ConnectionState.Connected) {\r\n // stop() was called and transitioned the client into the Disconnecting state.\r\n const message = \"HttpConnection.startInternal completed gracefully but didn't enter the connection into the connected state!\";\r\n this._logger.log(LogLevel.Error, message);\r\n return Promise.reject(new Error(message));\r\n }\r\n\r\n this._connectionStarted = true;\r\n }\r\n\r\n public send(data: string | ArrayBuffer): Promise {\r\n if (this._connectionState !== ConnectionState.Connected) {\r\n return Promise.reject(new Error(\"Cannot send data if the connection is not in the 'Connected' State.\"));\r\n }\r\n\r\n if (!this._sendQueue) {\r\n this._sendQueue = new TransportSendQueue(this.transport!);\r\n }\r\n\r\n // Transport will not be null if state is connected\r\n return this._sendQueue.send(data);\r\n }\r\n\r\n public async stop(error?: Error): Promise {\r\n if (this._connectionState === ConnectionState.Disconnected) {\r\n this._logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnected state.`);\r\n return Promise.resolve();\r\n }\r\n\r\n if (this._connectionState === ConnectionState.Disconnecting) {\r\n this._logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnecting state.`);\r\n return this._stopPromise;\r\n }\r\n\r\n this._connectionState = ConnectionState.Disconnecting;\r\n\r\n this._stopPromise = new Promise((resolve) => {\r\n // Don't complete stop() until stopConnection() completes.\r\n this._stopPromiseResolver = resolve;\r\n });\r\n\r\n // stopInternal should never throw so just observe it.\r\n await this._stopInternal(error);\r\n await this._stopPromise;\r\n }\r\n\r\n private async _stopInternal(error?: Error): Promise {\r\n // Set error as soon as possible otherwise there is a race between\r\n // the transport closing and providing an error and the error from a close message\r\n // We would prefer the close message error.\r\n this._stopError = error;\r\n\r\n try {\r\n await this._startInternalPromise;\r\n } catch (e) {\r\n // This exception is returned to the user as a rejected Promise from the start method.\r\n }\r\n\r\n // The transport's onclose will trigger stopConnection which will run our onclose event.\r\n // The transport should always be set if currently connected. If it wasn't set, it's likely because\r\n // stop was called during start() and start() failed.\r\n if (this.transport) {\r\n try {\r\n await this.transport.stop();\r\n } catch (e) {\r\n this._logger.log(LogLevel.Error, `HttpConnection.transport.stop() threw error '${e}'.`);\r\n this._stopConnection();\r\n }\r\n\r\n this.transport = undefined;\r\n } else {\r\n this._logger.log(LogLevel.Debug, \"HttpConnection.transport is undefined in HttpConnection.stop() because start() failed.\");\r\n }\r\n }\r\n\r\n private async _startInternal(transferFormat: TransferFormat): Promise {\r\n // Store the original base url and the access token factory since they may change\r\n // as part of negotiating\r\n let url = this.baseUrl;\r\n this._accessTokenFactory = this._options.accessTokenFactory;\r\n\r\n try {\r\n if (this._options.skipNegotiation) {\r\n if (this._options.transport === HttpTransportType.WebSockets) {\r\n // No need to add a connection ID in this case\r\n this.transport = this._constructTransport(HttpTransportType.WebSockets);\r\n // We should just call connect directly in this case.\r\n // No fallback or negotiate in this case.\r\n await this._startTransport(url, transferFormat);\r\n } else {\r\n throw new Error(\"Negotiation can only be skipped when using the WebSocket transport directly.\");\r\n }\r\n } else {\r\n let negotiateResponse: INegotiateResponse | null = null;\r\n let redirects = 0;\r\n\r\n do {\r\n negotiateResponse = await this._getNegotiationResponse(url);\r\n // the user tries to stop the connection when it is being started\r\n if (this._connectionState === ConnectionState.Disconnecting || this._connectionState === ConnectionState.Disconnected) {\r\n throw new Error(\"The connection was stopped during negotiation.\");\r\n }\r\n\r\n if (negotiateResponse.error) {\r\n throw new Error(negotiateResponse.error);\r\n }\r\n\r\n if ((negotiateResponse as any).ProtocolVersion) {\r\n throw new Error(\"Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.\");\r\n }\r\n\r\n if (negotiateResponse.url) {\r\n url = negotiateResponse.url;\r\n }\r\n\r\n if (negotiateResponse.accessToken) {\r\n // Replace the current access token factory with one that uses\r\n // the returned access token\r\n const accessToken = negotiateResponse.accessToken;\r\n this._accessTokenFactory = () => accessToken;\r\n }\r\n\r\n redirects++;\r\n }\r\n while (negotiateResponse.url && redirects < MAX_REDIRECTS);\r\n\r\n if (redirects === MAX_REDIRECTS && negotiateResponse.url) {\r\n throw new Error(\"Negotiate redirection limit exceeded.\");\r\n }\r\n\r\n await this._createTransport(url, this._options.transport, negotiateResponse, transferFormat);\r\n }\r\n\r\n if (this.transport instanceof LongPollingTransport) {\r\n this.features.inherentKeepAlive = true;\r\n }\r\n\r\n if (this._connectionState === ConnectionState.Connecting) {\r\n // Ensure the connection transitions to the connected state prior to completing this.startInternalPromise.\r\n // start() will handle the case when stop was called and startInternal exits still in the disconnecting state.\r\n this._logger.log(LogLevel.Debug, \"The HttpConnection connected successfully.\");\r\n this._connectionState = ConnectionState.Connected;\r\n }\r\n\r\n // stop() is waiting on us via this.startInternalPromise so keep this.transport around so it can clean up.\r\n // This is the only case startInternal can exit in neither the connected nor disconnected state because stopConnection()\r\n // will transition to the disconnected state. start() will wait for the transition using the stopPromise.\r\n } catch (e) {\r\n this._logger.log(LogLevel.Error, \"Failed to start the connection: \" + e);\r\n this._connectionState = ConnectionState.Disconnected;\r\n this.transport = undefined;\r\n\r\n // if start fails, any active calls to stop assume that start will complete the stop promise\r\n this._stopPromiseResolver();\r\n return Promise.reject(e);\r\n }\r\n }\r\n\r\n private async _getNegotiationResponse(url: string): Promise {\r\n const headers: {[k: string]: string} = {};\r\n if (this._accessTokenFactory) {\r\n const token = await this._accessTokenFactory();\r\n if (token) {\r\n headers[HeaderNames.Authorization] = `Bearer ${token}`;\r\n }\r\n }\r\n\r\n const [name, value] = getUserAgentHeader();\r\n headers[name] = value;\r\n\r\n const negotiateUrl = this._resolveNegotiateUrl(url);\r\n this._logger.log(LogLevel.Debug, `Sending negotiation request: ${negotiateUrl}.`);\r\n try {\r\n const response = await this._httpClient.post(negotiateUrl, {\r\n content: \"\",\r\n headers: { ...headers, ...this._options.headers },\r\n timeout: this._options.timeout,\r\n withCredentials: this._options.withCredentials,\r\n });\r\n\r\n if (response.statusCode !== 200) {\r\n return Promise.reject(new Error(`Unexpected status code returned from negotiate '${response.statusCode}'`));\r\n }\r\n\r\n const negotiateResponse = JSON.parse(response.content as string) as INegotiateResponse;\r\n if (!negotiateResponse.negotiateVersion || negotiateResponse.negotiateVersion < 1) {\r\n // Negotiate version 0 doesn't use connectionToken\r\n // So we set it equal to connectionId so all our logic can use connectionToken without being aware of the negotiate version\r\n negotiateResponse.connectionToken = negotiateResponse.connectionId;\r\n }\r\n return negotiateResponse;\r\n } catch (e) {\r\n let errorMessage = \"Failed to complete negotiation with the server: \" + e;\r\n if (e instanceof HttpError) {\r\n if (e.statusCode === 404) {\r\n errorMessage = errorMessage + \" Either this is not a SignalR endpoint or there is a proxy blocking the connection.\";\r\n }\r\n }\r\n this._logger.log(LogLevel.Error, errorMessage);\r\n\r\n return Promise.reject(new FailedToNegotiateWithServerError(errorMessage));\r\n }\r\n }\r\n\r\n private _createConnectUrl(url: string, connectionToken: string | null | undefined) {\r\n if (!connectionToken) {\r\n return url;\r\n }\r\n\r\n return url + (url.indexOf(\"?\") === -1 ? \"?\" : \"&\") + `id=${connectionToken}`;\r\n }\r\n\r\n private async _createTransport(url: string, requestedTransport: HttpTransportType | ITransport | undefined, negotiateResponse: INegotiateResponse, requestedTransferFormat: TransferFormat): Promise {\r\n let connectUrl = this._createConnectUrl(url, negotiateResponse.connectionToken);\r\n if (this._isITransport(requestedTransport)) {\r\n this._logger.log(LogLevel.Debug, \"Connection was provided an instance of ITransport, using that directly.\");\r\n this.transport = requestedTransport;\r\n await this._startTransport(connectUrl, requestedTransferFormat);\r\n\r\n this.connectionId = negotiateResponse.connectionId;\r\n return;\r\n }\r\n\r\n const transportExceptions: any[] = [];\r\n const transports = negotiateResponse.availableTransports || [];\r\n let negotiate: INegotiateResponse | undefined = negotiateResponse;\r\n for (const endpoint of transports) {\r\n const transportOrError = this._resolveTransportOrError(endpoint, requestedTransport, requestedTransferFormat);\r\n if (transportOrError instanceof Error) {\r\n // Store the error and continue, we don't want to cause a re-negotiate in these cases\r\n transportExceptions.push(`${endpoint.transport} failed:`);\r\n transportExceptions.push(transportOrError);\r\n } else if (this._isITransport(transportOrError)) {\r\n this.transport = transportOrError;\r\n if (!negotiate) {\r\n try {\r\n negotiate = await this._getNegotiationResponse(url);\r\n } catch (ex) {\r\n return Promise.reject(ex);\r\n }\r\n connectUrl = this._createConnectUrl(url, negotiate.connectionToken);\r\n }\r\n try {\r\n await this._startTransport(connectUrl, requestedTransferFormat);\r\n this.connectionId = negotiate.connectionId;\r\n return;\r\n } catch (ex) {\r\n this._logger.log(LogLevel.Error, `Failed to start the transport '${endpoint.transport}': ${ex}`);\r\n negotiate = undefined;\r\n transportExceptions.push(new FailedToStartTransportError(`${endpoint.transport} failed: ${ex}`, HttpTransportType[endpoint.transport]));\r\n\r\n if (this._connectionState !== ConnectionState.Connecting) {\r\n const message = \"Failed to select transport before stop() was called.\";\r\n this._logger.log(LogLevel.Debug, message);\r\n return Promise.reject(new Error(message));\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (transportExceptions.length > 0) {\r\n return Promise.reject(new AggregateErrors(`Unable to connect to the server with any of the available transports. ${transportExceptions.join(\" \")}`, transportExceptions));\r\n }\r\n return Promise.reject(new Error(\"None of the transports supported by the client are supported by the server.\"));\r\n }\r\n\r\n private _constructTransport(transport: HttpTransportType): ITransport {\r\n switch (transport) {\r\n case HttpTransportType.WebSockets:\r\n if (!this._options.WebSocket) {\r\n throw new Error(\"'WebSocket' is not supported in your environment.\");\r\n }\r\n return new WebSocketTransport(this._httpClient, this._accessTokenFactory, this._logger, this._options.logMessageContent!, this._options.WebSocket, this._options.headers || {});\r\n case HttpTransportType.ServerSentEvents:\r\n if (!this._options.EventSource) {\r\n throw new Error(\"'EventSource' is not supported in your environment.\");\r\n }\r\n return new ServerSentEventsTransport(this._httpClient, this._accessTokenFactory, this._logger, this._options);\r\n case HttpTransportType.LongPolling:\r\n return new LongPollingTransport(this._httpClient, this._accessTokenFactory, this._logger, this._options);\r\n default:\r\n throw new Error(`Unknown transport: ${transport}.`);\r\n }\r\n }\r\n\r\n private _startTransport(url: string, transferFormat: TransferFormat): Promise {\r\n this.transport!.onreceive = this.onreceive;\r\n this.transport!.onclose = (e) => this._stopConnection(e);\r\n return this.transport!.connect(url, transferFormat);\r\n }\r\n\r\n private _resolveTransportOrError(endpoint: IAvailableTransport, requestedTransport: HttpTransportType | undefined, requestedTransferFormat: TransferFormat): ITransport | Error {\r\n const transport = HttpTransportType[endpoint.transport];\r\n if (transport === null || transport === undefined) {\r\n this._logger.log(LogLevel.Debug, `Skipping transport '${endpoint.transport}' because it is not supported by this client.`);\r\n return new Error(`Skipping transport '${endpoint.transport}' because it is not supported by this client.`);\r\n } else {\r\n if (transportMatches(requestedTransport, transport)) {\r\n const transferFormats = endpoint.transferFormats.map((s) => TransferFormat[s]);\r\n if (transferFormats.indexOf(requestedTransferFormat) >= 0) {\r\n if ((transport === HttpTransportType.WebSockets && !this._options.WebSocket) ||\r\n (transport === HttpTransportType.ServerSentEvents && !this._options.EventSource)) {\r\n this._logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it is not supported in your environment.'`);\r\n return new UnsupportedTransportError(`'${HttpTransportType[transport]}' is not supported in your environment.`, transport);\r\n } else {\r\n this._logger.log(LogLevel.Debug, `Selecting transport '${HttpTransportType[transport]}'.`);\r\n try {\r\n return this._constructTransport(transport);\r\n } catch (ex) {\r\n return ex;\r\n }\r\n }\r\n } else {\r\n this._logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it does not support the requested transfer format '${TransferFormat[requestedTransferFormat]}'.`);\r\n return new Error(`'${HttpTransportType[transport]}' does not support ${TransferFormat[requestedTransferFormat]}.`);\r\n }\r\n } else {\r\n this._logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it was disabled by the client.`);\r\n return new DisabledTransportError(`'${HttpTransportType[transport]}' is disabled by the client.`, transport);\r\n }\r\n }\r\n }\r\n\r\n private _isITransport(transport: any): transport is ITransport {\r\n return transport && typeof (transport) === \"object\" && \"connect\" in transport;\r\n }\r\n\r\n private _stopConnection(error?: Error): void {\r\n this._logger.log(LogLevel.Debug, `HttpConnection.stopConnection(${error}) called while in state ${this._connectionState}.`);\r\n\r\n this.transport = undefined;\r\n\r\n // If we have a stopError, it takes precedence over the error from the transport\r\n error = this._stopError || error;\r\n this._stopError = undefined;\r\n\r\n if (this._connectionState === ConnectionState.Disconnected) {\r\n this._logger.log(LogLevel.Debug, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection is already in the disconnected state.`);\r\n return;\r\n }\r\n\r\n if (this._connectionState === ConnectionState.Connecting) {\r\n this._logger.log(LogLevel.Warning, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection is still in the connecting state.`);\r\n throw new Error(`HttpConnection.stopConnection(${error}) was called while the connection is still in the connecting state.`);\r\n }\r\n\r\n if (this._connectionState === ConnectionState.Disconnecting) {\r\n // A call to stop() induced this call to stopConnection and needs to be completed.\r\n // Any stop() awaiters will be scheduled to continue after the onclose callback fires.\r\n this._stopPromiseResolver();\r\n }\r\n\r\n if (error) {\r\n this._logger.log(LogLevel.Error, `Connection disconnected with error '${error}'.`);\r\n } else {\r\n this._logger.log(LogLevel.Information, \"Connection disconnected.\");\r\n }\r\n\r\n if (this._sendQueue) {\r\n this._sendQueue.stop().catch((e) => {\r\n this._logger.log(LogLevel.Error, `TransportSendQueue.stop() threw error '${e}'.`);\r\n });\r\n this._sendQueue = undefined;\r\n }\r\n\r\n this.connectionId = undefined;\r\n this._connectionState = ConnectionState.Disconnected;\r\n\r\n if (this._connectionStarted) {\r\n this._connectionStarted = false;\r\n try {\r\n if (this.onclose) {\r\n this.onclose(error);\r\n }\r\n } catch (e) {\r\n this._logger.log(LogLevel.Error, `HttpConnection.onclose(${error}) threw error '${e}'.`);\r\n }\r\n }\r\n }\r\n\r\n private _resolveUrl(url: string): string {\r\n // startsWith is not supported in IE\r\n if (url.lastIndexOf(\"https://\", 0) === 0 || url.lastIndexOf(\"http://\", 0) === 0) {\r\n return url;\r\n }\r\n\r\n if (!Platform.isBrowser) {\r\n throw new Error(`Cannot resolve '${url}'.`);\r\n }\r\n\r\n // Setting the url to the href propery of an anchor tag handles normalization\r\n // for us. There are 3 main cases.\r\n // 1. Relative path normalization e.g \"b\" -> \"http://localhost:5000/a/b\"\r\n // 2. Absolute path normalization e.g \"/a/b\" -> \"http://localhost:5000/a/b\"\r\n // 3. Networkpath reference normalization e.g \"//localhost:5000/a/b\" -> \"http://localhost:5000/a/b\"\r\n const aTag = window.document.createElement(\"a\");\r\n aTag.href = url;\r\n\r\n this._logger.log(LogLevel.Information, `Normalizing '${url}' to '${aTag.href}'.`);\r\n return aTag.href;\r\n }\r\n\r\n private _resolveNegotiateUrl(url: string): string {\r\n const index = url.indexOf(\"?\");\r\n let negotiateUrl = url.substring(0, index === -1 ? url.length : index);\r\n if (negotiateUrl[negotiateUrl.length - 1] !== \"/\") {\r\n negotiateUrl += \"/\";\r\n }\r\n negotiateUrl += \"negotiate\";\r\n negotiateUrl += index === -1 ? \"\" : url.substring(index);\r\n\r\n if (negotiateUrl.indexOf(\"negotiateVersion\") === -1) {\r\n negotiateUrl += index === -1 ? \"?\" : \"&\";\r\n negotiateUrl += \"negotiateVersion=\" + this._negotiateVersion;\r\n }\r\n return negotiateUrl;\r\n }\r\n}\r\n\r\nfunction transportMatches(requestedTransport: HttpTransportType | undefined, actualTransport: HttpTransportType) {\r\n return !requestedTransport || ((actualTransport & requestedTransport) !== 0);\r\n}\r\n\r\n/** @private */\r\nexport class TransportSendQueue {\r\n private _buffer: any[] = [];\r\n private _sendBufferedData: PromiseSource;\r\n private _executing: boolean = true;\r\n private _transportResult?: PromiseSource;\r\n private _sendLoopPromise: Promise;\r\n\r\n constructor(private readonly _transport: ITransport) {\r\n this._sendBufferedData = new PromiseSource();\r\n this._transportResult = new PromiseSource();\r\n\r\n this._sendLoopPromise = this._sendLoop();\r\n }\r\n\r\n public send(data: string | ArrayBuffer): Promise {\r\n this._bufferData(data);\r\n if (!this._transportResult) {\r\n this._transportResult = new PromiseSource();\r\n }\r\n return this._transportResult.promise;\r\n }\r\n\r\n public stop(): Promise {\r\n this._executing = false;\r\n this._sendBufferedData.resolve();\r\n return this._sendLoopPromise;\r\n }\r\n\r\n private _bufferData(data: string | ArrayBuffer): void {\r\n if (this._buffer.length && typeof(this._buffer[0]) !== typeof(data)) {\r\n throw new Error(`Expected data to be of type ${typeof(this._buffer)} but was of type ${typeof(data)}`);\r\n }\r\n\r\n this._buffer.push(data);\r\n this._sendBufferedData.resolve();\r\n }\r\n\r\n private async _sendLoop(): Promise {\r\n while (true) {\r\n await this._sendBufferedData.promise;\r\n\r\n if (!this._executing) {\r\n if (this._transportResult) {\r\n this._transportResult.reject(\"Connection stopped.\");\r\n }\r\n\r\n break;\r\n }\r\n\r\n this._sendBufferedData = new PromiseSource();\r\n\r\n const transportResult = this._transportResult!;\r\n this._transportResult = undefined;\r\n\r\n const data = typeof(this._buffer[0]) === \"string\" ?\r\n this._buffer.join(\"\") :\r\n TransportSendQueue._concatBuffers(this._buffer);\r\n\r\n this._buffer.length = 0;\r\n\r\n try {\r\n await this._transport.send(data);\r\n transportResult.resolve();\r\n } catch (error) {\r\n transportResult.reject(error);\r\n }\r\n }\r\n }\r\n\r\n private static _concatBuffers(arrayBuffers: ArrayBuffer[]): ArrayBuffer {\r\n const totalLength = arrayBuffers.map((b) => b.byteLength).reduce((a, b) => a + b);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const item of arrayBuffers) {\r\n result.set(new Uint8Array(item), offset);\r\n offset += item.byteLength;\r\n }\r\n\r\n return result.buffer;\r\n }\r\n}\r\n\r\nclass PromiseSource {\r\n private _resolver?: () => void;\r\n private _rejecter!: (reason?: any) => void;\r\n public promise: Promise;\r\n\r\n constructor() {\r\n this.promise = new Promise((resolve, reject) => [this._resolver, this._rejecter] = [resolve, reject]);\r\n }\r\n\r\n public resolve(): void {\r\n this._resolver!();\r\n }\r\n\r\n public reject(reason?: any): void {\r\n this._rejecter!(reason);\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\n// Not exported from index\r\n/** @private */\r\nexport class TextMessageFormat {\r\n public static RecordSeparatorCode = 0x1e;\r\n public static RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode);\r\n\r\n public static write(output: string): string {\r\n return `${output}${TextMessageFormat.RecordSeparator}`;\r\n }\r\n\r\n public static parse(input: string): string[] {\r\n if (input[input.length - 1] !== TextMessageFormat.RecordSeparator) {\r\n throw new Error(\"Message is incomplete.\");\r\n }\r\n\r\n const messages = input.split(TextMessageFormat.RecordSeparator);\r\n messages.pop();\r\n return messages;\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { TextMessageFormat } from \"./TextMessageFormat\";\r\nimport { isArrayBuffer } from \"./Utils\";\r\n\r\n/** @private */\r\nexport interface HandshakeRequestMessage {\r\n readonly protocol: string;\r\n readonly version: number;\r\n}\r\n\r\n/** @private */\r\nexport interface HandshakeResponseMessage {\r\n readonly error: string;\r\n readonly minorVersion: number;\r\n}\r\n\r\n/** @private */\r\nexport class HandshakeProtocol {\r\n // Handshake request is always JSON\r\n public writeHandshakeRequest(handshakeRequest: HandshakeRequestMessage): string {\r\n return TextMessageFormat.write(JSON.stringify(handshakeRequest));\r\n }\r\n\r\n public parseHandshakeResponse(data: any): [any, HandshakeResponseMessage] {\r\n let messageData: string;\r\n let remainingData: any;\r\n\r\n if (isArrayBuffer(data)) {\r\n // Format is binary but still need to read JSON text from handshake response\r\n const binaryData = new Uint8Array(data);\r\n const separatorIndex = binaryData.indexOf(TextMessageFormat.RecordSeparatorCode);\r\n if (separatorIndex === -1) {\r\n throw new Error(\"Message is incomplete.\");\r\n }\r\n\r\n // content before separator is handshake response\r\n // optional content after is additional messages\r\n const responseLength = separatorIndex + 1;\r\n messageData = String.fromCharCode.apply(null, Array.prototype.slice.call(binaryData.slice(0, responseLength)));\r\n remainingData = (binaryData.byteLength > responseLength) ? binaryData.slice(responseLength).buffer : null;\r\n } else {\r\n const textData: string = data;\r\n const separatorIndex = textData.indexOf(TextMessageFormat.RecordSeparator);\r\n if (separatorIndex === -1) {\r\n throw new Error(\"Message is incomplete.\");\r\n }\r\n\r\n // content before separator is handshake response\r\n // optional content after is additional messages\r\n const responseLength = separatorIndex + 1;\r\n messageData = textData.substring(0, responseLength);\r\n remainingData = (textData.length > responseLength) ? textData.substring(responseLength) : null;\r\n }\r\n\r\n // At this point we should have just the single handshake message\r\n const messages = TextMessageFormat.parse(messageData);\r\n const response = JSON.parse(messages[0]);\r\n if (response.type) {\r\n throw new Error(\"Expected a handshake response from the server.\");\r\n }\r\n const responseMessage: HandshakeResponseMessage = response;\r\n\r\n // multiple messages could have arrived with handshake\r\n // return additional data to be parsed as usual, or null if all parsed\r\n return [remainingData, responseMessage];\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { ILogger } from \"./ILogger\";\r\nimport { TransferFormat } from \"./ITransport\";\r\n\r\n/** Defines the type of a Hub Message. */\r\nexport enum MessageType {\r\n /** Indicates the message is an Invocation message and implements the {@link @microsoft/signalr.InvocationMessage} interface. */\r\n Invocation = 1,\r\n /** Indicates the message is a StreamItem message and implements the {@link @microsoft/signalr.StreamItemMessage} interface. */\r\n StreamItem = 2,\r\n /** Indicates the message is a Completion message and implements the {@link @microsoft/signalr.CompletionMessage} interface. */\r\n Completion = 3,\r\n /** Indicates the message is a Stream Invocation message and implements the {@link @microsoft/signalr.StreamInvocationMessage} interface. */\r\n StreamInvocation = 4,\r\n /** Indicates the message is a Cancel Invocation message and implements the {@link @microsoft/signalr.CancelInvocationMessage} interface. */\r\n CancelInvocation = 5,\r\n /** Indicates the message is a Ping message and implements the {@link @microsoft/signalr.PingMessage} interface. */\r\n Ping = 6,\r\n /** Indicates the message is a Close message and implements the {@link @microsoft/signalr.CloseMessage} interface. */\r\n Close = 7,\r\n}\r\n\r\n/** Defines a dictionary of string keys and string values representing headers attached to a Hub message. */\r\nexport interface MessageHeaders {\r\n /** Gets or sets the header with the specified key. */\r\n [key: string]: string;\r\n}\r\n\r\n/** Union type of all known Hub messages. */\r\nexport type HubMessage =\r\n InvocationMessage |\r\n StreamInvocationMessage |\r\n StreamItemMessage |\r\n CompletionMessage |\r\n CancelInvocationMessage |\r\n PingMessage |\r\n CloseMessage;\r\n\r\n/** Defines properties common to all Hub messages. */\r\nexport interface HubMessageBase {\r\n /** A {@link @microsoft/signalr.MessageType} value indicating the type of this message. */\r\n readonly type: MessageType;\r\n}\r\n\r\n/** Defines properties common to all Hub messages relating to a specific invocation. */\r\nexport interface HubInvocationMessage extends HubMessageBase {\r\n /** A {@link @microsoft/signalr.MessageHeaders} dictionary containing headers attached to the message. */\r\n readonly headers?: MessageHeaders;\r\n /** The ID of the invocation relating to this message.\r\n *\r\n * This is expected to be present for {@link @microsoft/signalr.StreamInvocationMessage} and {@link @microsoft/signalr.CompletionMessage}. It may\r\n * be 'undefined' for an {@link @microsoft/signalr.InvocationMessage} if the sender does not expect a response.\r\n */\r\n readonly invocationId?: string;\r\n}\r\n\r\n/** A hub message representing a non-streaming invocation. */\r\nexport interface InvocationMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.Invocation;\r\n /** The target method name. */\r\n readonly target: string;\r\n /** The target method arguments. */\r\n readonly arguments: any[];\r\n /** The target methods stream IDs. */\r\n readonly streamIds?: string[];\r\n}\r\n\r\n/** A hub message representing a streaming invocation. */\r\nexport interface StreamInvocationMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.StreamInvocation;\r\n\r\n /** The invocation ID. */\r\n readonly invocationId: string;\r\n /** The target method name. */\r\n readonly target: string;\r\n /** The target method arguments. */\r\n readonly arguments: any[];\r\n /** The target methods stream IDs. */\r\n readonly streamIds?: string[];\r\n}\r\n\r\n/** A hub message representing a single item produced as part of a result stream. */\r\nexport interface StreamItemMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.StreamItem;\r\n\r\n /** The invocation ID. */\r\n readonly invocationId: string;\r\n\r\n /** The item produced by the server. */\r\n readonly item?: any;\r\n}\r\n\r\n/** A hub message representing the result of an invocation. */\r\nexport interface CompletionMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.Completion;\r\n /** The invocation ID. */\r\n readonly invocationId: string;\r\n /** The error produced by the invocation, if any.\r\n *\r\n * Either {@link @microsoft/signalr.CompletionMessage.error} or {@link @microsoft/signalr.CompletionMessage.result} must be defined, but not both.\r\n */\r\n readonly error?: string;\r\n /** The result produced by the invocation, if any.\r\n *\r\n * Either {@link @microsoft/signalr.CompletionMessage.error} or {@link @microsoft/signalr.CompletionMessage.result} must be defined, but not both.\r\n */\r\n readonly result?: any;\r\n}\r\n\r\n/** A hub message indicating that the sender is still active. */\r\nexport interface PingMessage extends HubMessageBase {\r\n /** @inheritDoc */\r\n readonly type: MessageType.Ping;\r\n}\r\n\r\n/** A hub message indicating that the sender is closing the connection.\r\n *\r\n * If {@link @microsoft/signalr.CloseMessage.error} is defined, the sender is closing the connection due to an error.\r\n */\r\nexport interface CloseMessage extends HubMessageBase {\r\n /** @inheritDoc */\r\n readonly type: MessageType.Close;\r\n /** The error that triggered the close, if any.\r\n *\r\n * If this property is undefined, the connection was closed normally and without error.\r\n */\r\n readonly error?: string;\r\n\r\n /** If true, clients with automatic reconnects enabled should attempt to reconnect after receiving the CloseMessage. Otherwise, they should not. */\r\n readonly allowReconnect?: boolean;\r\n}\r\n\r\n/** A hub message sent to request that a streaming invocation be canceled. */\r\nexport interface CancelInvocationMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.CancelInvocation;\r\n /** The invocation ID. */\r\n readonly invocationId: string;\r\n}\r\n\r\n/** A protocol abstraction for communicating with SignalR Hubs. */\r\nexport interface IHubProtocol {\r\n /** The name of the protocol. This is used by SignalR to resolve the protocol between the client and server. */\r\n readonly name: string;\r\n /** The version of the protocol. */\r\n readonly version: number;\r\n /** The {@link @microsoft/signalr.TransferFormat} of the protocol. */\r\n readonly transferFormat: TransferFormat;\r\n\r\n /** Creates an array of {@link @microsoft/signalr.HubMessage} objects from the specified serialized representation.\r\n *\r\n * If {@link @microsoft/signalr.IHubProtocol.transferFormat} is 'Text', the `input` parameter must be a string, otherwise it must be an ArrayBuffer.\r\n *\r\n * @param {string | ArrayBuffer} input A string or ArrayBuffer containing the serialized representation.\r\n * @param {ILogger} logger A logger that will be used to log messages that occur during parsing.\r\n */\r\n parseMessages(input: string | ArrayBuffer, logger: ILogger): HubMessage[];\r\n\r\n /** Writes the specified {@link @microsoft/signalr.HubMessage} to a string or ArrayBuffer and returns it.\r\n *\r\n * If {@link @microsoft/signalr.IHubProtocol.transferFormat} is 'Text', the result of this method will be a string, otherwise it will be an ArrayBuffer.\r\n *\r\n * @param {HubMessage} message The message to write.\r\n * @returns {string | ArrayBuffer} A string or ArrayBuffer containing the serialized representation of the message.\r\n */\r\n writeMessage(message: HubMessage): string | ArrayBuffer;\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { IStreamResult, IStreamSubscriber, ISubscription } from \"./Stream\";\r\nimport { SubjectSubscription } from \"./Utils\";\r\n\r\n/** Stream implementation to stream items to the server. */\r\nexport class Subject implements IStreamResult {\r\n /** @internal */\r\n public observers: IStreamSubscriber[];\r\n\r\n /** @internal */\r\n public cancelCallback?: () => Promise;\r\n\r\n constructor() {\r\n this.observers = [];\r\n }\r\n\r\n public next(item: T): void {\r\n for (const observer of this.observers) {\r\n observer.next(item);\r\n }\r\n }\r\n\r\n public error(err: any): void {\r\n for (const observer of this.observers) {\r\n if (observer.error) {\r\n observer.error(err);\r\n }\r\n }\r\n }\r\n\r\n public complete(): void {\r\n for (const observer of this.observers) {\r\n if (observer.complete) {\r\n observer.complete();\r\n }\r\n }\r\n }\r\n\r\n public subscribe(observer: IStreamSubscriber): ISubscription {\r\n this.observers.push(observer);\r\n return new SubjectSubscription(this, observer);\r\n }\r\n}\r\n","// Licensed to the .NET Foundation under one or more agreements.\r\n// The .NET Foundation licenses this file to you under the MIT license.\r\n\r\nimport { HandshakeProtocol, HandshakeRequestMessage, HandshakeResponseMessage } from \"./HandshakeProtocol\";\r\nimport { IConnection } from \"./IConnection\";\r\nimport { CancelInvocationMessage, CompletionMessage, IHubProtocol, InvocationMessage, MessageType, StreamInvocationMessage, StreamItemMessage } from \"./IHubProtocol\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { IRetryPolicy } from \"./IRetryPolicy\";\r\nimport { IStreamResult } from \"./Stream\";\r\nimport { Subject } from \"./Subject\";\r\nimport { Arg, getErrorString, Platform } from \"./Utils\";\r\n\r\nconst DEFAULT_TIMEOUT_IN_MS: number = 30 * 1000;\r\nconst DEFAULT_PING_INTERVAL_IN_MS: number = 15 * 1000;\r\n\r\n/** Describes the current state of the {@link HubConnection} to the server. */\r\nexport enum HubConnectionState {\r\n /** The hub connection is disconnected. */\r\n Disconnected = \"Disconnected\",\r\n /** The hub connection is connecting. */\r\n Connecting = \"Connecting\",\r\n /** The hub connection is connected. */\r\n Connected = \"Connected\",\r\n /** The hub connection is disconnecting. */\r\n Disconnecting = \"Disconnecting\",\r\n /** The hub connection is reconnecting. */\r\n Reconnecting = \"Reconnecting\",\r\n}\r\n\r\n/** Represents a connection to a SignalR Hub. */\r\nexport class HubConnection {\r\n private readonly _cachedPingMessage: string | ArrayBuffer;\r\n // Needs to not start with _ for tests\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n private readonly connection: IConnection;\r\n private readonly _logger: ILogger;\r\n private readonly _reconnectPolicy?: IRetryPolicy;\r\n private _protocol: IHubProtocol;\r\n private _handshakeProtocol: HandshakeProtocol;\r\n private _callbacks: { [invocationId: string]: (invocationEvent: StreamItemMessage | CompletionMessage | null, error?: Error) => void };\r\n private _methods: { [name: string]: ((...args: any[]) => void)[] };\r\n private _invocationId: number;\r\n\r\n private _closedCallbacks: ((error?: Error) => void)[];\r\n private _reconnectingCallbacks: ((error?: Error) => void)[];\r\n private _reconnectedCallbacks: ((connectionId?: string) => void)[];\r\n\r\n private _receivedHandshakeResponse: boolean;\r\n private _handshakeResolver!: (value?: PromiseLike<{}>) => void;\r\n private _handshakeRejecter!: (reason?: any) => void;\r\n private _stopDuringStartError?: Error;\r\n\r\n private _connectionState: HubConnectionState;\r\n // connectionStarted is tracked independently from connectionState, so we can check if the\r\n // connection ever did successfully transition from connecting to connected before disconnecting.\r\n private _connectionStarted: boolean;\r\n private _startPromise?: Promise;\r\n private _stopPromise?: Promise;\r\n private _nextKeepAlive: number = 0;\r\n\r\n // The type of these a) doesn't matter and b) varies when building in browser and node contexts\r\n // Since we're building the WebPack bundle directly from the TypeScript, this matters (previously\r\n // we built the bundle from the compiled JavaScript).\r\n private _reconnectDelayHandle?: any;\r\n private _timeoutHandle?: any;\r\n private _pingServerHandle?: any;\r\n\r\n private _freezeEventListener = () =>\r\n {\r\n this._logger.log(LogLevel.Warning, \"The page is being frozen, this will likely lead to the connection being closed and messages being lost. For more information see the docs at https://docs.microsoft.com/aspnet/core/signalr/javascript-client#bsleep\");\r\n };\r\n\r\n /** The server timeout in milliseconds.\r\n *\r\n * If this timeout elapses without receiving any messages from the server, the connection will be terminated with an error.\r\n * The default timeout value is 30,000 milliseconds (30 seconds).\r\n */\r\n public serverTimeoutInMilliseconds: number;\r\n\r\n /** Default interval at which to ping the server.\r\n *\r\n * The default value is 15,000 milliseconds (15 seconds).\r\n * Allows the server to detect hard disconnects (like when a client unplugs their computer).\r\n * The ping will happen at most as often as the server pings.\r\n * If the server pings every 5 seconds, a value lower than 5 will ping every 5 seconds.\r\n */\r\n public keepAliveIntervalInMilliseconds: number;\r\n\r\n /** @internal */\r\n // Using a public static factory method means we can have a private constructor and an _internal_\r\n // create method that can be used by HubConnectionBuilder. An \"internal\" constructor would just\r\n // be stripped away and the '.d.ts' file would have no constructor, which is interpreted as a\r\n // public parameter-less constructor.\r\n public static create(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IRetryPolicy): HubConnection {\r\n return new HubConnection(connection, logger, protocol, reconnectPolicy);\r\n }\r\n\r\n private constructor(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IRetryPolicy) {\r\n Arg.isRequired(connection, \"connection\");\r\n Arg.isRequired(logger, \"logger\");\r\n Arg.isRequired(protocol, \"protocol\");\r\n\r\n this.serverTimeoutInMilliseconds = DEFAULT_TIMEOUT_IN_MS;\r\n this.keepAliveIntervalInMilliseconds = DEFAULT_PING_INTERVAL_IN_MS;\r\n\r\n this._logger = logger;\r\n this._protocol = protocol;\r\n this.connection = connection;\r\n this._reconnectPolicy = reconnectPolicy;\r\n this._handshakeProtocol = new HandshakeProtocol();\r\n\r\n this.connection.onreceive = (data: any) => this._processIncomingData(data);\r\n this.connection.onclose = (error?: Error) => this._connectionClosed(error);\r\n\r\n this._callbacks = {};\r\n this._methods = {};\r\n this._closedCallbacks = [];\r\n this._reconnectingCallbacks = [];\r\n this._reconnectedCallbacks = [];\r\n this._invocationId = 0;\r\n this._receivedHandshakeResponse = false;\r\n this._connectionState = HubConnectionState.Disconnected;\r\n this._connectionStarted = false;\r\n\r\n this._cachedPingMessage = this._protocol.writeMessage({ type: MessageType.Ping });\r\n }\r\n\r\n /** Indicates the state of the {@link HubConnection} to the server. */\r\n get state(): HubConnectionState {\r\n return this._connectionState;\r\n }\r\n\r\n /** Represents the connection id of the {@link HubConnection} on the server. The connection id will be null when the connection is either\r\n * in the disconnected state or if the negotiation step was skipped.\r\n */\r\n get connectionId(): string | null {\r\n return this.connection ? (this.connection.connectionId || null) : null;\r\n }\r\n\r\n /** Indicates the url of the {@link HubConnection} to the server. */\r\n get baseUrl(): string {\r\n return this.connection.baseUrl || \"\";\r\n }\r\n\r\n /**\r\n * Sets a new url for the HubConnection. Note that the url can only be changed when the connection is in either the Disconnected or\r\n * Reconnecting states.\r\n * @param {string} url The url to connect to.\r\n */\r\n set baseUrl(url: string) {\r\n if (this._connectionState !== HubConnectionState.Disconnected && this._connectionState !== HubConnectionState.Reconnecting) {\r\n throw new Error(\"The HubConnection must be in the Disconnected or Reconnecting state to change the url.\");\r\n }\r\n\r\n if (!url) {\r\n throw new Error(\"The HubConnection url must be a valid url.\");\r\n }\r\n\r\n this.connection.baseUrl = url;\r\n }\r\n\r\n /** Starts the connection.\r\n *\r\n * @returns {Promise} A Promise that resolves when the connection has been successfully established, or rejects with an error.\r\n */\r\n public start(): Promise {\r\n this._startPromise = this._startWithStateTransitions();\r\n return this._startPromise;\r\n }\r\n\r\n private async _startWithStateTransitions(): Promise {\r\n if (this._connectionState !== HubConnectionState.Disconnected) {\r\n return Promise.reject(new Error(\"Cannot start a HubConnection that is not in the 'Disconnected' state.\"));\r\n }\r\n\r\n this._connectionState = HubConnectionState.Connecting;\r\n this._logger.log(LogLevel.Debug, \"Starting HubConnection.\");\r\n\r\n try {\r\n await this._startInternal();\r\n\r\n if (Platform.isBrowser) {\r\n // Log when the browser freezes the tab so users know why their connection unexpectedly stopped working\r\n window.document.addEventListener(\"freeze\", this._freezeEventListener);\r\n }\r\n\r\n this._connectionState = HubConnectionState.Connected;\r\n this._connectionStarted = true;\r\n this._logger.log(LogLevel.Debug, \"HubConnection connected successfully.\");\r\n } catch (e) {\r\n this._connectionState = HubConnectionState.Disconnected;\r\n this._logger.log(LogLevel.Debug, `HubConnection failed to start successfully because of error '${e}'.`);\r\n return Promise.reject(e);\r\n }\r\n }\r\n\r\n private async _startInternal() {\r\n this._stopDuringStartError = undefined;\r\n this._receivedHandshakeResponse = false;\r\n // Set up the promise before any connection is (re)started otherwise it could race with received messages\r\n const handshakePromise = new Promise((resolve, reject) => {\r\n this._handshakeResolver = resolve;\r\n this._handshakeRejecter = reject;\r\n });\r\n\r\n await this.connection.start(this._protocol.transferFormat);\r\n\r\n try {\r\n const handshakeRequest: HandshakeRequestMessage = {\r\n protocol: this._protocol.name,\r\n version: this._protocol.version,\r\n };\r\n\r\n this._logger.log(LogLevel.Debug, \"Sending handshake request.\");\r\n\r\n await this._sendMessage(this._handshakeProtocol.writeHandshakeRequest(handshakeRequest));\r\n\r\n this._logger.log(LogLevel.Information, `Using HubProtocol '${this._protocol.name}'.`);\r\n\r\n // defensively cleanup timeout in case we receive a message from the server before we finish start\r\n this._cleanupTimeout();\r\n this._resetTimeoutPeriod();\r\n this._resetKeepAliveInterval();\r\n\r\n await handshakePromise;\r\n\r\n // It's important to check the stopDuringStartError instead of just relying on the handshakePromise\r\n // being rejected on close, because this continuation can run after both the handshake completed successfully\r\n // and the connection was closed.\r\n if (this._stopDuringStartError) {\r\n // It's important to throw instead of returning a rejected promise, because we don't want to allow any state\r\n // transitions to occur between now and the calling code observing the exceptions. Returning a rejected promise\r\n // will cause the calling continuation to get scheduled to run later.\r\n // eslint-disable-next-line @typescript-eslint/no-throw-literal\r\n throw this._stopDuringStartError;\r\n }\r\n } catch (e) {\r\n this._logger.log(LogLevel.Debug, `Hub handshake failed with error '${e}' during start(). Stopping HubConnection.`);\r\n\r\n this._cleanupTimeout();\r\n this._cleanupPingTimer();\r\n\r\n // HttpConnection.stop() should not complete until after the onclose callback is invoked.\r\n // This will transition the HubConnection to the disconnected state before HttpConnection.stop() completes.\r\n await this.connection.stop(e);\r\n throw e;\r\n }\r\n }\r\n\r\n /** Stops the connection.\r\n *\r\n * @returns {Promise} A Promise that resolves when the connection has been successfully terminated, or rejects with an error.\r\n */\r\n public async stop(): Promise {\r\n // Capture the start promise before the connection might be restarted in an onclose callback.\r\n const startPromise = this._startPromise;\r\n\r\n this._stopPromise = this._stopInternal();\r\n await this._stopPromise;\r\n\r\n try {\r\n // Awaiting undefined continues immediately\r\n await startPromise;\r\n } catch (e) {\r\n // This exception is returned to the user as a rejected Promise from the start method.\r\n }\r\n }\r\n\r\n private _stopInternal(error?: Error): Promise {\r\n if (this._connectionState === HubConnectionState.Disconnected) {\r\n this._logger.log(LogLevel.Debug, `Call to HubConnection.stop(${error}) ignored because it is already in the disconnected state.`);\r\n return Promise.resolve();\r\n }\r\n\r\n if (this._connectionState === HubConnectionState.Disconnecting) {\r\n this._logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnecting state.`);\r\n return this._stopPromise!;\r\n }\r\n\r\n this._connectionState = HubConnectionState.Disconnecting;\r\n\r\n this._logger.log(LogLevel.Debug, \"Stopping HubConnection.\");\r\n\r\n if (this._reconnectDelayHandle) {\r\n // We're in a reconnect delay which means the underlying connection is currently already stopped.\r\n // Just clear the handle to stop the reconnect loop (which no one is waiting on thankfully) and\r\n // fire the onclose callbacks.\r\n this._logger.log(LogLevel.Debug, \"Connection stopped during reconnect delay. Done reconnecting.\");\r\n\r\n clearTimeout(this._reconnectDelayHandle);\r\n this._reconnectDelayHandle = undefined;\r\n\r\n this._completeClose();\r\n return Promise.resolve();\r\n }\r\n\r\n this._cleanupTimeout();\r\n this._cleanupPingTimer();\r\n this._stopDuringStartError = error || new Error(\"The connection was stopped before the hub handshake could complete.\");\r\n\r\n // HttpConnection.stop() should not complete until after either HttpConnection.start() fails\r\n // or the onclose callback is invoked. The onclose callback will transition the HubConnection\r\n // to the disconnected state if need be before HttpConnection.stop() completes.\r\n return this.connection.stop(error);\r\n }\r\n\r\n /** Invokes a streaming hub method on the server using the specified name and arguments.\r\n *\r\n * @typeparam T The type of the items returned by the server.\r\n * @param {string} methodName The name of the server method to invoke.\r\n * @param {any[]} args The arguments used to invoke the server method.\r\n * @returns {IStreamResult} An object that yields results from the server as they are received.\r\n */\r\n public stream(methodName: string, ...args: any[]): IStreamResult {\r\n const [streams, streamIds] = this._replaceStreamingParams(args);\r\n const invocationDescriptor = this._createStreamInvocation(methodName, args, streamIds);\r\n\r\n // eslint-disable-next-line prefer-const\r\n let promiseQueue: Promise;\r\n\r\n const subject = new Subject();\r\n subject.cancelCallback = () => {\r\n const cancelInvocation: CancelInvocationMessage = this._createCancelInvocation(invocationDescriptor.invocationId);\r\n\r\n delete this._callbacks[invocationDescriptor.invocationId];\r\n\r\n return promiseQueue.then(() => {\r\n return this._sendWithProtocol(cancelInvocation);\r\n });\r\n };\r\n\r\n this._callbacks[invocationDescriptor.invocationId] = (invocationEvent: CompletionMessage | StreamItemMessage | null, error?: Error) => {\r\n if (error) {\r\n subject.error(error);\r\n return;\r\n } else if (invocationEvent) {\r\n // invocationEvent will not be null when an error is not passed to the callback\r\n if (invocationEvent.type === MessageType.Completion) {\r\n if (invocationEvent.error) {\r\n subject.error(new Error(invocationEvent.error));\r\n } else {\r\n subject.complete();\r\n }\r\n } else {\r\n subject.next((invocationEvent.item) as T);\r\n }\r\n }\r\n };\r\n\r\n promiseQueue = this._sendWithProtocol(invocationDescriptor)\r\n .catch((e) => {\r\n subject.error(e);\r\n delete this._callbacks[invocationDescriptor.invocationId];\r\n });\r\n\r\n this._launchStreams(streams, promiseQueue);\r\n\r\n return subject;\r\n }\r\n\r\n private _sendMessage(message: any) {\r\n this._resetKeepAliveInterval();\r\n return this.connection.send(message);\r\n }\r\n\r\n /**\r\n * Sends a js object to the server.\r\n * @param message The js object to serialize and send.\r\n */\r\n private _sendWithProtocol(message: any) {\r\n return this._sendMessage(this._protocol.writeMessage(message));\r\n }\r\n\r\n /** Invokes a hub method on the server using the specified name and arguments. Does not wait for a response from the receiver.\r\n *\r\n * The Promise returned by this method resolves when the client has sent the invocation to the server. The server may still\r\n * be processing the invocation.\r\n *\r\n * @param {string} methodName The name of the server method to invoke.\r\n * @param {any[]} args The arguments used to invoke the server method.\r\n * @returns {Promise} A Promise that resolves when the invocation has been successfully sent, or rejects with an error.\r\n */\r\n public send(methodName: string, ...args: any[]): Promise {\r\n const [streams, streamIds] = this._replaceStreamingParams(args);\r\n const sendPromise = this._sendWithProtocol(this._createInvocation(methodName, args, true, streamIds));\r\n\r\n this._launchStreams(streams, sendPromise);\r\n\r\n return sendPromise;\r\n }\r\n\r\n /** Invokes a hub method on the server using the specified name and arguments.\r\n *\r\n * The Promise returned by this method resolves when the server indicates it has finished invoking the method. When the promise\r\n * resolves, the server has finished invoking the method. If the server method returns a result, it is produced as the result of\r\n * resolving the Promise.\r\n *\r\n * @typeparam T The expected return type.\r\n * @param {string} methodName The name of the server method to invoke.\r\n * @param {any[]} args The arguments used to invoke the server method.\r\n * @returns {Promise} A Promise that resolves with the result of the server method (if any), or rejects with an error.\r\n */\r\n public invoke(methodName: string, ...args: any[]): Promise {\r\n const [streams, streamIds] = this._replaceStreamingParams(args);\r\n const invocationDescriptor = this._createInvocation(methodName, args, false, streamIds);\r\n\r\n const p = new Promise