Files
archived-teslamate/assets/js/hooks.js
James Bogosian 963831deee Enable zoom on live view of the car. (#2184)
* Enable zoom on live view of the car.

* Switch to using drag/pinch-to-zoom/scroll-to-zoom

* Remove data-zoom attrbiute as parameters are directly set in the JS.

* Directly set parameters for zoom instead of relying on attribute from DOM.

* Switch to use hover/tap to show/hide zoom control.
2022-01-21 19:28:20 +01:00

322 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const LANG = navigator.languages
? navigator.languages[0]
: navigator.language || navigator.userLanguage;
function toLocalTime(dateStr, opts) {
const date = new Date(dateStr);
return date instanceof Date && !isNaN(date.valueOf())
? date.toLocaleTimeString(LANG, opts)
: "";
}
function toLocalDate(dateStr, opts) {
const date = new Date(dateStr);
return date instanceof Date && !isNaN(date.valueOf())
? date.toLocaleDateString(LANG, opts)
: "";
}
export const Dropdown = {
mounted() {
const $el = this.el;
$el.querySelector("button").addEventListener("click", (e) => {
e.stopPropagation();
$el.classList.toggle("is-active");
});
document.addEventListener("click", () => {
$el.classList.remove("is-active");
});
},
};
export const LocalTime = {
mounted() {
this.el.innerText = toLocalTime(this.el.dataset.date);
},
updated() {
this.el.innerText = toLocalTime(this.el.dataset.date);
},
};
export const LocalTimeRange = {
exec() {
const date = toLocalDate(this.el.dataset.startDate, {
year: "numeric",
month: "short",
day: "numeric",
});
const time = [this.el.dataset.startDate, this.el.dataset.endDate]
.map((date) =>
toLocalTime(date, { hour: "2-digit", minute: "2-digit", hour12: false })
)
.join(" ");
this.el.innerText = `${date}, ${time}`;
},
mounted() {
this.exec();
},
updated() {
this.exec();
},
};
export const ConfirmGeoFenceDeletion = {
mounted() {
const { id, msg } = this.el.dataset;
this.el.addEventListener("click", () => {
if (window.confirm(msg)) {
this.pushEvent("delete", { id });
}
});
},
};
import {
Map as M,
TileLayer,
LatLng,
Control,
Marker,
Icon,
Circle,
CircleMarker,
} from "leaflet";
import markerIcon from "leaflet/dist/images/marker-icon.png";
import markerShadow from "leaflet/dist/images/marker-shadow.png";
const icon = new Icon({
iconUrl: markerIcon,
shadowUrl: markerShadow,
iconAnchor: [12, 40],
popupAnchor: [0, -25],
});
const DirectionArrow = CircleMarker.extend({
initialize(latLng, heading, options) {
this._heading = heading;
CircleMarker.prototype.initialize.call(this, latLng, {
fillOpacity: 1,
radius: 5,
...options,
});
},
setHeading(heading) {
this._heading = heading;
this.redraw();
},
_updatePath() {
const { x, y } = this._point;
if (this._heading === "")
return CircleMarker.prototype._updatePath.call(this);
this.getElement().setAttributeNS(
null,
"transform",
`translate(${x},${y}) rotate(${this._heading})`
);
const path = this._empty() ? "" : `M0,${3} L-4,${5} L0,${-5} L4,${5} z}`;
this._renderer._setPath(this, path);
},
});
function createMap(opts) {
const map = new M(opts.elId != null ? `map_${opts.elId}` : "map", opts);
const osm = new TileLayer(
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{ maxZoom: 19 }
);
if (opts.enableHybridLayer) {
const hybrid = new TileLayer(
"http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}",
{ maxZoom: 20, subdomains: ["mt0", "mt1", "mt2", "mt3"] }
);
new Control.Layers({ OSM: osm, Hybrid: hybrid }).addTo(map);
}
map.addLayer(osm);
return map;
}
export const SimpleMap = {
mounted() {
const $position = document.querySelector(`#position_${this.el.dataset.id}`);
const map = createMap({
elId: this.el.dataset.id,
zoomControl: !!this.el.dataset.zoom,
boxZoom: false,
doubleClickZoom: false,
keyboard: false,
scrollWheelZoom: false,
tap: false,
dragging: false,
touchZoom: false,
});
const isArrow = this.el.dataset.marker === "arrow";
const [lat, lng, heading] = $position.value.split(",");
const marker = isArrow
? new DirectionArrow([lat, lng], heading)
: new Marker([lat, lng], { icon });
map.setView([lat, lng], 17);
marker.addTo(map);
map.removeControl(map.zoomControl);
map.on('mouseover', function(e) { map.addControl( map.zoomControl ); });
map.on('mouseout', function(e) { map.removeControl( map.zoomControl ); });
if (isArrow) {
const setView = () => {
const [lat, lng, heading] = $position.value.split(",");
marker.setHeading(heading);
marker.setLatLng([lat, lng]);
map.setView([lat, lng], map.getZoom());
};
$position.addEventListener("change", setView);
}
},
};
export const TriggerChange = {
updated() {
this.el.dispatchEvent(new CustomEvent("change"));
},
};
import("leaflet-control-geocoder");
import("@geoman-io/leaflet-geoman-free");
export const Map = {
mounted() {
const geoFence = (name) =>
document.querySelector(`input[name='geo_fence[${name}]']`);
const $radius = geoFence("radius");
const $latitude = geoFence("latitude");
const $longitude = geoFence("longitude");
const location = new LatLng($latitude.value, $longitude.value);
const controlOpts = {
position: "topleft",
cutPolygon: false,
drawCircle: false,
drawCircleMarker: false,
drawMarker: false,
drawPolygon: false,
drawPolyline: false,
drawRectangle: false,
removalMode: false,
};
const editOpts = {
allowSelfIntersection: false,
preventMarkerRemoval: true,
};
const map = createMap({ enableHybridLayer: true });
map.setView(location, 17, { animate: false });
map.pm.setLang(LANG);
map.pm.addControls(controlOpts);
map.pm.enableGlobalEditMode(editOpts);
const circle = new Circle(location, { radius: $radius.value })
.addTo(map)
.on("pm:edit", (e) => {
const { lat, lng } = e.target.getLatLng();
const radius = Math.round(e.target.getRadius());
$radius.value = radius;
$latitude.value = lat;
$longitude.value = lng;
const mBox = map.getBounds();
const cBox = circle.getBounds();
const bounds = mBox.contains(cBox) ? mBox : cBox;
map.fitBounds(bounds);
});
new Control.geocoder({ defaultMarkGeocode: false })
.on("markgeocode", (e) => {
const { bbox, center } = e.geocode;
const poly = L.polygon([
bbox.getSouthEast(),
bbox.getNorthEast(),
bbox.getNorthWest(),
bbox.getSouthWest(),
]);
circle.setLatLng(center);
const lBox = poly.getBounds();
const cBox = circle.getBounds();
const bounds = cBox.contains(lBox) ? cBox : lBox;
map.fitBounds(bounds);
map.pm.enableGlobalEditMode();
const { lat, lng } = center;
$latitude.value = lat;
$longitude.value = lng;
})
.addTo(map);
map.fitBounds(circle.getBounds(), { animate: false });
},
};
export const Modal = {
_freeze() {
document.documentElement.classList.add("is-clipped");
},
_unfreeze() {
document.documentElement.classList.remove("is-clipped");
},
mounted() {
// assumption: 'is-active' is always added after the initial mount
},
updated() {
this.el.classList.contains("is-active") ? this._freeze() : this._unfreeze();
},
destroyed() {
this._unfreeze();
},
};
export const NumericInput = {
mounted() {
this.el.onkeypress = (evt) => {
const charCode = evt.which ? evt.which : evt.keyCode;
return !(charCode > 31 && (charCode < 48 || charCode > 57));
};
},
};