locar 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,7 +42,7 @@ import * as LocAR from 'locar';
42
42
  as long as you include `locar` as a dependency, e.g. in your `package.json`:
43
43
  ```
44
44
  "dependencies": {
45
- "locar" : "^0.1.1",
45
+ "locar" : "^0.1.2",
46
46
  "three" : "^0.175.0"
47
47
  }
48
48
  ```
package/dist/locar.es.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as g from "three";
2
- import { Vector3 as R, Euler as C, Quaternion as I, EventDispatcher as T, MathUtils as m } from "three";
3
- class w {
2
+ import { Vector3 as w, Euler as C, Quaternion as I, EventDispatcher as R, MathUtils as m } from "three";
3
+ class T {
4
4
  /**
5
5
  * Create a SphMercProjection.
6
6
  */
@@ -88,7 +88,7 @@ class k extends A {
88
88
  * @param {Object} serverLogger - an object which can optionally log GPS position to a server for debugging. null by default, so no logging will be done. This object should implement a sendData() method to send data (2nd arg) to a given endpoint (1st arg). Please see source code for details. Ensure you comply with privacy laws (GDPR or equivalent) if implementing this.
89
89
  */
90
90
  constructor(e, i, t = {}, c = null) {
91
- super(), this.scene = e, this.camera = i, this.#t = new w(), this.#e = null, this.#s = 0, this.#c = 100, this.#o = null, this.setGpsOptions(t), this.#i = null, this.#r = 0, this.#a = 0, this.#n = c;
91
+ super(), this.scene = e, this.camera = i, this.#t = new T(), this.#e = null, this.#s = 0, this.#c = 100, this.#o = null, this.setGpsOptions(t), this.#i = null, this.#r = 0, this.#a = 0, this.#n = c;
92
92
  }
93
93
  /**
94
94
  * Set the projection to use.
@@ -253,6 +253,14 @@ class k extends A {
253
253
  const t = g.MathUtils.degToRad(i.longitude - e.longitude), c = g.MathUtils.degToRad(i.latitude - e.latitude), h = Math.sin(c / 2) * Math.sin(c / 2) + Math.cos(g.MathUtils.degToRad(e.latitude)) * Math.cos(g.MathUtils.degToRad(i.latitude)) * (Math.sin(t / 2) * Math.sin(t / 2));
254
254
  return 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)) * 6371e3;
255
255
  }
256
+ /**
257
+ * Obtain the last known GPS location.
258
+ *
259
+ * @return {Object} object containing latitude and longitude fields, or null if no previous GPS location.
260
+ */
261
+ getLastKnownLocation() {
262
+ return this.lastCoords;
263
+ }
256
264
  }
257
265
  class W extends A {
258
266
  #t;
@@ -287,8 +295,8 @@ class W extends A {
287
295
  this.texture.dispose();
288
296
  }
289
297
  }
290
- const b = "locar-device-orientation-permission-modal", D = "locar-device-orientation-permission-button", S = "locar-device-orientation-permission-message", N = "locar-device-orientation-permission-inner", L = "locar-device-orientation-permission-button-inner", P = "This immersive website requires access to your device motion sensors.", O = navigator.userAgent.match(/iPhone|iPad|iPod/i) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1, x = new R(0, 0, 1), y = new C(), H = new I(), F = new I(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
291
- class V extends T {
298
+ const b = "locar-device-orientation-permission-modal", D = "locar-device-orientation-permission-button", S = "locar-device-orientation-permission-message", N = "locar-device-orientation-permission-inner", L = "locar-device-orientation-permission-button-inner", P = "This immersive website requires access to your device motion sensors.", O = navigator.userAgent.match(/iPhone|iPad|iPod/i) || /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints != null && navigator.maxTouchPoints > 1, x = new w(0, 0, 1), y = new C(), H = new I(), F = new I(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
299
+ class V extends R {
292
300
  /**
293
301
  * Create an instance of DeviceOrientationControls.
294
302
  * @param {Object} object - the object to attach the controls to
@@ -564,13 +572,13 @@ class j {
564
572
  return [];
565
573
  }
566
574
  }
567
- const U = "0.1.0";
575
+ const U = "0.1.3";
568
576
  export {
569
577
  j as ClickHandler,
570
578
  V as DeviceOrientationControls,
571
579
  A as EventEmitter,
572
580
  k as LocationBased,
573
- w as SphMercProjection,
581
+ T as SphMercProjection,
574
582
  W as Webcam,
575
583
  U as version
576
584
  };
package/dist/locar.umd.js CHANGED
@@ -1 +1 @@
1
- (function(h,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],c):(h=typeof globalThis<"u"?globalThis:h||self,c(h.locar={},h.THREE))})(this,(function(h,c){"use strict";function b(m){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(m){for(const i in m)if(i!=="default"){const e=Object.getOwnPropertyDescriptor(m,i);Object.defineProperty(t,i,e.get?e:{enumerable:!0,get:()=>m[i]})}}return t.default=m,Object.freeze(t)}const p=b(c);class A{constructor(){this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}project(t,i){return[this.#t(t),this.#l(i)]}unproject(t){return[this.#e(t[0]),this.#s(t[1])]}#t(t){return t/180*this.HALF_EARTH}#l(t){var i=Math.log(Math.tan((90+t)*Math.PI/360))/(Math.PI/180);return i*this.HALF_EARTH/180}#e(t){return t/this.HALF_EARTH*180}#s(t){var i=t/this.HALF_EARTH*180;return i=180/Math.PI*(2*Math.atan(Math.exp(i*Math.PI/180))-Math.PI/2),i}getID(){return"epsg:3857"}}class M{constructor(){this.eventHandlers={}}on(t,i){this.eventHandlers[t]=i}emit(t,...i){this.eventHandlers[t]?.call(this,...i)}}class w extends M{#t;#l;#e;#s;#c;#o;#i;#r;#a;#n;constructor(t,i,e={},l=null){super(),this.scene=t,this.camera=i,this.#t=new A,this.#e=null,this.#s=0,this.#c=100,this.#o=null,this.setGpsOptions(e),this.#i=null,this.#r=0,this.#a=0,this.#n=l}setProjection(t){this.#t=t}setGpsOptions(t={}){t.gpsMinDistance!==void 0&&(this.#s=t.gpsMinDistance),t.gpsMinAccuracy!==void 0&&(this.#c=t.gpsMinAccuracy)}async startGps(){if(this.#n){const i=await(await this.#n.sendData("/gps/start",{gpsMinDistance:this.#s,gpsMinAccuracy:this.#c})).json();this.#a=i.session}return this.#o===null?(this.#o=navigator.geolocation.watchPosition(t=>{this.#h(t)},t=>{this.emit("gpserror",t)},{enableHighAccuracy:!0}),!0):!1}stopGps(){return this.#o!==null?(navigator.geolocation.clearWatch(this.#o),this.#o=null,!0):!1}fakeGps(t,i,e=null,l=0){e!==null&&this.setElevation(e),this.#h({coords:{longitude:t,latitude:i,accuracy:l}})}lonLatToWorldCoords(t,i){const e=this.#t.project(t,i);if(this.#i)e[0]-=this.#i[0],e[1]-=this.#i[1];else throw"No initial position determined";return[e[0],-e[1]]}add(t,i,e,l,g={}){t.properties=g,this.#d(t,i,e,l),this.scene.add(t),this.#n?.sendData("/object/new",{position:t.position,x:t.position.x,z:t.position.z,session:this.#a,properties:g})}#d(t,i,e,l){const g=this.lonLatToWorldCoords(i,e);l!==void 0&&(t.position.y=l),[t.position.x,t.position.z]=g}setElevation(t){this.camera.position.y=t}#u(t,i){this.#i=this.#t.project(t,i)}#h(t){let i=Number.MAX_VALUE;this.#r++,this.#n?.sendData("/gps/new",{gpsCount:this.#r,lat:t.coords.latitude,lon:t.coords.longitude,acc:t.coords.accuracy,session:this.#a}),t.coords.accuracy<=this.#c&&(this.#e===null?this.#e={latitude:t.coords.latitude,longitude:t.coords.longitude}:i=this.#m(this.#e,t.coords),i>=this.#s&&(this.#e.longitude=t.coords.longitude,this.#e.latitude=t.coords.latitude,this.#i||(this.#u(t.coords.longitude,t.coords.latitude),this.#n?.sendData("/worldorigin/new",{gpsCount:this.#r,lat:t.coords.latitude,lon:t.coords.longitude,session:this.#a,initialPosition:this.#i})),this.#d(this.camera,t.coords.longitude,t.coords.latitude),this.#n?.sendData("/gps/accepted",{gpsCount:this.#r,cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:this.#a,distMoved:i}),this.emit("gpsupdate",{position:t,distMoved:i})))}#m(t,i){const e=p.MathUtils.degToRad(i.longitude-t.longitude),l=p.MathUtils.degToRad(i.latitude-t.latitude),g=Math.sin(l/2)*Math.sin(l/2)+Math.cos(p.MathUtils.degToRad(t.latitude))*Math.cos(p.MathUtils.degToRad(i.latitude))*(Math.sin(e/2)*Math.sin(e/2));return 2*Math.atan2(Math.sqrt(g),Math.sqrt(1-g))*6371e3}}class D extends M{#t;constructor(t={video:{facingMode:"environment"}},i){super(),this.sceneWebcam=new p.Scene,i?this.#t=document.querySelector(i):(this.#t=document.createElement("video"),this.#t.setAttribute("autoplay",!0),this.#t.setAttribute("playsinline",!0),this.#t.style.display="none",document.body.appendChild(this.#t)),this.texture=new p.VideoTexture(this.#t),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices.getUserMedia(t).then(e=>{this.#t.addEventListener("loadedmetadata",()=>{this.#t.setAttribute("width",this.#t.videoWidth),this.#t.setAttribute("height",this.#t.videoHeight),this.#t.play(),this.emit("webcamstarted",{texture:this.texture})}),this.#t.srcObject=e}).catch(e=>{this.emit("webcamerror",{code:e.name,message:e.message})}):this.emit("webcamerror",{code:"LOCAR_NO_MEDIA_DEVICES_API",message:"Media devices API not supported"})}dispose(){this.texture.dispose()}}const S="locar-device-orientation-permission-modal",T="locar-device-orientation-permission-button",N="locar-device-orientation-permission-message",R="locar-device-orientation-permission-inner",L="locar-device-orientation-permission-button-inner",P="This immersive website requires access to your device motion sensors.",I=navigator.userAgent.match(/iPhone|iPad|iPod/i)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1,x=new c.Vector3(0,0,1),_=new c.Euler,F=new c.Quaternion,U=new c.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5));class j extends c.EventDispatcher{constructor(t,i={}){super(),this.eventEmitter=new M;const e=this;this.object=t,this.object.rotation.reorder("YXZ"),this.enabled=!0,this.deviceOrientation=null,this.screenOrientation=0,this.alphaOffset=0,this.orientationOffset=0,this.initialOffset=null,this.lastCompassY=void 0,this.lastOrientation=null,this.TWO_PI=2*Math.PI,this.HALF_PI=.5*Math.PI,this.orientationChangeEventName="ondeviceorientationabsolute"in window?"deviceorientationabsolute":"deviceorientation",this.smoothingFactor=i.smoothingFactor||1,this.enablePermissionDialog=i.enablePermissionDialog??!0,this.enableInlineStyling=i.enableStyling??!0,this.preferConfirmDialog=i.preferConfirmDialog??!1;const l=function({alpha:n,beta:s,gamma:a,webkitCompassHeading:r}){if(I){const o=360-r;e.alphaOffset=c.MathUtils.degToRad(o-n),e.deviceOrientation={alpha:n,beta:s,gamma:a,webkitCompassHeading:r}}else n<0&&(n+=360),e.deviceOrientation={alpha:n,beta:s,gamma:a};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:t.rotation}}))},g=function(){e.screenOrientation=window.orientation||0,I&&(e.screenOrientation===90?e.orientationOffset=-e.HALF_PI:e.screenOrientation===-90?e.orientationOffset=e.HALF_PI:e.orientationOffset=0)},E=function(n,s,a,r,o){_.set(a,s,-r,"YXZ"),n.setFromEuler(_),n.multiply(U),n.multiply(F.setFromAxisAngle(x,-o))};this.connect=function(){g(),window.addEventListener("orientationchange",g),window.addEventListener(e.orientationChangeEventName,l),e.enabled=!0},this.disconnect=function(){window.removeEventListener("orientationchange",g),window.removeEventListener(e.orientationChangeEventName,l),e.enabled=!1,e.initialOffset=!1,e.deviceOrientation=null},this.requestOrientationPermissions=function(){window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(n=>{n==="granted"?this.eventEmitter.emit("deviceorientationgranted",{target:this}):this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_PERMISSION_DENIED",message:"Permission for device orientation denied - AR will not work correctly"})}).catch(function(n){this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_PERMISSION_FAILED",message:"Permission request for device orientation failed - AR will not work correctly",error:JSON.stringify(n,null,2)})}):this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_INTERNAL_ERROR",message:"Internal error: no requestPermission() found although requestOrientationPermissions() was called - please raise an issue on GitHub"})},this.update=function({theta:n=0}={theta:0}){if(e.enabled===!1)return;const s=e.deviceOrientation;if(s){let a=s.alpha?c.MathUtils.degToRad(s.alpha)+e.alphaOffset:0,r=s.beta?c.MathUtils.degToRad(s.beta):0,o=s.gamma?c.MathUtils.degToRad(s.gamma):0;const v=e.screenOrientation?c.MathUtils.degToRad(e.screenOrientation):0;if(e.smoothingFactor<1){if(e.lastOrientation){const u=e.smoothingFactor;a=e._getSmoothedAngle(a,e.lastOrientation.alpha,u),r=e._getSmoothedAngle(r+Math.PI,e.lastOrientation.beta,u),o=e._getSmoothedAngle(o+e.HALF_PI,e.lastOrientation.gamma,u,Math.PI)}else r+=Math.PI,o+=e.HALF_PI;e.lastOrientation={alpha:a,beta:r,gamma:o}}if(I){const u=new c.Quaternion;E(u,a,e.smoothingFactor<1?r-Math.PI:r,e.smoothingFactor<1?o-Math.PI/2:o,v);const f=new c.Euler().setFromQuaternion(u,"YXZ");let O=c.MathUtils.degToRad(360-s.webkitCompassHeading);e.smoothingFactor<1&&e.lastCompassY!==void 0&&(O=e._getSmoothedAngle(O,e.lastCompassY,e.smoothingFactor)),e.lastCompassY=O,f.y=O+(e.orientationOffset||0),u.setFromEuler(f),e.object.quaternion.copy(u)}else E(e.object.quaternion,I?a+e.alphaOffset:a,e.smoothingFactor<1?r-Math.PI:r,e.smoothingFactor<1?o-Math.PI/2:o,v)}},this.getCorrectedHeading=function(){const{deviceOrientation:n}=e;if(!n)return 0;let s=0;return I?(s=360-n.webkitCompassHeading,e.orientationOffset&&(s+=e.orientationOffset*(180/Math.PI),s=(s+360)%360)):(n.absolute===!0||e.orientationChangeEventName,s=n.alpha?n.alpha:0,s=(360-s)%360,s<0&&(s+=360)),s},this._orderAngle=function(n,s,a=e.TWO_PI){return s>n&&Math.abs(s-n)<a/2||n>s&&Math.abs(s-n)>a/2?{left:n,right:s}:{left:s,right:n}},this._getSmoothedAngle=function(n,s,a,r=e.TWO_PI){const o=e._orderAngle(n,s,r),v=o.left,u=o.right;o.left=0,o.right-=v,o.right<0&&(o.right+=r);let f=u==s?(1-a)*o.right+a*o.left:a*o.right+(1-a)*o.left;return f+=v,f>=r&&(f-=r),f},this.updateAlphaOffset=function(){e.initialOffset=!1},this.dispose=function(){e.disconnect()},this.getAlpha=function(){const{deviceOrientation:n}=e;return n&&n.alpha?c.MathUtils.degToRad(n.alpha)+e.alphaOffset:0},this.getBeta=function(){const{deviceOrientation:n}=e;return n&&n.beta?c.MathUtils.degToRad(n.beta):0},this.getGamma=function(){const{deviceOrientation:n}=e;return n&&n.gamma?c.MathUtils.degToRad(n.gamma):0},this.createObtainPermissionGestureDialog=function(){const n=document.createElement("div");n.classList.add(S);const s=document.createElement("div");s.classList.add(R);const a=document.createElement("div");a.classList.add(N);const r=document.createElement("div");r.classList.add(L);const o=document.createElement("button");if(o.classList.add(T),document.body.appendChild(n),this.enableInlineStyling===!0){const u={fontFamily:"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",display:"flex",position:"fixed",zIndex:1e5,justifyContent:"center",alignItems:"center",backgroundColor:"rgba(0,0,0,0.2)",inset:0,padding:"20px"},f={backgroundColor:"rgba(220, 220, 220, 0.85)",padding:"6px 0",borderRadius:"10px",width:"100%",maxWidth:"400px"},O={padding:"10px 12px",textAlign:"center",fontWeight:400,fontSize:"13px",display:"flex",justifyContent:"center",alignItems:"center"},y={display:"block",textAlign:"center",textDecoration:"none",borderTop:"rgb(180,180,180) solid 1px"},C={display:"block",width:"100%",textAlign:"center",appearance:"none",background:"none",border:"none",outline:"none",padding:"10px",fontWeight:400,fontSize:"16px",color:"#2e7cf1",cursor:"pointer"};for(let d in u)n.style[d]=u[d];for(let d in f)s.style[d]=f[d];for(let d in O)a.style[d]=O[d];for(let d in y)r.style[d]=y[d];for(let d in C)o.style[d]=C[d]}n.appendChild(s),s.appendChild(a),s.appendChild(r),a.appendChild(document.createTextNode(P));const v=()=>{this.requestOrientationPermissions(),n.style.display="none"};o.addEventListener("click",v),o.appendChild(document.createTextNode("OK")),r.appendChild(o),document.body.appendChild(n)},this.obtainPermissionGesture=function(){this.preferConfirmDialog===!0?window.confirm(P)&&this.requestOrientationPermissions():this.createObtainPermissionGestureDialog()}}on(t,i){this.eventEmitter.on(t,i)}init(){window.DeviceOrientationEvent===void 0?this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_NOT_SUPPORTED",message:"Device orientation API not supported"}):window.isSecureContext===!1?this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_NO_HTTPS",message:"DeviceOrientationEvent is only available in secure contexts (https)"}):typeof window.DeviceOrientationEvent.requestPermission=="function"&&this.enablePermissionDialog?this.obtainPermissionGesture():this.eventEmitter.emit("deviceorientationgranted",{target:this})}}class q{constructor(t){this.raycaster=new p.Raycaster,this.normalisedMousePosition=new p.Vector2(null,null),t.domElement.addEventListener("click",i=>{this.normalisedMousePosition.set(i.clientX/t.domElement.clientWidth*2-1,-(i.clientY/t.domElement.clientHeight*2)+1)})}raycast(t,i){if(this.normalisedMousePosition.x!==null&&this.normalisedMousePosition.y!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,t);const e=this.raycaster.intersectObjects(i.children,!1);return this.normalisedMousePosition.set(null,null),e}return[]}}const k="0.1.0";h.ClickHandler=q,h.DeviceOrientationControls=j,h.EventEmitter=M,h.LocationBased=w,h.SphMercProjection=A,h.Webcam=D,h.version=k,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(h,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],c):(h=typeof globalThis<"u"?globalThis:h||self,c(h.locar={},h.THREE))})(this,(function(h,c){"use strict";function w(m){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(m){for(const i in m)if(i!=="default"){const e=Object.getOwnPropertyDescriptor(m,i);Object.defineProperty(t,i,e.get?e:{enumerable:!0,get:()=>m[i]})}}return t.default=m,Object.freeze(t)}const p=w(c);class A{constructor(){this.EARTH=4007501668e-2,this.HALF_EARTH=2003750834e-2}project(t,i){return[this.#t(t),this.#l(i)]}unproject(t){return[this.#e(t[0]),this.#s(t[1])]}#t(t){return t/180*this.HALF_EARTH}#l(t){var i=Math.log(Math.tan((90+t)*Math.PI/360))/(Math.PI/180);return i*this.HALF_EARTH/180}#e(t){return t/this.HALF_EARTH*180}#s(t){var i=t/this.HALF_EARTH*180;return i=180/Math.PI*(2*Math.atan(Math.exp(i*Math.PI/180))-Math.PI/2),i}getID(){return"epsg:3857"}}class M{constructor(){this.eventHandlers={}}on(t,i){this.eventHandlers[t]=i}emit(t,...i){this.eventHandlers[t]?.call(this,...i)}}class b extends M{#t;#l;#e;#s;#c;#o;#i;#r;#a;#n;constructor(t,i,e={},l=null){super(),this.scene=t,this.camera=i,this.#t=new A,this.#e=null,this.#s=0,this.#c=100,this.#o=null,this.setGpsOptions(e),this.#i=null,this.#r=0,this.#a=0,this.#n=l}setProjection(t){this.#t=t}setGpsOptions(t={}){t.gpsMinDistance!==void 0&&(this.#s=t.gpsMinDistance),t.gpsMinAccuracy!==void 0&&(this.#c=t.gpsMinAccuracy)}async startGps(){if(this.#n){const i=await(await this.#n.sendData("/gps/start",{gpsMinDistance:this.#s,gpsMinAccuracy:this.#c})).json();this.#a=i.session}return this.#o===null?(this.#o=navigator.geolocation.watchPosition(t=>{this.#h(t)},t=>{this.emit("gpserror",t)},{enableHighAccuracy:!0}),!0):!1}stopGps(){return this.#o!==null?(navigator.geolocation.clearWatch(this.#o),this.#o=null,!0):!1}fakeGps(t,i,e=null,l=0){e!==null&&this.setElevation(e),this.#h({coords:{longitude:t,latitude:i,accuracy:l}})}lonLatToWorldCoords(t,i){const e=this.#t.project(t,i);if(this.#i)e[0]-=this.#i[0],e[1]-=this.#i[1];else throw"No initial position determined";return[e[0],-e[1]]}add(t,i,e,l,g={}){t.properties=g,this.#d(t,i,e,l),this.scene.add(t),this.#n?.sendData("/object/new",{position:t.position,x:t.position.x,z:t.position.z,session:this.#a,properties:g})}#d(t,i,e,l){const g=this.lonLatToWorldCoords(i,e);l!==void 0&&(t.position.y=l),[t.position.x,t.position.z]=g}setElevation(t){this.camera.position.y=t}#u(t,i){this.#i=this.#t.project(t,i)}#h(t){let i=Number.MAX_VALUE;this.#r++,this.#n?.sendData("/gps/new",{gpsCount:this.#r,lat:t.coords.latitude,lon:t.coords.longitude,acc:t.coords.accuracy,session:this.#a}),t.coords.accuracy<=this.#c&&(this.#e===null?this.#e={latitude:t.coords.latitude,longitude:t.coords.longitude}:i=this.#m(this.#e,t.coords),i>=this.#s&&(this.#e.longitude=t.coords.longitude,this.#e.latitude=t.coords.latitude,this.#i||(this.#u(t.coords.longitude,t.coords.latitude),this.#n?.sendData("/worldorigin/new",{gpsCount:this.#r,lat:t.coords.latitude,lon:t.coords.longitude,session:this.#a,initialPosition:this.#i})),this.#d(this.camera,t.coords.longitude,t.coords.latitude),this.#n?.sendData("/gps/accepted",{gpsCount:this.#r,cameraX:this.camera.position.x,cameraZ:this.camera.position.z,session:this.#a,distMoved:i}),this.emit("gpsupdate",{position:t,distMoved:i})))}#m(t,i){const e=p.MathUtils.degToRad(i.longitude-t.longitude),l=p.MathUtils.degToRad(i.latitude-t.latitude),g=Math.sin(l/2)*Math.sin(l/2)+Math.cos(p.MathUtils.degToRad(t.latitude))*Math.cos(p.MathUtils.degToRad(i.latitude))*(Math.sin(e/2)*Math.sin(e/2));return 2*Math.atan2(Math.sqrt(g),Math.sqrt(1-g))*6371e3}getLastKnownLocation(){return this.lastCoords}}class D extends M{#t;constructor(t={video:{facingMode:"environment"}},i){super(),this.sceneWebcam=new p.Scene,i?this.#t=document.querySelector(i):(this.#t=document.createElement("video"),this.#t.setAttribute("autoplay",!0),this.#t.setAttribute("playsinline",!0),this.#t.style.display="none",document.body.appendChild(this.#t)),this.texture=new p.VideoTexture(this.#t),navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices.getUserMedia(t).then(e=>{this.#t.addEventListener("loadedmetadata",()=>{this.#t.setAttribute("width",this.#t.videoWidth),this.#t.setAttribute("height",this.#t.videoHeight),this.#t.play(),this.emit("webcamstarted",{texture:this.texture})}),this.#t.srcObject=e}).catch(e=>{this.emit("webcamerror",{code:e.name,message:e.message})}):this.emit("webcamerror",{code:"LOCAR_NO_MEDIA_DEVICES_API",message:"Media devices API not supported"})}dispose(){this.texture.dispose()}}const S="locar-device-orientation-permission-modal",T="locar-device-orientation-permission-button",N="locar-device-orientation-permission-message",R="locar-device-orientation-permission-inner",L="locar-device-orientation-permission-button-inner",P="This immersive website requires access to your device motion sensors.",I=navigator.userAgent.match(/iPhone|iPad|iPod/i)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints!=null&&navigator.maxTouchPoints>1,x=new c.Vector3(0,0,1),_=new c.Euler,F=new c.Quaternion,U=new c.Quaternion(-Math.sqrt(.5),0,0,Math.sqrt(.5));class j extends c.EventDispatcher{constructor(t,i={}){super(),this.eventEmitter=new M;const e=this;this.object=t,this.object.rotation.reorder("YXZ"),this.enabled=!0,this.deviceOrientation=null,this.screenOrientation=0,this.alphaOffset=0,this.orientationOffset=0,this.initialOffset=null,this.lastCompassY=void 0,this.lastOrientation=null,this.TWO_PI=2*Math.PI,this.HALF_PI=.5*Math.PI,this.orientationChangeEventName="ondeviceorientationabsolute"in window?"deviceorientationabsolute":"deviceorientation",this.smoothingFactor=i.smoothingFactor||1,this.enablePermissionDialog=i.enablePermissionDialog??!0,this.enableInlineStyling=i.enableStyling??!0,this.preferConfirmDialog=i.preferConfirmDialog??!1;const l=function({alpha:n,beta:s,gamma:a,webkitCompassHeading:r}){if(I){const o=360-r;e.alphaOffset=c.MathUtils.degToRad(o-n),e.deviceOrientation={alpha:n,beta:s,gamma:a,webkitCompassHeading:r}}else n<0&&(n+=360),e.deviceOrientation={alpha:n,beta:s,gamma:a};window.dispatchEvent(new CustomEvent("camera-rotation-change",{detail:{cameraRotation:t.rotation}}))},g=function(){e.screenOrientation=window.orientation||0,I&&(e.screenOrientation===90?e.orientationOffset=-e.HALF_PI:e.screenOrientation===-90?e.orientationOffset=e.HALF_PI:e.orientationOffset=0)},E=function(n,s,a,r,o){_.set(a,s,-r,"YXZ"),n.setFromEuler(_),n.multiply(U),n.multiply(F.setFromAxisAngle(x,-o))};this.connect=function(){g(),window.addEventListener("orientationchange",g),window.addEventListener(e.orientationChangeEventName,l),e.enabled=!0},this.disconnect=function(){window.removeEventListener("orientationchange",g),window.removeEventListener(e.orientationChangeEventName,l),e.enabled=!1,e.initialOffset=!1,e.deviceOrientation=null},this.requestOrientationPermissions=function(){window.DeviceOrientationEvent!==void 0&&typeof window.DeviceOrientationEvent.requestPermission=="function"?window.DeviceOrientationEvent.requestPermission().then(n=>{n==="granted"?this.eventEmitter.emit("deviceorientationgranted",{target:this}):this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_PERMISSION_DENIED",message:"Permission for device orientation denied - AR will not work correctly"})}).catch(function(n){this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_PERMISSION_FAILED",message:"Permission request for device orientation failed - AR will not work correctly",error:JSON.stringify(n,null,2)})}):this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_INTERNAL_ERROR",message:"Internal error: no requestPermission() found although requestOrientationPermissions() was called - please raise an issue on GitHub"})},this.update=function({theta:n=0}={theta:0}){if(e.enabled===!1)return;const s=e.deviceOrientation;if(s){let a=s.alpha?c.MathUtils.degToRad(s.alpha)+e.alphaOffset:0,r=s.beta?c.MathUtils.degToRad(s.beta):0,o=s.gamma?c.MathUtils.degToRad(s.gamma):0;const v=e.screenOrientation?c.MathUtils.degToRad(e.screenOrientation):0;if(e.smoothingFactor<1){if(e.lastOrientation){const u=e.smoothingFactor;a=e._getSmoothedAngle(a,e.lastOrientation.alpha,u),r=e._getSmoothedAngle(r+Math.PI,e.lastOrientation.beta,u),o=e._getSmoothedAngle(o+e.HALF_PI,e.lastOrientation.gamma,u,Math.PI)}else r+=Math.PI,o+=e.HALF_PI;e.lastOrientation={alpha:a,beta:r,gamma:o}}if(I){const u=new c.Quaternion;E(u,a,e.smoothingFactor<1?r-Math.PI:r,e.smoothingFactor<1?o-Math.PI/2:o,v);const f=new c.Euler().setFromQuaternion(u,"YXZ");let O=c.MathUtils.degToRad(360-s.webkitCompassHeading);e.smoothingFactor<1&&e.lastCompassY!==void 0&&(O=e._getSmoothedAngle(O,e.lastCompassY,e.smoothingFactor)),e.lastCompassY=O,f.y=O+(e.orientationOffset||0),u.setFromEuler(f),e.object.quaternion.copy(u)}else E(e.object.quaternion,I?a+e.alphaOffset:a,e.smoothingFactor<1?r-Math.PI:r,e.smoothingFactor<1?o-Math.PI/2:o,v)}},this.getCorrectedHeading=function(){const{deviceOrientation:n}=e;if(!n)return 0;let s=0;return I?(s=360-n.webkitCompassHeading,e.orientationOffset&&(s+=e.orientationOffset*(180/Math.PI),s=(s+360)%360)):(n.absolute===!0||e.orientationChangeEventName,s=n.alpha?n.alpha:0,s=(360-s)%360,s<0&&(s+=360)),s},this._orderAngle=function(n,s,a=e.TWO_PI){return s>n&&Math.abs(s-n)<a/2||n>s&&Math.abs(s-n)>a/2?{left:n,right:s}:{left:s,right:n}},this._getSmoothedAngle=function(n,s,a,r=e.TWO_PI){const o=e._orderAngle(n,s,r),v=o.left,u=o.right;o.left=0,o.right-=v,o.right<0&&(o.right+=r);let f=u==s?(1-a)*o.right+a*o.left:a*o.right+(1-a)*o.left;return f+=v,f>=r&&(f-=r),f},this.updateAlphaOffset=function(){e.initialOffset=!1},this.dispose=function(){e.disconnect()},this.getAlpha=function(){const{deviceOrientation:n}=e;return n&&n.alpha?c.MathUtils.degToRad(n.alpha)+e.alphaOffset:0},this.getBeta=function(){const{deviceOrientation:n}=e;return n&&n.beta?c.MathUtils.degToRad(n.beta):0},this.getGamma=function(){const{deviceOrientation:n}=e;return n&&n.gamma?c.MathUtils.degToRad(n.gamma):0},this.createObtainPermissionGestureDialog=function(){const n=document.createElement("div");n.classList.add(S);const s=document.createElement("div");s.classList.add(R);const a=document.createElement("div");a.classList.add(N);const r=document.createElement("div");r.classList.add(L);const o=document.createElement("button");if(o.classList.add(T),document.body.appendChild(n),this.enableInlineStyling===!0){const u={fontFamily:"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",display:"flex",position:"fixed",zIndex:1e5,justifyContent:"center",alignItems:"center",backgroundColor:"rgba(0,0,0,0.2)",inset:0,padding:"20px"},f={backgroundColor:"rgba(220, 220, 220, 0.85)",padding:"6px 0",borderRadius:"10px",width:"100%",maxWidth:"400px"},O={padding:"10px 12px",textAlign:"center",fontWeight:400,fontSize:"13px",display:"flex",justifyContent:"center",alignItems:"center"},y={display:"block",textAlign:"center",textDecoration:"none",borderTop:"rgb(180,180,180) solid 1px"},C={display:"block",width:"100%",textAlign:"center",appearance:"none",background:"none",border:"none",outline:"none",padding:"10px",fontWeight:400,fontSize:"16px",color:"#2e7cf1",cursor:"pointer"};for(let d in u)n.style[d]=u[d];for(let d in f)s.style[d]=f[d];for(let d in O)a.style[d]=O[d];for(let d in y)r.style[d]=y[d];for(let d in C)o.style[d]=C[d]}n.appendChild(s),s.appendChild(a),s.appendChild(r),a.appendChild(document.createTextNode(P));const v=()=>{this.requestOrientationPermissions(),n.style.display="none"};o.addEventListener("click",v),o.appendChild(document.createTextNode("OK")),r.appendChild(o),document.body.appendChild(n)},this.obtainPermissionGesture=function(){this.preferConfirmDialog===!0?window.confirm(P)&&this.requestOrientationPermissions():this.createObtainPermissionGestureDialog()}}on(t,i){this.eventEmitter.on(t,i)}init(){window.DeviceOrientationEvent===void 0?this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_NOT_SUPPORTED",message:"Device orientation API not supported"}):window.isSecureContext===!1?this.eventEmitter.emit("deviceorientationerror",{code:"LOCAR_DEVICE_ORIENTATION_NO_HTTPS",message:"DeviceOrientationEvent is only available in secure contexts (https)"}):typeof window.DeviceOrientationEvent.requestPermission=="function"&&this.enablePermissionDialog?this.obtainPermissionGesture():this.eventEmitter.emit("deviceorientationgranted",{target:this})}}class q{constructor(t){this.raycaster=new p.Raycaster,this.normalisedMousePosition=new p.Vector2(null,null),t.domElement.addEventListener("click",i=>{this.normalisedMousePosition.set(i.clientX/t.domElement.clientWidth*2-1,-(i.clientY/t.domElement.clientHeight*2)+1)})}raycast(t,i){if(this.normalisedMousePosition.x!==null&&this.normalisedMousePosition.y!==null){this.raycaster.setFromCamera(this.normalisedMousePosition,t);const e=this.raycaster.intersectObjects(i.children,!1);return this.normalisedMousePosition.set(null,null),e}return[]}}const k="0.1.3";h.ClickHandler=q,h.DeviceOrientationControls=j,h.EventEmitter=M,h.LocationBased=b,h.SphMercProjection=A,h.Webcam=D,h.version=k,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,336 @@
1
+ declare module "locar" {
2
+ import * as THREE from "three";
3
+
4
+ /**
5
+ * Generic, small event emitter that only stores a single handler per event name.
6
+ */
7
+ export class EventEmitter {
8
+ protected eventHandlers: Record<string, (...args: any[]) => void>;
9
+ on(eventName: string, handler: (...args: any[]) => void): void;
10
+ emit(eventName: string, ...params: any[]): void;
11
+ }
12
+
13
+ /**
14
+ * Spherical Mercator (EPSG:3857) projection used by `LocationBased`.
15
+ */
16
+ export class SphMercProjection {
17
+ constructor();
18
+ /**
19
+ * @returns [easting, northing]
20
+ */
21
+ project(lon: number, lat: number): [number, number];
22
+ /**
23
+ * @returns [lon, lat]
24
+ */
25
+ unproject(projected: [number, number]): [number, number];
26
+ getID(): "epsg:3857";
27
+ }
28
+
29
+ /**
30
+ * Options for GPS in `LocationBased`.
31
+ * Mirrored from `setGpsOptions(...)`
32
+ */
33
+ export interface GpsOptions {
34
+ /**
35
+ * Meters the device must move to accept the next GPS reading.
36
+ */
37
+ gpsMinDistance?: number;
38
+ /**
39
+ * Minimum accuracy in meters to accept a GPS reading.
40
+ */
41
+ gpsMinAccuracy?: number;
42
+ }
43
+
44
+ /**
45
+ * GPS coordinates.
46
+ * Returned from `getLastKnownLocation()`
47
+ */
48
+ export interface LonLat {
49
+ /**
50
+ * Longitude.
51
+ */
52
+ longitude: number;
53
+ /**
54
+ * Latitude.
55
+ */
56
+ latitude: number;
57
+ }
58
+
59
+ /**
60
+ * Optional logger object passed to `LocationBased` for debug.
61
+ */
62
+ export interface ServerLogger {
63
+ /**
64
+ * Should send data to server. Implementation-specific in userland.
65
+ */
66
+ sendData(endpoint: string, data: any): Promise<Response> | Response;
67
+ }
68
+
69
+ /**
70
+ * Main location AR class.
71
+ */
72
+ export class LocationBased extends EventEmitter {
73
+ readonly scene: THREE.Scene;
74
+ readonly camera: THREE.Camera;
75
+
76
+ constructor(
77
+ scene: THREE.Scene,
78
+ camera: THREE.Camera,
79
+ options?: GpsOptions,
80
+ serverLogger?: ServerLogger | null,
81
+ );
82
+
83
+ /**
84
+ * Set the projection (must have `project(lon,lat): [x,y]`).
85
+ */
86
+ setProjection(proj: {
87
+ project(lon: number, lat: number): [number, number];
88
+ }): void;
89
+
90
+ /**
91
+ * Update GPS options at runtime.
92
+ */
93
+ setGpsOptions(options?: GpsOptions): void;
94
+
95
+ /**
96
+ * Start real GPS (`navigator.geolocation.watchPosition`).
97
+ */
98
+ startGps(): Promise<boolean> | boolean;
99
+
100
+ /**
101
+ * Stop real GPS.
102
+ */
103
+ stopGps(): boolean;
104
+
105
+ /**
106
+ * Send a fake GPS position.
107
+ */
108
+ fakeGps(lon: number, lat: number, elev?: number | null, acc?: number): void;
109
+
110
+ /**
111
+ * Convert lon/lat to world coordinates (needs initial position).
112
+ */
113
+ lonLatToWorldCoords(lon: number, lat: number): [number, number];
114
+
115
+ /**
116
+ * Add a THREE object at lon/lat/(elev) and put in the scene.
117
+ */
118
+ add(
119
+ object: THREE.Object3D,
120
+ lon: number,
121
+ lat: number,
122
+ elev?: number,
123
+ properties?: Record<string, any>,
124
+ ): void;
125
+
126
+ /**
127
+ * Set camera elevation (y).
128
+ */
129
+ setElevation(elev: number): void;
130
+
131
+ /**
132
+ * Gwet last known location.
133
+ */
134
+ getLastKnownLocation(): LonLat | null;
135
+
136
+ /**
137
+ * Events:
138
+ * - "gpsupdate": `{ position: GeolocationPosition, distMoved: number }``
139
+ * - "gpserror": `GeolocationPositionError`
140
+ */
141
+ on(
142
+ eventName: "gpsupdate",
143
+ handler: (data: {
144
+ position: GeolocationPosition;
145
+ distMoved: number;
146
+ }) => void,
147
+ ): void;
148
+ on(
149
+ eventName: "gpserror",
150
+ handler: (error: GeolocationPositionError) => void,
151
+ ): void;
152
+ on(eventName: string, handler: (...args: any[]) => void): void;
153
+ }
154
+
155
+ /**
156
+ * Small webcam wrapper that creates a hidden `<video>` and a `THREE.VideoTexture`.
157
+ */
158
+ export interface WebcamStartedEvent {
159
+ texture: THREE.VideoTexture;
160
+ }
161
+ export interface WebcamErrorEvent {
162
+ code: string;
163
+ message: string;
164
+ }
165
+
166
+ export class Webcam extends EventEmitter {
167
+ /**
168
+ * @param constraints `MediaDevices.getUserMedia` constraints
169
+ * @param videoElementSelector selector for an existing `<video>`; if falsy, it creates one
170
+ */
171
+ constructor(
172
+ constraints?: MediaStreamConstraints,
173
+ videoElementSelector?: string | null,
174
+ );
175
+
176
+ /**
177
+ * Texture that streams the camera feed.
178
+ */
179
+ readonly texture: THREE.VideoTexture;
180
+
181
+ /**
182
+ * Free GPU resources.
183
+ */
184
+ dispose(): void;
185
+
186
+ // events
187
+ on(
188
+ eventName: "webcamstarted",
189
+ handler: (e: WebcamStartedEvent) => void,
190
+ ): void;
191
+ on(eventName: "webcamerror", handler: (e: WebcamErrorEvent) => void): void;
192
+ on(eventName: string, handler: (...args: any[]) => void): void;
193
+ }
194
+
195
+ /**
196
+ * Click handler/raycaster wrapper.
197
+ */
198
+ export class ClickHandler {
199
+ constructor(renderer: THREE.WebGLRenderer);
200
+
201
+ /**
202
+ * Cast a ray and return intersects with scene children.
203
+ */
204
+ raycast(camera: THREE.Camera, scene: THREE.Scene): THREE.Intersection[];
205
+ }
206
+
207
+ export interface DeviceOrientationControlsOptions {
208
+ /**
209
+ * 0 < k <= 1. Lower = more smoothing.
210
+ */
211
+ smoothingFactor?: number;
212
+ /**
213
+ * Show iOS permission dialog.
214
+ */
215
+ enablePermissionDialog?: boolean;
216
+ /**
217
+ * Apply inline styles on the created dialog.
218
+ */
219
+ enableStyling?: boolean;
220
+ /**
221
+ * Use `window.confirm(...)` instead of building DOM.
222
+ */
223
+ preferConfirmDialog?: boolean;
224
+ }
225
+
226
+ export interface DeviceOrientationGrantedEvent {
227
+ target: DeviceOrientationControls;
228
+ }
229
+
230
+ export interface DeviceOrientationErrorEvent {
231
+ code: string;
232
+ message: string;
233
+ error?: string;
234
+ }
235
+
236
+ export class DeviceOrientationControls extends THREE.EventDispatcher {
237
+ /**
238
+ * @param object usually a `THREE.Camera`
239
+ */
240
+ constructor(
241
+ object: THREE.Object3D,
242
+ options?: DeviceOrientationControlsOptions,
243
+ );
244
+
245
+ /**
246
+ * Initialize the device orientation controls. Should be called first to set up permission flow.
247
+ */
248
+ init(): void;
249
+
250
+ /**
251
+ * Begin listening to orientation + screenorientation (must be called after permission is granted on iOS).
252
+ */
253
+ connect(): void;
254
+
255
+ /**
256
+ * Stop listening.
257
+ */
258
+ disconnect(): void;
259
+
260
+ /**
261
+ * iOS: must be called in a user gesture to ask for perms.
262
+ */
263
+ requestOrientationPermissions(): void;
264
+
265
+ /**
266
+ * Create the DOM dialog (iOS-style) and wire it to requestOrientationPermissions.
267
+ */
268
+ createObtainPermissionGestureDialog(): void;
269
+
270
+ /**
271
+ * Choose `confirm()` vs DOM dialog.
272
+ */
273
+ obtainPermissionGesture(): void;
274
+
275
+ /**
276
+ * Call each frame.
277
+ */
278
+ update(args?: { theta?: number }): void;
279
+
280
+ /**
281
+ * iOS heading correction.
282
+ */
283
+ getCorrectedHeading(): number;
284
+
285
+ /**
286
+ * Provided in AR.js fix — forces re-evaluation of alpha offset.
287
+ */
288
+ updateAlphaOffset(): void;
289
+
290
+ /**
291
+ * Getters (radians).
292
+ */
293
+ getAlpha(): number;
294
+ getBeta(): number;
295
+ getGamma(): number;
296
+
297
+ dispose(): void;
298
+
299
+ on(
300
+ eventName: "deviceorientationgranted",
301
+ handler: (ev: DeviceOrientationGrantedEvent) => void,
302
+ ): void;
303
+ on(
304
+ eventName: "deviceorientationerror",
305
+ handler: (ev: DeviceOrientationErrorEvent) => void,
306
+ ): void;
307
+ on(eventName: string, handler: (...args: any[]) => void): void;
308
+
309
+ enabled: boolean;
310
+ }
311
+
312
+ /**
313
+ * Version string.
314
+ */
315
+ export const version: string;
316
+
317
+ /**
318
+ * Default export from build (UMD/ES) is the namespace with all the above.
319
+ * Modeled so that:
320
+ * `import * as LocAR from "locar"`
321
+ * and
322
+ * `import LocAR from "locar"``
323
+ * both typecheck.
324
+ */
325
+ const LocAR: {
326
+ EventEmitter: typeof EventEmitter;
327
+ SphMercProjection: typeof SphMercProjection;
328
+ LocationBased: typeof LocationBased;
329
+ Webcam: typeof Webcam;
330
+ ClickHandler: typeof ClickHandler;
331
+ DeviceOrientationControls: typeof DeviceOrientationControls;
332
+ version: string;
333
+ };
334
+
335
+ export default LocAR;
336
+ }
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "locar",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Location-based AR from AR.js.",
5
5
  "files": [
6
- "dist"
6
+ "dist",
7
+ "lib/types"
7
8
  ],
8
9
  "main": "./dist/locar.umd.js",
9
10
  "module": "./dist/locar.es.js",
11
+ "types": "lib/types/locar.d.ts",
10
12
  "exports": {
11
13
  ".": {
14
+ "types": "./lib/types/locar.d.ts",
12
15
  "import": "./dist/locar.es.js",
13
16
  "require": "./dist/locar.umd.js"
14
17
  }
@@ -23,7 +26,7 @@
23
26
  "@eslint/css": "^0.11.0",
24
27
  "@eslint/js": "^9.35.0",
25
28
  "@eslint/json": "^0.13.2",
26
- "eslint": "^9.35.0",
29
+ "eslint": "^9.38.0",
27
30
  "globals": "^16.4.0",
28
31
  "jsdoc": "^4.0.4",
29
32
  "prettier-eslint": "^16.4.2",